diff options
author | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
commit | 0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch) | |
tree | 276c4d099e178eb67fbd14f61d77b05e3808e9e3 /platform | |
parent | 0e49da1687bc8192ed210947da52c9e5c5f301bb (diff) |
GODOT IS OPEN SOURCE
Diffstat (limited to 'platform')
256 files changed, 39296 insertions, 0 deletions
diff --git a/platform/android/.old/SCsub b/platform/android/.old/SCsub new file mode 100644 index 0000000000..59e6d10b85 --- /dev/null +++ b/platform/android/.old/SCsub @@ -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]) diff --git a/platform/android/.old/audio_driver_android.cpp b/platform/android/.old/audio_driver_android.cpp new file mode 100644 index 0000000000..7e2d065d65 --- /dev/null +++ b/platform/android/.old/audio_driver_android.cpp @@ -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; +} + diff --git a/platform/android/.old/audio_driver_android.h b/platform/android/.old/audio_driver_android.h new file mode 100644 index 0000000000..08fbaa927e --- /dev/null +++ b/platform/android/.old/audio_driver_android.h @@ -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 diff --git a/platform/android/.old/context_gl_android.cpp b/platform/android/.old/context_gl_android.cpp new file mode 100644 index 0000000000..e48ed2e7f9 --- /dev/null +++ b/platform/android/.old/context_gl_android.cpp @@ -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() { + +}; + diff --git a/platform/android/.old/context_gl_android.h b/platform/android/.old/context_gl_android.h new file mode 100644 index 0000000000..bf09d08ad2 --- /dev/null +++ b/platform/android/.old/context_gl_android.h @@ -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 diff --git a/platform/android/.old/detect.py b/platform/android/.old/detect.py new file mode 100644 index 0000000000..c29b56aed4 --- /dev/null +++ b/platform/android/.old/detect.py @@ -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']) diff --git a/platform/android/.old/dir_access_jandroid.cpp b/platform/android/.old/dir_access_jandroid.cpp new file mode 100644 index 0000000000..649a25a902 --- /dev/null +++ b/platform/android/.old/dir_access_jandroid.cpp @@ -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();; +} diff --git a/platform/android/.old/dir_access_jandroid.h b/platform/android/.old/dir_access_jandroid.h new file mode 100644 index 0000000000..80973004aa --- /dev/null +++ b/platform/android/.old/dir_access_jandroid.h @@ -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 diff --git a/platform/android/.old/file_access_jandroid.cpp b/platform/android/.old/file_access_jandroid.cpp new file mode 100644 index 0000000000..96c064f9f5 --- /dev/null +++ b/platform/android/.old/file_access_jandroid.cpp @@ -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(); +} + + diff --git a/platform/android/.old/file_access_jandroid.h b/platform/android/.old/file_access_jandroid.h new file mode 100644 index 0000000000..7b432fc4fd --- /dev/null +++ b/platform/android/.old/file_access_jandroid.h @@ -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 diff --git a/platform/android/.old/gdb.setup.base b/platform/android/.old/gdb.setup.base new file mode 100644 index 0000000000..a622b1caf6 --- /dev/null +++ b/platform/android/.old/gdb.setup.base @@ -0,0 +1,3 @@ +set solib-search-path . +directory ../.. +file app_process diff --git a/platform/android/.old/java/AndroidManifest.xml b/platform/android/.old/java/AndroidManifest.xml new file mode 100644 index 0000000000..c05a681fe3 --- /dev/null +++ b/platform/android/.old/java/AndroidManifest.xml @@ -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> diff --git a/platform/android/.old/java/build.properties b/platform/android/.old/java/build.properties new file mode 100644 index 0000000000..ee52d86d94 --- /dev/null +++ b/platform/android/.old/java/build.properties @@ -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. + diff --git a/platform/android/.old/java/build.xml b/platform/android/.old/java/build.xml new file mode 100644 index 0000000000..6dc5eb874c --- /dev/null +++ b/platform/android/.old/java/build.xml @@ -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> diff --git a/platform/android/.old/java/default.properties b/platform/android/.old/java/default.properties new file mode 100644 index 0000000000..e2e8061f26 --- /dev/null +++ b/platform/android/.old/java/default.properties @@ -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 diff --git a/platform/android/.old/java/libs/armeabi/gdbserver b/platform/android/.old/java/libs/armeabi/gdbserver Binary files differnew file mode 100755 index 0000000000..39634a8147 --- /dev/null +++ b/platform/android/.old/java/libs/armeabi/gdbserver diff --git a/platform/android/.old/java/local.properties b/platform/android/.old/java/local.properties new file mode 100644 index 0000000000..18e2b6b0cb --- /dev/null +++ b/platform/android/.old/java/local.properties @@ -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 diff --git a/platform/android/.old/java/proguard.cfg b/platform/android/.old/java/proguard.cfg new file mode 100644 index 0000000000..12dd0392c0 --- /dev/null +++ b/platform/android/.old/java/proguard.cfg @@ -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 *; +} diff --git a/platform/android/.old/java/res/drawable-hdpi/icon.png b/platform/android/.old/java/res/drawable-hdpi/icon.png Binary files differnew file mode 100644 index 0000000000..8074c4c571 --- /dev/null +++ b/platform/android/.old/java/res/drawable-hdpi/icon.png diff --git a/platform/android/.old/java/res/drawable-ldpi/icon.png b/platform/android/.old/java/res/drawable-ldpi/icon.png Binary files differnew file mode 100644 index 0000000000..1095584ec2 --- /dev/null +++ b/platform/android/.old/java/res/drawable-ldpi/icon.png diff --git a/platform/android/.old/java/res/drawable-mdpi/icon.png b/platform/android/.old/java/res/drawable-mdpi/icon.png Binary files differnew file mode 100644 index 0000000000..a07c69fa5a --- /dev/null +++ b/platform/android/.old/java/res/drawable-mdpi/icon.png diff --git a/platform/android/.old/java/res/values/strings.xml b/platform/android/.old/java/res/values/strings.xml new file mode 100644 index 0000000000..f92332ad4c --- /dev/null +++ b/platform/android/.old/java/res/values/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot">Godot</string> +</resources> diff --git a/platform/android/.old/java/src/com/android/godot/Godot.java b/platform/android/.old/java/src/com/android/godot/Godot.java new file mode 100644 index 0000000000..475caf3362 --- /dev/null +++ b/platform/android/.old/java/src/com/android/godot/Godot.java @@ -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; + } + +} diff --git a/platform/android/.old/java/src/com/android/godot/GodotIO.java b/platform/android/.old/java/src/com/android/godot/GodotIO.java new file mode 100644 index 0000000000..a673f79b8d --- /dev/null +++ b/platform/android/.old/java/src/com/android/godot/GodotIO.java @@ -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>(); + + + } + + +} diff --git a/platform/android/.old/java/src/com/android/godot/GodotLib.java b/platform/android/.old/java/src/com/android/godot/GodotLib.java new file mode 100644 index 0000000000..2ef49a5222 --- /dev/null +++ b/platform/android/.old/java/src/com/android/godot/GodotLib.java @@ -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); +} diff --git a/platform/android/.old/java/src/com/android/godot/GodotView.java b/platform/android/.old/java/src/com/android/godot/GodotView.java new file mode 100644 index 0000000000..c5eb3d17ad --- /dev/null +++ b/platform/android/.old/java/src/com/android/godot/GodotView.java @@ -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. + } + } +} diff --git a/platform/android/.old/java_glue.cpp b/platform/android/.old/java_glue.cpp new file mode 100644 index 0000000000..38dbcc104f --- /dev/null +++ b/platform/android/.old/java_glue.cpp @@ -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(); diff --git a/platform/android/.old/java_glue.h b/platform/android/.old/java_glue.h new file mode 100644 index 0000000000..9940af3d7d --- /dev/null +++ b/platform/android/.old/java_glue.h @@ -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 diff --git a/platform/android/.old/os_android.cpp b/platform/android/.old/os_android.cpp new file mode 100644 index 0000000000..ecd87a3af9 --- /dev/null +++ b/platform/android/.old/os_android.cpp @@ -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() { + + +} diff --git a/platform/android/.old/os_android.h b/platform/android/.old/os_android.h new file mode 100644 index 0000000000..25ed84851a --- /dev/null +++ b/platform/android/.old/os_android.h @@ -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 diff --git a/platform/android/.old/platform_config.h b/platform/android/.old/platform_config.h new file mode 100644 index 0000000000..dad24432a5 --- /dev/null +++ b/platform/android/.old/platform_config.h @@ -0,0 +1 @@ +#include <alloca.h> diff --git a/platform/android/.old/run_debug.sh b/platform/android/.old/run_debug.sh new file mode 100755 index 0000000000..e39193bd55 --- /dev/null +++ b/platform/android/.old/run_debug.sh @@ -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 + + + + diff --git a/platform/android/AndroidManifest.xml.template b/platform/android/AndroidManifest.xml.template new file mode 100644 index 0000000000..208d24059e --- /dev/null +++ b/platform/android/AndroidManifest.xml.template @@ -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> diff --git a/platform/android/SCsub b/platform/android/SCsub new file mode 100644 index 0000000000..327b1ffe09 --- /dev/null +++ b/platform/android/SCsub @@ -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')) diff --git a/platform/android/android_native_app_glue.c b/platform/android/android_native_app_glue.c new file mode 100644 index 0000000000..965f6284cd --- /dev/null +++ b/platform/android/android_native_app_glue.c @@ -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 diff --git a/platform/android/android_native_app_glue.h b/platform/android/android_native_app_glue.h new file mode 100644 index 0000000000..fe8684b5d2 --- /dev/null +++ b/platform/android/android_native_app_glue.h @@ -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 diff --git a/platform/android/audio_driver_android.cpp b/platform/android/audio_driver_android.cpp new file mode 100644 index 0000000000..4f7b4ee348 --- /dev/null +++ b/platform/android/audio_driver_android.cpp @@ -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 diff --git a/platform/android/audio_driver_android.h b/platform/android/audio_driver_android.h new file mode 100644 index 0000000000..655772c772 --- /dev/null +++ b/platform/android/audio_driver_android.h @@ -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 diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp new file mode 100644 index 0000000000..6e3f6f9505 --- /dev/null +++ b/platform/android/audio_driver_jandroid.cpp @@ -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 diff --git a/platform/android/audio_driver_jandroid.h b/platform/android/audio_driver_jandroid.h new file mode 100644 index 0000000000..f102acf154 --- /dev/null +++ b/platform/android/audio_driver_jandroid.h @@ -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 diff --git a/platform/android/detect.py b/platform/android/detect.py new file mode 100644 index 0000000000..b89024a81a --- /dev/null +++ b/platform/android/detect.py @@ -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') } ) + diff --git a/platform/android/dir_access_android.cpp b/platform/android/dir_access_android.cpp new file mode 100644 index 0000000000..60bde61fc4 --- /dev/null +++ b/platform/android/dir_access_android.cpp @@ -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 diff --git a/platform/android/dir_access_android.h b/platform/android/dir_access_android.h new file mode 100644 index 0000000000..a6aead6eb3 --- /dev/null +++ b/platform/android/dir_access_android.h @@ -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 diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp new file mode 100644 index 0000000000..0c8a5785f8 --- /dev/null +++ b/platform/android/dir_access_jandroid.cpp @@ -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 diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h new file mode 100644 index 0000000000..b6e3fe393f --- /dev/null +++ b/platform/android/dir_access_jandroid.h @@ -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 diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp new file mode 100644 index 0000000000..a9b96a116b --- /dev/null +++ b/platform/android/export/export.cpp @@ -0,0 +1,1229 @@ +#include "version.h" +#include "export.h" +#include "tools/editor/editor_settings.h" +#include "tools/editor/editor_import_export.h" +#include "tools/editor/editor_node.h" +#include "io/zip_io.h" +#include "io/marshalls.h" +#include "globals.h" +#include "os/file_access.h" +#include "os/os.h" +#include "platform/android/logo.h" + +class EditorExportPlatformAndroid : public EditorExportPlatform { + + OBJ_TYPE( EditorExportPlatformAndroid,EditorExportPlatform ); + + String custom_release_package; + String custom_debug_package; + + int version_code; + String version_name; + String package; + String name; + String icon; + bool _signed; + int orientation; + + String release_keystore; + String release_username; + + struct APKExportData { + + zipFile apk; + EditorProgress *ep; + }; + + struct Device { + + String id; + String name; + String description; + }; + + Vector<Device> devices; + bool devices_changed; + Mutex *device_lock; + Thread *device_thread; + Ref<ImageTexture> logo; + + volatile bool quit_request; + + + static void _device_poll_thread(void *ud); + + String get_project_name() const; + void _fix_manifest(Vector<uint8_t>& p_manifest); + void _fix_resources(Vector<uint8_t>& p_manifest); + static Error save_apk_file(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total); + +protected: + + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + +public: + + virtual String get_name() const { return "Android"; } + virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_ETC1; } + virtual Ref<Texture> get_logo() const { return logo; } + + + virtual bool poll_devices(); + virtual int get_device_count() const; + virtual String get_device_name(int p_device) const; + virtual String get_device_info(int p_device) const; + virtual Error run(int p_device); + + virtual bool requieres_password(bool p_debug) const { return !p_debug; } + virtual String get_binary_extension() const { return "apk"; } + virtual Error export_project(const String& p_path,bool p_debug,const String& p_password=""); + + virtual bool can_export(String *r_error=NULL) const; + + EditorExportPlatformAndroid(); + ~EditorExportPlatformAndroid(); +}; + +bool EditorExportPlatformAndroid::_set(const StringName& p_name, const Variant& p_value) { + + String n=p_name; + + if (n=="version/code") + version_code=p_value; + else if (n=="version/name") + version_name=p_value; + else if (n=="package/unique_name") + package=p_value; + else if (n=="package/name") + name=p_value; + else if (n=="package/icon") + icon=p_value; + else if (n=="package/signed") + _signed=p_value; + else if (n=="screen/orientation") + orientation=p_value; + else if (n=="keystore/release") + release_keystore=p_value; + else if (n=="keystore/release_user") + release_username=p_value; + else + return false; + + return true; +} + +bool EditorExportPlatformAndroid::_get(const StringName& p_name,Variant &r_ret) const{ + + String n=p_name; + + if (n=="version/code") + r_ret=version_code; + else if (n=="version/name") + r_ret=version_name; + else if (n=="package/unique_name") + r_ret=package; + else if (n=="package/name") + r_ret=name; + else if (n=="package/icon") + r_ret=icon; + else if (n=="package/signed") + r_ret=_signed; + else if (n=="screen/orientation") + r_ret=orientation; + else if (n=="keystore/release") + r_ret=release_keystore; + else if (n=="keystore/release_user") + r_ret=release_username; + else + return false; + + return true; +} +void EditorExportPlatformAndroid::_get_property_list( List<PropertyInfo> *p_list) const{ + + p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/debug", PROPERTY_HINT_FILE,"apk")); + p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/release", PROPERTY_HINT_FILE,"apk")); + p_list->push_back( PropertyInfo( Variant::INT, "version/code", PROPERTY_HINT_RANGE,"1,65535,1")); + p_list->push_back( PropertyInfo( Variant::STRING, "version/name") ); + p_list->push_back( PropertyInfo( Variant::STRING, "package/unique_name") ); + p_list->push_back( PropertyInfo( Variant::STRING, "package/name") ); + p_list->push_back( PropertyInfo( Variant::STRING, "package/icon",PROPERTY_HINT_FILE,"png") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "package/signed") ); + p_list->push_back( PropertyInfo( Variant::INT, "screen/orientation",PROPERTY_HINT_ENUM,"Landscape,Portrait") ); + p_list->push_back( PropertyInfo( Variant::STRING, "keystore/release",PROPERTY_HINT_FILE,"keystore") ); + p_list->push_back( PropertyInfo( Variant::STRING, "keystore/release_user" ) ); + + //p_list->push_back( PropertyInfo( Variant::INT, "resources/pack_mode", PROPERTY_HINT_ENUM,"Copy,Single Exec.,Pack (.pck),Bundles (Optical)")); + +} + + +static String _parse_string(const uint8_t *p_bytes,bool p_utf8) { + + uint32_t offset=0; + uint32_t len = decode_uint16(&p_bytes[offset]); + + if (p_utf8) { + //don't know how to read extended utf8, this will have to be for now + len>>=8; + + } + offset+=2; + printf("len %i, unicode: %i\n",len,int(p_utf8)); + + if (p_utf8) { + + Vector<uint8_t> str8; + str8.resize(len+1); + for(int i=0;i<len;i++) { + str8[i]=p_bytes[offset+i]; + } + str8[len]=0; + String str; + str.parse_utf8((const char*)str8.ptr()); + return str; + } else { + + String str; + for(int i=0;i<len;i++) { + CharType c = decode_uint16(&p_bytes[offset+i*2]); + if (c==0) + break; + str += String::chr(c); + } + return str; + } + +} + +void EditorExportPlatformAndroid::_fix_resources(Vector<uint8_t>& p_manifest) { + + + const int UTF8_FLAG = 0x00000100; + print_line("*******************GORRRGLE***********************"); + + uint32_t header = decode_uint32(&p_manifest[0]); + uint32_t filesize = decode_uint32(&p_manifest[4]); + uint32_t string_block_len = decode_uint32(&p_manifest[16]); + uint32_t string_count = decode_uint32(&p_manifest[20]); + uint32_t string_flags = decode_uint32(&p_manifest[28]); + const uint32_t string_table_begins = 40; + + Vector<String> string_table; + + printf("stirng block len: %i\n",string_block_len); + printf("stirng count: %i\n",string_count); + printf("flags: %x\n",string_flags); + + for(int i=0;i<string_count;i++) { + + uint32_t offset = decode_uint32(&p_manifest[string_table_begins+i*4]); + offset+=string_table_begins+string_count*4; + + String str = _parse_string(&p_manifest[offset],string_flags&UTF8_FLAG); + + if (str.begins_with("godot-project-name")) { + + + if (str=="godot-project-name") { + //project name + str = get_project_name(); + + } else { + + String lang = str.substr(str.find_last("-")+1,str.length()).replace("-","_"); + String prop = "application/name_"+lang; + if (Globals::get_singleton()->has(prop)) { + str = Globals::get_singleton()->get(prop); + } else { + str = get_project_name(); + } + } + } + + string_table.push_back(str); + + } + + //write a new string table, but use 16 bits + Vector<uint8_t> ret; + ret.resize(string_table_begins+string_table.size()*4); + + for(int i=0;i<string_table_begins;i++) { + + ret[i]=p_manifest[i]; + } + + int ofs=0; + for(int i=0;i<string_table.size();i++) { + + encode_uint32(ofs,&ret[string_table_begins+i*4]); + ofs+=string_table[i].length()*2+2+2; + } + + ret.resize(ret.size()+ofs); + uint8_t *chars=&ret[ret.size()-ofs]; + for(int i=0;i<string_table.size();i++) { + + String s = string_table[i]; + encode_uint16(s.length(),chars); + chars+=2; + for(int j=0;j<s.length();j++) { + encode_uint16(s[j],chars); + chars+=2; + } + encode_uint16(0,chars); + chars+=2; + } + + //pad + while(ret.size()%4) + ret.push_back(0); + + //change flags to not use utf8 + encode_uint32(string_flags&~0x100,&ret[28]); + //change length + encode_uint32(ret.size()-12,&ret[16]); + //append the rest... + int rest_from = 12+string_block_len; + int rest_to = ret.size(); + int rest_len = (p_manifest.size() - rest_from); + ret.resize(ret.size() + (p_manifest.size() - rest_from) ); + for(int i=0;i<rest_len;i++) { + ret[rest_to+i]=p_manifest[rest_from+i]; + } + //finally update the size + encode_uint32(ret.size(),&ret[4]); + + + p_manifest=ret; + printf("end\n"); +} + +String EditorExportPlatformAndroid::get_project_name() const { + + String aname; + if (this->name!="") { + aname=this->name; + } else { + aname = Globals::get_singleton()->get("application/name"); + + } + + if (aname=="") { + aname=_MKSTR(VERSION_NAME); + } + + return aname; +} + + +void EditorExportPlatformAndroid::_fix_manifest(Vector<uint8_t>& p_manifest) { + + + const int CHUNK_AXML_FILE = 0x00080003; + const int CHUNK_RESOURCEIDS = 0x00080180; + const int CHUNK_STRINGS = 0x001C0001; + const int CHUNK_XML_END_NAMESPACE = 0x00100101; + const int CHUNK_XML_END_TAG = 0x00100103; + const int CHUNK_XML_START_NAMESPACE = 0x00100100; + const int CHUNK_XML_START_TAG = 0x00100102; + const int CHUNK_XML_TEXT = 0x00100104; + const int UTF8_FLAG = 0x00000100; + + Vector<String> string_table; + + uint32_t ofs=0; + + + uint32_t header = decode_uint32(&p_manifest[ofs]); + uint32_t filesize = decode_uint32(&p_manifest[ofs+4]); + ofs+=8; + +// print_line("FILESIZE: "+itos(filesize)+" ACTUAL: "+itos(p_manifest.size())); + + uint32_t string_count; + uint32_t styles_count; + uint32_t string_flags; + uint32_t string_data_offset; + + uint32_t styles_offset; + uint32_t string_table_begins; + uint32_t string_table_ends; + Vector<uint8_t> stable_extra; + + while(ofs < p_manifest.size()) { + + uint32_t chunk = decode_uint32(&p_manifest[ofs]); + uint32_t size = decode_uint32(&p_manifest[ofs+4]); + + + switch(chunk) { + + case CHUNK_STRINGS: { + + + int iofs=ofs+8; + + uint32_t string_count=decode_uint32(&p_manifest[iofs]); + uint32_t styles_count=decode_uint32(&p_manifest[iofs+4]); + uint32_t string_flags=decode_uint32(&p_manifest[iofs+8]); + uint32_t string_data_offset=decode_uint32(&p_manifest[iofs+12]); + uint32_t styles_offset=decode_uint32(&p_manifest[iofs+16]); +/* + printf("string count: %i\n",string_count); + printf("flags: %i\n",string_flags); + printf("sdata ofs: %i\n",string_data_offset); + printf("styles ofs: %i\n",styles_offset); +*/ + uint32_t st_offset=iofs+20; + string_table.resize(string_count); + uint32_t string_end=0; + + string_table_begins=st_offset; + + + for(int i=0;i<string_count;i++) { + + uint32_t string_at = decode_uint32(&p_manifest[st_offset+i*4]); + string_at+=st_offset+string_count*4; + + ERR_EXPLAIN("Unimplemented, can't read utf8 string table."); + ERR_FAIL_COND(string_flags&UTF8_FLAG); + + if (string_flags&UTF8_FLAG) { + + + + } else { + uint32_t len = decode_uint16(&p_manifest[string_at]); + Vector<CharType> ucstring; + ucstring.resize(len+1); + for(int j=0;j<len;j++) { + uint16_t c=decode_uint16(&p_manifest[string_at+2+2*j]); + ucstring[j]=c; + } + string_end=MAX(string_at+2+2*len,string_end); + ucstring[len]=0; + string_table[i]=ucstring.ptr(); + } + + +// print_line("String "+itos(i)+": "+string_table[i]); + } + + for(int i=string_end;i<(ofs+size);i++) { + stable_extra.push_back(p_manifest[i]); + } + +// printf("stable extra: %i\n",int(stable_extra.size())); + string_table_ends=ofs+size; + +// print_line("STABLE SIZE: "+itos(size)+" ACTUAL: "+itos(string_table_ends)); + + } break; + case CHUNK_XML_START_TAG: { + + int iofs=ofs+8; + uint32_t line=decode_uint32(&p_manifest[iofs]); + uint32_t nspace=decode_uint32(&p_manifest[iofs+8]); + uint32_t name=decode_uint32(&p_manifest[iofs+12]); + uint32_t check=decode_uint32(&p_manifest[iofs+16]); + + String tname=string_table[name]; + +// printf("NSPACE: %i\n",nspace); + //printf("NAME: %i (%s)\n",name,tname.utf8().get_data()); + //printf("CHECK: %x\n",check); + uint32_t attrcount=decode_uint32(&p_manifest[iofs+20]); + iofs+=28; + //printf("ATTRCOUNT: %x\n",attrcount); + for(int i=0;i<attrcount;i++) { + uint32_t attr_nspace=decode_uint32(&p_manifest[iofs]); + uint32_t attr_name=decode_uint32(&p_manifest[iofs+4]); + uint32_t attr_value=decode_uint32(&p_manifest[iofs+8]); + uint32_t attr_flags=decode_uint32(&p_manifest[iofs+12]); + uint32_t attr_resid=decode_uint32(&p_manifest[iofs+16]); + + + String value; + if (attr_value!=0xFFFFFFFF) + value=string_table[attr_value]; + else + value="Res #"+itos(attr_resid); + String attrname = string_table[attr_name]; + String nspace; + if (attr_nspace!=0xFFFFFFFF) + nspace=string_table[attr_nspace]; + else + nspace=""; + + printf("ATTR %i NSPACE: %i\n",i,attr_nspace); + printf("ATTR %i NAME: %i (%s)\n",i,attr_name,attrname.utf8().get_data()); + printf("ATTR %i VALUE: %i (%s)\n",i,attr_value,value.utf8().get_data()); + printf("ATTR %i FLAGS: %x\n",i,attr_flags); + printf("ATTR %i RESID: %x\n",i,attr_resid); + + //replace project information + if (tname=="manifest" && attrname=="package") { + + print_line("FOUND PACKAGE"); + string_table[attr_value]=package; + } + + //print_line("tname: "+tname); + //print_line("nspace: "+nspace); + //print_line("attrname: "+attrname); + if (tname=="manifest" && /*nspace=="android" &&*/ attrname=="versionCode") { + + print_line("FOUND versioncode"); + encode_uint32(version_code,&p_manifest[iofs+16]); + } + + + if (tname=="manifest" && /*nspace=="android" &&*/ attrname=="versionName") { + + print_line("FOUND versionname"); + if (attr_value==0xFFFFFFFF) { + WARN_PRINT("Version name in a resource, should be plaintext") + } else + string_table[attr_value]=version_name; + } + + if (tname=="activity" && /*nspace=="android" &&*/ attrname=="screenOrientation") { + + print_line("FOUND screen orientation"); + if (attr_value==0xFFFFFFFF) { + WARN_PRINT("Version name in a resource, should be plaintext") + } else { + string_table[attr_value]=(orientation==0?"landscape":"portrait"); + } + } + + if (tname=="application" && /*nspace=="android" &&*/ attrname=="label") { + + print_line("FOUND application"); + if (attr_value==0xFFFFFFFF) { + WARN_PRINT("Application name in a resource, should be plaintext.") + } else { + + String aname = get_project_name(); + string_table[attr_value]=aname; + } + } + if (tname=="activity" && /*nspace=="android" &&*/ attrname=="label") { + + print_line("FOUND activity name"); + if (attr_value==0xFFFFFFFF) { + WARN_PRINT("Activity name in a resource, should be plaintext") + } else { + String aname; + if (this->name!="") { + aname=this->name; + } else { + aname = Globals::get_singleton()->get("application/name"); + + } + + if (aname=="") { + aname=_MKSTR(VERSION_NAME); + } + + print_line("APP NAME IS..."+aname); + string_table[attr_value]=aname; + } + } + + iofs+=20; + } + + } break; + } + printf("chunk %x: size: %d\n",chunk,size); + + ofs+=size; + } + + printf("end\n"); + + //create new andriodmanifest binary + + Vector<uint8_t> ret; + ret.resize(string_table_begins+string_table.size()*4); + + for(int i=0;i<string_table_begins;i++) { + + ret[i]=p_manifest[i]; + } + + ofs=0; + for(int i=0;i<string_table.size();i++) { + + encode_uint32(ofs,&ret[string_table_begins+i*4]); + ofs+=string_table[i].length()*2+2+2; + print_line("ofs: "+itos(i)+": "+itos(ofs)); + } + ret.resize(ret.size()+ofs); + uint8_t *chars=&ret[ret.size()-ofs]; + for(int i=0;i<string_table.size();i++) { + + String s = string_table[i]; + print_line("savint string :"+s); + encode_uint16(s.length(),chars); + chars+=2; + for(int j=0;j<s.length();j++) { //include zero? + encode_uint16(s[j],chars); + chars+=2; + } + encode_uint16(0,chars); + chars+=2; + + } + + + ret.resize(ret.size()+stable_extra.size()); + while(ret.size()%4) + ret.push_back(0); + + for(int i=0;i<stable_extra.size();i++) { + + chars[i]=stable_extra[i]; + } + + + uint32_t new_stable_end=ret.size(); + + uint32_t extra = (p_manifest.size()-string_table_ends); + ret.resize(new_stable_end + extra); + for(int i=0;i<extra;i++) + ret[new_stable_end+i]=p_manifest[string_table_ends+i]; + + while(ret.size()%4) + ret.push_back(0); + encode_uint32(ret.size(),&ret[4]); //update new file size + + encode_uint32(new_stable_end-8,&ret[12]); //update new string table size + + print_line("file size: "+itos(ret.size())); + + p_manifest=ret; + + + + + + +#if 0 + uint32_t header[9]; + for(int i=0;i<9;i++) { + header[i]=decode_uint32(&p_manifest[i*4]); + } + + print_line("STO: "+itos(header[3])); + uint32_t st_offset=9*4; + //ERR_FAIL_COND(header[3]!=0x24) + uint32_t string_count=header[4]; + + + string_table.resize(string_count); + + for(int i=0;i<string_count;i++) { + + uint32_t string_at = decode_uint32(&p_manifest[st_offset+i*4]); + string_at+=st_offset+string_count*4; + uint32_t len = decode_uint16(&p_manifest[string_at]); + Vector<CharType> ucstring; + ucstring.resize(len+1); + for(int j=0;j<len;j++) { + uint16_t c=decode_uint16(&p_manifest[string_at+2+2*j]); + ucstring[j]=c; + } + ucstring[len]=0; + string_table[i]=ucstring.ptr(); + } + + +#endif + +} + + + +Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total) { + + APKExportData *ed=(APKExportData*)p_userdata; + String dst_path=p_path; + dst_path=dst_path.replace_first("res://","assets/"); + + zipOpenNewFileInZip(ed->apk, + dst_path.utf8().get_data(), + NULL, + NULL, + 0, + NULL, + 0, + NULL, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + + zipWriteInFileInZip(ed->apk,p_data.ptr(),p_data.size()); + zipCloseFileInZip(ed->apk); + ed->ep->step("File: "+p_path,3+p_file*100/p_total); + return OK; + +} + + + +Error EditorExportPlatformAndroid::export_project(const String& p_path,bool p_debug,const String& p_password) { + + String src_apk; + + EditorProgress ep("export","Exporting for Android",104); + + String apk_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + if (p_debug) { + + src_apk=custom_debug_package!=""?custom_debug_package:apk_path+"android_debug.apk"; + } else { + + src_apk=custom_release_package!=""?custom_release_package:apk_path+"android_release.apk"; + + } + + + FileAccess *src_f=NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + + + ep.step("Creating APK",0); + + unzFile pkg = unzOpen2(src_apk.utf8().get_data(), &io); + if (!pkg) { + + EditorNode::add_io_error("Could not find template APK to export:\n"+src_apk); + return ERR_FILE_NOT_FOUND; + } + + ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(pkg); + + zlib_filefunc_def io2=io; + FileAccess *dst_f=NULL; + io2.opaque=&dst_f; + zipFile apk=zipOpen2(p_path.utf8().get_data(),APPEND_STATUS_CREATE,NULL,&io2); + + + while(ret==UNZ_OK) { + + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(pkg,&info,fname,16384,NULL,0,NULL,0); + + String file=fname; + + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg,data.ptr(),data.size()); + unzCloseCurrentFile(pkg); + + //write + + if (file=="AndroidManifest.xml") { + + _fix_manifest(data); + } + + if (file=="resources.arsc") { + + _fix_resources(data); + } + + if (file=="res/drawable/icon.png") { + bool found=false; + + if (this->icon!="" && this->icon.ends_with(".png")) { + + FileAccess *f = FileAccess::open(this->icon,FileAccess::READ); + if (f) { + + data.resize(f->get_len()); + f->get_buffer(data.ptr(),data.size()); + memdelete(f); + found=true; + } + + } + + if (!found) { + + String appicon = Globals::get_singleton()->get("application/icon"); + if (appicon!="" && appicon.ends_with(".png")) { + FileAccess*f = FileAccess::open(appicon,FileAccess::READ); + if (f) { + data.resize(f->get_len()); + f->get_buffer(data.ptr(),data.size()); + memdelete(f); + } + } + } + } + + print_line("ADDING: "+file); + zipOpenNewFileInZip(apk, + file.utf8().get_data(), + NULL, + NULL, + 0, + NULL, + 0, + NULL, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + zipWriteInFileInZip(apk,data.ptr(),data.size()); + zipCloseFileInZip(apk); + + ret = unzGoToNextFile(pkg); + } + + + ep.step("Adding Files..",1); + + + + APKExportData ed; + ed.ep=&ep; + ed.apk=apk; + + Error err = export_project_files(save_apk_file,&ed,false); + + zipClose(apk,NULL); + unzClose(pkg); + + if (err) { + return err; + } + + + + if (_signed) { + + + String jarsigner=EditorSettings::get_singleton()->get("android/jarsigner"); + if (!FileAccess::exists(jarsigner)) { + EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the editor settings.\nResulting apk is unsigned."); + return OK; + } + + String keystore; + String password; + String user; + if (p_debug) { + keystore=EditorSettings::get_singleton()->get("android/debug_keystore"); + password=EditorSettings::get_singleton()->get("android/debug_keystore_pass"); + user=EditorSettings::get_singleton()->get("android/debug_keystore_user"); + + ep.step("Signing Debug APK..",103); + + } else { + keystore=release_keystore; + password=p_password; + user=release_username; + + ep.step("Signing Release APK..",103); + + } + + if (!FileAccess::exists(keystore)) { + EditorNode::add_io_error("Could not find keytore, unable to export."); + return ERR_FILE_CANT_OPEN; + } + + List<String> args; + args.push_back("-digestalg"); + args.push_back("SHA1"); + args.push_back("-sigalg"); + args.push_back("MD5withRSA"); + args.push_back("-verbose"); + args.push_back("-keystore"); + args.push_back(keystore); + args.push_back("-storepass"); + args.push_back(password); + args.push_back(p_path); + args.push_back(user); + int retval; + int err = OS::get_singleton()->execute(jarsigner,args,true,NULL,NULL,&retval); + if (retval) { + EditorNode::add_io_error("'jarsigner' returned with error #"+itos(retval)); + return ERR_CANT_CREATE; + } + + ep.step("Verifying APK..",104); + + args.clear(); + args.push_back("-verify"); + args.push_back(p_path); + args.push_back("-verbose"); + + err = OS::get_singleton()->execute(jarsigner,args,true,NULL,NULL,&retval); + if (retval) { + EditorNode::add_io_error("'jarsigner' verificaiton of APK failed. Make sure to use jarsigner from Java 6."); + return ERR_CANT_CREATE; + } + + } + return OK; + +} + + +bool EditorExportPlatformAndroid::poll_devices() { + + bool dc=devices_changed; + devices_changed=false; + return dc; +} + +int EditorExportPlatformAndroid::get_device_count() const { + + device_lock->lock(); + int dc=devices.size(); + device_lock->unlock(); + + return dc; + +} +String EditorExportPlatformAndroid::get_device_name(int p_device) const { + + ERR_FAIL_INDEX_V(p_device,devices.size(),""); + device_lock->lock(); + String s=devices[p_device].name; + device_lock->unlock(); + return s; +} +String EditorExportPlatformAndroid::get_device_info(int p_device) const { + + ERR_FAIL_INDEX_V(p_device,devices.size(),""); + device_lock->lock(); + String s=devices[p_device].description; + device_lock->unlock(); + return s; +} + +void EditorExportPlatformAndroid::_device_poll_thread(void *ud) { + + EditorExportPlatformAndroid *ea=(EditorExportPlatformAndroid *)ud; + + + while(!ea->quit_request) { + + String adb=EditorSettings::get_singleton()->get("android/adb"); + if (!FileAccess::exists(adb)) { + OS::get_singleton()->delay_usec(3000000); + continue; //adb not configured + } + + String devices; + List<String> args; + args.push_back("devices"); + int ec; + Error err = OS::get_singleton()->execute(adb,args,true,NULL,&devices,&ec); + Vector<String> ds = devices.split("\n"); + Vector<String> ldevices; + for(int i=1;i<ds.size();i++) { + + String d = ds[i]; + int dpos = d.find("device"); + if (dpos==-1) + continue; + d=d.substr(0,dpos).strip_edges(); +// print_line("found devuce: "+d); + ldevices.push_back(d); + } + + ea->device_lock->lock(); + + bool different=false; + + if (devices.size()!=ldevices.size()) { + + different=true; + } else { + + for(int i=0;i<ea->devices.size();i++) { + + if (ea->devices[i].id!=ldevices[i]) { + different=true; + break; + } + } + } + + if (different) { + + + Vector<Device> ndevices; + + for(int i=0;i<ldevices.size();i++) { + + Device d; + d.id=ldevices[i]; + for(int j=0;j<ea->devices.size();j++) { + if (ea->devices[j].id==ldevices[i]) { + d.description=ea->devices[j].description; + d.name=ea->devices[j].name; + } + } + + if (d.description=="") { + //in the oven, request! + args.clear(); + args.push_back("-s"); + args.push_back(d.id); + args.push_back("shell"); + args.push_back("cat"); + args.push_back("/system/build.prop"); + int ec; + String dp; + + Error err = OS::get_singleton()->execute(adb,args,true,NULL,&dp,&ec); + print_line("RV: "+itos(ec)); + Vector<String> props = dp.split("\n"); + String vendor; + String device; + d.description+"Device ID: "+d.id+"\n"; + for(int j=0;j<props.size();j++) { + + String p = props[j]; + if (p.begins_with("ro.product.model=")) { + device=p.get_slice("=",1).strip_edges(); + } else if (p.begins_with("ro.product.brand=")) { + vendor=p.get_slice("=",1).strip_edges().capitalize(); + } else if (p.begins_with("ro.build.display.id=")) { + d.description+="Build: "+p.get_slice("=",1).strip_edges()+"\n"; + } else if (p.begins_with("ro.build.version.release=")) { + d.description+="Release: "+p.get_slice("=",1).strip_edges()+"\n"; + } else if (p.begins_with("ro.product.cpu.abi=")) { + d.description+="CPU: "+p.get_slice("=",1).strip_edges()+"\n"; + } else if (p.begins_with("ro.product.manufacturer=")) { + d.description+="Manufacturer: "+p.get_slice("=",1).strip_edges()+"\n"; + } else if (p.begins_with("ro.board.platform=")) { + d.description+="Chipset: "+p.get_slice("=",1).strip_edges()+"\n"; + } else if (p.begins_with("ro.opengles.version=")) { + uint32_t opengl = p.get_slice("=",1).to_int(); + d.description+="OpenGL: "+itos(opengl>>16)+"."+itos((opengl>>8)&0xFF)+"."+itos((opengl)&0xFF)+"\n"; + } + } + + d.name=vendor+" "+device; +// print_line("name: "+d.name); +// print_line("description: "+d.description); + + } + + ndevices.push_back(d); + + } + + ea->devices=ndevices; + ea->devices_changed=true; + } + + ea->device_lock->unlock(); + + OS::get_singleton()->delay_usec(3000000); + } + +} + +Error EditorExportPlatformAndroid::run(int p_device) { + + ERR_FAIL_INDEX_V(p_device,devices.size(),ERR_INVALID_PARAMETER); + device_lock->lock(); + + EditorProgress ep("run","Running on "+devices[p_device].name,3); + + String adb=EditorSettings::get_singleton()->get("android/adb"); + if (adb=="") { + + EditorNode::add_io_error("ADB executable not configured in settings, can't run."); + device_lock->unlock(); + return ERR_UNCONFIGURED; + } + + //export_temp + ep.step("Exporting APK",0); + + String export_to=EditorSettings::get_singleton()->get_settings_path()+"/tmp/tmpexport.apk"; + Error err = export_project(export_to,true); + if (err) { + device_lock->unlock(); + return err; + } + + ep.step("Uninstalling..",1); + + print_line("Uninstalling previous version: "+devices[p_device].name); + List<String> args; + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("uninstall"); + args.push_back(package); + int rv; + err = OS::get_singleton()->execute(adb,args,true,NULL,NULL,&rv); +#if 0 + if (err || rv!=0) { + EditorNode::add_io_error("Could not install to device."); + device_lock->unlock(); + return ERR_CANT_CREATE; + } +#endif + print_line("Installing into device (please wait..): "+devices[p_device].name); + ep.step("Installing to Device (please wait..)..",2); + + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("install"); + args.push_back(export_to); + rv; + err = OS::get_singleton()->execute(adb,args,true,NULL,NULL,&rv); + if (err || rv!=0) { + EditorNode::add_io_error("Could not install to device."); + device_lock->unlock(); + return ERR_CANT_CREATE; + } + + ep.step("Running on Device..",3); + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("shell"); + args.push_back("am"); + args.push_back("start"); + args.push_back("-a"); + args.push_back("android.intent.action.MAIN"); + args.push_back("-n"); + args.push_back(package+"/com.android.godot.Godot"); + + err = OS::get_singleton()->execute(adb,args,true,NULL,NULL,&rv); + if (err || rv!=0) { + EditorNode::add_io_error("Could not execute ondevice."); + device_lock->unlock(); + return ERR_CANT_CREATE; + } + device_lock->unlock(); + return OK; +} + + +EditorExportPlatformAndroid::EditorExportPlatformAndroid() { + + version_code=1; + version_name="1.0"; + package="com.android.noname"; + name=""; + _signed=true; + device_lock = Mutex::create(); + quit_request=false; + orientation=0; + + device_thread=Thread::create(_device_poll_thread,this); + devices_changed=true; + + Image img( _android_logo ); + logo = Ref<ImageTexture>( memnew( ImageTexture )); + logo->create_from_image(img); +} + +bool EditorExportPlatformAndroid::can_export(String *r_error) const { + + bool valid=true; + String adb=EditorSettings::get_singleton()->get("android/adb"); + String err; + + if (!FileAccess::exists(adb)) { + + valid=false; + err+="ADB executable not configured in editor settings.\n"; + } + + String js = EditorSettings::get_singleton()->get("android/jarsigner"); + + if (!FileAccess::exists(js)) { + + valid=false; + err+="OpenJDK 6 jarsigner not configured in editor settings.\n"; + } + + String dk = EditorSettings::get_singleton()->get("android/debug_keystore"); + + if (!FileAccess::exists(dk)) { + + valid=false; + err+="Debug Keystore not configured in editor settings.\n"; + } + + + String exe_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + if (!FileAccess::exists(exe_path+"android_debug.apk") || !FileAccess::exists(exe_path+"android_release.apk")) { + valid=false; + err+="No export templates found.\nDownload and install export templates.\n"; + } + + if (custom_debug_package!="" && !FileAccess::exists(custom_debug_package)) { + valid=false; + err+="Custom debug package not found.\n"; + } + + if (custom_release_package!="" && !FileAccess::exists(custom_release_package)) { + valid=false; + err+="Custom release package not found.\n"; + } + + if (r_error) + *r_error=err; + + return valid; +} + + +EditorExportPlatformAndroid::~EditorExportPlatformAndroid() { + + quit_request=true; + Thread::wait_to_finish(device_thread); +} + + +void register_android_exporter() { + + String exe_ext=OS::get_singleton()->get_name()=="Windows"?"exe":""; + EDITOR_DEF("android/adb",""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"android/adb",PROPERTY_HINT_GLOBAL_FILE,exe_ext)); + EDITOR_DEF("android/jarsigner",""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"android/jarsigner",PROPERTY_HINT_GLOBAL_FILE,exe_ext)); + EDITOR_DEF("android/debug_keystore",""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"android/debug_keystore",PROPERTY_HINT_GLOBAL_FILE,"keystore")); + EDITOR_DEF("android/debug_keystore_user","androiddebugkey"); + EDITOR_DEF("android/debug_keystore_pass","android"); + //EDITOR_DEF("android/release_keystore",""); + //EDITOR_DEF("android/release_username",""); + //EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"android/release_keystore",PROPERTY_HINT_GLOBAL_FILE,"*.keystore")); + + Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>( memnew(EditorExportPlatformAndroid) ); + EditorImportExport::get_singleton()->add_export_platform(exporter); + + +} + diff --git a/platform/android/export/export.h b/platform/android/export/export.h new file mode 100644 index 0000000000..88581802b8 --- /dev/null +++ b/platform/android/export/export.h @@ -0,0 +1,3 @@ + + +void register_android_exporter(); diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp new file mode 100644 index 0000000000..f1a2bf5882 --- /dev/null +++ b/platform/android/file_access_android.cpp @@ -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 diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h new file mode 100644 index 0000000000..080ab1a6ce --- /dev/null +++ b/platform/android/file_access_android.h @@ -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 diff --git a/platform/android/file_access_jandroid.cpp b/platform/android/file_access_jandroid.cpp new file mode 100644 index 0000000000..e1dec4f601 --- /dev/null +++ b/platform/android/file_access_jandroid.cpp @@ -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 diff --git a/platform/android/file_access_jandroid.h b/platform/android/file_access_jandroid.h new file mode 100644 index 0000000000..3cbeab5ffc --- /dev/null +++ b/platform/android/file_access_jandroid.h @@ -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 diff --git a/platform/android/globals/global_defaults.cpp b/platform/android/globals/global_defaults.cpp new file mode 100644 index 0000000000..c6f852a592 --- /dev/null +++ b/platform/android/globals/global_defaults.cpp @@ -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")); +} diff --git a/platform/android/globals/global_defaults.h b/platform/android/globals/global_defaults.h new file mode 100644 index 0000000000..64eb26c482 --- /dev/null +++ b/platform/android/globals/global_defaults.h @@ -0,0 +1,3 @@ + + +void register_android_global_defaults();
\ No newline at end of file diff --git a/platform/android/godot_android.cpp b/platform/android/godot_android.cpp new file mode 100644 index 0000000000..673ff91641 --- /dev/null +++ b/platform/android/godot_android.cpp @@ -0,0 +1,993 @@ +/*************************************************************************/ +/* godot_android.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifdef ANDROID_NATIVE_ACTIVITY + +#include <jni.h> +#include <errno.h> + +#include <EGL/egl.h> +#include <GLES2/gl2.h> + +#include <android/sensor.h> +#include <android/window.h> +#include <android/log.h> +#include <android_native_app_glue.h> +#include "file_access_android.h" +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include "os_android.h" +#include "globals.h" +#include "main/main.h" +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "godot", __VA_ARGS__)) +#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "godot", __VA_ARGS__)) + + +extern "C" { + JNIEXPORT void JNICALL Java_com_android_godot_Godot_registerSingleton(JNIEnv * env, jobject obj, jstring name,jobject p_object); + JNIEXPORT void JNICALL Java_com_android_godot_Godot_registerMethod(JNIEnv * env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args); + JNIEXPORT jstring JNICALL Java_com_android_godot_Godot_getGlobal(JNIEnv * env, jobject obj, jstring path); +}; + +class JNISingleton : public Object { + + OBJ_TYPE( JNISingleton, Object ); + + + struct MethodData { + + + jmethodID method; + Variant::Type ret_type; + Vector<Variant::Type> argtypes; + }; + + jobject instance; + Map<StringName,MethodData> method_map; + JNIEnv *env; + +public: + + void update_env(JNIEnv *p_env) { env=p_env; } + + virtual Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error) { + + print_line("attempt to call "+String(p_method)); + + r_error.error=Variant::CallError::CALL_OK; + + Map<StringName,MethodData >::Element *E=method_map.find(p_method); + if (!E) { + + print_line("no exists"); + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } + + + int ac = E->get().argtypes.size(); + if (ac<p_argcount) { + + print_line("fewargs"); + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=ac; + return Variant(); + } + + if (ac>p_argcount) { + + print_line("manyargs"); + r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument=ac; + return Variant(); + } + + + + for(int i=0;i<p_argcount;i++) { + + if (!Variant::can_convert(p_args[i]->get_type(),E->get().argtypes[i])) { + + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=i; + r_error.expected=E->get().argtypes[i]; + } + } + + + jvalue *v=NULL; + + if (p_argcount) { + + v=(jvalue*)alloca( sizeof(jvalue)*p_argcount ); + } + + for(int i=0;i<p_argcount;i++) { + + + switch(E->get().argtypes[i]) { + + case Variant::BOOL: { + + v[i].z=*p_args[i]; + } break; + case Variant::INT: { + + v[i].i=*p_args[i]; + } break; + case Variant::REAL: { + + v[i].f=*p_args[i]; + } break; + case Variant::STRING: { + + String s = *p_args[i]; + jstring jStr = env->NewStringUTF(s.utf8().get_data()); + v[i].l=jStr; + } break; + case Variant::STRING_ARRAY: { + + DVector<String> sarray = *p_args[i]; + jobjectArray arr = env->NewObjectArray(sarray.size(),env->FindClass("java/lang/String"),env->NewStringUTF("")); + + for(int j=0;j<sarray.size();j++) { + + env->SetObjectArrayElement(arr,j,env->NewStringUTF( sarray[i].utf8().get_data() )); + } + v[i].l=arr; + + } break; + case Variant::INT_ARRAY: { + + DVector<int> array = *p_args[i]; + jintArray arr = env->NewIntArray(array.size()); + DVector<int>::Read r = array.read(); + env->SetIntArrayRegion(arr,0,array.size(),r.ptr()); + v[i].l=arr; + + } break; + case Variant::REAL_ARRAY: { + + DVector<float> array = *p_args[i]; + jfloatArray arr = env->NewFloatArray(array.size()); + DVector<float>::Read r = array.read(); + env->SetFloatArrayRegion(arr,0,array.size(),r.ptr()); + v[i].l=arr; + + } break; + default: { + + ERR_FAIL_V(Variant()); + } break; + + } + } + + print_line("calling method!!"); + + Variant ret; + + switch(E->get().ret_type) { + + case Variant::NIL: { + + + print_line("call void"); + env->CallVoidMethodA(instance,E->get().method,v); + } break; + case Variant::BOOL: { + + ret = env->CallBooleanMethodA(instance,E->get().method,v); + print_line("call bool"); + } break; + case Variant::INT: { + + ret = env->CallIntMethodA(instance,E->get().method,v); + print_line("call int"); + } break; + case Variant::REAL: { + + ret = env->CallFloatMethodA(instance,E->get().method,v); + } break; + case Variant::STRING: { + + jobject o = env->CallObjectMethodA(instance,E->get().method,v); + String singname = env->GetStringUTFChars((jstring)o, NULL ); + } break; + case Variant::STRING_ARRAY: { + + jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance,E->get().method,v); + + int stringCount = env->GetArrayLength(arr); + DVector<String> sarr; + + for (int i=0; i<stringCount; i++) { + jstring string = (jstring) env->GetObjectArrayElement(arr, i); + const char *rawString = env->GetStringUTFChars(string, 0); + sarr.push_back(String(rawString)); + } + + ret=sarr; + + } break; + case Variant::INT_ARRAY: { + + jintArray arr = (jintArray)env->CallObjectMethodA(instance,E->get().method,v); + + int fCount = env->GetArrayLength(arr); + DVector<int> sarr; + sarr.resize(fCount); + + DVector<int>::Write w = sarr.write(); + env->GetIntArrayRegion(arr,0,fCount,w.ptr()); + w = DVector<int>::Write(); + ret=sarr; + } break; + case Variant::REAL_ARRAY: { + + jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance,E->get().method,v); + + int fCount = env->GetArrayLength(arr); + DVector<float> sarr; + sarr.resize(fCount); + + DVector<float>::Write w = sarr.write(); + env->GetFloatArrayRegion(arr,0,fCount,w.ptr()); + w = DVector<float>::Write(); + ret=sarr; + } break; + default: { + + + print_line("failure.."); + ERR_FAIL_V(Variant()); + } break; + } + + print_line("success"); + + return ret; + } + + + jobject get_instance() const { + + return instance; + } + void set_instance(jobject p_instance) { + + instance=p_instance; + } + + + void add_method(const StringName& p_name, jmethodID p_method,const Vector<Variant::Type>& p_args, Variant::Type p_ret_type) { + + MethodData md; + md.method=p_method; + md.argtypes=p_args; + md.ret_type=p_ret_type; + method_map[p_name]=md; + + } + + + JNISingleton() {} + +}; + +//JNIEnv *JNISingleton::env=NULL; + +static HashMap<String,JNISingleton*> jni_singletons; + + +struct engine { + struct android_app* app; + OS_Android *os; + JNIEnv *jni; + + ASensorManager* sensorManager; + const ASensor* accelerometerSensor; + ASensorEventQueue* sensorEventQueue; + + bool display_active; + bool requested_quit; + int animating; + EGLDisplay display; + EGLSurface surface; + EGLContext context; + int32_t width; + int32_t height; + +}; + +/** + * Initialize an EGL context for the current display. + */ +static int engine_init_display(struct engine* engine,bool p_gl2) { + // initialize OpenGL ES and EGL + + /* + * Here specify the attributes of the desired configuration. + * Below, we select an EGLConfig with at least 8 bits per color + * component compatible with on-screen windows + */ + const EGLint gl2_attribs[] = { + // EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_BLUE_SIZE, 4, + EGL_GREEN_SIZE, 4, + EGL_RED_SIZE, 4, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, EGL_DONT_CARE, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + const EGLint gl1_attribs[] = { + // EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_BLUE_SIZE, 4, + EGL_GREEN_SIZE, 4, + EGL_RED_SIZE, 4, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, EGL_DONT_CARE, + EGL_NONE + }; + + const EGLint *attribs=p_gl2?gl2_attribs:gl1_attribs; + + + EGLint w, h, dummy, format; + EGLint numConfigs; + EGLConfig config; + EGLSurface surface; + EGLContext context; + + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + eglInitialize(display, 0, 0); + + + /* Here, the application chooses the configuration it desires. In this + * sample, we have a very simplified selection process, where we pick + * the first EGLConfig that matches our criteria */ + + eglChooseConfig(display, attribs, &config, 1, &numConfigs); + + LOGI("Num configs: %i\n",numConfigs); + + /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is + * guaranteed to be accepted by ANativeWindow_setBuffersGeometry(). + * As soon as we picked a EGLConfig, we can safely reconfigure the + * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */ + eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format); + + ANativeWindow_setBuffersGeometry(engine->app->window, 0, 0, format); + //ANativeWindow_setFlags(engine->app->window, 0, 0, format|); + + surface = eglCreateWindowSurface(display, config, engine->app->window, NULL); + + const EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION,2, + EGL_NONE + }; + context = eglCreateContext(display, config, EGL_NO_CONTEXT, p_gl2?context_attribs:NULL); + + if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { + LOGW("Unable to eglMakeCurrent"); + return -1; + } + + eglQuerySurface(display, surface, EGL_WIDTH, &w); + eglQuerySurface(display, surface, EGL_HEIGHT, &h); + print_line("INIT VIDEO MODE: "+itos(w)+","+itos(h)); + + //engine->os->set_egl_extensions(eglQueryString(display,EGL_EXTENSIONS)); + engine->os->init_video_mode(w,h); + + + engine->display = display; + engine->context = context; + engine->surface = surface; + engine->width = w; + engine->height = h; + engine->display_active=true; + + //engine->state.angle = 0; + + // Initialize GL state. + //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + glEnable(GL_CULL_FACE); + // glShadeModel(GL_SMOOTH); + glDisable(GL_DEPTH_TEST); + LOGI("GL Version: %s - %s %s\n", glGetString(GL_VERSION),glGetString(GL_VENDOR), glGetString(GL_RENDERER)); + + return 0; +} + + +static void engine_draw_frame(struct engine* engine) { + if (engine->display == NULL) { + // No display. + return; + } + + // Just fill the screen with a color. + //glClearColor(0,1,0,1); + //glClear(GL_COLOR_BUFFER_BIT); + if (engine->os && engine->os->main_loop_iterate()==true) { + + engine->requested_quit=true; + return; //should exit instead + } + + eglSwapBuffers(engine->display, engine->surface); +} + + +static void engine_term_display(struct engine* engine) { + if (engine->display != EGL_NO_DISPLAY) { + eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (engine->context != EGL_NO_CONTEXT) { + eglDestroyContext(engine->display, engine->context); + } + if (engine->surface != EGL_NO_SURFACE) { + eglDestroySurface(engine->display, engine->surface); + } + eglTerminate(engine->display); + } + + engine->animating = 0; + engine->display = EGL_NO_DISPLAY; + engine->context = EGL_NO_CONTEXT; + engine->surface = EGL_NO_SURFACE; + engine->display_active=false; + +} + +/** + * Process the next input event. + */ +static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { + struct engine* engine = (struct engine*)app->userData; + + if (!engine->os) + return 0; + + switch(AInputEvent_getType(event)) { + + case AINPUT_EVENT_TYPE_KEY: { + + int ac = AKeyEvent_getAction(event); + switch(ac) { + + case AKEY_EVENT_ACTION_DOWN: { + + int32_t code = AKeyEvent_getKeyCode(event); + if (code==AKEYCODE_BACK) { + + //AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled); + if (engine->os) + engine->os->main_loop_request_quit(); + return 1; + + } + + + } break; + case AKEY_EVENT_ACTION_UP: { + + + } break; + } + + + } break; + case AINPUT_EVENT_TYPE_MOTION: { + + + Vector<OS_Android::TouchPos> touchvec; + + int pc = AMotionEvent_getPointerCount(event); + + touchvec.resize(pc); + + for(int i=0;i<pc;i++) { + + touchvec[i].pos.x=AMotionEvent_getX(event,i); + touchvec[i].pos.y=AMotionEvent_getY(event,i); + touchvec[i].id=AMotionEvent_getPointerId(event,i); + } + + + //System.out.printf("gaction: %d\n",event.getAction()); + int pidx=(AMotionEvent_getAction(event)&AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)>>8; + switch(AMotionEvent_getAction(event)&AMOTION_EVENT_ACTION_MASK) { + + case AMOTION_EVENT_ACTION_DOWN: { + engine->os->process_touch(0,0,touchvec); + + //System.out.printf("action down at: %f,%f\n", event.getX(),event.getY()); + } break; + case AMOTION_EVENT_ACTION_MOVE: { + engine->os->process_touch(1,0,touchvec); + //for(int i=0;i<event.getPointerCount();i++) { + // System.out.printf("%d - moved to: %f,%f\n",i, event.getX(i),event.getY(i)); + //} + } break; + case AMOTION_EVENT_ACTION_POINTER_UP: { + + engine->os->process_touch(4,pidx,touchvec); + //System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); + } break; + case AMOTION_EVENT_ACTION_POINTER_DOWN: { + engine->os->process_touch(3,pidx,touchvec); + //System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); + } break; + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_UP: { + engine->os->process_touch(2,0,touchvec); + //for(int i=0;i<event.getPointerCount();i++) { + // System.out.printf("%d - up! %f,%f\n",i, event.getX(i),event.getY(i)); + //} + } break; + } + + return 1; + } break; + + } + + return 0; +} + +/** + * Process the next main command. + */ + +static void _gfx_init(void *ud,bool p_gl2) { + + struct engine* engine = (struct engine*)ud; + engine_init_display(engine,p_gl2); +} + +static void engine_handle_cmd(struct android_app* app, int32_t cmd) { + struct engine* engine = (struct engine*)app->userData; + // LOGI("**** CMD %i\n",cmd); + switch (cmd) { + case APP_CMD_SAVE_STATE: + // The system has asked us to save our current state. Do so. + //engine->app->savedState = malloc(sizeof(struct saved_state)); + //*((struct saved_state*)engine->app->savedState) = engine->state; + //engine->app->savedStateSize = sizeof(struct saved_state); + break; + case APP_CMD_CONFIG_CHANGED: + case APP_CMD_WINDOW_RESIZED: { + +#if 0 +// android blows + if (engine->display_active) { + + EGLint w,h; + eglQuerySurface(engine->display, engine->surface, EGL_WIDTH, &w); + eglQuerySurface(engine->display, engine->surface, EGL_HEIGHT, &h); + engine->os->init_video_mode(w,h); + //print_line("RESIZED VIDEO MODE: "+itos(w)+","+itos(h)); + engine_draw_frame(engine); + + } +#else + + if (engine->display_active) { + + + EGLint w,h; + eglQuerySurface(engine->display, engine->surface, EGL_WIDTH, &w); + eglQuerySurface(engine->display, engine->surface, EGL_HEIGHT, &h); + // if (w==engine->os->get_video_mode().width && h==engine->os->get_video_mode().height) + // break; + + engine_term_display(engine); + + + } + + + engine->os->reload_gfx(); + engine_draw_frame(engine); + engine->animating=1; + + /* + EGLint w,h; + eglQuerySurface(engine->display, engine->surface, EGL_WIDTH, &w); + eglQuerySurface(engine->display, engine->surface, EGL_HEIGHT, &h); + engine->os->init_video_mode(w,h); + //print_line("RESIZED VIDEO MODE: "+itos(w)+","+itos(h)); + + }*/ + +#endif + + } break; + case APP_CMD_INIT_WINDOW: + //The window is being shown, get it ready. + // LOGI("INIT WINDOW"); + if (engine->app->window != NULL) { + + if (engine->os==NULL) { + + //do initialization here, when there's OpenGL! hackish but the only way + engine->os = new OS_Android(_gfx_init,engine); + + // char *args[]={"-test","gui",NULL}; + __android_log_print(ANDROID_LOG_INFO,"godot","pre asdasd setup..."); +#if 0 + Error err = Main::setup("apk",2,args); +#else + Error err = Main::setup("apk",0,NULL); + + String modules = Globals::get_singleton()->get("android/modules"); + Vector<String> mods = modules.split(",",false); + mods.push_back("GodotOS"); + __android_log_print(ANDROID_LOG_INFO,"godot","mod count: %i",mods.size()); + + if (mods.size()) { + + jclass activityClass = engine->jni->FindClass("android/app/NativeActivity"); + + jmethodID getClassLoader = engine->jni->GetMethodID(activityClass,"getClassLoader", "()Ljava/lang/ClassLoader;"); + + jobject cls = engine->jni->CallObjectMethod(app->activity->clazz, getClassLoader); + + jclass classLoader = engine->jni->FindClass("java/lang/ClassLoader"); + + jmethodID findClass = engine->jni->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + + + static JNINativeMethod methods[] = { + {"registerSingleton", "(Ljava/lang/String;Ljava/lang/Object;)V",(void *)&Java_com_android_godot_Godot_registerSingleton}, + {"registerMethod", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V",(void *)&Java_com_android_godot_Godot_registerMethod}, + {"getGlobal", "(Ljava/lang/String;)Ljava/lang/String;", (void *)&Java_com_android_godot_Godot_getGlobal}, + }; + + jstring gstrClassName = engine->jni->NewStringUTF("com/android/godot/Godot"); + jclass GodotClass = (jclass)engine->jni->CallObjectMethod(cls, findClass, gstrClassName); + + __android_log_print(ANDROID_LOG_INFO,"godot","godot ****^*^*?^*^*class data %x",GodotClass); + + engine->jni->RegisterNatives(GodotClass,methods,sizeof(methods)/sizeof(methods[0])); + + for (int i=0;i<mods.size();i++) { + + String m = mods[i]; + //jclass singletonClass = engine->jni->FindClass(m.utf8().get_data()); + + jstring strClassName = engine->jni->NewStringUTF(m.utf8().get_data()); + jclass singletonClass = (jclass)engine->jni->CallObjectMethod(cls, findClass, strClassName); + + __android_log_print(ANDROID_LOG_INFO,"godot","****^*^*?^*^*class data %x",singletonClass); + jmethodID initialize = engine->jni->GetStaticMethodID(singletonClass, "initialize", "(Landroid/app/Activity;)Lcom/android/godot/Godot$SingletonBase;"); + + + jobject obj = engine->jni->CallStaticObjectMethod(singletonClass,initialize,app->activity->clazz); + __android_log_print(ANDROID_LOG_INFO,"godot","****^*^*?^*^*class instance %x",obj); + jobject gob = engine->jni->NewGlobalRef(obj); + + + } + + } + +#endif + + + if (!Main::start()) + return; //should exit instead and print the error + + engine->os->main_loop_begin(); + } else { + //i guess recreate resources? + engine->os->reload_gfx(); + } + + + engine->animating=1; + engine_draw_frame(engine); + } + break; + case APP_CMD_TERM_WINDOW: + // The window is being hidden or closed, clean it up. + // LOGI("TERM WINDOW"); + engine_term_display(engine); + break; + case APP_CMD_GAINED_FOCUS: + // When our app gains focus, we start monitoring the accelerometer. + if (engine->accelerometerSensor != NULL) { + ASensorEventQueue_enableSensor(engine->sensorEventQueue, + engine->accelerometerSensor); + // We'd like to get 60 events per second (in us). + ASensorEventQueue_setEventRate(engine->sensorEventQueue, + engine->accelerometerSensor, (1000L/60)*1000); + + } + engine->animating = 1; + break; + case APP_CMD_LOST_FOCUS: + // When our app loses focus, we stop monitoring the accelerometer. + // This is to avoid consuming battery while not being used. + if (engine->accelerometerSensor != NULL) { + ASensorEventQueue_disableSensor(engine->sensorEventQueue, + engine->accelerometerSensor); + } + // Also stop animating. + engine->animating = 0; + engine_draw_frame(engine); + break; + } +} + +void android_main(struct android_app* state) { + struct engine engine; + // Make sure glue isn't stripped. + app_dummy(); + + memset(&engine, 0, sizeof(engine)); + state->userData = &engine; + state->onAppCmd = engine_handle_cmd; + state->onInputEvent = engine_handle_input; + engine.app = state; + engine.requested_quit=false; + engine.os=NULL; + engine.display_active=false; + + FileAccessAndroid::asset_manager=state->activity->assetManager; + + // Prepare to monitor accelerometer + engine.sensorManager = ASensorManager_getInstance(); + engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, + ASENSOR_TYPE_ACCELEROMETER); + engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager, + state->looper, LOOPER_ID_USER, NULL, NULL); + + + ANativeActivity_setWindowFlags(state->activity,AWINDOW_FLAG_FULLSCREEN|AWINDOW_FLAG_KEEP_SCREEN_ON,0); + + state->activity->vm->AttachCurrentThread(&engine.jni, NULL); + + + + // loop waiting for stuff to do. + + while (1) { + // Read all pending events. + int ident; + int events; + struct android_poll_source* source; + + // If not animating, we will block forever waiting for events. + // If animating, we loop until all events are read, then continue + // to draw the next frame of animation. + + int nullmax=50; + while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, + (void**)&source)) >= 0) { + + // Process this event. + + if (source != NULL) { + // LOGI("process\n"); + source->process(state, source); + } else { + nullmax--; + if (nullmax<0) + break; + } + + // If a sensor has data, process it now. + // LOGI("events\n"); + if (ident == LOOPER_ID_USER) { + if (engine.accelerometerSensor != NULL) { + ASensorEvent event; + while (ASensorEventQueue_getEvents(engine.sensorEventQueue, + &event, 1) > 0) { + + + if (engine.os) { + engine.os->process_accelerometer(Vector3(event.acceleration.x, event.acceleration.y, + event.acceleration.z)); + + } + + } + } + } + + // Check if we are exiting. + if (state->destroyRequested != 0) { + if (engine.os) { + engine.os->main_loop_request_quit(); + } + state->destroyRequested=0; + } + + if (engine.requested_quit) { + engine_term_display(&engine); + exit(0); + return; + } + +// LOGI("end\n"); + + + } + +// LOGI("engine animating? %i\n",engine.animating); + + if (engine.animating) { + //do os render + + engine_draw_frame(&engine); + //LOGI("TERM WINDOW"); + + } + } + +} + + + + +JNIEXPORT void JNICALL Java_com_android_godot_Godot_registerSingleton(JNIEnv * env, jobject obj, jstring name,jobject p_object){ + + String singname = env->GetStringUTFChars( name, NULL ); + JNISingleton *s = memnew( JNISingleton ); + s->update_env(env); + s->set_instance(env->NewGlobalRef(p_object)); + jni_singletons[singname]=s; + + Globals::get_singleton()->add_singleton(Globals::Singleton(singname,s)); + +} + + +static Variant::Type get_jni_type(const String& p_type) { + + static struct { + const char *name; + Variant::Type type; + } _type_to_vtype[]={ + {"void",Variant::NIL}, + {"boolean",Variant::BOOL}, + {"int",Variant::INT}, + {"float",Variant::REAL}, + {"java.lang.String",Variant::STRING}, + {"[I",Variant::INT_ARRAY}, + {"[F",Variant::REAL_ARRAY}, + {"[java.lang.String",Variant::STRING_ARRAY}, + {NULL,Variant::NIL} + }; + + int idx=0; + + while (_type_to_vtype[idx].name) { + + if (p_type==_type_to_vtype[idx].name) + return _type_to_vtype[idx].type; + + idx++; + } + + return Variant::NIL; +} + + +static const char* get_jni_sig(const String& p_type) { + + static struct { + const char *name; + const char *sig; + } _type_to_vtype[]={ + {"void","V"}, + {"boolean","Z"}, + {"int","I"}, + {"float","F"}, + {"java.lang.String","Ljava/lang/String;"}, + {"[I","[I"}, + {"[F","[F"}, + {"[java.lang.String","[Ljava/lang/String;"}, + {NULL,"V"} + }; + + int idx=0; + + while (_type_to_vtype[idx].name) { + + if (p_type==_type_to_vtype[idx].name) + return _type_to_vtype[idx].sig; + + idx++; + } + + + return ""; +} + +JNIEXPORT jstring JNICALL Java_com_android_godot_Godot_getGlobal(JNIEnv * env, jobject obj, jstring path) { + + String js = env->GetStringUTFChars( path, NULL ); + + return env->NewStringUTF(Globals::get_singleton()->get(js).operator String().utf8().get_data()); + + +} + + + +JNIEXPORT void JNICALL Java_com_android_godot_Godot_registerMethod(JNIEnv * env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args){ + + String singname = env->GetStringUTFChars( sname, NULL ); + + ERR_FAIL_COND(!jni_singletons.has(singname)); + + JNISingleton *s = jni_singletons.get(singname); + + + String mname = env->GetStringUTFChars( name, NULL ); + String retval = env->GetStringUTFChars( ret, NULL ); + Vector<Variant::Type> types; + String cs="("; + + + int stringCount = env->GetArrayLength(args); + + print_line("Singl: "+singname+" Method: "+mname+" RetVal: "+retval); + for (int i=0; i<stringCount; i++) { + + jstring string = (jstring) env->GetObjectArrayElement(args, i); + const char *rawString = env->GetStringUTFChars(string, 0); + types.push_back(get_jni_type(String(rawString))); + cs+=get_jni_sig(String(rawString)); + } + + cs+=")"; + cs+=get_jni_sig(retval); + jclass cls = env->GetObjectClass(s->get_instance()); + print_line("METHOD: "+mname+" sig: "+cs); + jmethodID mid = env->GetMethodID(cls, mname.ascii().get_data(), cs.ascii().get_data()); + if (!mid) { + + print_line("FAILED GETTING METHOID "+mname); + } + + s->add_method(mname,mid,types,get_jni_type(retval)); + + +} + +#endif diff --git a/platform/android/java/ant.properties b/platform/android/java/ant.properties new file mode 100644 index 0000000000..950b8b0199 --- /dev/null +++ b/platform/android/java/ant.properties @@ -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 diff --git a/platform/android/java/build.properties b/platform/android/java/build.properties new file mode 100644 index 0000000000..ee52d86d94 --- /dev/null +++ b/platform/android/java/build.properties @@ -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. + diff --git a/platform/android/java/build.xml b/platform/android/java/build.xml new file mode 100644 index 0000000000..424e2827dc --- /dev/null +++ b/platform/android/java/build.xml @@ -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> diff --git a/platform/android/java/default.properties b/platform/android/java/default.properties new file mode 100644 index 0000000000..e2e8061f26 --- /dev/null +++ b/platform/android/java/default.properties @@ -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 diff --git a/platform/android/java/my-release-key.keystore b/platform/android/java/my-release-key.keystore Binary files differnew file mode 100644 index 0000000000..410cccd865 --- /dev/null +++ b/platform/android/java/my-release-key.keystore diff --git a/platform/android/java/proguard-project.txt b/platform/android/java/proguard-project.txt new file mode 100644 index 0000000000..f2fe1559a2 --- /dev/null +++ b/platform/android/java/proguard-project.txt @@ -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 *; +#} diff --git a/platform/android/java/proguard.cfg b/platform/android/java/proguard.cfg new file mode 100644 index 0000000000..12dd0392c0 --- /dev/null +++ b/platform/android/java/proguard.cfg @@ -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 *; +} diff --git a/platform/android/java/res/drawable/icon.png b/platform/android/java/res/drawable/icon.png Binary files differnew file mode 100644 index 0000000000..050a1cf930 --- /dev/null +++ b/platform/android/java/res/drawable/icon.png diff --git a/platform/android/java/res/values-ar/strings.xml b/platform/android/java/res/values-ar/strings.xml new file mode 100644 index 0000000000..9f3dc6d6ac --- /dev/null +++ b/platform/android/java/res/values-ar/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-ar</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-bg/strings.xml b/platform/android/java/res/values-bg/strings.xml new file mode 100644 index 0000000000..bd8109277e --- /dev/null +++ b/platform/android/java/res/values-bg/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-bg</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-ca/strings.xml b/platform/android/java/res/values-ca/strings.xml new file mode 100644 index 0000000000..494cb88468 --- /dev/null +++ b/platform/android/java/res/values-ca/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-ca</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-cs/strings.xml b/platform/android/java/res/values-cs/strings.xml new file mode 100644 index 0000000000..30ce00f895 --- /dev/null +++ b/platform/android/java/res/values-cs/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-cs</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-da/strings.xml b/platform/android/java/res/values-da/strings.xml new file mode 100644 index 0000000000..4c2a1cf0f4 --- /dev/null +++ b/platform/android/java/res/values-da/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-da</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-de/strings.xml b/platform/android/java/res/values-de/strings.xml new file mode 100644 index 0000000000..52946d4cce --- /dev/null +++ b/platform/android/java/res/values-de/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-de</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-el/strings.xml b/platform/android/java/res/values-el/strings.xml new file mode 100644 index 0000000000..181dc51762 --- /dev/null +++ b/platform/android/java/res/values-el/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-el</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-en/strings.xml b/platform/android/java/res/values-en/strings.xml new file mode 100644 index 0000000000..976a565013 --- /dev/null +++ b/platform/android/java/res/values-en/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-en</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-es-rES/strings.xml b/platform/android/java/res/values-es-rES/strings.xml new file mode 100644 index 0000000000..73f63a08f8 --- /dev/null +++ b/platform/android/java/res/values-es-rES/strings.xml @@ -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>
\ No newline at end of file diff --git a/platform/android/java/res/values-es/strings.xml b/platform/android/java/res/values-es/strings.xml new file mode 100644 index 0000000000..07b718a641 --- /dev/null +++ b/platform/android/java/res/values-es/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-es</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-fi/strings.xml b/platform/android/java/res/values-fi/strings.xml new file mode 100644 index 0000000000..323d82aff1 --- /dev/null +++ b/platform/android/java/res/values-fi/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-fi</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-fr/strings.xml b/platform/android/java/res/values-fr/strings.xml new file mode 100644 index 0000000000..32bead2661 --- /dev/null +++ b/platform/android/java/res/values-fr/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-fr</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-he/strings.xml b/platform/android/java/res/values-he/strings.xml new file mode 100644 index 0000000000..f52ede2085 --- /dev/null +++ b/platform/android/java/res/values-he/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-he</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-hi/strings.xml b/platform/android/java/res/values-hi/strings.xml new file mode 100644 index 0000000000..8aab2a8c63 --- /dev/null +++ b/platform/android/java/res/values-hi/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-hi</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-hr/strings.xml b/platform/android/java/res/values-hr/strings.xml new file mode 100644 index 0000000000..caf55e2241 --- /dev/null +++ b/platform/android/java/res/values-hr/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-hr</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-hu/strings.xml b/platform/android/java/res/values-hu/strings.xml new file mode 100644 index 0000000000..e7f9e51226 --- /dev/null +++ b/platform/android/java/res/values-hu/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-hu</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-id/strings.xml b/platform/android/java/res/values-id/strings.xml new file mode 100644 index 0000000000..9e9a8b0c03 --- /dev/null +++ b/platform/android/java/res/values-id/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-id</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-it/strings.xml b/platform/android/java/res/values-it/strings.xml new file mode 100644 index 0000000000..1f5e5a049e --- /dev/null +++ b/platform/android/java/res/values-it/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-it</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-ja/strings.xml b/platform/android/java/res/values-ja/strings.xml new file mode 100644 index 0000000000..7f85f57df7 --- /dev/null +++ b/platform/android/java/res/values-ja/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-ja</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-ko/strings.xml b/platform/android/java/res/values-ko/strings.xml new file mode 100644 index 0000000000..6d27498af8 --- /dev/null +++ b/platform/android/java/res/values-ko/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-ko</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-lt/strings.xml b/platform/android/java/res/values-lt/strings.xml new file mode 100644 index 0000000000..6e3677fde7 --- /dev/null +++ b/platform/android/java/res/values-lt/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-lt</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-lv/strings.xml b/platform/android/java/res/values-lv/strings.xml new file mode 100644 index 0000000000..701fc271ac --- /dev/null +++ b/platform/android/java/res/values-lv/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-lv</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-nb/strings.xml b/platform/android/java/res/values-nb/strings.xml new file mode 100644 index 0000000000..73147ca1af --- /dev/null +++ b/platform/android/java/res/values-nb/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-nb</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-nl/strings.xml b/platform/android/java/res/values-nl/strings.xml new file mode 100644 index 0000000000..e501928a35 --- /dev/null +++ b/platform/android/java/res/values-nl/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-nl</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-pl/strings.xml b/platform/android/java/res/values-pl/strings.xml new file mode 100644 index 0000000000..ea5da73b6f --- /dev/null +++ b/platform/android/java/res/values-pl/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-pl</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-pt/strings.xml b/platform/android/java/res/values-pt/strings.xml new file mode 100644 index 0000000000..bdda7cd2c7 --- /dev/null +++ b/platform/android/java/res/values-pt/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-pt</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-ro/strings.xml b/platform/android/java/res/values-ro/strings.xml new file mode 100644 index 0000000000..3686da4c19 --- /dev/null +++ b/platform/android/java/res/values-ro/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-ro</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-ru/strings.xml b/platform/android/java/res/values-ru/strings.xml new file mode 100644 index 0000000000..954067658b --- /dev/null +++ b/platform/android/java/res/values-ru/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-ru</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-sk/strings.xml b/platform/android/java/res/values-sk/strings.xml new file mode 100644 index 0000000000..37d1283124 --- /dev/null +++ b/platform/android/java/res/values-sk/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-sk</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-sl/strings.xml b/platform/android/java/res/values-sl/strings.xml new file mode 100644 index 0000000000..0bb249c375 --- /dev/null +++ b/platform/android/java/res/values-sl/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-sl</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-sr/strings.xml b/platform/android/java/res/values-sr/strings.xml new file mode 100644 index 0000000000..0e83cab1a1 --- /dev/null +++ b/platform/android/java/res/values-sr/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-sr</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-sv/strings.xml b/platform/android/java/res/values-sv/strings.xml new file mode 100644 index 0000000000..e3a04ac2ec --- /dev/null +++ b/platform/android/java/res/values-sv/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-sv</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-th/strings.xml b/platform/android/java/res/values-th/strings.xml new file mode 100644 index 0000000000..0aa893b8bf --- /dev/null +++ b/platform/android/java/res/values-th/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-th</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-tl/strings.xml b/platform/android/java/res/values-tl/strings.xml new file mode 100644 index 0000000000..e7e2af4909 --- /dev/null +++ b/platform/android/java/res/values-tl/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-tl</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-tr/strings.xml b/platform/android/java/res/values-tr/strings.xml new file mode 100644 index 0000000000..97af1243a6 --- /dev/null +++ b/platform/android/java/res/values-tr/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-tr</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-uk/strings.xml b/platform/android/java/res/values-uk/strings.xml new file mode 100644 index 0000000000..3dea6908a9 --- /dev/null +++ b/platform/android/java/res/values-uk/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-uk</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-vi/strings.xml b/platform/android/java/res/values-vi/strings.xml new file mode 100644 index 0000000000..a6552130b0 --- /dev/null +++ b/platform/android/java/res/values-vi/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-vi</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-zh/strings.xml b/platform/android/java/res/values-zh/strings.xml new file mode 100644 index 0000000000..397e662851 --- /dev/null +++ b/platform/android/java/res/values-zh/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-zh</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values/strings.xml b/platform/android/java/res/values/strings.xml new file mode 100644 index 0000000000..3a38d40599 --- /dev/null +++ b/platform/android/java/res/values/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name</string> + <string name="testuf8">元気です</string> + <string name="testuf2">元気です元気です元気です</string> + +</resources> diff --git a/platform/android/java/src/com/android/godot/Dictionary.java b/platform/android/java/src/com/android/godot/Dictionary.java new file mode 100644 index 0000000000..4ed12f5818 --- /dev/null +++ b/platform/android/java/src/com/android/godot/Dictionary.java @@ -0,0 +1,80 @@ +/*************************************************************************/ +/* Dictionary.java */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ +package com.android.godot; + +import java.util.HashMap; +import java.util.Set; + + +public class Dictionary extends HashMap<String, Object> { + + protected String[] keys_cache; + + public String[] get_keys() { + + String[] ret = new String[size()]; + int i=0; + Set<String> keys = keySet(); + for (String key : keys) { + + ret[i] = key; + i++; + }; + + return ret; + }; + + public Object[] get_values() { + + Object[] ret = new Object[size()]; + int i=0; + Set<String> keys = keySet(); + for (String key : keys) { + + ret[i] = get(key); + i++; + }; + + return ret; + }; + + public void set_keys(String[] keys) { + keys_cache = keys; + }; + + public void set_values(Object[] vals) { + + int i=0; + for (String key : keys_cache) { + put(key, vals[i]); + i++; + }; + keys_cache = null; + }; +}; diff --git a/platform/android/java/src/com/android/godot/Godot.java b/platform/android/java/src/com/android/godot/Godot.java new file mode 100644 index 0000000000..cf1545df82 --- /dev/null +++ b/platform/android/java/src/com/android/godot/Godot.java @@ -0,0 +1,300 @@ +/*************************************************************************/ +/* Godot.java */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ +package com.android.godot; + +import android.app.Activity; +import android.os.Bundle; +import android.view.MotionEvent; +import android.widget.RelativeLayout; +import android.widget.LinearLayout; +import android.view.ViewGroup.LayoutParams; + + +import android.app.*; +import android.content.*; +import android.view.*; +import android.view.inputmethod.InputMethodManager; +import android.os.*; +import android.util.Log; +import android.graphics.*; +import android.text.method.*; +import android.text.*; +import android.media.*; +import android.hardware.*; +import android.content.*; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.ArrayList; +import android.provider.Settings.Secure; + + +public class Godot extends Activity implements SensorEventListener +{ + + static public class SingletonBase { + + protected void registerClass(String p_name, String[] p_methods) { + + GodotLib.singleton(p_name,this); + + Class clazz = getClass(); + Method[] methods = clazz.getDeclaredMethods(); + for (Method method : methods) { + boolean found=false; + System.out.printf("METHOD: %s\n",method.getName()); + + for (String s : p_methods) { + System.out.printf("METHOD CMP WITH: %s\n",s); + if (s.equals(method.getName())) { + found=true; + System.out.printf("METHOD CMP VALID"); + break; + } + } + if (!found) + continue; + + System.out.printf("METHOD FOUND: %s\n",method.getName()); + + List<String> ptr = new ArrayList<String>(); + + Class[] paramTypes = method.getParameterTypes(); + for (Class c : paramTypes) { + ptr.add(c.getName()); + } + + String[] pt = new String[ptr.size()]; + ptr.toArray(pt); + + GodotLib.method(p_name,method.getName(),method.getReturnType().getName(),pt); + + + } + } + + public void registerMethods() {} + } + +/* + protected List<SingletonBase> singletons = new ArrayList<SingletonBase>(); + protected void instanceSingleton(SingletonBase s) { + + s.registerMethods(); + singletons.add(s); + } + +*/ + + public GodotView mView; + + private SensorManager mSensorManager; + private Sensor mAccelerometer; + + public RelativeLayout layout; + + + static public GodotIO io; + + public static void setWindowTitle(String title) { + //setTitle(title); + } + + public interface ResultCallback { + public void callback(int requestCode, int resultCode, Intent data); + }; + public ResultCallback result_callback; + + @Override protected void onActivityResult (int requestCode, int resultCode, Intent data) { + if (result_callback != null) { + result_callback.callback(requestCode, resultCode, data); + result_callback = null; + }; + }; + + public void onVideoInit(boolean use_gl2) { + +// mView = new GodotView(getApplication(),io,use_gl2); +// setContentView(mView); + + layout = new RelativeLayout(this); + layout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); + setContentView(layout); + mView = new GodotView(getApplication(),io,use_gl2, this); + layout.addView(mView,new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); + mView.setKeepScreenOn(true); + + } + + @Override protected void onCreate(Bundle icicle) { + + + super.onCreate(icicle); + + + + Window window = getWindow(); + window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON + | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + io = new GodotIO(this); + io.unique_id = Secure.getString(getContentResolver(), Secure.ANDROID_ID); + GodotLib.io=io; + GodotLib.initialize(this,io.needsReloadHooks()); + mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); + mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); + + result_callback = null; + + // instanceSingleton( new GodotFacebook(this) ); + + + } + + @Override protected void onPause() { + super.onPause(); + mView.onPause(); + mSensorManager.unregisterListener(this); + GodotLib.focusout(); + + } + + @Override protected void onResume() { + super.onResume(); + mView.onResume(); + mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); + GodotLib.focusin(); + } + + @Override public void onSensorChanged(SensorEvent event) { + float x = event.values[0]; + float y = event.values[1]; + float z = event.values[2]; + GodotLib.accelerometer(x,y,z); + } + + @Override public final void onAccuracyChanged(Sensor sensor, int accuracy) { + // Do something here if sensor accuracy changes. + } + +/* + @Override public boolean dispatchKeyEvent(KeyEvent event) { + + if (event.getKeyCode()==KeyEvent.KEYCODE_BACK) { + + System.out.printf("** BACK REQUEST!\n"); + + GodotLib.quit(); + return true; + } + System.out.printf("** OTHER KEY!\n"); + + return false; + } +*/ + + @Override public void onBackPressed() { + + GodotLib.quit(); + } + + public void forceQuit() { + + System.exit(0); + } + + + //@Override public boolean dispatchTouchEvent (MotionEvent event) { + public boolean gotTouchEvent(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; + } + + @Override public boolean onKeyUp(int keyCode, KeyEvent event) { + GodotLib.key(event.getUnicodeChar(0), false); + return super.onKeyUp(keyCode, event); + }; + + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { + GodotLib.key(event.getUnicodeChar(0), true); + return super.onKeyDown(keyCode, event); + }; + + + // Audio + + +} diff --git a/platform/android/java/src/com/android/godot/GodotIO.java b/platform/android/java/src/com/android/godot/GodotIO.java new file mode 100644 index 0000000000..1f3967cb8f --- /dev/null +++ b/platform/android/java/src/com/android/godot/GodotIO.java @@ -0,0 +1,514 @@ +/*************************************************************************/ +/* GodotIO.java */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ +package com.android.godot; +import java.util.HashMap; +import java.util.Locale; +import android.net.Uri; +import android.content.Intent; +import android.content.res.AssetManager; +import java.io.InputStream; +import java.io.IOException; +import android.app.*; +import android.content.*; +import android.view.*; +import android.view.inputmethod.InputMethodManager; +import android.os.*; +import android.util.Log; +import android.graphics.*; +import android.text.method.*; +import android.text.*; +import android.media.*; +import android.hardware.*; +import android.content.*; +import android.content.pm.ActivityInfo; +//android.os.Build + +// Wrapper for native library + +public class GodotIO { + + + AssetManager am; + Activity activity; + + final int SCREEN_LANDSCAPE=0; + final int SCREEN_PORTRAIT=1; + final int SCREEN_REVERSE_LANDSCAPE=2; + final int SCREEN_REVERSE_PORTRAIT=3; + final int SCREEN_SENSOR_LANDSCAPE=4; + final int SCREEN_SENSOR_PORTRAIT=5; + final int SCREEN_SENSOR=6; + + ///////////////////////// + /// 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(Activity p_activity) { + + am=p_activity.getAssets(); + activity=p_activity; + streams=new HashMap<Integer,AssetData>(); + dirs=new HashMap<Integer,AssetDir>(); + + + } + + + ///////////////////////// + // AUDIO + ///////////////////////// + + private Object buf; + private Thread mAudioThread; + private AudioTrack mAudioTrack; + + public Object audioInit(int sampleRate, int desiredFrames) { + int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_STEREO; + int audioFormat = AudioFormat.ENCODING_PCM_16BIT; + int frameSize = 4; + + System.out.printf("audioInit: initializing audio:\n"); + + //Log.v("Godot", "Godot audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + ((float)sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer"); + + // Let the user pick a larger buffer if they really want -- but ye + // gods they probably shouldn't, the minimums are horrifyingly high + // latency already + desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize); + + mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, + channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); + + audioStartThread(); + + //Log.v("Godot", "Godot audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + ((float)mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer"); + + buf = new short[desiredFrames * 2]; + return buf; + } + + public void audioStartThread() { + mAudioThread = new Thread(new Runnable() { + public void run() { + mAudioTrack.play(); + GodotLib.audio(); + } + }); + + // I'd take REALTIME if I could get it! + mAudioThread.setPriority(Thread.MAX_PRIORITY); + mAudioThread.start(); + } + + public void audioWriteShortBuffer(short[] buffer) { + for (int i = 0; i < buffer.length; ) { + int result = mAudioTrack.write(buffer, i, buffer.length - i); + if (result > 0) { + i += result; + } else if (result == 0) { + try { + Thread.sleep(1); + } catch(InterruptedException e) { + // Nom nom + } + } else { + Log.w("Godot", "Godot audio: error return from write(short)"); + return; + } + } + } + + + + public void audioQuit() { + if (mAudioThread != null) { + try { + mAudioThread.join(); + } catch(Exception e) { + Log.v("Godot", "Problem stopping audio thread: " + e); + } + mAudioThread = null; + + //Log.v("Godot", "Finished waiting for audio thread"); + } + + if (mAudioTrack != null) { + mAudioTrack.stop(); + mAudioTrack = null; + } + } + + public void audioPause(boolean p_pause) { + + if (p_pause) + mAudioTrack.pause(); + else + mAudioTrack.play(); + } + + ///////////////////////// + // MISCELANEOUS OS IO + ///////////////////////// + + + + public int openURI(String p_uri) { + + try { + Log.v("MyApp", "TRYING TO OPEN URI: " + p_uri); + Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(p_uri)); + activity.startActivity(myIntent); + return 0; + } catch (ActivityNotFoundException e) { + + return 1; + } + } + + public String getDataDir() { + + return activity.getFilesDir().getAbsolutePath(); + } + + public String getLocale() { + + return Locale.getDefault().toString(); + } + + public String getModel() { + return Build.MODEL; + } + + public boolean needsReloadHooks() { + + return android.os.Build.VERSION.SDK_INT < 11; + } + + public void showKeyboard(String p_existing_text) { + + InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); + inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + }; + + public void hideKeyboard() { + + InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); + inputMgr.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + }; + + public void setScreenOrientation(int p_orientation) { + + switch(p_orientation) { + + case SCREEN_LANDSCAPE: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } break; + case SCREEN_PORTRAIT: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } break; + case SCREEN_REVERSE_LANDSCAPE: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + } break; + case SCREEN_REVERSE_PORTRAIT: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); + } break; + case SCREEN_SENSOR_LANDSCAPE: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); + } break; + case SCREEN_SENSOR_PORTRAIT: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); + } break; + case SCREEN_SENSOR: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); + } break; + + } + }; + + protected static final String PREFS_FILE = "device_id.xml"; + protected static final String PREFS_DEVICE_ID = "device_id"; + + public static String unique_id=""; + public String getUniqueID() { + + return unique_id; + } + +} diff --git a/platform/android/java/src/com/android/godot/GodotLib.java b/platform/android/java/src/com/android/godot/GodotLib.java new file mode 100644 index 0000000000..8d002c5ebc --- /dev/null +++ b/platform/android/java/src/com/android/godot/GodotLib.java @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* GodotLib.java */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ +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 initialize(Godot p_instance,boolean need_reload_hook); + public static native void resize(int width, int height,boolean reload); + public static native void newcontext(); + public static native void quit(); + public static native void step(); + public static native void touch(int what,int pointer,int howmany, int[] arr); + public static native void accelerometer(float x, float y, float z); + public static native void key(int p_unicode_char, boolean p_pressed); + public static native void focusin(); + public static native void focusout(); + public static native void audio(); + public static native void singleton(String p_name,Object p_object); + public static native void method(String p_sname,String p_name,String p_ret,String[] p_params); + public static native String getGlobal(String p_key); + public static native void callobject(int p_ID, String p_method, Object[] p_params); + +} diff --git a/platform/android/java/src/com/android/godot/GodotView.java b/platform/android/java/src/com/android/godot/GodotView.java new file mode 100644 index 0000000000..093757bfb0 --- /dev/null +++ b/platform/android/java/src/com/android/godot/GodotView.java @@ -0,0 +1,378 @@ +/*************************************************************************/ +/* GodotView.java */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ +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; + private static boolean firsttime=true; + private static boolean use_gl2=false; + + private Godot activity; + + public GodotView(Context context,GodotIO p_io,boolean p_use_gl2, Godot p_activity) { + super(context); + ctx=context; + io=p_io; + use_gl2=p_use_gl2; + + activity = p_activity; + + if (!p_io.needsReloadHooks()) { + //will only work on SDK 11+!! + setPreserveEGLContextOnPause(true); + } + + init(false, 16, 0); + } + + public GodotView(Context context, boolean translucent, int depth, int stencil) { + super(context); + init(translucent, depth, stencil); + } + + @Override public boolean onTouchEvent (MotionEvent event) { + + return activity.gotTouchEvent(event); + }; + + 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) { + if (use_gl2) + Log.w(TAG, "creating OpenGL ES 2.0 context :"); + else + Log.w(TAG, "creating OpenGL ES 1.1 context :"); + + checkEglError("Before eglCreateContext", egl); + int[] attrib_list2 = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; + EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, use_gl2?attrib_list2:null); + 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_DEPTH_SIZE, 16, + // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, + EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL10.EGL_NONE + }; + private static int[] s_configAttribs = + { + EGL10.EGL_RED_SIZE, 4, + EGL10.EGL_GREEN_SIZE, 4, + EGL10.EGL_BLUE_SIZE, 4, + // EGL10.EGL_DEPTH_SIZE, 16, + // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, + 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, use_gl2?s_configAttribs2:s_configAttribs, 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, use_gl2?s_configAttribs2:s_configAttribs, 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) { + + GodotLib.resize(width, height,!firsttime); + firsttime=false; + } + + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + GodotLib.newcontext(); + } + } +} diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp new file mode 100644 index 0000000000..9c5003cb17 --- /dev/null +++ b/platform/android/java_glue.cpp @@ -0,0 +1,1153 @@ +/*************************************************************************/ +/* java_glue.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 "java_glue.h" +#include "os_android.h" +#include "main/main.h" +#include <unistd.h> +#include "file_access_jandroid.h" +#include "dir_access_jandroid.h" +#include "audio_driver_jandroid.h" +#include "globals.h" +#include "thread_jandroid.h" +#include "core/os/keyboard.h" +static OS_Android *os_android=NULL; + + +jvalue _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant* p_arg, bool force_jobject = false) { + + jvalue v; + + switch(p_type) { + + case Variant::BOOL: { + + if (force_jobject) { + jclass bclass = env->FindClass("java/lang/Boolean"); + jmethodID ctor = env->GetMethodID(bclass, "<init>", "(Z)V"); + jvalue val; + val.z = (bool)(*p_arg); + jobject obj = env->NewObjectA(bclass, ctor, &val); + v.l = obj; + } else { + v.z=*p_arg; + }; + } break; + case Variant::INT: { + + if (force_jobject) { + + jclass bclass = env->FindClass("java/lang/Integer"); + jmethodID ctor = env->GetMethodID(bclass, "<init>", "(I)V"); + jvalue val; + val.i = (int)(*p_arg); + jobject obj = env->NewObjectA(bclass, ctor, &val); + v.l = obj; + + } else { + v.i=*p_arg; + }; + } break; + case Variant::REAL: { + + if (force_jobject) { + + jclass bclass = env->FindClass("java/lang/Double"); + jmethodID ctor = env->GetMethodID(bclass, "<init>", "(D)V"); + jvalue val; + val.d = (double)(*p_arg); + jobject obj = env->NewObjectA(bclass, ctor, &val); + v.l = obj; + + } else { + v.f=*p_arg; + }; + } break; + case Variant::STRING: { + + String s = *p_arg; + jstring jStr = env->NewStringUTF(s.utf8().get_data()); + v.l=jStr; + } break; + case Variant::STRING_ARRAY: { + + DVector<String> sarray = *p_arg; + 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[j].utf8().get_data() )); + } + v.l=arr; + + } break; + + case Variant::DICTIONARY: { + + Dictionary dict = *p_arg; + jclass dclass = env->FindClass("com/android/godot/Dictionary"); + jmethodID ctor = env->GetMethodID(dclass, "<init>", "()V"); + jobject jdict = env->NewObject(dclass, ctor); + + Array keys = dict.keys(); + + jobjectArray jkeys = env->NewObjectArray(keys.size(), env->FindClass("java/lang/String"), env->NewStringUTF("")); + for (int j=0; j<keys.size(); j++) { + env->SetObjectArrayElement(jkeys, j, env->NewStringUTF(String(keys[j]).utf8().get_data())); + }; + + jmethodID set_keys = env->GetMethodID(dclass, "set_keys", "([Ljava/lang/String;)V"); + jvalue val; + val.l = jkeys; + env->CallVoidMethodA(jdict, set_keys, &val); + + jobjectArray jvalues = env->NewObjectArray(keys.size(), env->FindClass("java/lang/Object"), NULL); + + for (int j=0; j<keys.size(); j++) { + Variant var = dict[keys[j]]; + val = _variant_to_jvalue(env, var.get_type(), &var, true); + env->SetObjectArrayElement(jvalues, j, val.l); + }; + + jmethodID set_values = env->GetMethodID(dclass, "set_values", "([Ljava/lang/Object;)V"); + val.l = jvalues; + env->CallVoidMethodA(jdict, set_values, &val); + + v.l = jdict; + } break; + + case Variant::INT_ARRAY: { + + DVector<int> array = *p_arg; + jintArray arr = env->NewIntArray(array.size()); + DVector<int>::Read r = array.read(); + env->SetIntArrayRegion(arr,0,array.size(),r.ptr()); + v.l=arr; + + } break; + case Variant::REAL_ARRAY: { + + DVector<float> array = *p_arg; + jfloatArray arr = env->NewFloatArray(array.size()); + DVector<float>::Read r = array.read(); + env->SetFloatArrayRegion(arr,0,array.size(),r.ptr()); + v.l=arr; + + } break; + default: { + + v.i = 0; + } break; + + } + return v; +}; + +String _get_class_name(JNIEnv * env, jclass cls, bool* array) { + + jclass cclass = env->FindClass("java/lang/Class"); + jmethodID getName = env->GetMethodID(cclass, "getName", "()Ljava/lang/String;"); + jstring clsName=(jstring) env->CallObjectMethod(cls, getName); + + if (array) { + jmethodID isArray = env->GetMethodID(cclass, "isArray", "()Z"); + jboolean isarr = env->CallBooleanMethod(cls, isArray); + (*array) = isarr ? true : false; + } + + return env->GetStringUTFChars( clsName, NULL ); +}; + + +Variant _jobject_to_variant(JNIEnv * env, jobject obj) { + + jclass c = env->GetObjectClass(obj); + bool array; + String name = _get_class_name(env, c, &array); + //print_line("name is " + name + ", array "+Variant(array)); + + if (name == "java.lang.String") { + + return String::utf8(env->GetStringUTFChars( (jstring)obj, NULL )); + }; + + + if (name == "[Ljava.lang.String;") { + + jobjectArray arr = (jobjectArray)obj; + int stringCount = env->GetArrayLength(arr); + //print_line("String array! " + String::num(stringCount)); + 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)); + } + + return sarr; + }; + + if (name == "java.lang.Boolean") { + + jmethodID boolValue = env->GetMethodID(c, "booleanValue", "()Z"); + bool ret = env->CallBooleanMethod(obj, boolValue); + return ret; + }; + + if (name == "java.lang.Integer") { + + jclass nclass = env->FindClass("java/lang/Number"); + jmethodID intValue = env->GetMethodID(nclass, "intValue", "()I"); + int ret = env->CallIntMethod(obj, intValue); + return ret; + }; + + if (name == "[I") { + + jintArray arr = (jintArray)obj; + 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(); + return sarr; + }; + + if (name == "java.lang.Float" || name == "java.lang.Double") { + + jclass nclass = env->FindClass("java/lang/Number"); + jmethodID doubleValue = env->GetMethodID(nclass, "doubleValue", "()D"); + double ret = env->CallDoubleMethod(obj, doubleValue); + return ret; + }; + + if (name == "[D") { + + jdoubleArray arr = (jdoubleArray)obj; + int fCount = env->GetArrayLength(arr); + RealArray sarr; + sarr.resize(fCount); + + RealArray::Write w = sarr.write(); + + for (int i=0; i<fCount; i++) { + + double n; + env->GetDoubleArrayRegion(arr, i, 1, &n); + w.ptr()[i] = n; + + }; + return sarr; + }; + + if (name == "[F") { + + jfloatArray arr = (jfloatArray)obj; + int fCount = env->GetArrayLength(arr); + RealArray sarr; + sarr.resize(fCount); + + + RealArray::Write w = sarr.write(); + + for (int i=0; i<fCount; i++) { + + float n; + env->GetFloatArrayRegion(arr, i, 1, &n); + w.ptr()[i] = n; + + }; + return sarr; + }; + + + if (name == "[Ljava.lang.Object;") { + + jobjectArray arr = (jobjectArray)obj; + int objCount = env->GetArrayLength(arr); + Array varr; + + for (int i=0; i<objCount; i++) { + jobject jobj = env->GetObjectArrayElement(arr, i); + Variant v = _jobject_to_variant(env, jobj); + varr.push_back(v); + } + + return varr; + }; + + if (name == "com.android.godot.Dictionary") { + + Dictionary ret; + jclass oclass = c; + jmethodID get_keys = env->GetMethodID(oclass, "get_keys", "()[Ljava/lang/String;"); + jobjectArray arr = (jobjectArray)env->CallObjectMethod(obj, get_keys); + + StringArray keys = _jobject_to_variant(env, arr); + + jmethodID get_values = env->GetMethodID(oclass, "get_values", "()[Ljava/lang/Object;"); + arr = (jobjectArray)env->CallObjectMethod(obj, get_values); + + Array vals = _jobject_to_variant(env, arr); + + //print_line("adding " + String::num(keys.size()) + " to Dictionary!"); + for (int i=0; i<keys.size(); i++) { + + ret[keys[i]] = vals[i]; + }; + + return ret; + }; + + return Variant(); +}; + +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; + +public: + + 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 ); + } + + JNIEnv *env = ThreadAndroid::get_env(); + + //print_line("argcount "+String::num(p_argcount)); + for(int i=0;i<p_argcount;i++) { + + v[i] = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]); + } + + //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); + + ret = _jobject_to_variant(env, arr); + + } 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; + + case Variant::DICTIONARY: { + + //print_line("call dictionary"); + jobject obj = env->CallObjectMethodA(instance, E->get().method, v); + ret = _jobject_to_variant(env, obj); + + } 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() {} + +}; + + +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 List<InputEvent> key_events; +static bool initialized=false; +static Mutex *input_mutex=NULL; +static Mutex *suspend_mutex=NULL; +static int step=0; +static bool resized=false; +static bool resized_reload=false; +static bool quit_request=false; +static Size2 new_size; +static Vector3 accelerometer; +static HashMap<String,JNISingleton*> jni_singletons; +static jobject godot_io; + +typedef void (*GFXInitFunc)(void *ud,bool gl2); + +static jmethodID _on_video_init=0; +static jobject _godot_instance; + +static jmethodID _openURI=0; +static jmethodID _getDataDir=0; +static jmethodID _getLocale=0; +static jmethodID _getModel=0; +static jmethodID _showKeyboard=0; +static jmethodID _hideKeyboard=0; +static jmethodID _setScreenOrientation=0; +static jmethodID _getUniqueID=0; + + +static void _gfx_init_func(void* ud, bool gl2) { + +} + + +static int _open_uri(const String& p_uri) { + + JNIEnv *env = ThreadAndroid::get_env(); + jstring jStr = env->NewStringUTF(p_uri.utf8().get_data()); + return env->CallIntMethod(godot_io,_openURI,jStr) ; +} + +static String _get_data_dir() { + + JNIEnv *env = ThreadAndroid::get_env(); + jstring s =(jstring)env->CallObjectMethod(godot_io,_getDataDir); + return String(env->GetStringUTFChars( s, NULL )); +} + +static String _get_locale() { + + JNIEnv *env = ThreadAndroid::get_env(); + jstring s =(jstring)env->CallObjectMethod(godot_io,_getLocale); + return String(env->GetStringUTFChars( s, NULL )); +} + +static String _get_model() { + + JNIEnv *env = ThreadAndroid::get_env(); + jstring s =(jstring)env->CallObjectMethod(godot_io,_getModel); + return String(env->GetStringUTFChars( s, NULL )); +} + +static String _get_unique_id() { + + JNIEnv *env = ThreadAndroid::get_env(); + jstring s =(jstring)env->CallObjectMethod(godot_io,_getUniqueID); + return String(env->GetStringUTFChars( s, NULL )); +} + +static void _show_vk(const String& p_existing) { + + JNIEnv* env = ThreadAndroid::get_env(); + jstring jStr = env->NewStringUTF(p_existing.utf8().get_data()); + env->CallVoidMethod(godot_io, _showKeyboard, jStr); +}; + +static void _set_screen_orient(int p_orient) { + + JNIEnv* env = ThreadAndroid::get_env(); + env->CallVoidMethod(godot_io, _setScreenOrientation, p_orient ); +}; + +static void _hide_vk() { + + JNIEnv* env = ThreadAndroid::get_env(); + env->CallVoidMethod(godot_io, _hideKeyboard); +}; + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_initialize(JNIEnv * env, jobject obj, jobject activity,jboolean p_need_reload_hook) { + + __android_log_print(ANDROID_LOG_INFO,"godot","**INIT EVENT! - %p\n",env); + + + initialized=true; + _godot_instance=activity; + + JavaVM *jvm; + env->GetJavaVM(&jvm); + + + __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); + + __android_log_print(ANDROID_LOG_INFO,"godot","STEP4.5, %p",gob); + godot_io=gob; + + _on_video_init = env->GetMethodID(cls, "onVideoInit", "(Z)V"); + + jclass clsio = env->FindClass("com/android/godot/Godot"); + if (cls) { + jclass c = env->GetObjectClass(gob); + _openURI = env->GetMethodID(c,"openURI","(Ljava/lang/String;)I"); + _getDataDir = env->GetMethodID(c,"getDataDir","()Ljava/lang/String;"); + _getLocale = env->GetMethodID(c,"getLocale","()Ljava/lang/String;"); + _getModel = env->GetMethodID(c,"getModel","()Ljava/lang/String;"); + _getUniqueID = env->GetMethodID(c,"getUniqueID","()Ljava/lang/String;"); + _showKeyboard = env->GetMethodID(c,"showKeyboard","(Ljava/lang/String;)V"); + _hideKeyboard = env->GetMethodID(c,"hideKeyboard","()V"); + _setScreenOrientation = env->GetMethodID(c,"setScreenOrientation","(I)V"); + } + + ThreadAndroid::make_default(jvm); + FileAccessJAndroid::setup(gob); + DirAccessJAndroid::setup(gob); + AudioDriverAndroid::setup(gob); + } + + + + os_android = new OS_Android(_gfx_init_func,env,_open_uri,_get_data_dir,_get_locale, _get_model,_show_vk, _hide_vk,_set_screen_orient,_get_unique_id); + os_android->set_need_reload_hooks(p_need_reload_hook); + + 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,false); +#else + Error err = Main::setup("apk",0,NULL,false); +#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","*****SETUP OK"); + + //video driver is determined here, because once initialized, it cant be changed + String vd = Globals::get_singleton()->get("display/driver"); + + + if (vd.to_upper()=="GLES1") + env->CallVoidMethod(_godot_instance, _on_video_init, (jboolean)false); + else + env->CallVoidMethod(_godot_instance, _on_video_init, (jboolean)true); + + __android_log_print(ANDROID_LOG_INFO,"godot","**START"); + + input_mutex=Mutex::create(); + suspend_mutex=Mutex::create(); + + +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_resize(JNIEnv * env, jobject obj, jint width, jint height, jboolean reload) { + + __android_log_print(ANDROID_LOG_INFO,"godot","^_^_^_^_^ resize %lld, %i, %i\n",Thread::get_caller_ID(),width,height); + if (os_android) + os_android->set_display_size(Size2(width,height)); + + /*input_mutex->lock(); + resized=true; + if (reload) + resized_reload=true; + new_size=Size2(width,height); + input_mutex->unlock();*/ + +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_newcontext(JNIEnv * env, jobject obj) { + + __android_log_print(ANDROID_LOG_INFO,"godot","^_^_^_^_^ newcontext %lld\n",Thread::get_caller_ID()); + if (os_android && step > 0) { + + os_android->reload_gfx(); + } + +} + + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_quit(JNIEnv * env, jobject obj) { + + input_mutex->lock(); + quit_request=true; + input_mutex->unlock(); + +} + +static void _initialize_java_modules() { + + + String modules = Globals::get_singleton()->get("android/modules"); + Vector<String> mods = modules.split(",",false); + __android_log_print(ANDROID_LOG_INFO,"godot","mod count: %i",mods.size()); + + if (mods.size()) { + + JNIEnv *env = ThreadAndroid::get_env(); + + jclass activityClass = env->FindClass("com/android/godot/Godot"); + + jmethodID getClassLoader = env->GetMethodID(activityClass,"getClassLoader", "()Ljava/lang/ClassLoader;"); + + jobject cls = env->CallObjectMethod(_godot_instance, getClassLoader); + + jclass classLoader = env->FindClass("java/lang/ClassLoader"); + + jmethodID findClass = env->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + + for (int i=0;i<mods.size();i++) { + + String m = mods[i]; + //jclass singletonClass = env->FindClass(m.utf8().get_data()); + + print_line("LOADING MODULE: "+m); + jstring strClassName = env->NewStringUTF(m.utf8().get_data()); + jclass singletonClass = (jclass)env->CallObjectMethod(cls, findClass, strClassName); + + if (!singletonClass) { + + ERR_EXPLAIN("Couldn't find singleton for class: "+m); + ERR_CONTINUE(!singletonClass); + } + + __android_log_print(ANDROID_LOG_INFO,"godot","****^*^*?^*^*class data %x",singletonClass); + jmethodID initialize = env->GetStaticMethodID(singletonClass, "initialize", "(Landroid/app/Activity;)Lcom/android/godot/Godot$SingletonBase;"); + + jobject obj = env->CallStaticObjectMethod(singletonClass,initialize,_godot_instance); + __android_log_print(ANDROID_LOG_INFO,"godot","****^*^*?^*^*class instance %x",obj); + jobject gob = env->NewGlobalRef(obj); + + + } + + } + +} + + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_step(JNIEnv * env, jobject obj) +{ + + + ThreadAndroid::setup_thread(); + + //__android_log_print(ANDROID_LOG_INFO,"godot","**STEP EVENT! - %p-%i\n",env,Thread::get_caller_ID()); + + + suspend_mutex->lock(); + input_mutex->lock(); + //first time step happens, initialize + if (step == 0) { + // ugly hack to initialize the rest of the engine + // because of the way android forces you to do everything with threads + + _initialize_java_modules(); + + Main::setup2(); + ++step; + suspend_mutex->unlock(); + input_mutex->unlock(); + return; + }; + if (step == 1) { + if (!Main::start()) { + + input_mutex->unlock(); + suspend_mutex->lock(); + return; //should exit instead and print the error + } + + os_android->main_loop_begin(); + ++step; + } + + while(pointer_events.size()) { + + JAndroidPointerEvent jpe=pointer_events.front()->get(); + os_android->process_touch(jpe.what,jpe.pointer,jpe.points); + + pointer_events.pop_front(); + } + + while (key_events.size()) { + + InputEvent event = key_events.front()->get(); + os_android->process_event(event); + + key_events.pop_front(); + }; + + if (quit_request) { + + os_android->main_loop_request_quit(); + quit_request=false; + } + + + input_mutex->unlock(); + + os_android->process_accelerometer(accelerometer); + + if (os_android->main_loop_iterate()==true) { + + jclass cls = env->FindClass("com/android/godot/Godot"); + jmethodID _finish = env->GetMethodID(cls, "forceQuit", "()V"); + env->CallVoidMethod(_godot_instance, _finish); + __android_log_print(ANDROID_LOG_INFO,"godot","**FINISH REQUEST!!! - %p-%i\n",env,Thread::get_caller_ID()); + + } + + suspend_mutex->unlock(); + +} + +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); + +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_key(JNIEnv * env, jobject obj, jint ev, jint p_unicode_char, jboolean p_pressed) { + + InputEvent ievent; + ievent.type = InputEvent::KEY; + ievent.device = 0; + int val = p_unicode_char; + ievent.key.scancode = val; + ievent.key.unicode = val; + if (val == 61448) { + ievent.key.scancode = KEY_BACKSPACE; + ievent.key.unicode = KEY_BACKSPACE; + }; + if (val == 61453) { + ievent.key.scancode = KEY_ENTER; + ievent.key.unicode = KEY_ENTER; + }; + + input_mutex->lock(); + key_events.push_back(ievent); + input_mutex->unlock(); +}; + + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_accelerometer(JNIEnv * env, jobject obj, jfloat x, jfloat y, jfloat z) { + + input_mutex->lock(); + accelerometer=Vector3(x,y,z); + input_mutex->unlock(); + +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_focusin(JNIEnv * env, jobject obj){ + + if (!suspend_mutex) + return; + suspend_mutex->lock(); + + if (os_android && step > 0) + os_android->main_loop_focusin(); + + suspend_mutex->unlock(); +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_focusout(JNIEnv * env, jobject obj){ + + if (!suspend_mutex) + return; + suspend_mutex->lock(); + + if (os_android && step > 0) + os_android->main_loop_focusout(); + + suspend_mutex->unlock(); + +} + + + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_audio(JNIEnv * env, jobject obj) { + + ThreadAndroid::setup_thread(); + AudioDriverAndroid::thread_func(env); + + +} + + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_singleton(JNIEnv * env, jobject obj, jstring name,jobject p_object){ + + String singname = env->GetStringUTFChars( name, NULL ); + JNISingleton *s = memnew( JNISingleton ); + s->set_instance(env->NewGlobalRef(p_object)); + jni_singletons[singname]=s; + + Globals::get_singleton()->add_singleton(Globals::Singleton(singname,s)); + Globals::get_singleton()->set(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}, + {"double", Variant::REAL}, + {"java.lang.String",Variant::STRING}, + {"[I",Variant::INT_ARRAY}, + {"[F",Variant::REAL_ARRAY}, + {"[java.lang.String",Variant::STRING_ARRAY}, + {"com.android.godot.Dictionary", Variant::DICTIONARY}, + {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) { + + print_line("getting sig for " + p_type); + static struct { + const char *name; + const char *sig; + } _type_to_vtype[]={ + {"void","V"}, + {"boolean","Z"}, + {"int","I"}, + {"float","F"}, + {"double","D"}, + {"java.lang.String","Ljava/lang/String;"}, + {"com.android.godot.Dictionary", "Lcom/android/godot/Dictionary;"}, + {"[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 "Ljava/lang/Object;"; +} + +JNIEXPORT jstring JNICALL Java_com_android_godot_GodotLib_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_GodotLib_method(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)); + + +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_callobject(JNIEnv * env, jobject p_obj, jint ID, jstring method, jobjectArray params) { + + String str_method = env->GetStringUTFChars( method, NULL ); + + Object* obj = ObjectDB::get_instance(ID); + ERR_FAIL_COND(!obj); + + int count = env->GetArrayLength(params); + Variant* vlist = (Variant*)alloca(sizeof(Variant) * count); + Variant** vptr = (Variant**)alloca(sizeof(Variant*) * count); + for (int i=0; i<count; i++) { + + jobject obj = env->GetObjectArrayElement(params, i); + Variant v = _jobject_to_variant(env, obj); + memnew_placement(&vlist[i], Variant); + vlist[i] = v; + vptr[i] = &vlist[i]; + }; + + Variant::CallError err; + obj->call(str_method, (const Variant**)vptr, count, err); + // something +}; + + +//Main::cleanup(); + +//return os.get_exit_code(); +#endif diff --git a/platform/android/java_glue.h b/platform/android/java_glue.h new file mode 100644 index 0000000000..7a0666f63d --- /dev/null +++ b/platform/android/java_glue.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* java_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. */ +/*************************************************************************/ +#ifndef ANDROID_NATIVE_ACTIVITY + +#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_initialize(JNIEnv * env, jobject obj, jobject activity,jboolean p_need_reload_hook); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_resize(JNIEnv * env, jobject obj, jint width, jint height, jboolean reload); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_newcontext(JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_step(JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_quit(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); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_key(JNIEnv * env, jobject obj, jint ev, jint p_unicode_char, jboolean p_pressed); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_audio(JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_accelerometer(JNIEnv * env, jobject obj, jfloat x, jfloat y, jfloat z); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_focusin(JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_focusout(JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_singleton(JNIEnv * env, jobject obj, jstring name,jobject p_object); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_method(JNIEnv * env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args); + JNIEXPORT jstring JNICALL Java_com_android_godot_GodotLib_getGlobal(JNIEnv * env, jobject obj, jstring path); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_callobject(JNIEnv * env, jobject obj, jint ID, jstring method, jobjectArray params); +}; + + +#endif +#endif // JAVA_GLUE_H diff --git a/platform/android/logo.png b/platform/android/logo.png Binary files differnew file mode 100644 index 0000000000..a7e2c6f130 --- /dev/null +++ b/platform/android/logo.png diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp new file mode 100644 index 0000000000..9d9da3750e --- /dev/null +++ b/platform/android/os_android.cpp @@ -0,0 +1,709 @@ +/*************************************************************************/ +/* os_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 "os_android.h" +#include "drivers/gles2/rasterizer_gles2.h" +#include "drivers/gles1/rasterizer_gles1.h" +#include "core/io/file_access_buffered_fa.h" +#include "drivers/unix/file_access_unix.h" +#include "drivers/unix/dir_access_unix.h" + +#include "servers/visual/visual_server_raster.h" + +#include "main/main.h" + +#include "core/globals.h" + +#ifdef ANDROID_NATIVE_ACTIVITY +#include "file_access_android.h" +#include "dir_access_android.h" +#else +#include "file_access_jandroid.h" +#include "dir_access_jandroid.h" +#endif + +int OS_Android::get_video_driver_count() const { + + return 2; +} +const char * OS_Android::get_video_driver_name(int p_driver) const { + + return p_driver==0?"GLES2":"GLES1"; +} + +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(); + +#ifdef ANDROID_NATIVE_ACTIVITY + + FileAccess::make_default<FileAccessAndroid>(FileAccess::ACCESS_RESOURCES); + FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA); + FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM); + //FileAccessBufferedFA<FileAccessUnix>::make_default(); + DirAccess::make_default<DirAccessAndroid>(DirAccess::ACCESS_RESOURCES); + DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA); + DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM); + +#else + + FileAccess::make_default<FileAccessBufferedFA<FileAccessJAndroid> >(FileAccess::ACCESS_RESOURCES); + FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA); + FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM); + //FileAccessBufferedFA<FileAccessUnix>::make_default(); + DirAccess::make_default<DirAccessJAndroid>(DirAccess::ACCESS_RESOURCES); + DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA); + DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM); + +#endif + +} + +void OS_Android::set_opengl_extensions(const char* p_gl_extensions) { + + ERR_FAIL_COND(!p_gl_extensions); + gl_extensions=p_gl_extensions; +} + +void OS_Android::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + + use_gl2=p_video_driver!=1; + + + if (gfx_init_func) + gfx_init_func(gfx_init_ud,use_gl2); + + AudioDriverManagerSW::add_driver(&audio_driver_android); + + + if (use_gl2) { + RasterizerGLES2 *rasterizer_gles22=memnew( RasterizerGLES2(false,use_reload_hooks,false,use_reload_hooks ) ); + if (gl_extensions) + rasterizer_gles22->set_extensions(gl_extensions); + rasterizer = rasterizer_gles22; + } else { + rasterizer = memnew( RasterizerGLES1(use_reload_hooks, use_reload_hooks) ); + + } + + 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; + input->set_main_loop(p_main_loop); +#if 0 + + print_line("preGS"); + FileAccess *f=memnew( FileAccessAndroid ); + print("made f %p\n",f); + Error err = f->open("AndroidManifest.xml",FileAccess::READ); + if (err) { + + print("************NO FILE!!\n"); + } else { + print("************YES FILE!!\n"); + } + f->close(); + print_line("end"); + + + AAssetDir* aad = AAssetManager_openDir(FileAccessAndroid::asset_manager,"."); + + if (aad) { + + print_line("DIR OPEN OK"); + + const char *fn= AAssetDir_getNextFileName(aad); + + while(fn) { + + print_line("FNAME: "+String(fn)); + fn= AAssetDir_getNextFileName(aad); + } + + AAssetDir_close(aad); + } else { + + print_line("DIR NO OPEN"); + } + +#endif + +} + +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::main_loop_focusout() { + + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); + audio_driver_android.set_pause(true); + +} + +void OS_Android::main_loop_focusin(){ + + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); + audio_driver_android.set_pause(false); + +} + + +void OS_Android::process_event(InputEvent p_event) { + + p_event.ID = last_id++; + input->parse_input_event(p_event); +}; + + +void OS_Android::process_touch(int p_what,int p_pointer, const Vector<TouchPos>& p_points) { + +// print_line("ev: "+itos(p_what)+" pnt: "+itos(p_pointer)+" pointc: "+itos(p_points.size())); + + 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; + input->parse_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; + input->parse_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; + input->parse_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; + input->parse_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; + input->parse_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.relative_x=p_points[idx].pos.x - touch[i].pos.x; + ev.screen_drag.relative_y=p_points[idx].pos.y - touch[i].pos.y; + input->parse_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; + input->parse_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; + input->parse_input_event(ev); + + } + touch.clear(); + } + + } 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; + input->parse_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; + input->parse_input_event(ev); + touch.remove(i); + i--; + } + } + + } break; + + } + +} + +void OS_Android::process_accelerometer(const Vector3& p_accelerometer) { + + input->set_accelerometer(p_accelerometer); +} + +bool OS_Android::has_touchscreen_ui_hint() const { + + return true; +} + +bool OS_Android::has_virtual_keyboard() const { + + return true; +}; + +void OS_Android::show_virtual_keyboard(const String& p_existing_text,const Rect2& p_screen_rect) { + + if (show_virtual_keyboard_func) { + show_virtual_keyboard_func(p_existing_text); + } else { + + ERR_PRINT("Virtual keyboard not available"); + }; +}; + +void OS_Android::hide_virtual_keyboard() { + + if (hide_virtual_keyboard_func) { + + hide_virtual_keyboard_func(); + } else { + + ERR_PRINT("Virtual keyboard not available"); + }; +}; + + +void OS_Android::init_video_mode(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; +} + +void OS_Android::main_loop_request_quit() { + + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); +} + +void OS_Android::set_display_size(Size2 p_size) { + + default_videomode.width=p_size.x; + default_videomode.height=p_size.y; +} + +void OS_Android::reload_gfx() { + + if (gfx_init_func) + gfx_init_func(gfx_init_ud,use_gl2); + if (rasterizer) + rasterizer->reload_vram(); +} + +Error OS_Android::shell_open(String p_uri) { + + if (open_uri_func) + return open_uri_func(p_uri)?ERR_CANT_OPEN:OK; + return ERR_UNAVAILABLE; +}; + +String OS_Android::get_resource_dir() const { + + return "/"; //android has it's own filesystem for resources inside the APK +} + +String OS_Android::get_locale() const { + + if (get_locale_func) + return get_locale_func(); + return OS_Unix::get_locale(); +} + +String OS_Android::get_model_name() const { + + if (get_model_func) + return get_model_func(); + return OS_Unix::get_model_name(); +} + + +void OS_Android::set_need_reload_hooks(bool p_needs_them) { + + use_reload_hooks=p_needs_them; +} + +String OS_Android::get_data_dir() const { + + if (get_data_dir_func) + return get_data_dir_func(); + return "."; + //return Globals::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); +}; + + +void OS_Android::set_screen_orientation(ScreenOrientation p_orientation) { + + + if (set_screen_orientation_func) + set_screen_orientation_func(p_orientation); +} + + +String OS_Android::get_unique_ID() const { + + if (get_unique_id_func) + return get_unique_id_func(); + return OS::get_unique_ID(); +} + + +OS_Android::OS_Android(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func,GetLocaleFunc p_get_locale_func,GetModelFunc p_get_model_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, SetScreenOrientationFunc p_screen_orient,GetUniqueIDFunc p_get_unique_id) { + + + default_videomode.width=800; + default_videomode.height=600; + default_videomode.fullscreen=true; + default_videomode.resizable=false; + + gfx_init_func=p_gfx_init_func; + gfx_init_ud=p_gfx_init_ud; + main_loop=NULL; + last_id=1; + gl_extensions=NULL; + rasterizer=NULL; + use_gl2=false; + + open_uri_func=p_open_uri_func; + get_data_dir_func=p_get_data_dir_func; + get_locale_func=p_get_locale_func; + get_model_func=p_get_model_func; + get_unique_id_func=p_get_unique_id; + + show_virtual_keyboard_func = p_show_vk; + hide_virtual_keyboard_func = p_hide_vk; + + set_screen_orientation_func=p_screen_orient; + use_reload_hooks=false; + +} + +OS_Android::~OS_Android() { + + +} diff --git a/platform/android/os_android.h b/platform/android/os_android.h new file mode 100644 index 0000000000..93c672927e --- /dev/null +++ b/platform/android/os_android.h @@ -0,0 +1,197 @@ +/*************************************************************************/ +/* os_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 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" + + +#ifdef ANDROID_NATIVE_ACTIVITY +#include "audio_driver_android.h" +#include <android/sensor.h> +#include <android/log.h> +#include <android_native_app_glue.h> + +#else +#include "audio_driver_jandroid.h" + +#endif + +typedef void (*GFXInitFunc)(void *ud,bool gl2); +typedef int (*OpenURIFunc)(const String&); +typedef String (*GetDataDirFunc)(); +typedef String (*GetLocaleFunc)(); +typedef String (*GetModelFunc)(); +typedef String (*GetUniqueIDFunc)(); +typedef void (*ShowVirtualKeyboardFunc)(const String&); +typedef void (*HideVirtualKeyboardFunc)(); +typedef void (*SetScreenOrientationFunc)(int); + +class OS_Android : public OS_Unix { +public: + + struct TouchPos { + int id; + Point2 pos; + }; + +private: + + Vector<TouchPos> touch; + + Point2 last_mouse; + unsigned int last_id; + GFXInitFunc gfx_init_func; + void*gfx_init_ud; + + bool use_gl2; + bool use_reload_hooks; + + Rasterizer *rasterizer; + VisualServer *visual_server; + 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; + const char* gl_extensions; + + InputDefault *input; + VideoMode default_videomode; + MainLoop * main_loop; + + OpenURIFunc open_uri_func; + GetDataDirFunc get_data_dir_func; + GetLocaleFunc get_locale_func; + GetModelFunc get_model_func; + ShowVirtualKeyboardFunc show_virtual_keyboard_func; + HideVirtualKeyboardFunc hide_virtual_keyboard_func; + SetScreenOrientationFunc set_screen_orientation_func; + GetUniqueIDFunc get_unique_id_func; + +public: + + // 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_core(); + 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_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_request_quit(); + void main_loop_end(); + void main_loop_focusout(); + void main_loop_focusin(); + + virtual bool has_touchscreen_ui_hint() const; + + virtual bool has_virtual_keyboard() const; + virtual void show_virtual_keyboard(const String& p_existing_text,const Rect2& p_screen_rect=Rect2()); + virtual void hide_virtual_keyboard(); + + void set_opengl_extensions(const char* p_gl_extensions); + void set_display_size(Size2 p_size); + + void reload_gfx(); + + void set_need_reload_hooks(bool p_needs_them); + virtual void set_screen_orientation(ScreenOrientation p_orientation); + + virtual Error shell_open(String p_uri); + virtual String get_data_dir() const; + virtual String get_resource_dir() const; + virtual String get_locale() const; + virtual String get_model_name() const; + + virtual String get_unique_ID() const; + + + void process_accelerometer(const Vector3& p_accelerometer); + void process_touch(int p_what,int p_pointer, const Vector<TouchPos>& p_points); + void process_event(InputEvent p_event); + void init_video_mode(int p_video_width,int p_video_height); + OS_Android(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func,GetLocaleFunc p_get_locale_func,GetModelFunc p_get_model_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, SetScreenOrientationFunc p_screen_orient,GetUniqueIDFunc p_get_unique_id); + ~OS_Android(); + +}; + +#endif diff --git a/platform/android/platform_config.h b/platform/android/platform_config.h new file mode 100644 index 0000000000..38fc934ae4 --- /dev/null +++ b/platform/android/platform_config.h @@ -0,0 +1,29 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include <alloca.h> diff --git a/platform/android/project.properties.template b/platform/android/project.properties.template new file mode 100644 index 0000000000..72bea9659c --- /dev/null +++ b/platform/android/project.properties.template @@ -0,0 +1,15 @@ +# 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 edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +#android.library=true +target=android-13 diff --git a/platform/android/sign.sh b/platform/android/sign.sh new file mode 100755 index 0000000000..830da05a37 --- /dev/null +++ b/platform/android/sign.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +jarsigner -digestalg SHA1 -sigalg MD5withRSA -verbose -keystore my-release-key.keystore "$1" reduz + +echo "" +echo "" +echo "Checking if APK is verified..." +jarsigner -verify "$1" -verbose -certs + diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp new file mode 100644 index 0000000000..ec6bef89a0 --- /dev/null +++ b/platform/android/thread_jandroid.cpp @@ -0,0 +1,135 @@ +/*************************************************************************/ +/* thread_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 "thread_jandroid.h" + +#include "os/memory.h" + +Thread::ID ThreadAndroid::get_ID() const { + + return id; +} + +Thread* ThreadAndroid::create_thread_jandroid() { + + return memnew( ThreadAndroid ); +} + +void *ThreadAndroid::thread_callback(void *userdata) { + + ThreadAndroid *t=reinterpret_cast<ThreadAndroid*>(userdata); + setup_thread(); + t->id=(ID)pthread_self(); + t->callback(t->user); + return NULL; +} + +Thread* ThreadAndroid::create_func_jandroid(ThreadCreateCallback p_callback,void *p_user,const Settings&) { + + ThreadAndroid *tr= memnew(ThreadAndroid); + tr->callback=p_callback; + tr->user=p_user; + pthread_attr_init(&tr->pthread_attr); + pthread_attr_setdetachstate(&tr->pthread_attr, PTHREAD_CREATE_JOINABLE); + + pthread_create(&tr->pthread, &tr->pthread_attr, thread_callback, tr); + + return tr; +} +Thread::ID ThreadAndroid::get_thread_ID_func_jandroid() { + + return (ID)pthread_self(); +} +void ThreadAndroid::wait_to_finish_func_jandroid(Thread* p_thread) { + + ThreadAndroid *tp=static_cast<ThreadAndroid*>(p_thread); + ERR_FAIL_COND(!tp); + ERR_FAIL_COND(tp->pthread==0); + + pthread_join(tp->pthread,NULL); + tp->pthread=0; +} + +void ThreadAndroid::_thread_destroyed(void* value) { + + /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */ + JNIEnv *env = (JNIEnv*) value; + if (env != NULL) { + java_vm->DetachCurrentThread(); + pthread_setspecific(jvm_key, NULL); + } + +} + +pthread_key_t ThreadAndroid::jvm_key; +JavaVM* ThreadAndroid::java_vm=NULL; + +void ThreadAndroid::setup_thread() { + + if (pthread_getspecific(jvm_key)) + return; //already setup + JNIEnv *env; + java_vm->AttachCurrentThread(&env, NULL); + pthread_setspecific(jvm_key, (void*) env); + +} + +void ThreadAndroid::make_default(JavaVM* p_java_vm) { + + java_vm=p_java_vm; + create_func=create_func_jandroid; + get_thread_ID_func=get_thread_ID_func_jandroid; + wait_to_finish_func=wait_to_finish_func_jandroid; + pthread_key_create(&jvm_key, _thread_destroyed); + setup_thread(); + +} + +JNIEnv *ThreadAndroid::get_env() { + + if (!pthread_getspecific(jvm_key)) { + setup_thread(); + } + + JNIEnv *env=NULL; + int status = java_vm->AttachCurrentThread(&env, NULL); + return env; +} + + +ThreadAndroid::ThreadAndroid() { + + pthread=0; +} + + +ThreadAndroid::~ThreadAndroid() { + +} + + diff --git a/platform/android/thread_jandroid.h b/platform/android/thread_jandroid.h new file mode 100644 index 0000000000..38b4be01ec --- /dev/null +++ b/platform/android/thread_jandroid.h @@ -0,0 +1,82 @@ +/*************************************************************************/ +/* thread_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 THREAD_POSIX_H +#define THREAD_POSIX_H + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + + +#include <sys/types.h> +#include <pthread.h> +#include "os/thread.h" +#include <jni.h> + +class ThreadAndroid : public Thread { + + pthread_t pthread; + pthread_attr_t pthread_attr; + ThreadCreateCallback callback; + void *user; + ID id; + + static Thread* create_thread_jandroid(); + + + static void *thread_callback(void *userdata); + + static Thread* create_func_jandroid(ThreadCreateCallback p_callback,void *,const Settings&); + static ID get_thread_ID_func_jandroid(); + static void wait_to_finish_func_jandroid(Thread* p_thread); + + static void _thread_destroyed(void* value); + ThreadAndroid(); + + static pthread_key_t jvm_key; + static JavaVM* java_vm; +public: + + + + + virtual ID get_ID() const; + + static void make_default(JavaVM* p_java_vm); + static void setup_thread(); + static JNIEnv *get_env(); + + + ~ThreadAndroid(); + +}; + + + +#endif diff --git a/platform/bb10/SCsub b/platform/bb10/SCsub new file mode 100644 index 0000000000..2e8ef47005 --- /dev/null +++ b/platform/bb10/SCsub @@ -0,0 +1,30 @@ +Import('env') + +bb10_lib = [ + + 'bbutil.c', + 'os_bb10.cpp', + 'audio_driver_bb10.cpp', + 'godot_bb10.cpp', + 'payment_service.cpp', +] + +env_bps = env.Clone() +if env['bb10_payment_service'] == "yes": + env_bps.Append(CPPFLAGS=['-DPAYMENT_SERVICE_ENABLED']) + +if env['bb10_lgles_override'] == "yes": + env_bps.Append(CPPFLAGS=['-DBB10_LGLES_OVERRIDE']) + + +prog = None +if env["target"]=="release": + prog = env_bps.Program('#platform/bb10/godot_bb10_opt', bb10_lib) +else: + prog = env_bps.Program('#platform/bb10/godot_bb10', bb10_lib) + +import os +fname = os.path.basename(str(prog[0])) + +env.Command('#bin/'+fname, prog, Copy('bin/'+fname, prog[0])) + diff --git a/platform/bb10/audio_driver_bb10.cpp b/platform/bb10/audio_driver_bb10.cpp new file mode 100644 index 0000000000..ac6a008a00 --- /dev/null +++ b/platform/bb10/audio_driver_bb10.cpp @@ -0,0 +1,255 @@ +/*************************************************************************/ +/* audio_driver_bb10.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_bb10.h" + +#include <errno.h> + +Error AudioDriverBB10::init() { + return init(NULL); +}; + +Error AudioDriverBB10::init(const char* p_name) { + + active=false; + thread_exited=false; + exit_thread=false; + pcm_open = false; + samples_in = NULL; + samples_out = NULL; + + mix_rate = 44100; + output_format = OUTPUT_STEREO; + + char* dev_name; + if (p_name == NULL) { + dev_name = "pcmPreferred"; + } else { + dev_name = (char *) p_name; + } + printf("******** reconnecting to device %s\n", dev_name); + int card, dev; + int ret = snd_pcm_open_name(&pcm_handle, dev_name, SND_PCM_OPEN_PLAYBACK); + ERR_FAIL_COND_V(ret < 0, FAILED); + pcm_open = true; + + snd_pcm_channel_info_t cinfo; + zeromem(&cinfo, sizeof (cinfo)); + cinfo.channel = SND_PCM_CHANNEL_PLAYBACK; + snd_pcm_plugin_info(pcm_handle, &cinfo); + + printf("rates %i, %i, %i, %i, %i\n", cinfo.rates, cinfo.rates & SND_PCM_RATE_44100, cinfo.rates & SND_PCM_RATE_32000, cinfo.rates & SND_PCM_RATE_22050, cinfo.max_rate); + + mix_rate = cinfo.max_rate; + + printf("formats %i, %i, %i\n", cinfo.formats, cinfo.formats & SND_PCM_FMT_S16_BE, cinfo.formats & SND_PCM_FMT_S16_LE); + ERR_FAIL_COND_V(!(cinfo.formats & SND_PCM_FMT_S16_LE), FAILED); + + printf("voices %i\n", cinfo.max_voices); + output_format = cinfo.max_voices >= 2 ? OUTPUT_STEREO : OUTPUT_MONO; + + snd_pcm_channel_params_t cp; + zeromem(&cp, sizeof(cp)); + cp.mode = SND_PCM_MODE_BLOCK; + cp.channel = SND_PCM_CHANNEL_PLAYBACK; + cp.start_mode = SND_PCM_START_DATA; + cp.stop_mode = SND_PCM_STOP_STOP; + //cp.buf.block.frag_size = cinfo.max_fragment_size; + cp.buf.block.frag_size = 512; + cp.buf.block.frags_max = 1; + cp.buf.block.frags_min = 1; + cp.format.interleave = 1; + cp.format.rate = mix_rate; + cp.format.voices = output_format == OUTPUT_MONO ? 1 : 2; + cp.format.format = SND_PCM_SFMT_S16_LE; + + ret = snd_pcm_plugin_params(pcm_handle, &cp); + printf("ret is %i, %i\n", ret, cp.why_failed); + ERR_FAIL_COND_V(ret < 0, FAILED); + + ret = snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK); + ERR_FAIL_COND_V(ret < 0, FAILED); + + snd_mixer_group_t group; + zeromem(&group, sizeof(group)); + snd_pcm_channel_setup_t setup; + zeromem(&setup, sizeof(setup)); + setup.channel = SND_PCM_CHANNEL_PLAYBACK; + setup.mode = SND_PCM_MODE_BLOCK; + setup.mixer_gid = &group.gid; + ret = snd_pcm_plugin_setup(pcm_handle, &setup); + ERR_FAIL_COND_V(ret < 0, FAILED); + + pcm_frag_size = setup.buf.block.frag_size; + pcm_max_frags = 1; + + sample_buf_count = pcm_frag_size * pcm_max_frags / 2; + printf("sample count %i, %i, %i\n", sample_buf_count, pcm_frag_size, pcm_max_frags); + samples_in = memnew_arr(int32_t, sample_buf_count); + samples_out = memnew_arr(int16_t, sample_buf_count); + + thread = Thread::create(AudioDriverBB10::thread_func, this); + + return OK; +}; + +void AudioDriverBB10::thread_func(void* p_udata) { + + AudioDriverBB10* ad = (AudioDriverBB10*)p_udata; + + int channels = (ad->output_format == OUTPUT_MONO ? 1 : 2); + int frame_count = ad->sample_buf_count / channels; + int bytes_out = frame_count * channels * 2; + + while (!ad->exit_thread) { + + + if (!ad->active) { + + for (int i=0; i < ad->sample_buf_count; i++) { + + ad->samples_out[i] = 0; + }; + } else { + + ad->lock(); + + ad->audio_server_process(frame_count, ad->samples_in); + + ad->unlock(); + + for(int i=0;i<frame_count*channels;i++) { + + ad->samples_out[i]=ad->samples_in[i]>>16; + } + }; + + + int todo = bytes_out; + int total = 0; + + while (todo) { + + uint8_t* src = (uint8_t*)ad->samples_out; + int wrote = snd_pcm_plugin_write(ad->pcm_handle, (void*)(src + total), todo); + if (wrote < 0) { + // error? + break; + }; + total += wrote; + todo -= wrote; + if (wrote < todo) { + if (ad->thread_exited) { + break; + }; + printf("pcm_write underrun %i, errno %i\n", (int)ad->thread_exited, errno); + snd_pcm_channel_status_t status; + zeromem(&status, sizeof(status)); + // put in non-blocking mode + snd_pcm_nonblock_mode(ad->pcm_handle, 1); + status.channel = SND_PCM_CHANNEL_PLAYBACK; + int ret = snd_pcm_plugin_status(ad->pcm_handle, &status); + //printf("status return %i, %i, %i, %i, %i\n", ret, errno, status.status, SND_PCM_STATUS_READY, SND_PCM_STATUS_UNDERRUN); + snd_pcm_nonblock_mode(ad->pcm_handle, 0); + if (ret < 0) { + break; + }; + if (status.status == SND_PCM_STATUS_READY || + status.status == SND_PCM_STATUS_UNDERRUN) { + snd_pcm_plugin_prepare(ad->pcm_handle, SND_PCM_CHANNEL_PLAYBACK); + } else { + break; + }; + }; + }; + }; + + snd_pcm_plugin_flush (ad->pcm_handle, SND_PCM_CHANNEL_PLAYBACK); + + ad->thread_exited=true; + printf("**************** audio thread exit\n"); +}; + +void AudioDriverBB10::start() { + + active = true; +}; + +int AudioDriverBB10::get_mix_rate() const { + + return mix_rate; +}; + +AudioDriverSW::OutputFormat AudioDriverBB10::get_output_format() const { + + return output_format; +}; +void AudioDriverBB10::lock() { + + if (!thread) + return; + mutex->lock(); +}; +void AudioDriverBB10::unlock() { + + if (!thread) + return; + mutex->unlock(); +}; + +void AudioDriverBB10::finish() { + + if (!thread) + return; + + exit_thread = true; + Thread::wait_to_finish(thread); + + if (pcm_open) + snd_pcm_close(pcm_handle); + + if (samples_in) { + memdelete_arr(samples_in); + memdelete_arr(samples_out); + }; + + memdelete(thread); + thread = NULL; +}; + +AudioDriverBB10::AudioDriverBB10() { + + mutex = Mutex::create(); +}; + +AudioDriverBB10::~AudioDriverBB10() { + + memdelete(mutex); + mutex = NULL; +}; + diff --git a/platform/bb10/audio_driver_bb10.h b/platform/bb10/audio_driver_bb10.h new file mode 100644 index 0000000000..98b99107d4 --- /dev/null +++ b/platform/bb10/audio_driver_bb10.h @@ -0,0 +1,78 @@ +/*************************************************************************/ +/* audio_driver_bb10.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. */ +/*************************************************************************/ +#include "servers/audio/audio_server_sw.h" + +#include "core/os/thread.h" +#include "core/os/mutex.h" + +#include <sys/asoundlib.h> + +class AudioDriverBB10 : public AudioDriverSW { + + Thread* thread; + Mutex* mutex; + + snd_pcm_t* pcm_handle; + + int32_t* samples_in; + int16_t* samples_out; + int sample_buf_count; + + static void thread_func(void* p_udata); + + int mix_rate; + OutputFormat output_format; + + int pcm_frag_size; + int pcm_max_frags; + + bool active; + bool thread_exited; + mutable bool exit_thread; + bool pcm_open; + +public: + + const char* get_name() const { + return "BB10"; + }; + + virtual Error init(); + virtual Error init(const char* p_name); + virtual void start(); + virtual int get_mix_rate() const; + virtual OutputFormat get_output_format() const; + virtual void lock(); + virtual void unlock(); + virtual void finish(); + + AudioDriverBB10(); + ~AudioDriverBB10(); +}; + diff --git a/platform/bb10/bar/bar-descriptor.xml b/platform/bb10/bar/bar-descriptor.xml new file mode 100644 index 0000000000..df5d34e077 --- /dev/null +++ b/platform/bb10/bar/bar-descriptor.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<qnx xmlns="http://www.qnx.com/schemas/application/1.0"> + +<!-- BlackBerry® 10 application descriptor file. + + Specifies parameters for identifying, installing, and launching native applications on BlackBerry® 10 OS. +--> + + <!-- A universally unique application identifier. Must be unique across all BlackBerry applications. + Using a reverse DNS-style name as the id is recommended. (Eg. com.example.ExampleApplication.) Required. --> + <id>com.godot.game</id> + + <!-- The name that is displayed in the BlackBerry application installer. + May have multiple values for each language. See samples or xsd schema file. Optional. --> + <name>Godot Game</name> + + <!-- A string value of the format <0-999>.<0-999>.<0-999> that represents application version which can be used to check for application upgrade. + Values can also be 1-part or 2-part. It is not necessary to have a 3-part value. + An updated version of application must have a versionNumber value higher than the previous version. Required. --> + <versionNumber>0.0.1</versionNumber> + + <!-- Fourth digit segment of the package version. First three segments are taken from the + <versionNumber> element. Must be an integer from 0 to 2^16-1 --> + <buildId>0</buildId> + + <!-- Description, displayed in the BlackBerry application installer. + May have multiple values for each language. See samples or xsd schema file. Optional. --> + <description>Game made with Godot Engine</description> + + <!-- Name of author which is used for signing. Must match the developer name of your development certificate. --> + <author>You Name or Company</author> + <authorId>authorIDherePlease</authorId> + + <!-- Unique author ID assigned by signing authority. Required if using debug tokens. --> + <!-- <authorId>ABC1234YjsnUk235h</authorId> --> + + <initialWindow> + <aspectRatio>landscape</aspectRatio> + <autoOrients>false</autoOrients> + <systemChrome>none</systemChrome> + <transparent>false</transparent> + </initialWindow> + + <!-- The category where the application appears. Either core.games or core.media. --> + <category>core.games</category> + <permission>read_device_identifying_information</permission> + <permission>access_internet</permission> + <asset path="assets">assets</asset> + <configuration name="Device-Debug"> + <platformArchitecture>armle-v7</platformArchitecture> + <asset path="godot_bb10.qnx.armle" entry="true" type="Qnx/Elf">godot_bb10.qnx.armle</asset> + </configuration> + <configuration name="Device-Release"> + <platformArchitecture>armle-v7</platformArchitecture> + <asset path="godot_bb10_opt.qnx.armle" entry="true" type="Qnx/Elf">godot_bb10_opt.qnx.armle</asset> + </configuration> + <!-- The icon for the application. --> + <icon> + <image>icon.png</image> + </icon> + + <!-- Ensure that shared libraries in the package are found at run-time. --> + <env var="LD_LIBRARY_PATH" value="app/native/lib:/usr/lib/qt4/lib"/> + +</qnx> diff --git a/platform/bb10/bar/icon.png b/platform/bb10/bar/icon.png Binary files differnew file mode 100644 index 0000000000..a837c8009a --- /dev/null +++ b/platform/bb10/bar/icon.png diff --git a/platform/bb10/bbutil.c b/platform/bb10/bbutil.c new file mode 100644 index 0000000000..fce52c5b81 --- /dev/null +++ b/platform/bb10/bbutil.c @@ -0,0 +1,513 @@ +#include <assert.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/keycodes.h> +#include <time.h> + +#include "bbutil.h" + +EGLDisplay egl_disp; +EGLSurface egl_surf; + +static EGLConfig egl_conf; +static EGLContext egl_ctx; + +static screen_context_t screen_ctx; +static screen_window_t screen_win; +static screen_display_t screen_disp; + + +static void +bbutil_egl_perror(const char *msg) { + static const char *errmsg[] = { + "function succeeded", + "EGL is not initialized, or could not be initialized, for the specified display", + "cannot access a requested resource", + "failed to allocate resources for the requested operation", + "an unrecognized attribute or attribute value was passed in an attribute list", + "an EGLConfig argument does not name a valid EGLConfig", + "an EGLContext argument does not name a valid EGLContext", + "the current surface of the calling thread is no longer valid", + "an EGLDisplay argument does not name a valid EGLDisplay", + "arguments are inconsistent", + "an EGLNativePixmapType argument does not refer to a valid native pixmap", + "an EGLNativeWindowType argument does not refer to a valid native window", + "one or more argument values are invalid", + "an EGLSurface argument does not name a valid surface configured for rendering", + "a power management event has occurred", + }; + + fprintf(stderr, "%s: %s\n", msg, errmsg[eglGetError() - EGL_SUCCESS]); +} +EGLConfig bbutil_choose_config(EGLDisplay egl_disp, enum RENDERING_API api) { + EGLConfig egl_conf = (EGLConfig)0; + EGLConfig *egl_configs; + EGLint egl_num_configs; + EGLint val; + EGLBoolean rc; + EGLint i; + + rc = eglGetConfigs(egl_disp, NULL, 0, &egl_num_configs); + if (rc != EGL_TRUE) { + bbutil_egl_perror("eglGetConfigs"); + return egl_conf; + } + if (egl_num_configs == 0) { + fprintf(stderr, "eglGetConfigs: could not find a configuration\n"); + return egl_conf; + } + + egl_configs = malloc(egl_num_configs * sizeof(*egl_configs)); + if (egl_configs == NULL) { + fprintf(stderr, "could not allocate memory for %d EGL configs\n", egl_num_configs); + return egl_conf; + } + + rc = eglGetConfigs(egl_disp, egl_configs, + egl_num_configs, &egl_num_configs); + if (rc != EGL_TRUE) { + bbutil_egl_perror("eglGetConfigs"); + free(egl_configs); + return egl_conf; + } + + for (i = 0; i < egl_num_configs; i++) { + eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_SURFACE_TYPE, &val); + if (!(val & EGL_WINDOW_BIT)) { + continue; + } + + eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_RENDERABLE_TYPE, &val); + if (!(val & api)) { + continue; + } + + eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_DEPTH_SIZE, &val); + if ((api & (GL_ES_1|GL_ES_2)) && (val == 0)) { + continue; + } + + eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_RED_SIZE, &val); + if (val != 8) { + continue; + } + eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_GREEN_SIZE, &val); + if (val != 8) { + continue; + } + + eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_BLUE_SIZE, &val); + if (val != 8) { + continue; + } + + eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_BUFFER_SIZE, &val); + if (val != 32) { + continue; + } + + egl_conf = egl_configs[i]; + break; + } + + free(egl_configs); + + if (egl_conf == (EGLConfig)0) { + fprintf(stderr, "bbutil_choose_config: could not find a matching configuration\n"); + } + + return egl_conf; +} + +int +bbutil_init_egl(screen_context_t ctx, enum RENDERING_API api) { + int usage; + int format = SCREEN_FORMAT_RGBX8888; + int nbuffers = 2; + EGLint interval = 1; + int rc; + EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + + if (api == GL_ES_1) { + usage = SCREEN_USAGE_OPENGL_ES1 | SCREEN_USAGE_ROTATION; + } else if (api == GL_ES_2) { + usage = SCREEN_USAGE_OPENGL_ES2 | SCREEN_USAGE_ROTATION; + } else if (api == VG) { + usage = SCREEN_USAGE_OPENVG | SCREEN_USAGE_ROTATION; + } else { + fprintf(stderr, "invalid api setting\n"); + return EXIT_FAILURE; + } + + //Simple egl initialization + screen_ctx = ctx; + + egl_disp = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (egl_disp == EGL_NO_DISPLAY) { + bbutil_egl_perror("eglGetDisplay"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + rc = eglInitialize(egl_disp, NULL, NULL); + if (rc != EGL_TRUE) { + bbutil_egl_perror("eglInitialize"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + if ((api == GL_ES_1) || (api == GL_ES_2)) { + rc = eglBindAPI(EGL_OPENGL_ES_API); + } else if (api == VG) { + rc = eglBindAPI(EGL_OPENVG_API); + } + + if (rc != EGL_TRUE) { + bbutil_egl_perror("eglBindApi"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + egl_conf = bbutil_choose_config(egl_disp, api); + if (egl_conf == (EGLConfig)0) { + bbutil_terminate(); + return EXIT_FAILURE; + } + + if (api == GL_ES_2) { + egl_ctx = eglCreateContext(egl_disp, egl_conf, EGL_NO_CONTEXT, attributes); + } else { + egl_ctx = eglCreateContext(egl_disp, egl_conf, EGL_NO_CONTEXT, NULL); + } + + if (egl_ctx == EGL_NO_CONTEXT) { + bbutil_egl_perror("eglCreateContext"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + rc = screen_create_window(&screen_win, screen_ctx); + if (rc) { + perror("screen_create_window"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_FORMAT, &format); + if (rc) { + perror("screen_set_window_property_iv(SCREEN_PROPERTY_FORMAT)"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_USAGE, &usage); + if (rc) { + perror("screen_set_window_property_iv(SCREEN_PROPERTY_USAGE)"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + rc = screen_get_window_property_pv(screen_win, SCREEN_PROPERTY_DISPLAY, (void **)&screen_disp); + if (rc) { + perror("screen_get_window_property_pv"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + int screen_resolution[2]; + + rc = screen_get_display_property_iv(screen_disp, SCREEN_PROPERTY_SIZE, screen_resolution); + if (rc) { + perror("screen_get_display_property_iv"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + int angle = atoi(getenv("ORIENTATION")); + + screen_display_mode_t screen_mode; + rc = screen_get_display_property_pv(screen_disp, SCREEN_PROPERTY_MODE, (void**)&screen_mode); + if (rc) { + perror("screen_get_display_property_pv"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + int size[2]; + rc = screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_BUFFER_SIZE, size); + if (rc) { + perror("screen_get_window_property_iv"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + int buffer_size[2] = {size[0], size[1]}; + + if ((angle == 0) || (angle == 180)) { + if (((screen_mode.width > screen_mode.height) && (size[0] < size[1])) || + ((screen_mode.width < screen_mode.height) && (size[0] > size[1]))) { + buffer_size[1] = size[0]; + buffer_size[0] = size[1]; + } + } else if ((angle == 90) || (angle == 270)){ + if (((screen_mode.width > screen_mode.height) && (size[0] > size[1])) || + ((screen_mode.width < screen_mode.height && size[0] < size[1]))) { + buffer_size[1] = size[0]; + buffer_size[0] = size[1]; + } + } else { + fprintf(stderr, "Navigator returned an unexpected orientation angle.\n"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_BUFFER_SIZE, buffer_size); + if (rc) { + perror("screen_set_window_property_iv"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_ROTATION, &angle); + if (rc) { + perror("screen_set_window_property_iv"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + rc = screen_create_window_buffers(screen_win, nbuffers); + if (rc) { + perror("screen_create_window_buffers"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + rc = screen_create_window_group(screen_win, get_window_group_id()); + if (rc) { + perror("screen_create_window_group"); + bbutil_terminate(); + return EXIT_FAILURE; + } + /* if (screen_create_window_group(screen_win, get_window_group_id()) != 0) goto fail; */ + + int idle_mode = SCREEN_IDLE_MODE_KEEP_AWAKE; + screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_IDLE_MODE, &idle_mode); + + egl_surf = eglCreateWindowSurface(egl_disp, egl_conf, screen_win, NULL); + if (egl_surf == EGL_NO_SURFACE) { + bbutil_egl_perror("eglCreateWindowSurface"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + rc = eglMakeCurrent(egl_disp, egl_surf, egl_surf, egl_ctx); + if (rc != EGL_TRUE) { + bbutil_egl_perror("eglMakeCurrent"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + rc = eglSwapInterval(egl_disp, interval); + if (rc != EGL_TRUE) { + bbutil_egl_perror("eglSwapInterval"); + bbutil_terminate(); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int +bbutil_init_gl2d() { +#if 0 + EGLint surface_width, surface_height; + + if ((egl_disp == EGL_NO_DISPLAY) || (egl_surf == EGL_NO_SURFACE) ){ + return EXIT_FAILURE; + } + + eglQuerySurface(egl_disp, egl_surf, EGL_WIDTH, &surface_width); + eglQuerySurface(egl_disp, egl_surf, EGL_HEIGHT, &surface_height); + + glShadeModel(GL_SMOOTH); + + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + + glViewport(0, 0, surface_width, surface_height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glOrthof(0.0f, (float)(surface_width) / (float)(surface_height), 0.0f, 1.0f, -1.0f, 1.0f); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +#endif + + return EXIT_SUCCESS; +} + +int +bbutil_init(screen_context_t ctx, enum RENDERING_API api) { + if (EXIT_SUCCESS != bbutil_init_egl(ctx, api)) { + return EXIT_FAILURE; + } + + if ((GL_ES_1 == api) && (EXIT_SUCCESS != bbutil_init_gl2d())) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int bbutil_is_flipped() { + + int ret; + screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_FLIP, &ret); + return ret; +}; + +int bbutil_get_rotation() { + + int ret; + screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_ROTATION, &ret); + return ret; +}; + + +void +bbutil_terminate() { + //Typical EGL cleanup + if (egl_disp != EGL_NO_DISPLAY) { + eglMakeCurrent(egl_disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (egl_surf != EGL_NO_SURFACE) { + eglDestroySurface(egl_disp, egl_surf); + egl_surf = EGL_NO_SURFACE; + } + if (egl_ctx != EGL_NO_CONTEXT) { + eglDestroyContext(egl_disp, egl_ctx); + egl_ctx = EGL_NO_CONTEXT; + } + if (screen_win != NULL) { + screen_destroy_window(screen_win); + screen_win = NULL; + } + eglTerminate(egl_disp); + egl_disp = EGL_NO_DISPLAY; + } + eglReleaseThread(); +} + +void +bbutil_swap() { + int rc = eglSwapBuffers(egl_disp, egl_surf); + if (rc != EGL_TRUE) { + bbutil_egl_perror("eglSwapBuffers"); + } +} + +void +bbutil_clear() { + glClear(GL_COLOR_BUFFER_BIT); +} + +char * +get_window_group_id() +{ + static char s_window_group_id[16] = ""; + + if (s_window_group_id[0] == '\0') { + snprintf(s_window_group_id, sizeof(s_window_group_id), "%d", getpid()); + } + + return s_window_group_id; +} + + +int bbutil_rotate_screen_surface(int angle) { + int rc, rotation, skip = 1, temp;; + EGLint interval = 1; + int size[2]; + + if ((angle != 0) && (angle != 90) && (angle != 180) && (angle != 270)) { + fprintf(stderr, "Invalid angle\n"); + return EXIT_FAILURE; + } + + rc = screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_ROTATION, &rotation); + if (rc) { + perror("screen_set_window_property_iv"); + return EXIT_FAILURE; + } + + rc = screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_BUFFER_SIZE, size); + if (rc) { + perror("screen_set_window_property_iv"); + return EXIT_FAILURE; + } + + switch (angle - rotation) { + case -270: + case -90: + case 90: + case 270: + temp = size[0]; + size[0] = size[1]; + size[1] = temp; + skip = 0; + break; + } + + if (!skip) { + rc = eglMakeCurrent(egl_disp, NULL, NULL, NULL); + if (rc != EGL_TRUE) { + bbutil_egl_perror("eglMakeCurrent"); + return EXIT_FAILURE; + } + + rc = eglDestroySurface(egl_disp, egl_surf); + if (rc != EGL_TRUE) { + bbutil_egl_perror("eglMakeCurrent"); + return EXIT_FAILURE; + } + + rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_SOURCE_SIZE, size); + if (rc) { + perror("screen_set_window_property_iv"); + return EXIT_FAILURE; + } + + rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_BUFFER_SIZE, size); + if (rc) { + perror("screen_set_window_property_iv"); + return EXIT_FAILURE; + } + egl_surf = eglCreateWindowSurface(egl_disp, egl_conf, screen_win, NULL); + if (egl_surf == EGL_NO_SURFACE) { + bbutil_egl_perror("eglCreateWindowSurface"); + return EXIT_FAILURE; + } + + rc = eglMakeCurrent(egl_disp, egl_surf, egl_surf, egl_ctx); + if (rc != EGL_TRUE) { + bbutil_egl_perror("eglMakeCurrent"); + return EXIT_FAILURE; + } + + rc = eglSwapInterval(egl_disp, interval); + if (rc != EGL_TRUE) { + bbutil_egl_perror("eglSwapInterval"); + return EXIT_FAILURE; + } + } + + rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_ROTATION, &angle); + if (rc) { + perror("screen_set_window_property_iv"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + diff --git a/platform/bb10/bbutil.h b/platform/bb10/bbutil.h new file mode 100644 index 0000000000..8df513cb7d --- /dev/null +++ b/platform/bb10/bbutil.h @@ -0,0 +1,96 @@ +/*************************************************************************/ +/* bbutil.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 _UTILITY_H_INCLUDED +#define _UTILITY_H_INCLUDED + +#include <EGL/egl.h> +#include <GLES2/gl2.h> +#include <screen/screen.h> +#include <sys/platform.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern EGLDisplay egl_disp; +extern EGLSurface egl_surf; + +enum RENDERING_API {GL_ES_1 = EGL_OPENGL_ES_BIT, GL_ES_2 = EGL_OPENGL_ES2_BIT, VG = EGL_OPENVG_BIT}; + +/** + * Initializes EGL, GL and loads a default font + * + * \param libscreen context that will be used for EGL setup + * \return EXIT_SUCCESS if initialization succeeded otherwise EXIT_FAILURE + */ +int bbutil_init(screen_context_t ctx, enum RENDERING_API api); + +/** + * Initializes EGL + * + * \param libscreen context that will be used for EGL setup + * \return EXIT_SUCCESS if initialization succeeded otherwise EXIT_FAILURE + */ +int bbutil_init_egl(screen_context_t ctx, enum RENDERING_API api); + +/** + * Initializes GL 1.1 for simple 2D rendering. GL2 initialization will be added at a later point. + * + * \return EXIT_SUCCESS if initialization succeeded otherwise EXIT_FAILURE + */ +int bbutil_init_gl2d(); + +int bbutil_is_flipped(); +int bbutil_get_rotation(); + +char *get_window_group_id(); + +int bbutil_rotate_screen_surface(int angle); + +/** + * Terminates EGL + */ +void bbutil_terminate(); + +/** + * Swaps default bbutil window surface to the screen + */ +void bbutil_swap(); + +/** + * Clears the screen of any existing text. + * NOTE: must be called after a successful return from bbutil_init() or bbutil_init_egl() call + */ +void bbutil_clear(); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/platform/bb10/detect.py b/platform/bb10/detect.py new file mode 100644 index 0000000000..22940c0f2d --- /dev/null +++ b/platform/bb10/detect.py @@ -0,0 +1,91 @@ +import os +import sys +import string +import methods + + +def is_active(): + return True + +def get_name(): + return "BlackBerry 10" + +def can_build(): + + import os + if (not os.environ.has_key("QNX_TARGET")): + return False + return True + +def get_opts(): + + return [ + ('QNX_HOST', 'path to qnx host', os.environ.get("QNX_HOST", 0)), + ('QNX_TARGET', 'path to qnx target', os.environ.get("QNX_TARGET", 0)), + ('QNX_CONFIGURATION', 'path to qnx configuration', os.environ.get("QNX_CONFIGURATION", 0)), + ('qnx_target', 'Qnx target (armle or x86', 'armle'), + ('bb10_payment_service', 'Enable Payment Service for BlackBerry10', 'yes'), + ('bb10_lgles_override', 'Force legacy GLES (1.1) on iOS', 'no'), + ('bb10_exceptions', 'Use exceptions when compiling on bb10', 'no'), + ] + +def get_flags(): + + return [ + ('lua', 'no'), + ('tools', 'no'), + ('nedmalloc', 'no'), + ] + +def configure(env): + + if env['PLATFORM'] == 'win32': + env.Tool('mingw') + env['SPAWN'] = methods.win32_spawn + + env['qnx_target_ver'] = env['qnx_target'] + if env['qnx_target'] == "armle": + env['qnx_prefix'] = 'ntoarmv7' + env['qnx_target_ver'] = 'armle-v7' + else: + env['qnx_prefix'] = 'ntox86' + + env['OBJSUFFIX'] = ".qnx.${qnx_target}.o" + env['LIBSUFFIX'] = ".qnx.${qnx_target}.a" + env['PROGSUFFIX'] = ".qnx.${qnx_target}" + print("PROGSUFFIX: "+env['PROGSUFFIX']+" target: "+env['qnx_target']) + + env.PrependENVPath('PATH', env['QNX_CONFIGURATION'] + '/bin') + env.PrependENVPath('PATH', env['QNX_CONFIGURATION'] + '/usr/bin') + env['ENV']['QNX_HOST'] = env['QNX_HOST'] + env['ENV']['QNX_TARGET'] = env['QNX_TARGET'] + env['ENV']['QNX_CONFIGURATION'] = env['QNX_CONFIGURATION'] + + env['CC'] = '$qnx_prefix-gcc' + env['CXX'] = '$qnx_prefix-g++' + env['AR'] = '$qnx_prefix-ar' + env['RANLIB'] = '$qnx_prefix-ranlib' + + env.Append(CPPPATH = ['#platform/bb10']) + env.Append(LIBPATH = ['#platform/bb10/lib/$qnx_target', '#platform/bb10/lib/$qnx_target_ver']) + env.Append(CCFLAGS = string.split('-DBB10_ENABLED -DUNIX_ENABLED -DGLES2_ENABLED -DGLES1_ENABLED -D_LITTLE_ENDIAN -DNO_THREADS -DNO_FCNTL')) + if env['bb10_exceptions']=="yes": + env.Append(CCFLAGS = ['-fexceptions']) + else: + env.Append(CCFLAGS = ['-fno-exceptions']) + + #env.Append(LINKFLAGS = string.split() + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['-O3','-DRELEASE_BUILD']) + env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX'] + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-g', '-O0','-DDEBUG_ENABLED', '-D_DEBUG']) + env.Append(LINKFLAGS=['-g']) + + env.Append(LIBS=['bps', 'pps', 'screen', 'socket', 'EGL', 'GLESv2', 'GLESv1_CM', 'm', 'asound']) + diff --git a/platform/bb10/export/export.cpp b/platform/bb10/export/export.cpp new file mode 100644 index 0000000000..0a19e71f08 --- /dev/null +++ b/platform/bb10/export/export.cpp @@ -0,0 +1,805 @@ +#include "version.h" +#include "export.h" +#include "tools/editor/editor_settings.h" +#include "tools/editor/editor_import_export.h" +#include "tools/editor/editor_node.h" +#include "io/zip_io.h" +#include "io/marshalls.h" +#include "globals.h" +#include "os/file_access.h" +#include "os/os.h" +#include "platform/bb10/logo.h" +#include "io/xml_parser.h" + +#define MAX_DEVICES 5 + +class EditorExportPlatformBB10 : public EditorExportPlatform { + + OBJ_TYPE( EditorExportPlatformBB10,EditorExportPlatform ); + + String custom_package; + + int version_code; + String version_name; + String package; + String name; + String category; + String description; + String author_name; + String author_id; + String icon; + + + + struct Device { + + int index; + String name; + String description; + }; + + Vector<Device> devices; + bool devices_changed; + Mutex *device_lock; + Thread *device_thread; + Ref<ImageTexture> logo; + + volatile bool quit_request; + + + static void _device_poll_thread(void *ud); + + void _fix_descriptor(Vector<uint8_t>& p_manifest); +protected: + + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + +public: + + virtual String get_name() const { return "BlackBerry 10"; } + virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_ETC1; } + virtual Ref<Texture> get_logo() const { return logo; } + + + virtual bool poll_devices(); + virtual int get_device_count() const; + virtual String get_device_name(int p_device) const; + virtual String get_device_info(int p_device) const; + virtual Error run(int p_device); + + virtual bool requieres_password(bool p_debug) const { return !p_debug; } + virtual String get_binary_extension() const { return "bar"; } + virtual Error export_project(const String& p_path,bool p_debug,const String& p_password=""); + + virtual bool can_export(String *r_error=NULL) const; + + EditorExportPlatformBB10(); + ~EditorExportPlatformBB10(); +}; + +bool EditorExportPlatformBB10::_set(const StringName& p_name, const Variant& p_value) { + + String n=p_name; + + if (n=="version/code") + version_code=p_value; + else if (n=="version/name") + version_name=p_value; + else if (n=="package/unique_name") + package=p_value; + else if (n=="package/category") + category=p_value; + else if (n=="package/name") + name=p_value; + else if (n=="package/description") + description=p_value; + else if (n=="package/icon") + icon=p_value; + else if (n=="package/custom_template") + custom_package=p_value; + else if (n=="release/author") + author_name=p_value; + else if (n=="release/author_id") + author_id=p_value; + else + return false; + + return true; +} + +bool EditorExportPlatformBB10::_get(const StringName& p_name,Variant &r_ret) const{ + + String n=p_name; + + if (n=="version/code") + r_ret=version_code; + else if (n=="version/name") + r_ret=version_name; + else if (n=="package/unique_name") + r_ret=package; + else if (n=="package/category") + r_ret=category; + else if (n=="package/name") + r_ret=name; + else if (n=="package/description") + r_ret=description; + else if (n=="package/icon") + r_ret=icon; + else if (n=="package/custom_template") + r_ret=custom_package; + else if (n=="release/author") + r_ret=author_name; + else if (n=="release/author_id") + r_ret=author_id; + else + return false; + + return true; +} +void EditorExportPlatformBB10::_get_property_list( List<PropertyInfo> *p_list) const{ + + p_list->push_back( PropertyInfo( Variant::INT, "version/code", PROPERTY_HINT_RANGE,"1,65535,1")); + p_list->push_back( PropertyInfo( Variant::STRING, "version/name") ); + p_list->push_back( PropertyInfo( Variant::STRING, "package/unique_name") ); + p_list->push_back( PropertyInfo( Variant::STRING, "package/category") ); + p_list->push_back( PropertyInfo( Variant::STRING, "package/name") ); + p_list->push_back( PropertyInfo( Variant::STRING, "package/description",PROPERTY_HINT_MULTILINE_TEXT) ); + p_list->push_back( PropertyInfo( Variant::STRING, "package/icon",PROPERTY_HINT_FILE,"png") ); + p_list->push_back( PropertyInfo( Variant::STRING, "package/custom_template", PROPERTY_HINT_FILE,"zip")); + p_list->push_back( PropertyInfo( Variant::STRING, "release/author") ); + p_list->push_back( PropertyInfo( Variant::STRING, "release/author_id") ); + + //p_list->push_back( PropertyInfo( Variant::INT, "resources/pack_mode", PROPERTY_HINT_ENUM,"Copy,Single Exec.,Pack (.pck),Bundles (Optical)")); + +} + +void EditorExportPlatformBB10::_fix_descriptor(Vector<uint8_t>& p_descriptor) { + + String fpath = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp_bar-settings.xml"); + { + FileAccessRef f = FileAccess::open(fpath,FileAccess::WRITE); + f->store_buffer(p_descriptor.ptr(),p_descriptor.size()); + } + + Ref<XMLParser> parser = memnew( XMLParser ); + Error err = parser->open(fpath); + ERR_FAIL_COND(err!=OK); + + String txt; + err = parser->read(); + Vector<String> depth; + + while(err!=ERR_FILE_EOF) { + + ERR_FAIL_COND(err!=OK); + + switch(parser->get_node_type()) { + + case XMLParser::NODE_NONE: { + print_line("???"); + } break; + case XMLParser::NODE_ELEMENT: { + String e="<"; + e+=parser->get_node_name(); + for(int i=0;i<parser->get_attribute_count();i++) { + e+=" "; + e+=parser->get_attribute_name(i)+"=\""; + e+=parser->get_attribute_value(i)+"\" "; + } + + + + if (parser->is_empty()) { + e+="/"; + } else { + depth.push_back(parser->get_node_name()); + } + + e+=">"; + txt+=e; + + } break; + case XMLParser::NODE_ELEMENT_END: { + + txt+="</"+parser->get_node_name()+">"; + if (depth.size() && depth[depth.size()-1]==parser->get_node_name()) { + depth.resize(depth.size()-1); + } + + + } break; + case XMLParser::NODE_TEXT: { + if (depth.size()==2 && depth[0]=="qnx" && depth[1]=="id") { + + txt+=package; + } else if (depth.size()==2 && depth[0]=="qnx" && depth[1]=="name") { + + String aname; + if (this->name!="") { + aname=this->name; + } else { + aname = Globals::get_singleton()->get("application/name"); + + } + + if (aname=="") { + aname=_MKSTR(VERSION_NAME); + } + + txt+=aname; + + } else if (depth.size()==2 && depth[0]=="qnx" && depth[1]=="versionNumber") { + txt+=itos(version_code); + } else if (depth.size()==2 && depth[0]=="qnx" && depth[1]=="description") { + txt+=description; + } else if (depth.size()==2 && depth[0]=="qnx" && depth[1]=="author") { + txt+=author_name; + } else if (depth.size()==2 && depth[0]=="qnx" && depth[1]=="authorId") { + txt+=author_id; + } else if (depth.size()==2 && depth[0]=="qnx" && depth[1]=="category") { + txt+=category; + } else { + txt+=parser->get_node_data(); + } + } break; + case XMLParser::NODE_COMMENT: { + txt+="<!--"+parser->get_node_name()+"-->"; + } break; + case XMLParser::NODE_CDATA: { + //ignore + //print_line("cdata"); + } break; + case XMLParser::NODE_UNKNOWN: { + //ignore + txt+="<"+parser->get_node_name()+">"; + } break; + } + + err = parser->read(); + } + + + CharString cs = txt.utf8(); + p_descriptor.resize(cs.length()); + for(int i=0;i<cs.length();i++) + p_descriptor[i]=cs[i]; + +} + + + +Error EditorExportPlatformBB10::export_project(const String& p_path,bool p_debug,const String& p_password) { + + + EditorProgress ep("export","Exporting for BlackBerry 10",104); + + String template_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + String src_template=custom_package!=""?custom_package:template_path.plus_file("bb10.zip"); + + + FileAccess *src_f=NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + ep.step("Creating FileSystem for BAR",0); + + unzFile pkg = unzOpen2(src_template.utf8().get_data(), &io); + if (!pkg) { + + EditorNode::add_io_error("Could not find template zip to export:\n"+src_template); + return ERR_FILE_NOT_FOUND; + } + + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + da->change_dir(EditorSettings::get_singleton()->get_settings_path()); + + + if (da->change_dir("tmp")!=OK) { + da->make_dir("tmp"); + if (da->change_dir("tmp")!=OK) + return ERR_CANT_CREATE; + } + + if (da->change_dir("bb10_export")!=OK) { + da->make_dir("bb10_export"); + if (da->change_dir("bb10_export")!=OK) { + return ERR_CANT_CREATE; + } + } + + + String bar_dir = da->get_current_dir(); + if (bar_dir.ends_with("/")) { + bar_dir=bar_dir.substr(0,bar_dir.length()-1); + } + + //THIS IS SUPER, SUPER DANGEROUS!!!! + //CAREFUL WITH THIS CODE, MIGHT DELETE USERS HARD DRIVE OR HOME DIR + //EXTRA CHECKS ARE IN PLACE EVERYWERE TO MAKE SURE NOTHING BAD HAPPENS BUT STILL.... + //BE SUPER CAREFUL WITH THIS PLEASE!!! + //BLACKBERRY THIS IS YOUR FAULT FOR NOT MAKING A BETTER WAY!! + + if (bar_dir.ends_with("bb10_export")) { + Error err = da->erase_contents_recursive(); + if (err!=OK) { + EditorNode::add_io_error("Can't ensure that dir is empty:\n"+bar_dir); + ERR_FAIL_COND_V(err!=OK,err); + } + + } else { + print_line("ARE YOU CRAZY??? THIS IS A SERIOUS BUG HERE!!!"); + ERR_FAIL_V(ERR_OMFG_THIS_IS_VERY_VERY_BAD); + } + + + ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(pkg); + + + + while(ret==UNZ_OK) { + + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(pkg,&info,fname,16384,NULL,0,NULL,0); + + String file=fname; + + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg,data.ptr(),data.size()); + unzCloseCurrentFile(pkg); + + //write + + if (file=="bar-descriptor.xml") { + + _fix_descriptor(data); + } + + if (file=="icon.png") { + bool found=false; + + if (this->icon!="" && this->icon.ends_with(".png")) { + + FileAccess *f = FileAccess::open(this->icon,FileAccess::READ); + if (f) { + + data.resize(f->get_len()); + f->get_buffer(data.ptr(),data.size()); + memdelete(f); + found=true; + } + + } + + if (!found) { + + String appicon = Globals::get_singleton()->get("application/icon"); + if (appicon!="" && appicon.ends_with(".png")) { + FileAccess*f = FileAccess::open(appicon,FileAccess::READ); + if (f) { + data.resize(f->get_len()); + f->get_buffer(data.ptr(),data.size()); + memdelete(f); + } + } + } + } + + + if (file.find("/")) { + + da->make_dir_recursive(file.get_base_dir()); + } + + FileAccessRef wf = FileAccess::open(bar_dir.plus_file(file),FileAccess::WRITE); + wf->store_buffer(data.ptr(),data.size()); + + ret = unzGoToNextFile(pkg); + } + + ep.step("Finding Files..",1); + + Vector<StringName> files=get_dependencies(false); + + ep.step("Adding Files..",2); + + da->change_dir(bar_dir); + da->make_dir("assets"); + Error err = da->change_dir("assets"); + ERR_FAIL_COND_V(err,err); + + String asset_dir=da->get_current_dir(); + if (!asset_dir.ends_with("/")) + asset_dir+="/"; + + for(int i=0;i<files.size();i++) { + + String fname=files[i]; + Vector<uint8_t> data = get_exported_file(fname); + /* + FileAccess *f=FileAccess::open(files[i],FileAccess::READ); + if (!f) { + EditorNode::add_io_error("Couldn't read: "+String(files[i])); + } + ERR_CONTINUE(!f); + data.resize(f->get_len()); + f->get_buffer(data.ptr(),data.size()); +*/ + String dst_path=fname; + dst_path=dst_path.replace_first("res://",asset_dir); + + da->make_dir_recursive(dst_path.get_base_dir()); + + ep.step("Adding File: "+String(files[i]).get_file(),3+i*100/files.size()); + + FileAccessRef fr = FileAccess::open(dst_path,FileAccess::WRITE); + fr->store_buffer(data.ptr(),data.size()); + } + + + ep.step("Creating BAR Package..",104); + + String bb_packager=EditorSettings::get_singleton()->get("blackberry/host_tools"); + bb_packager=bb_packager.plus_file("blackberry-nativepackager"); + if (OS::get_singleton()->get_name()=="Windows") + bb_packager+=".exe"; + + + if (!FileAccess::exists(bb_packager)) { + EditorNode::add_io_error("Can't find packager:\n"+bb_packager); + return ERR_CANT_OPEN; + } + + List<String> args; + args.push_back("-package"); + args.push_back(p_path); + if (p_debug) { + + String debug_token=EditorSettings::get_singleton()->get("blackberry/debug_token"); + if (!FileAccess::exists(debug_token)) { + EditorNode::add_io_error("Debug token not found!"); + } else { + args.push_back("-debugToken"); + args.push_back(debug_token); + } + args.push_back("-devMode"); + args.push_back("-configuration"); + args.push_back("Device-Debug"); + } else { + + args.push_back("-configuration"); + args.push_back("Device-Release"); + } + args.push_back(bar_dir.plus_file("bar-descriptor.xml")); + + int ec; + + err = OS::get_singleton()->execute(bb_packager,args,true,NULL,NULL,&ec); + + if (err!=OK) + return err; + if (ec!=0) + return ERR_CANT_CREATE; + + return OK; + +} + + +bool EditorExportPlatformBB10::poll_devices() { + + bool dc=devices_changed; + devices_changed=false; + return dc; +} + +int EditorExportPlatformBB10::get_device_count() const { + + device_lock->lock(); + int dc=devices.size(); + device_lock->unlock(); + + return dc; + +} +String EditorExportPlatformBB10::get_device_name(int p_device) const { + + ERR_FAIL_INDEX_V(p_device,devices.size(),""); + device_lock->lock(); + String s=devices[p_device].name; + device_lock->unlock(); + return s; +} +String EditorExportPlatformBB10::get_device_info(int p_device) const { + + ERR_FAIL_INDEX_V(p_device,devices.size(),""); + device_lock->lock(); + String s=devices[p_device].description; + device_lock->unlock(); + return s; +} + +void EditorExportPlatformBB10::_device_poll_thread(void *ud) { + + EditorExportPlatformBB10 *ea=(EditorExportPlatformBB10 *)ud; + + while(!ea->quit_request) { + + String bb_deploy=EditorSettings::get_singleton()->get("blackberry/host_tools"); + bb_deploy=bb_deploy.plus_file("blackberry-deploy"); + bool windows = OS::get_singleton()->get_name()=="Windows"; + if (windows) + bb_deploy+=".exe"; + + if (!FileAccess::exists(bb_deploy)) { + OS::get_singleton()->delay_usec(3000000); + continue; //adb not configured + } + + Vector<Device> devices; + + + for (int i=0;i<MAX_DEVICES;i++) { + + String host = EditorSettings::get_singleton()->get("blackberry/device_"+itos(i+1)+"/host"); + if (host==String()) + continue; + String pass = EditorSettings::get_singleton()->get("blackberry/device_"+itos(i+1)+"/password"); + if (pass==String()) + continue; + + List<String> args; + args.push_back("-listDeviceInfo"); + args.push_back(host); + args.push_back("-password"); + args.push_back(pass); + + + int ec; + String dp; + + Error err = OS::get_singleton()->execute(bb_deploy,args,true,NULL,&dp,&ec); + + if (err==OK && ec==0) { + + Device dev; + dev.index=i; + String descr; + Vector<String> ls=dp.split("\n"); + + for(int i=0;i<ls.size();i++) { + + String l = ls[i].strip_edges(); + if (l.begins_with("modelfullname::")) { + dev.name=l.get_slice("::",1); + descr+="Model: "+dev.name+"\n"; + } + if (l.begins_with("modelnumber::")) { + String s = l.get_slice("::",1); + dev.name+=" ("+s+")"; + descr+="Model Number: "+s+"\n"; + } + if (l.begins_with("scmbundle::")) + descr+="OS Version: "+l.get_slice("::",1)+"\n"; + if (l.begins_with("[n]debug_token_expiration::")) + descr+="Debug Token Expires:: "+l.get_slice("::",1)+"\n"; + + } + + dev.description=descr; + devices.push_back(dev); + } + + } + + bool changed=false; + + + ea->device_lock->lock(); + + if (ea->devices.size()!=devices.size()) { + changed=true; + } else { + + for(int i=0;i<ea->devices.size();i++) { + + if (ea->devices[i].index!=devices[i].index) { + changed=true; + break; + } + } + } + + if (changed) { + + ea->devices=devices; + ea->devices_changed=true; + } + + ea->device_lock->unlock(); + + OS::get_singleton()->delay_usec(3000000); + + } + +} + +Error EditorExportPlatformBB10::run(int p_device) { + + ERR_FAIL_INDEX_V(p_device,devices.size(),ERR_INVALID_PARAMETER); + + String bb_deploy=EditorSettings::get_singleton()->get("blackberry/host_tools"); + bb_deploy=bb_deploy.plus_file("blackberry-deploy"); + if (OS::get_singleton()->get_name()=="Windows") + bb_deploy+=".exe"; + + if (!FileAccess::exists(bb_deploy)) { + EditorNode::add_io_error("Blackberry Deploy not found:\n"+bb_deploy); + return ERR_FILE_NOT_FOUND; + } + + + device_lock->lock(); + + + EditorProgress ep("run","Running on "+devices[p_device].name,3); + + //export_temp + ep.step("Exporting APK",0); + + String export_to=EditorSettings::get_singleton()->get_settings_path().plus_file("/tmp/tmpexport.bar"); + Error err = export_project(export_to,true); + if (err) { + device_lock->unlock(); + return err; + } +#if 0 + ep.step("Uninstalling..",1); + + print_line("Uninstalling previous version: "+devices[p_device].name); + List<String> args; + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("uninstall"); + args.push_back(package); + int rv; + err = OS::get_singleton()->execute(adb,args,true,NULL,NULL,&rv); + + if (err || rv!=0) { + EditorNode::add_io_error("Could not install to device."); + device_lock->unlock(); + return ERR_CANT_CREATE; + } + + print_line("Installing into device (please wait..): "+devices[p_device].name); + +#endif + ep.step("Installing to Device (please wait..)..",2); + + List<String> args; + args.clear(); + args.push_back("-installApp"); + args.push_back("-launchApp"); + args.push_back("-device"); + int idx = devices[p_device].index; + String host = EditorSettings::get_singleton()->get("blackberry/device_"+itos(p_device+1)+"/host"); + String pass = EditorSettings::get_singleton()->get("blackberry/device_"+itos(p_device+1)+"/password"); + args.push_back(host); + args.push_back("-password"); + args.push_back(pass); + args.push_back(export_to); + + int rv; + err = OS::get_singleton()->execute(bb_deploy,args,true,NULL,NULL,&rv); + if (err || rv!=0) { + EditorNode::add_io_error("Could not install to device."); + device_lock->unlock(); + return ERR_CANT_CREATE; + } + + device_lock->unlock(); + return OK; + + +} + + +EditorExportPlatformBB10::EditorExportPlatformBB10() { + + version_code=1; + version_name="1.0"; + package="com.godot.noname"; + category="core.games"; + name=""; + author_name="Cert. Name"; + author_id="Cert. ID"; + description="Game made with Godot Engine"; + + device_lock = Mutex::create(); + quit_request=false; + + device_thread=Thread::create(_device_poll_thread,this); + devices_changed=true; + + Image img( _bb10_logo ); + logo = Ref<ImageTexture>( memnew( ImageTexture )); + logo->create_from_image(img); +} + +bool EditorExportPlatformBB10::can_export(String *r_error) const { + + bool valid=true; + String bb_deploy=EditorSettings::get_singleton()->get("blackberry/host_tools"); + String err; + + if (!FileAccess::exists(bb_deploy.plus_file("blackberry-deploy"))) { + + valid=false; + err+="Blackberry host tools not configured in editor settings.\n"; + } + + String exe_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + if (!FileAccess::exists(exe_path+"bb10.zip")) { + valid=false; + err+="No export template found.\nDownload and install export templates.\n"; + } + + String debug_token=EditorSettings::get_singleton()->get("blackberry/debug_token"); + + if (!FileAccess::exists(debug_token)) { + valid=false; + err+="No debug token set, will not be able to test on device.\n"; + } + + + if (custom_package!="" && !FileAccess::exists(custom_package)) { + valid=false; + err+="Custom release package not found.\n"; + } + + if (r_error) + *r_error=err; + + return valid; +} + + +EditorExportPlatformBB10::~EditorExportPlatformBB10() { + + quit_request=true; + Thread::wait_to_finish(device_thread); +} + + +void register_bb10_exporter() { + + EDITOR_DEF("blackberry/host_tools",""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"blackberry/host_tools",PROPERTY_HINT_GLOBAL_DIR)); + EDITOR_DEF("blackberry/debug_token",""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"blackberry/debug_token",PROPERTY_HINT_GLOBAL_FILE,"bar")); + EDITOR_DEF("blackberry/device_1/host",""); + EDITOR_DEF("blackberry/device_1/password",""); + EDITOR_DEF("blackberry/device_2/host",""); + EDITOR_DEF("blackberry/device_2/password",""); + EDITOR_DEF("blackberry/device_3/host",""); + EDITOR_DEF("blackberry/device_3/password",""); + EDITOR_DEF("blackberry/device_4/host",""); + EDITOR_DEF("blackberry/device_4/password",""); + EDITOR_DEF("blackberry/device_5/host",""); + EDITOR_DEF("blackberry/device_5/password",""); + + Ref<EditorExportPlatformBB10> exporter = Ref<EditorExportPlatformBB10>( memnew(EditorExportPlatformBB10) ); + EditorImportExport::get_singleton()->add_export_platform(exporter); + + +} + diff --git a/platform/bb10/export/export.h b/platform/bb10/export/export.h new file mode 100644 index 0000000000..06c7a681be --- /dev/null +++ b/platform/bb10/export/export.h @@ -0,0 +1,3 @@ + + +void register_bb10_exporter(); diff --git a/platform/bb10/godot_bb10.cpp b/platform/bb10/godot_bb10.cpp new file mode 100644 index 0000000000..f7e154d647 --- /dev/null +++ b/platform/bb10/godot_bb10.cpp @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* godot_bb10.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 "main/main.h" +#include "os_bb10.h" + +#include <unistd.h> + +int main(int argc, char* argv[]) { + + OSBB10 os; + + Error err = Main::setup(argv[0],argc-1,&argv[1]); + if (err!=OK) + return 255; + + if (Main::start()) + os.run(); // it is actually the OS that decides how to run + Main::cleanup(); + + return os.get_exit_code(); +} + diff --git a/platform/bb10/logo.png b/platform/bb10/logo.png Binary files differnew file mode 100644 index 0000000000..d0fb1966ae --- /dev/null +++ b/platform/bb10/logo.png diff --git a/platform/bb10/os_bb10.cpp b/platform/bb10/os_bb10.cpp new file mode 100644 index 0000000000..ff43a68b1d --- /dev/null +++ b/platform/bb10/os_bb10.cpp @@ -0,0 +1,630 @@ +/*************************************************************************/ +/* os_bb10.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 "os_bb10.h" + +#include "drivers/gles2/rasterizer_gles2.h" +#include "drivers/gles1/rasterizer_gles1.h" +#include "servers/visual/visual_server_raster.h" +#include "core/os/dir_access.h" + +#include "core/globals.h" +#include "main/main.h" +#include "bbutil.h" +#include <stdlib.h> +#include <stdbool.h> +#include <assert.h> +#include "core/os/keyboard.h" + +#include "bps/bps.h" +#include "bps/screen.h" +#include "bps/navigator.h" +#include "bps/accelerometer.h" +#include "bps/orientation.h" +#include "bps/virtualkeyboard.h" +#include "bps/audiodevice.h" + +#ifdef BB10_SCORELOOP_ENABLED +#include "modules/scoreloop/scoreloop_bb10.h" +#endif + +static char launch_dir[512]; +char* launch_dir_ptr; + +int OSBB10::get_video_driver_count() const { + + return 1; +} +const char * OSBB10::get_video_driver_name(int p_driver) const { + + return "GLES2"; +} + +OS::VideoMode OSBB10::get_default_video_mode() const { + + return OS::VideoMode(); +} + +int OSBB10::get_audio_driver_count() const { + + return 1; +} +const char * OSBB10::get_audio_driver_name(int p_driver) const { + + return "BB10"; +} + +void OSBB10::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + data_dir = getenv("HOME"); + + //Create a screen context that will be used to create an EGL surface to to receive libscreen events + screen_create_context(&screen_cxt,0); + + //Initialize BPS library + bps_initialize(); + + //Use utility code to initialize EGL for 2D rendering with GL ES 1.1 + enum RENDERING_API api = GL_ES_2; + #ifdef BB10_LGLES_OVERRIDE + api = GL_ES_1; + #endif + if (EXIT_SUCCESS != bbutil_init(screen_cxt, api)) { + bbutil_terminate(); + screen_destroy_context(screen_cxt); + return; + }; + + EGLint surface_width, surface_height; + + eglQuerySurface(egl_disp, egl_surf, EGL_WIDTH, &surface_width); + eglQuerySurface(egl_disp, egl_surf, EGL_HEIGHT, &surface_height); + printf("screen size: %ix%i\n", surface_width, surface_height); + VideoMode mode; + mode.width = surface_width; + mode.height = surface_height; + mode.fullscreen = true; + mode.resizable = false; + set_video_mode(mode); + + //Signal BPS library that navigator and screen events will be requested + screen_request_events(screen_cxt); + navigator_request_events(0); + virtualkeyboard_request_events(0); + audiodevice_request_events(0); + + #ifdef DEBUG_ENABLED + bps_set_verbosity(3); + #endif + + accel_supported = accelerometer_is_supported(); + if (accel_supported) + accelerometer_set_update_frequency(FREQ_40_HZ); + pitch = 0; + roll = 0; + + #ifdef BB10_LGLES_OVERRIDE + rasterizer = memnew( RasterizerGLES1(false) ); + #else + rasterizer = memnew( RasterizerGLES2(false, false) ); + #endif + + visual_server = memnew( VisualServerRaster(rasterizer) ); + visual_server->init(); + visual_server->cursor_set_visible(false, 0); + + audio_driver = memnew(AudioDriverBB10); + audio_driver->set_singleton(); + audio_driver->init(NULL); + + 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 ); + + #ifdef PAYMENT_SERVICE_ENABLED + payment_service = memnew(PaymentService); + Globals::get_singleton()->add_singleton(Globals::Singleton("InAppStore", payment_service)); + #endif + +} + +void OSBB10::set_main_loop( MainLoop * p_main_loop ) { + + input->set_main_loop(p_main_loop); + main_loop=p_main_loop; +} + +void OSBB10::delete_main_loop() { + + memdelete( main_loop ); + main_loop = NULL; +} + +void OSBB10::finalize() { + + if(main_loop) + memdelete(main_loop); + main_loop=NULL; + + spatial_sound_server->finish(); + memdelete(spatial_sound_server); + spatial_sound_2d_server->finish(); + memdelete(spatial_sound_2d_server); + + //if (debugger_connection_console) { +// memdelete(debugger_connection_console); +//} + + audio_server->finish(); + memdelete(audio_server); + memdelete(sample_manager); + + visual_server->finish(); + memdelete(visual_server); + memdelete(rasterizer); + + physics_server->finish(); + memdelete(physics_server); + + physics_2d_server->finish(); + memdelete(physics_2d_server); + + #ifdef PAYMENT_SERVICE_ENABLED + memdelete(payment_service); + #endif + + memdelete(input); + + bbutil_terminate(); + screen_destroy_context(screen_cxt); + + bps_shutdown(); +} + +void OSBB10::set_mouse_show(bool p_show) { + + //android has no mouse... +} + +void OSBB10::set_mouse_grab(bool p_grab) { + + //it really has no mouse...! +} + +bool OSBB10::is_mouse_grab_enabled() const { + + //*sigh* technology has evolved so much since i was a kid.. + return false; +} +Point2 OSBB10::get_mouse_pos() const { + + return Point2(); +} +int OSBB10::get_mouse_button_state() const { + + return 0; +} +void OSBB10::set_window_title(const String& p_title) { + + +} + +//interesting byt not yet +//void set_clipboard(const String& p_text); +//String get_clipboard() const; + +void OSBB10::set_video_mode(const VideoMode& p_video_mode,int p_screen) { + + default_videomode = p_video_mode; +} + +OS::VideoMode OSBB10::get_video_mode(int p_screen) const { + + return default_videomode; +} +void OSBB10::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const { + + p_list->push_back(default_videomode); +} + +String OSBB10::get_name() { + + return "BlackBerry 10"; +} + +MainLoop *OSBB10::get_main_loop() const { + + return main_loop; +} + +bool OSBB10::can_draw() const { + + return !minimized; +} + +void OSBB10::set_cursor_shape(CursorShape p_shape) { + + //android really really really has no mouse.. how amazing.. +} + +void OSBB10::handle_screen_event(bps_event_t *event) { + + screen_event_t screen_event = screen_event_get_event(event); + + int screen_val; + screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &screen_val); + + int pos[2]; + + switch (screen_val) { + case SCREEN_EVENT_MTOUCH_TOUCH: + case SCREEN_EVENT_MTOUCH_RELEASE: { + + screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_POSITION, pos); + + InputEvent ievent; + ievent.type = InputEvent::SCREEN_TOUCH; + ievent.ID = ++last_id; + ievent.device = 0; + ievent.screen_touch.pressed = (screen_val == SCREEN_EVENT_MTOUCH_TOUCH); + ievent.screen_touch.x = pos[0]; + ievent.screen_touch.y = pos[1]; + Point2 mpos(ievent.screen_touch.x, ievent.screen_touch.y); + + screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TOUCH_ID, &pos[0]); + ievent.screen_touch.index = pos[0]; + + last_touch_x[pos[0]] = ievent.screen_touch.x; + last_touch_y[pos[0]] = ievent.screen_touch.y; + + input->parse_input_event( ievent ); + + if (ievent.screen_touch.index == 0) { + + InputEvent ievent; + ievent.type = InputEvent::MOUSE_BUTTON; + ievent.ID = ++last_id; + ievent.device = 0; + ievent.mouse_button.pressed = (screen_val == SCREEN_EVENT_MTOUCH_TOUCH); + ievent.mouse_button.button_index = BUTTON_LEFT; + ievent.mouse_button.doubleclick = 0; + ievent.mouse_button.x = ievent.mouse_button.global_x = mpos.x; + ievent.mouse_button.y = ievent.mouse_button.global_y = mpos.y; + input->parse_input_event( ievent ); + }; + + + } break; + case SCREEN_EVENT_MTOUCH_MOVE: { + + screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_POSITION, pos); + + InputEvent ievent; + ievent.type = InputEvent::SCREEN_DRAG; + ievent.ID = ++last_id; + ievent.device = 0; + ievent.screen_drag.x = pos[0]; + ievent.screen_drag.y = pos[1]; + + /* + screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_SOURCE_POSITION, pos); + ievent.screen_drag.relative_x = ievent.screen_drag.x - pos[0]; + ievent.screen_drag.relative_y = ievent.screen_drag.y - pos[1]; + */ + + screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TOUCH_ID, &pos[0]); + ievent.screen_drag.index = pos[0]; + + + ievent.screen_drag.relative_x = ievent.screen_drag.x - last_touch_x[ievent.screen_drag.index]; + ievent.screen_drag.relative_y = ievent.screen_drag.y - last_touch_y[ievent.screen_drag.index]; + + last_touch_x[ievent.screen_drag.index] = ievent.screen_drag.x; + last_touch_y[ievent.screen_drag.index] = ievent.screen_drag.y; + + Point2 mpos(ievent.screen_drag.x, ievent.screen_drag.y); + Point2 mrel(ievent.screen_drag.relative_x, ievent.screen_drag.relative_y); + + input->parse_input_event( ievent ); + + if (ievent.screen_touch.index == 0) { + + InputEvent ievent; + ievent.type = InputEvent::MOUSE_MOTION; + ievent.ID = ++last_id; + ievent.device = 0; + ievent.mouse_motion.x = ievent.mouse_motion.global_x = mpos.x; + ievent.mouse_motion.y = ievent.mouse_motion.global_y = mpos.y; + input->set_mouse_pos(Point2(ievent.mouse_motion.x,ievent.mouse_motion.y)); + ievent.mouse_motion.speed_x=input->get_mouse_speed().x; + ievent.mouse_motion.speed_y=input->get_mouse_speed().y; + ievent.mouse_motion.relative_x = mrel.x; + ievent.mouse_motion.relative_y = mrel.y; + ievent.mouse_motion.button_mask = 1; // pressed + + input->parse_input_event( ievent ); + }; + } break; + + case SCREEN_EVENT_KEYBOARD: { + + InputEvent ievent; + ievent.type = InputEvent::KEY; + ievent.ID = ++last_id; + ievent.device = 0; + int val = 0; + screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_KEY_SCAN, &val); + ievent.key.scancode = val; + screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_KEY_SYM, &val); + ievent.key.unicode = val; + if (val == 61448) { + ievent.key.scancode = KEY_BACKSPACE; + ievent.key.unicode = KEY_BACKSPACE; + }; + if (val == 61453) { + ievent.key.scancode = KEY_ENTER; + ievent.key.unicode = KEY_ENTER; + }; + + int flags; + screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_KEY_FLAGS, &flags); + ievent.key.pressed = flags & 1; // bit 1 is pressed apparently + + int mod; + screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_KEY_MODIFIERS, &mod); + + input->parse_input_event( ievent ); + } break; + + default: + break; + + } +}; + +void OSBB10::handle_accelerometer() { + + if (!accel_supported) + return; + + if (!fullscreen) + return; + + double force_x, force_y, force_z; + accelerometer_read_forces(&force_x, &force_y, &force_z); + Vector3 accel = Vector3(force_x, flip_accelerometer ? force_y : -force_y, force_z); + input->set_accelerometer(accel); + // rotate 90 degrees + //input->set_accelerometer(Vector3(force_y, flip_accelerometer?force_x:(-force_x), force_z)); +}; + + +void OSBB10::_resize(bps_event_t* event) { + + int angle = navigator_event_get_orientation_angle(event); + bbutil_rotate_screen_surface(angle); + + EGLint surface_width, surface_height; + eglQuerySurface(egl_disp, egl_surf, EGL_WIDTH, &surface_width); + eglQuerySurface(egl_disp, egl_surf, EGL_HEIGHT, &surface_height); + + VideoMode mode; + mode.width = surface_width; + mode.height = surface_height; + mode.fullscreen = true; + mode.resizable = false; + set_video_mode(mode); +}; + +void OSBB10::process_events() { + + handle_accelerometer(); + + bps_event_t *event = NULL; + + do { + int rc = bps_get_event(&event, 0); + assert(rc == BPS_SUCCESS); + + if (!event) break; + + #ifdef BB10_SCORELOOP_ENABLED + ScoreloopBB10* sc = Globals::get_singleton()->get_singleton_object("Scoreloop")->cast_to<ScoreloopBB10>(); + if (sc->handle_event(event)) + continue; + #endif + + #ifdef PAYMENT_SERVICE_ENABLED + if (payment_service->handle_event(event)) + continue; + #endif + + int domain = bps_event_get_domain(event); + if (domain == screen_get_domain()) { + + handle_screen_event(event); + + } else if (domain == navigator_get_domain()) { + + if (NAVIGATOR_EXIT == bps_event_get_code(event)) { + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); + bps_event_destroy(event); + exit(0); + return; + /* + } else if (bps_event_get_code(event) == NAVIGATOR_ORIENTATION_CHECK) { + + int angle = navigator_event_get_orientation_angle(event); + navigator_orientation_check_response(event, false); + + } else if (bps_event_get_code(event) == NAVIGATOR_ORIENTATION) { + + _resize(event); + */ + } else if (bps_event_get_code(event) == NAVIGATOR_WINDOW_STATE) { + + int state = navigator_event_get_window_state(event); + bool was_fullscreen = fullscreen; + minimized = state == NAVIGATOR_WINDOW_INVISIBLE; + fullscreen = state == NAVIGATOR_WINDOW_FULLSCREEN; + set_low_processor_usage_mode(!fullscreen); + if (fullscreen != was_fullscreen) { + if (fullscreen) { + audio_server->set_fx_global_volume_scale(fullscreen_mixer_volume); + audio_server->set_stream_global_volume_scale(fullscreen_stream_volume); + } else { + fullscreen_mixer_volume = audio_server->get_fx_global_volume_scale(); + fullscreen_stream_volume = audio_server->get_stream_global_volume_scale(); + audio_server->set_fx_global_volume_scale(0); + audio_server->set_stream_global_volume_scale(0); + }; + }; + }; + } else if (domain == audiodevice_get_domain()) { + + const char * audiodevice_path = audiodevice_event_get_path(event); + printf("************* got audiodevice event, path %s\n", audiodevice_path); + audio_driver->finish(); + audio_driver->init(audiodevice_path); + audio_driver->start(); + }; + + //bps_event_destroy(event); + } while (event); +}; + +bool OSBB10::has_virtual_keyboard() const { + + return true; +}; + +void OSBB10::show_virtual_keyboard(const String& p_existing_text,const Rect2& p_screen_rect) { + + virtualkeyboard_show(); +}; + +void OSBB10::hide_virtual_keyboard() { + + virtualkeyboard_hide(); +}; + +void OSBB10::run() { + + if (!main_loop) + return; + + main_loop->init(); + + int flip = bbutil_is_flipped(); + int rot = bbutil_get_rotation(); + flip_accelerometer = rot == 90; + printf("**************** rot is %i, flip %i\n", rot, (int)flip_accelerometer); + /* + orientation_direction_t orientation; + int angle; + orientation_get(&orientation, &angle); + printf("******************** orientation %i, %i, %i\n", orientation, ORIENTATION_BOTTOM_UP, ORIENTATION_TOP_UP); + if (orientation == ORIENTATION_BOTTOM_UP) { + flip_accelerometer = true; + }; + */ + + + while (true) { + + process_events(); // get rid of pending events + if (Main::iteration()==true) + break; + bbutil_swap(); + //#ifdef DEBUG_ENABLED + fflush(stdout); + //#endif + }; + + main_loop->finish(); + +}; + +bool OSBB10::has_touchscreen_ui_hint() const { + + return true; +} + +Error OSBB10::shell_open(String p_uri) { + + char* msg = NULL; + int ret = navigator_invoke(p_uri.utf8().get_data(), &msg); + + return ret == BPS_SUCCESS ? OK : FAILED; +}; + +String OSBB10::get_data_dir() const { + + return data_dir; +}; + + +OSBB10::OSBB10() { + + main_loop=NULL; + last_id=1; + minimized = false; + fullscreen = true; + flip_accelerometer = true; + fullscreen_mixer_volume = 1; + fullscreen_stream_volume = 1; + + + printf("godot bb10!\n"); + getcwd(launch_dir, sizeof(launch_dir)); + printf("launch dir %s\n", launch_dir); + chdir("app/native/assets"); + launch_dir_ptr = launch_dir; +} + +OSBB10::~OSBB10() { + + +} + diff --git a/platform/bb10/os_bb10.h b/platform/bb10/os_bb10.h new file mode 100644 index 0000000000..28149c15b5 --- /dev/null +++ b/platform/bb10/os_bb10.h @@ -0,0 +1,157 @@ +/*************************************************************************/ +/* os_bb10.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 OS_BB10_H +#define OS_BB10_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_bb10.h" +#include "payment_service.h" + +#include <screen/screen.h> +#include <sys/platform.h> +#include "bps/event.h" + +#include <stdint.h> + +class OSBB10 : public OS_Unix { + + unsigned int last_id; + + screen_context_t screen_cxt; + float fullscreen_mixer_volume; + float fullscreen_stream_volume; + + 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; + AudioDriverBB10* audio_driver; + +#ifdef PAYMENT_SERVICE_ENABLED + PaymentService* payment_service; +#endif + + VideoMode default_videomode; + MainLoop * main_loop; + + void process_events(); + + void _resize(bps_event_t *event); + void handle_screen_event(bps_event_t *event); + void handle_accelerometer(); + + int last_touch_x[16]; + int last_touch_y[16]; + + bool accel_supported; + float pitch; + float roll; + + bool minimized; + bool fullscreen; + bool flip_accelerometer; + String data_dir; + + InputDefault *input; +public: + + // 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 String get_data_dir() 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 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 bool has_virtual_keyboard() const; + virtual void show_virtual_keyboard(const String& p_existing_text,const Rect2& p_screen_rect); + virtual void hide_virtual_keyboard(); + + 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); + + virtual bool has_touchscreen_ui_hint() const; + + virtual Error shell_open(String p_uri); + + void run(); + + OSBB10(); + ~OSBB10(); + +}; + +#endif + diff --git a/platform/bb10/payment_service.cpp b/platform/bb10/payment_service.cpp new file mode 100644 index 0000000000..f6ffffd831 --- /dev/null +++ b/platform/bb10/payment_service.cpp @@ -0,0 +1,150 @@ +/*************************************************************************/ +/* payment_service.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 PAYMENT_SERVICE_ENABLED + +#include "payment_service.h" + +#include "bbutil.h" +#include <string.h> +#include <errno.h> +#include <unistd.h> +extern char* launch_dir_ptr; + +void PaymentService::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("request_product_info"),&PaymentService::request_product_info); + ObjectTypeDB::bind_method(_MD("purchase"),&PaymentService::purchase); + + ObjectTypeDB::bind_method(_MD("get_pending_event_count"),&PaymentService::get_pending_event_count); + ObjectTypeDB::bind_method(_MD("pop_pending_event"),&PaymentService::pop_pending_event); +}; + +Error PaymentService::request_product_info(Variant p_params) { + + return ERR_UNAVAILABLE; +}; + +Error PaymentService::purchase(Variant p_params) { + + Dictionary params = p_params; + ERR_FAIL_COND_V((!params.has("product_id")) && (!params.has("product_sku")), ERR_INVALID_PARAMETER); + + char* id = NULL; + char* sku = NULL; + + CharString p_id = params.has("product_id")?String(params["product_id"]).ascii():CharString(); + CharString p_sku = params.has("product_sku")?String(params["product_sku"]).ascii():CharString(); + unsigned int request_id; + chdir(launch_dir_ptr); + int ret = paymentservice_purchase_request(params.has("product_sku") ? NULL : p_id.get_data(), + params.has("product_sku") ? p_sku.get_data() : NULL, + NULL, NULL, NULL, NULL, get_window_group_id(), &request_id); + chdir("app/native"); + + if (ret != BPS_SUCCESS) { + int eret = errno; + printf("purchase error %i, %x, %i, %x\n", ret, ret, eret, eret); + ERR_FAIL_V((Error)eret); + return (Error)eret; + }; + return OK; +}; + + +bool PaymentService::handle_event(bps_event_t* p_event) { + + if (bps_event_get_domain(p_event) != paymentservice_get_domain()) { + return false; + }; + + Dictionary dict; + int res = paymentservice_event_get_response_code(p_event); + if (res == SUCCESS_RESPONSE) { + dict["result"] = "ok"; + + res = bps_event_get_code(p_event); + if (res == PURCHASE_RESPONSE) { + dict["type"] = "purchase"; + const char* pid = paymentservice_event_get_digital_good_id(p_event, 0); + dict["product_id"] = String(pid?pid:""); + }; + + } else { + const char* desc = paymentservice_event_get_error_text(p_event); + if (strcmp(desc, "alreadyPurchased") == 0) { + dict["result"] = "ok"; + } else { + dict["result"] = "error"; + dict["error_description"] = paymentservice_event_get_error_text(p_event); + dict["error_code"] = paymentservice_event_get_error_id(p_event); + printf("error code is %i\n", paymentservice_event_get_error_id(p_event)); + printf("error description is %s\n", paymentservice_event_get_error_text(p_event)); + }; + dict["product_id"] = ""; + }; + + res = bps_event_get_code(p_event); + if (res == PURCHASE_RESPONSE) { + dict["type"] = "purchase"; + }; + + printf("********** adding event with result %ls\n", String(dict["result"]).c_str()); + pending_events.push_back(dict); + + return true; +}; + +int PaymentService::get_pending_event_count() { + return pending_events.size(); +}; + +Variant PaymentService::pop_pending_event() { + + Variant front = pending_events.front()->get(); + pending_events.pop_front(); + + return front; +}; + +PaymentService::PaymentService() { + + paymentservice_request_events(0); +#ifdef DEBUG_ENABLED + paymentservice_set_connection_mode(true); +#else + paymentservice_set_connection_mode(false); +#endif +}; + +PaymentService::~PaymentService() { + +}; + + +#endif diff --git a/platform/bb10/payment_service.h b/platform/bb10/payment_service.h new file mode 100644 index 0000000000..09eda11d2e --- /dev/null +++ b/platform/bb10/payment_service.h @@ -0,0 +1,69 @@ +/*************************************************************************/ +/* payment_service.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. */ +/*************************************************************************/ +#ifdef PAYMENT_SERVICE_ENABLED + +#ifndef PAYMENT_SERVICE_H +#define PAYMENT_SERVICE_H + +#include <bps/bps.h> +#include <bps/event.h> +#include <bps/paymentservice.h> + +#include "core/object.h" + +class PaymentService : public Object { + + OBJ_TYPE(PaymentService, Object); + + static void _bind_methods(); + + List<Variant> pending_events; + +public: + + Error request_product_info(Variant p_params); + Error purchase(Variant p_params); + + int get_pending_event_count(); + Variant pop_pending_event(); + + bool handle_event(bps_event_t* p_event); + + PaymentService(); + ~PaymentService(); +}; + + + +#endif + + +#endif + + diff --git a/platform/bb10/platform_config.h b/platform/bb10/platform_config.h new file mode 100644 index 0000000000..38fc934ae4 --- /dev/null +++ b/platform/bb10/platform_config.h @@ -0,0 +1,29 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include <alloca.h> diff --git a/platform/iphone/Appirater.h b/platform/iphone/Appirater.h new file mode 100644 index 0000000000..d11b2f7793 --- /dev/null +++ b/platform/iphone/Appirater.h @@ -0,0 +1,227 @@ +/*************************************************************************/ +/* Appirater.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. */ +/*************************************************************************/ + This file is part of Appirater. + + Copyright (c) 2010, Arash Payan + All rights reserved. + + 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. + */ +/* + * Appirater.h + * appirater + * + * Created by Arash Payan on 9/5/09. + * http://arashpayan.com + * Copyright 2010 Arash Payan. All rights reserved. + */ + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +extern NSString *const kAppiraterFirstUseDate; +extern NSString *const kAppiraterUseCount; +extern NSString *const kAppiraterSignificantEventCount; +extern NSString *const kAppiraterCurrentVersion; +extern NSString *const kAppiraterRatedCurrentVersion; +extern NSString *const kAppiraterDeclinedToRate; + +/* + Place your Apple generated software id here. + */ +#define APPIRATER_APP_ID 301377083 + +/* + Your app's name. + */ +#define APPIRATER_APP_NAME [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleNameKey] + +/* + This is the message your users will see once they've passed the day+launches + threshold. + */ +#define APPIRATER_MESSAGE [NSString stringWithFormat:@"If you enjoy using %@, would you mind taking a moment to rate it? It won't take more than a minute. Thanks for your support!", APPIRATER_APP_NAME] + +/* + This is the title of the message alert that users will see. + */ +#define APPIRATER_MESSAGE_TITLE [NSString stringWithFormat:@"Rate %@", APPIRATER_APP_NAME] + +/* + The text of the button that rejects reviewing the app. + */ +#define APPIRATER_CANCEL_BUTTON @"No, Thanks" + +/* + Text of button that will send user to app review page. + */ +#define APPIRATER_RATE_BUTTON [NSString stringWithFormat:@"Rate %@", APPIRATER_APP_NAME] + +/* + Text for button to remind the user to review later. + */ +#define APPIRATER_RATE_LATER @"Remind me later" + +/* + Users will need to have the same version of your app installed for this many + days before they will be prompted to rate it. + */ +#define APPIRATER_DAYS_UNTIL_PROMPT 30 // double + +/* + An example of a 'use' would be if the user launched the app. Bringing the app + into the foreground (on devices that support it) would also be considered + a 'use'. You tell Appirater about these events using the two methods: + [Appirater appLaunched:] + [Appirater appEnteredForeground:] + + Users need to 'use' the same version of the app this many times before + before they will be prompted to rate it. + */ +#define APPIRATER_USES_UNTIL_PROMPT 20 // integer + +/* + A significant event can be anything you want to be in your app. In a + telephone app, a significant event might be placing or receiving a call. + In a game, it might be beating a level or a boss. This is just another + layer of filtering that can be used to make sure that only the most + loyal of your users are being prompted to rate you on the app store. + If you leave this at a value of -1, then this won't be a criteria + used for rating. To tell Appirater that the user has performed + a significant event, call the method: + [Appirater userDidSignificantEvent:]; + */ +#define APPIRATER_SIG_EVENTS_UNTIL_PROMPT -1 // integer + +/* + Once the rating alert is presented to the user, they might select + 'Remind me later'. This value specifies how long (in days) Appirater + will wait before reminding them. + */ +#define APPIRATER_TIME_BEFORE_REMINDING 1 // double + +/* + 'YES' will show the Appirater alert everytime. Useful for testing how your message + looks and making sure the link to your app's review page works. + */ +#define APPIRATER_DEBUG NO + +@interface Appirater : NSObject <UIAlertViewDelegate> { + + UIAlertView *ratingAlert; +} + +@property(nonatomic, retain) UIAlertView *ratingAlert; + +/* + DEPRECATED: While still functional, it's better to use + appLaunched:(BOOL)canPromptForRating instead. + + Calls [Appirater appLaunched:YES]. See appLaunched: for details of functionality. + */ ++ (void)appLaunched:(int)p_app_id; + +/* + Tells Appirater that the app has launched, and on devices that do NOT + support multitasking, the 'uses' count will be incremented. You should + call this method at the end of your application delegate's + application:didFinishLaunchingWithOptions: method. + + If the app has been used enough to be rated (and enough significant events), + you can suppress the rating alert + by passing NO for canPromptForRating. The rating alert will simply be postponed + until it is called again with YES for canPromptForRating. The rating alert + can also be triggered by appEnteredForeground: and userDidSignificantEvent: + (as long as you pass YES for canPromptForRating in those methods). + */ ++ (void)appLaunched:(BOOL)canPromptForRating app_id:(int)p_app_id; + +/* + Tells Appirater that the app was brought to the foreground on multitasking + devices. You should call this method from the application delegate's + applicationWillEnterForeground: method. + + If the app has been used enough to be rated (and enough significant events), + you can suppress the rating alert + by passing NO for canPromptForRating. The rating alert will simply be postponed + until it is called again with YES for canPromptForRating. The rating alert + can also be triggered by appLaunched: and userDidSignificantEvent: + (as long as you pass YES for canPromptForRating in those methods). + */ ++ (void)appEnteredForeground:(BOOL)canPromptForRating; + +/* + Tells Appirater that the user performed a significant event. A significant + event is whatever you want it to be. If you're app is used to make VoIP + calls, then you might want to call this method whenever the user places + a call. If it's a game, you might want to call this whenever the user + beats a level boss. + + If the user has performed enough significant events and used the app enough, + you can suppress the rating alert by passing NO for canPromptForRating. The + rating alert will simply be postponed until it is called again with YES for + canPromptForRating. The rating alert can also be triggered by appLaunched: + and appEnteredForeground: (as long as you pass YES for canPromptForRating + in those methods). + */ ++ (void)userDidSignificantEvent:(BOOL)canPromptForRating; + +/* + Tells Appirater to open the App Store page where the user can specify a + rating for the app. Also records the fact that this has happened, so the + user won't be prompted again to rate the app. + + The only case where you should call this directly is if your app has an + explicit "Rate this app" command somewhere. In all other cases, don't worry + about calling this -- instead, just call the other functions listed above, + and let Appirater handle the bookkeeping of deciding when to ask the user + whether to rate the app. + */ ++ (void)rateApp; + +@end diff --git a/platform/iphone/Appirater.m b/platform/iphone/Appirater.m new file mode 100644 index 0000000000..d9144eda3e --- /dev/null +++ b/platform/iphone/Appirater.m @@ -0,0 +1,383 @@ +#ifdef APPIRATER_ENABLED + +/* + This file is part of Appirater. + + Copyright (c) 2010, Arash Payan + All rights reserved. + + 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. + */ +/* + * Appirater.m + * appirater + * + * Created by Arash Payan on 9/5/09. + * http://arashpayan.com + * Copyright 2010 Arash Payan. All rights reserved. + */ + +#import "Appirater.h" +#import <SystemConfiguration/SCNetworkReachability.h> +#include <netinet/in.h> + +NSString *const kAppiraterFirstUseDate = @"kAppiraterFirstUseDate"; +NSString *const kAppiraterUseCount = @"kAppiraterUseCount"; +NSString *const kAppiraterSignificantEventCount = @"kAppiraterSignificantEventCount"; +NSString *const kAppiraterCurrentVersion = @"kAppiraterCurrentVersion"; +NSString *const kAppiraterRatedCurrentVersion = @"kAppiraterRatedCurrentVersion"; +NSString *const kAppiraterDeclinedToRate = @"kAppiraterDeclinedToRate"; +NSString *const kAppiraterReminderRequestDate = @"kAppiraterReminderRequestDate"; + +NSString *templateReviewURL = @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID"; + +static int app_id = 0; + +@interface Appirater (hidden) +- (BOOL)connectedToNetwork; ++ (Appirater*)sharedInstance; +- (void)showRatingAlert; +- (BOOL)ratingConditionsHaveBeenMet; +- (void)incrementUseCount; +@end + +@implementation Appirater (hidden) + +- (BOOL)connectedToNetwork { + // Create zero addy + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + + // Recover reachability flags + SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress); + SCNetworkReachabilityFlags flags; + + BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags); + CFRelease(defaultRouteReachability); + + if (!didRetrieveFlags) + { + NSLog(@"Error. Could not recover network reachability flags"); + return NO; + } + + BOOL isReachable = flags & kSCNetworkFlagsReachable; + BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired; + BOOL nonWiFi = flags & kSCNetworkReachabilityFlagsTransientConnection; + + NSURL *testURL = [NSURL URLWithString:@"http://www.apple.com/"]; + NSURLRequest *testRequest = [NSURLRequest requestWithURL:testURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:20.0]; + NSURLConnection *testConnection = [[NSURLConnection alloc] initWithRequest:testRequest delegate:self]; + + return ((isReachable && !needsConnection) || nonWiFi) ? (testConnection ? YES : NO) : NO; +} + ++ (Appirater*)sharedInstance { + static Appirater *appirater = nil; + if (appirater == nil) + { + @synchronized(self) { + if (appirater == nil) { + appirater = [[Appirater alloc] init]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive) name:@"UIApplicationWillResignActiveNotification" object:nil]; + } + } + } + + return appirater; +} + +- (void)showRatingAlert { + UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:APPIRATER_MESSAGE_TITLE + message:APPIRATER_MESSAGE + delegate:self + cancelButtonTitle:APPIRATER_CANCEL_BUTTON + otherButtonTitles:APPIRATER_RATE_BUTTON, APPIRATER_RATE_LATER, nil] autorelease]; + self.ratingAlert = alertView; + [alertView show]; +} + +- (BOOL)ratingConditionsHaveBeenMet { + if (APPIRATER_DEBUG) + return YES; + + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + + NSDate *dateOfFirstLaunch = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterFirstUseDate]]; + NSTimeInterval timeSinceFirstLaunch = [[NSDate date] timeIntervalSinceDate:dateOfFirstLaunch]; + NSTimeInterval timeUntilRate = 60 * 60 * 24 * APPIRATER_DAYS_UNTIL_PROMPT; + if (timeSinceFirstLaunch < timeUntilRate) + return NO; + + // check if the app has been used enough + int useCount = [userDefaults integerForKey:kAppiraterUseCount]; + if (useCount <= APPIRATER_USES_UNTIL_PROMPT) + return NO; + + // check if the user has done enough significant events + int sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount]; + if (sigEventCount <= APPIRATER_SIG_EVENTS_UNTIL_PROMPT) + return NO; + + // has the user previously declined to rate this version of the app? + if ([userDefaults boolForKey:kAppiraterDeclinedToRate]) + return NO; + + // has the user already rated the app? + if ([userDefaults boolForKey:kAppiraterRatedCurrentVersion]) + return NO; + + // if the user wanted to be reminded later, has enough time passed? + NSDate *reminderRequestDate = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterReminderRequestDate]]; + NSTimeInterval timeSinceReminderRequest = [[NSDate date] timeIntervalSinceDate:reminderRequestDate]; + NSTimeInterval timeUntilReminder = 60 * 60 * 24 * APPIRATER_TIME_BEFORE_REMINDING; + if (timeSinceReminderRequest < timeUntilReminder) + return NO; + + return YES; +} + +- (void)incrementUseCount { + // get the app's version + NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleVersionKey]; + + // get the version number that we've been tracking + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + NSString *trackingVersion = [userDefaults stringForKey:kAppiraterCurrentVersion]; + if (trackingVersion == nil) + { + trackingVersion = version; + [userDefaults setObject:version forKey:kAppiraterCurrentVersion]; + } + + if (APPIRATER_DEBUG) + NSLog(@"APPIRATER Tracking version: %@", trackingVersion); + + if ([trackingVersion isEqualToString:version]) + { + // check if the first use date has been set. if not, set it. + NSTimeInterval timeInterval = [userDefaults doubleForKey:kAppiraterFirstUseDate]; + if (timeInterval == 0) + { + timeInterval = [[NSDate date] timeIntervalSince1970]; + [userDefaults setDouble:timeInterval forKey:kAppiraterFirstUseDate]; + } + + // increment the use count + int useCount = [userDefaults integerForKey:kAppiraterUseCount]; + useCount++; + [userDefaults setInteger:useCount forKey:kAppiraterUseCount]; + if (APPIRATER_DEBUG) + NSLog(@"APPIRATER Use count: %d", useCount); + } + else + { + // it's a new version of the app, so restart tracking + [userDefaults setObject:version forKey:kAppiraterCurrentVersion]; + [userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterFirstUseDate]; + [userDefaults setInteger:1 forKey:kAppiraterUseCount]; + [userDefaults setInteger:0 forKey:kAppiraterSignificantEventCount]; + [userDefaults setBool:NO forKey:kAppiraterRatedCurrentVersion]; + [userDefaults setBool:NO forKey:kAppiraterDeclinedToRate]; + [userDefaults setDouble:0 forKey:kAppiraterReminderRequestDate]; + } + + [userDefaults synchronize]; +} + +- (void)incrementSignificantEventCount { + // get the app's version + NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleVersionKey]; + + // get the version number that we've been tracking + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + NSString *trackingVersion = [userDefaults stringForKey:kAppiraterCurrentVersion]; + if (trackingVersion == nil) + { + trackingVersion = version; + [userDefaults setObject:version forKey:kAppiraterCurrentVersion]; + } + + if (APPIRATER_DEBUG) + NSLog(@"APPIRATER Tracking version: %@", trackingVersion); + + if ([trackingVersion isEqualToString:version]) + { + // check if the first use date has been set. if not, set it. + NSTimeInterval timeInterval = [userDefaults doubleForKey:kAppiraterFirstUseDate]; + if (timeInterval == 0) + { + timeInterval = [[NSDate date] timeIntervalSince1970]; + [userDefaults setDouble:timeInterval forKey:kAppiraterFirstUseDate]; + } + + // increment the significant event count + int sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount]; + sigEventCount++; + [userDefaults setInteger:sigEventCount forKey:kAppiraterSignificantEventCount]; + if (APPIRATER_DEBUG) + NSLog(@"APPIRATER Significant event count: %d", sigEventCount); + } + else + { + // it's a new version of the app, so restart tracking + [userDefaults setObject:version forKey:kAppiraterCurrentVersion]; + [userDefaults setDouble:0 forKey:kAppiraterFirstUseDate]; + [userDefaults setInteger:0 forKey:kAppiraterUseCount]; + [userDefaults setInteger:1 forKey:kAppiraterSignificantEventCount]; + [userDefaults setBool:NO forKey:kAppiraterRatedCurrentVersion]; + [userDefaults setBool:NO forKey:kAppiraterDeclinedToRate]; + [userDefaults setDouble:0 forKey:kAppiraterReminderRequestDate]; + } + + [userDefaults synchronize]; +} + +@end + + +@interface Appirater () +- (void)hideRatingAlert; +@end + +@implementation Appirater + +@synthesize ratingAlert; + +- (void)incrementAndRate:(NSNumber*)_canPromptForRating { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + [self incrementUseCount]; + + if ([_canPromptForRating boolValue] == YES && + [self ratingConditionsHaveBeenMet] && + [self connectedToNetwork]) + { + [self performSelectorOnMainThread:@selector(showRatingAlert) withObject:nil waitUntilDone:NO]; + } + + [pool release]; +} + +- (void)incrementSignificantEventAndRate:(NSNumber*)_canPromptForRating { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + [self incrementSignificantEventCount]; + + if ([_canPromptForRating boolValue] == YES && + [self ratingConditionsHaveBeenMet] && + [self connectedToNetwork]) + { + [self performSelectorOnMainThread:@selector(showRatingAlert) withObject:nil waitUntilDone:NO]; + } + + [pool release]; +} + ++ (void)appLaunched:(int)p_app_id { + app_id = p_app_id; + [Appirater appLaunched:YES]; +} + ++ (void)appLaunched:(BOOL)canPromptForRating app_id:(int)p_app_id { + app_id = p_app_id; + NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; + [NSThread detachNewThreadSelector:@selector(incrementAndRate:) + toTarget:[Appirater sharedInstance] + withObject:_canPromptForRating]; + [_canPromptForRating release]; +} + +- (void)hideRatingAlert { + if (self.ratingAlert.visible) { + if (APPIRATER_DEBUG) + NSLog(@"APPIRATER Hiding Alert"); + [self.ratingAlert dismissWithClickedButtonIndex:-1 animated:NO]; + } +} + ++ (void)appWillResignActive { + if (APPIRATER_DEBUG) + NSLog(@"APPIRATER appWillResignActive"); + [[Appirater sharedInstance] hideRatingAlert]; +} + ++ (void)appEnteredForeground:(BOOL)canPromptForRating { + NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; + [NSThread detachNewThreadSelector:@selector(incrementAndRate:) + toTarget:[Appirater sharedInstance] + withObject:_canPromptForRating]; + [_canPromptForRating release]; +} + ++ (void)userDidSignificantEvent:(BOOL)canPromptForRating { + NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; + [NSThread detachNewThreadSelector:@selector(incrementSignificantEventAndRate:) + toTarget:[Appirater sharedInstance] + withObject:_canPromptForRating]; + [_canPromptForRating release]; +} + ++ (void)rateApp { +#if TARGET_IPHONE_SIMULATOR + NSLog(@"APPIRATER NOTE: iTunes App Store is not supported on the iOS simulator. Unable to open App Store page."); +#else + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%d", app_id]]; + [userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion]; + [userDefaults synchronize]; + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]]; +#endif +} + +- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + + switch (buttonIndex) { + case 0: + { + // they don't want to rate it + [userDefaults setBool:YES forKey:kAppiraterDeclinedToRate]; + [userDefaults synchronize]; + break; + } + case 1: + { + // they want to rate it + [Appirater rateApp]; + break; + } + case 2: + // remind them later + [userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterReminderRequestDate]; + [userDefaults synchronize]; + break; + default: + break; + } +} + +@end + +#endif diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub new file mode 100644 index 0000000000..0113f75697 --- /dev/null +++ b/platform/iphone/SCsub @@ -0,0 +1,45 @@ +Import('env') + +iphone_lib = [ + + 'os_iphone.cpp', + #'rasterizer_iphone.cpp', + 'audio_driver_iphone.cpp', + 'sem_iphone.cpp', + 'gl_view.mm', + 'main.m', + 'app_delegate.mm', + 'view_controller.mm', + 'game_center.mm', + 'in_app_store.mm', + 'Appirater.m', +] + +#env.Depends('#core/math/vector3.h', 'vector3_psp.h') + +#iphone_lib = env.Library('iphone', iphone_lib) + +env_ios = env.Clone(); + + +if env['ios_gles22_override'] == "yes": + env_ios.Append(CPPFLAGS=['-DGLES2_OVERRIDE']) + +if env['ios_GLES1_override'] == "yes": + env_ios.Append(CPPFLAGS=['-DGLES1_OVERRIDE']) + +if env['ios_appirater'] == "yes": + env_ios.Append(CPPFLAGS=['-DAPPIRATER_ENABLED']) + + +obj = env_ios.Object('godot_iphone.cpp') + +prog = None +if env["target"]=="release": + prog = env_ios.Program('#bin/godot_opt', [obj] + iphone_lib) + #action = "dsymutil "+File(prog)[0].path+" -o ../build/script_exec/build/Debug-iphoneos/script_exec.app.dSYM" + #env.AddPostAction(prog, action) +else: + prog = env_ios.Program('#bin/godot', [obj] + iphone_lib) + action = "dsymutil "+File(prog)[0].path+" -o bin/godot.iphone.dSYM" + env.AddPostAction(prog, action) diff --git a/platform/iphone/app_delegate.h b/platform/iphone/app_delegate.h new file mode 100644 index 0000000000..db641b1f78 --- /dev/null +++ b/platform/iphone/app_delegate.h @@ -0,0 +1,42 @@ +/*************************************************************************/ +/* app_delegate.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. */ +/*************************************************************************/ +#import <UIKit/UIKit.h> +#import "gl_view.h" +#import "view_controller.h" + +@interface AppDelegate : NSObject <UIApplicationDelegate, UIAccelerometerDelegate, GLViewDelegate> { + UIWindow *window; + ViewController* view_controller; + UIAccelerationValue accel[3]; + UIAccelerationValue last_accel[3]; +}; + ++ (ViewController*)getViewController; + +@end diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm new file mode 100644 index 0000000000..f80a855ef0 --- /dev/null +++ b/platform/iphone/app_delegate.mm @@ -0,0 +1,268 @@ +/*************************************************************************/ +/* app_delegate.mm */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ +#import "app_delegate.h" +#import "gl_view.h" + +#include "os_iphone.h" +#include "core/globals.h" +#include "main/main.h" + +#ifdef FACEBOOK_ENABLED +#include "modules/FacebookScorer_ios/FacebookScorer.h" +#endif + +#define kFilteringFactor 0.1 +#define kRenderingFrequency 30 +#define kAccelerometerFrequency 100.0 // Hz + +#ifdef APPIRATER_ENABLED +#import "Appirater.h" +#endif + +Error _shell_open(String p_uri) { + NSString* url = [[NSString alloc] initWithUTF8String:p_uri.utf8().get_data()]; + + if (![[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:url]]) + return ERR_CANT_OPEN; + + printf("opening url %ls\n", p_uri.c_str()); + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]]; + [url release]; + return OK; +}; + +@implementation AppDelegate + +extern int gargc; +extern char** gargv; +extern int iphone_main(int, int, int, char**); +extern void iphone_finish(); + +static ViewController* mainViewController = nil; ++ (ViewController*) getViewController +{ + return mainViewController; +} + +static int frame_count = 0; +- (void)drawView:(GLView*)view; { + + switch (frame_count) { + + case 0: { + + int backingWidth; + int backingHeight; + glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); + glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); + + iphone_main(backingWidth, backingHeight, gargc, gargv); + + OS::VideoMode vm; + vm.fullscreen = true; + vm.width = backingWidth; + vm.height = backingHeight; + vm.resizable = false; + OS::get_singleton()->set_video_mode(vm); + + if (!OS::get_singleton()) { + exit(0); + }; + ++frame_count; + + NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + //NSString *documentsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; + OSIPhone::get_singleton()->set_data_dir(String::utf8([documentsDirectory UTF8String])); + + NSString *locale_code = [[[NSLocale preferredLanguages] objectAtIndex:0] substringToIndex:2]; + OSIPhone::get_singleton()->set_locale(String::utf8([locale_code UTF8String])); + + NSString* uuid; + if ([[UIDevice currentDevice]respondsToSelector:@selector(identifierForVendor)]) { + uuid = [UIDevice currentDevice].identifierForVendor.UUIDString; + }else{ + // return [UIDevice currentDevice]. uniqueIdentifier + uuid = [[UIDevice currentDevice] performSelector:@selector(uniqueIdentifier)]; + } + + OSIPhone::get_singleton()->set_unique_ID(String::utf8([uuid UTF8String])); + + }; // break; +/* + case 1: { + ++frame_count; + } break; +*/ + case 1: { + + Main::setup2(); + ++frame_count; + + } break; +/* + case 3: { + ++frame_count; + } break; +*/ + case 2: { + + Main::start(); + ++frame_count; + + #ifdef APPIRATER_ENABLED + int aid = Globals::get_singleton()->get("ios/app_id"); + [Appirater appLaunched:YES app_id:aid]; + #endif + + }; // break; fallthrough + + default: { + + if (OSIPhone::get_singleton()) { + OSIPhone::get_singleton()->update_accelerometer(accel[0], accel[1], accel[2]); + bool quit_request = OSIPhone::get_singleton()->iterate(); + }; + + }; + + }; +}; + +- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { + + printf("****************** did receive memory warning!\n"); +}; + +- (void)applicationDidFinishLaunching:(UIApplication*)application { + + printf("**************** app delegate init\n"); + CGRect rect = [[UIScreen mainScreen] bounds]; + + application.statusBarHidden = YES; + // disable idle timer + application.idleTimerDisabled = YES; + + //Create a full-screen window + window = [[UIWindow alloc] initWithFrame:rect]; + //window.autoresizesSubviews = YES; + //[window setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleWidth]; + + //Create the OpenGL ES view and add it to the window + GLView *glView = [[GLView alloc] initWithFrame:rect]; + printf("glview is %p\n", glView); + //[window addSubview:glView]; + glView.delegate = self; + //glView.autoresizesSubviews = YES; + //[glView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleWidth]; + + view_controller = [[ViewController alloc] init]; + view_controller.view = glView; + window.rootViewController = view_controller; + + glView.animationInterval = 1.0 / kRenderingFrequency; + [glView startAnimation]; + + //Show the window + [window makeKeyAndVisible]; + + //Configure and start accelerometer + last_accel[0] = 0; + last_accel[1] = 0; + last_accel[2] = 0; + [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / kAccelerometerFrequency)]; + [[UIAccelerometer sharedAccelerometer] setDelegate:self]; + + //OSIPhone::screen_width = rect.size.width - rect.origin.x; + //OSIPhone::screen_height = rect.size.height - rect.origin.y; + + mainViewController = view_controller; +}; + +- (void)applicationWillTerminate:(UIApplication*)application { + + printf("********************* will terminate\n"); + iphone_finish(); +}; + +- (void)applicationDidEnterBackground:(UIApplication *)application +{ + printf("********************* did enter background\n"); + [view_controller.view stopAnimation]; +} + +- (void)applicationWillEnterForeground:(UIApplication *)application +{ + printf("********************* did enter foreground\n"); + [view_controller.view startAnimation]; +} + +- (void) applicationWillResignActive:(UIApplication *)application +{ + printf("********************* will resign active\n"); + [view_controller.view stopAnimation]; // FIXME: pause seems to be recommended elsewhere +} + +- (void) applicationDidBecomeActive:(UIApplication *)application +{ + printf("********************* did become active\n"); + [view_controller.view startAnimation]; // FIXME: resume seems to be recommended elsewhere +} + +- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration { + //Use a basic low-pass filter to only keep the gravity in the accelerometer values + accel[0] = acceleration.x; // * kFilteringFactor + accel[0] * (1.0 - kFilteringFactor); + accel[1] = acceleration.y; // * kFilteringFactor + accel[1] * (1.0 - kFilteringFactor); + accel[2] = acceleration.z; // * kFilteringFactor + accel[2] * (1.0 - kFilteringFactor); +} + +- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { +#ifdef FACEBOOK_ENABLED + return [[[FacebookScorer sharedInstance] facebook] handleOpenURL:url]; +#else + return false; +#endif +} + +// For 4.2+ support +- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { +#ifdef FACEBOOK_ENABLED + return [[[FacebookScorer sharedInstance] facebook] handleOpenURL:url]; +#else + return false; +#endif +} + +- (void)dealloc +{ + [window release]; + [super dealloc]; +} + +@end diff --git a/platform/iphone/audio_driver_iphone.cpp b/platform/iphone/audio_driver_iphone.cpp new file mode 100644 index 0000000000..d39b8f3c4d --- /dev/null +++ b/platform/iphone/audio_driver_iphone.cpp @@ -0,0 +1,157 @@ +/*************************************************************************/ +/* audio_driver_iphone.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_iphone.h" + +Error AudioDriverIphone::init() { + + active = false; + channels = 2; + + AudioStreamBasicDescription strdesc; + strdesc.mFormatID = kAudioFormatLinearPCM; + strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; + strdesc.mChannelsPerFrame = channels; + strdesc.mSampleRate = 44100; + strdesc.mFramesPerPacket = 1; + strdesc.mBitsPerChannel = 16; + strdesc.mBytesPerFrame = + strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8; + strdesc.mBytesPerPacket = + strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; + + OSStatus result = noErr; + AURenderCallbackStruct callback; + AudioComponentDescription desc; + AudioComponent comp = NULL; + const AudioUnitElement output_bus = 0; + const AudioUnitElement bus = output_bus; + const AudioUnitScope scope = kAudioUnitScope_Input; + + zeromem(&desc, sizeof(desc)); + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_RemoteIO; /* !!! FIXME: ? */ + comp = AudioComponentFindNext(NULL, &desc); + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + + result = AudioComponentInstanceNew(comp, &audio_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + ERR_FAIL_COND_V(comp == NULL, FAILED); + + result = AudioUnitSetProperty(audio_unit, + kAudioUnitProperty_StreamFormat, + scope, bus, &strdesc, sizeof(strdesc)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + zeromem(&callback, sizeof(AURenderCallbackStruct)); + callback.inputProc = &AudioDriverIphone::output_callback; + callback.inputProcRefCon = this; + result = AudioUnitSetProperty(audio_unit, + kAudioUnitProperty_SetRenderCallback, + scope, bus, &callback, sizeof(callback)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + result = AudioUnitInitialize(audio_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + + result = AudioOutputUnitStart(audio_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + + const int samples = 1024; + samples_in = memnew_arr(int32_t, samples); // whatever + buffer_frames = samples / channels; + + return FAILED; +}; + +OSStatus AudioDriverIphone::output_callback(void *inRefCon, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList * ioData) { + + + AudioBuffer *abuf; + AudioDriverIphone* ad = (AudioDriverIphone*)inRefCon; + + if (!ad->active) { + for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { + abuf = &ioData->mBuffers[i]; + zeromem(abuf->mData, abuf->mDataByteSize); + }; + return 0; + }; + + int frames_left; + + for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { + + abuf = &ioData->mBuffers[i]; + frames_left = inNumberFrames; + int16_t* out = (int16_t*)abuf->mData; + + while (frames_left) { + + int frames = MIN(frames_left, ad->buffer_frames); + ad->lock(); + ad->audio_server_process(frames, ad->samples_in); + ad->unlock(); + + for(int i = 0; i < frames * ad->channels; i++) { + + out[i] = ad->samples_in[i]>>16; + } + + frames_left -= frames; + out += frames * ad->channels; + }; + }; + + return 0; +}; + +void AudioDriverIphone::start() { + active = true; +}; + +int AudioDriverIphone::get_mix_rate() const { + return 44100; +}; + +AudioDriverSW::OutputFormat AudioDriverIphone::get_output_format() const { + return OUTPUT_STEREO; +}; + +void AudioDriverIphone::lock() {}; +void AudioDriverIphone::unlock() {}; + +void AudioDriverIphone::finish() { + + memdelete_arr(samples_in); +}; + diff --git a/platform/iphone/audio_driver_iphone.h b/platform/iphone/audio_driver_iphone.h new file mode 100644 index 0000000000..eec54d9ee3 --- /dev/null +++ b/platform/iphone/audio_driver_iphone.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* audio_driver_iphone.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. */ +/*************************************************************************/ +#include "servers/audio/audio_server_sw.h" + +#include <AudioUnit/AudioUnit.h> + +class AudioDriverIphone : public AudioDriverSW { + + AudioComponentInstance audio_unit; + bool active; + + int channels; + int32_t* samples_in; + int buffer_frames; + + static OSStatus output_callback(void *inRefCon, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList * ioData); + + +public: + + const char* get_name() const { + return "IPhone"; + }; + + 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(); +}; + diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py new file mode 100644 index 0000000000..ed935839d8 --- /dev/null +++ b/platform/iphone/detect.py @@ -0,0 +1,130 @@ +import os +import sys + + +def is_active(): + return True + +def get_name(): + return "iOS" + +def can_build(): + + import sys + if sys.platform == 'darwin': + return True + + return False + +def get_opts(): + + return [ + ('IPHONEPLATFORM', 'name of the iphone platform', 'iPhoneOS'), + ('IPHONEPATH', 'the path to iphone toolchain', '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain'), + ('IPHONESDK', 'path to the iphone SDK', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.0.sdk/'), + ('game_center', 'Support for game center', 'yes'), + ('store_kit', 'Support for in-app store', 'yes'), + ('ios_gles22_override', 'Force GLES2.0 on iOS', 'yes'), + ('ios_GLES1_override', 'Force legacy GLES (1.1) on iOS', 'no'), + ('ios_appirater', 'Enable Appirater', 'no'), + ('ios_exceptions', 'Use exceptions when compiling on playbook', 'no'), + ] + +def get_flags(): + + return [ + ('lua', 'no'), + ('tools', 'yes'), + ('nedmalloc', 'no'), + ('webp', 'yes'), + ('module_FacebookScorer_ios_enabled', 'no'), + ] + + + +def configure(env): + + env.Append(CPPPATH=['#platform/iphone', '#platform/iphone/include']) + + env['OBJSUFFIX'] = ".iphone.o" + env['LIBSUFFIX'] = ".iphone.a" + env['PROGSUFFIX'] = ".iphone" + + env['ENV']['PATH'] = env['IPHONEPATH']+"/Developer/usr/bin/:"+env['ENV']['PATH'] + +# env['CC'] = '$IPHONEPATH/Developer/usr/bin/gcc' +# env['CXX'] = '$IPHONEPATH/Developer/usr/bin/g++' + env['CC'] = '$IPHONEPATH/usr/bin/clang' + env['CXX'] = '$IPHONEPATH/usr/bin/clang++' + env['AR'] = 'ar' + + import string + #env['CCFLAGS'] = string.split('-arch armv7 -Wall -fno-strict-aliasing -fno-common -D__IPHONE_OS_VERSION_MIN_REQUIRED=20000 -isysroot $IPHONESDK -fvisibility=hidden -mmacosx-version-min=10.5 -DCUSTOM_MATRIX_TRANSFORM_H=\\\"build/iphone/matrix4_iphone.h\\\" -DCUSTOM_VECTOR3_TRANSFORM_H=\\\"build/iphone/vector3_iphone.h\\\" -DNO_THUMB') + env['CCFLAGS'] = string.split('-fno-objc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -Wno-trigraphs -fpascal-strings -Wmissing-prototypes -Wreturn-type -Wparentheses -Wswitch -Wno-unused-parameter -Wunused-variable -Wunused-value -Wno-shorten-64-to-32 -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk -gdwarf-2 -fvisibility=hidden -Wno-sign-conversion -mthumb "-DIBOutlet=__attribute__((iboutlet))" "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))" "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=4.3 -MMD -MT dependencies -isysroot $IPHONESDK') + + +#/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang++ fno-objc-arc -arch armv7 -fmessage-length=0 -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -Wno-trigraphs -fpascal-strings -Wmissing-prototypes -Wreturn-type -Wparentheses -Wswitch -Wno-unused-parameter -Wunused-variable -Wunused-value -Wno-shorten-64-to-32 -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk -gdwarf-2 -fvisibility=hidden -Wno-sign-conversion -mthumb "-DIBOutlet=__attribute__((iboutlet))" "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))" "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=4.3 -MMD -MT dependencies -v -Os -ffast-math -DSTOREKIT_ENABLED -DIPHONE_ENABLED -DUNIX_ENABLED -DGLES2_ENABLED -DNO_THREADS -DMODULE_GRIDMAP_ENABLED -DMUSEPACK_ENABLED -DOLD_SCENE_FORMAT_ENABLED -DSQUIRREL_ENABLED -DVORBIS_ENABLED -DTHEORA_ENABLED -DPNG_ENABLED -DDDS_ENABLED -DPVR_ENABLED -DJPG_ENABLED -DSPEEX_ENABLED -DTOOLS_ENABLED -DGDSCRIPT_ENABLED -DMINIZIP_ENABLED -DXML_ENABLED -Icore -Icore/math -Itools -Idrivers -I. -Iplatform/iphone -Iplatform/iphone/include -Iplatform/iphone/scoreloop/SDKs -I/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/OpenGLES.framework/Headers -Iscript/squirrel/src -Iscript/vorbis script/gdscript/gd_script.cpp +#/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -x objective-c -arch armv7 -fmessage-length=0 -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -std=gnu99 -fobjc-arc -Wno-trigraphs -fpascal-strings -Os -Wmissing-prototypes -Wreturn-type -Wparentheses -Wswitch -Wno-unused-parameter -Wunused-variable -Wunused-value -Wno-shorten-64-to-32 -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk -gdwarf-2 -fvisibility=hidden -Wno-sign-conversion -mthumb "-DIBOutlet=__attribute__((iboutlet))" "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))" "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=4.3 -iquote /Users/red/test2/build/test2.build/Release-iphoneos/test2.build/test2-generated-files.hmap -I/Users/red/test2/build/test2.build/Release-iphoneos/test2.build/test2-own-target-headers.hmap -I/Users/red/test2/build/test2.build/Release-iphoneos/test2.build/test2-all-target-headers.hmap -iquote /Users/red/test2/build/test2.build/Release-iphoneos/test2.build/test2-project-headers.hmap -I/Users/red/test2/build/Release-iphoneos/include -I/Users/red/test2/build/test2.build/Release-iphoneos/test2.build/DerivedSources/armv7 -I/Users/red/test2/build/test2.build/Release-iphoneos/test2.build/DerivedSources -F/Users/red/test2/build/Release-iphoneos -DNS_BLOCK_ASSERTIONS=1 -include /var/folders/LX/LXYXHTeSHSqbkhuPJRIsuE+++TI/-Caches-/com.apple.Xcode.501/SharedPrecompiledHeaders/test2-Prefix-dvdhnltoisfpmyalexovdrmfyeky/test2-Prefix.pch -MMD -MT dependencies -MF /Users/red/test2/build/test2.build/Release-iphoneos/test2.build/Objects-normal/armv7/main.d -c /Users/red/test2/test2/main.m -o /Users/red/test2/build/test2.build/Release-iphoneos/test2.build/Objects-normal/armv7/main.o + + + + + + env.Append(LINKFLAGS=['-arch', 'armv7', '-Wl,-dead_strip', '-miphoneos-version-min=4.3', + '-isysroot', '$IPHONESDK', + #'-mmacosx-version-min=10.5', + '-framework', 'Foundation', + '-framework', 'UIKit', + '-framework', 'CoreGraphics', + '-framework', 'OpenGLES', + '-framework', 'QuartzCore', + '-framework', 'CoreAudio', + '-framework', 'AudioToolbox', + '-framework', 'SystemConfiguration', + '-framework', 'Security', + '-framework', 'AdSupport', + ]) + + if env['game_center'] == 'yes': + env.Append(CPPFLAGS=['-fblocks', '-DGAME_CENTER_ENABLED']) + env.Append(LINKFLAGS=['-framework', 'GameKit']) + + if env['store_kit'] == 'yes': + env.Append(CPPFLAGS=['-DSTOREKIT_ENABLED']) + env.Append(LINKFLAGS=['-framework', 'StoreKit']) + + env.Append(CPPPATH = ['$IPHONESDK/usr/include', '$IPHONESDK/System/Library/Frameworks/OpenGLES.framework/Headers', '$IPHONESDK/System/Library/Frameworks/AudioUnit.framework/Headers']) + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['-Os', '-ffast-math', '-DNS_BLOCK_ASSERTIONS=1','-Wall']) + env.Append(LINKFLAGS=['-Os', '-ffast-math']) + env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX'] + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-D_DEBUG', '-DDEBUG=1', '-gdwarf-2', '-Wall', '-O0', '-DDEBUG_ENABLED']) + env.Append(CPPFLAGS=['-DDEBUG_MEMORY_ALLOC']) + + elif (env["target"]=="profile"): + + env.Append(CCFLAGS=['-g','-pg', '-Os', '-ffast-math']) + env.Append(LINKFLAGS=['-pg']) + + + env['ENV']['CODESIGN_ALLOCATE'] = '/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate' + env.Append(CPPFLAGS=['-DIPHONE_ENABLED', '-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DGLES1_ENABLED', '-DMPC_FIXED_POINT']) + if env['ios_exceptions'] == 'yes': + env.Append(CPPFLAGS=['-fexceptions']) + else: + env.Append(CPPFLAGS=['-fno-exceptions']) + #env['neon_enabled']=True + env['S_compiler'] = '$IPHONEPATH/Developer/usr/bin/gcc' + + if env['lua'] == "yes": + env.Append(CCFLAGS=['-DLUA_USE_FLOAT']) + + +# /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x objective-c-header -arch armv7s -fmessage-length=0 -std=gnu99 -fobjc-arc -Wno-trigraphs -fpascal-strings -Os -Wno-missing-field-initializers -Wno-missing-prototypes -Wreturn-type -Wno-implicit-atomic-properties -Wno-receiver-is-weak -Wduplicate-method-match -Wformat -Wno-missing-braces -Wparentheses -Wswitch -Wno-unused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wno-shorten-64-to-32 -Wpointer-sign -Wno-newline-eof -Wno-selector -Wno-strict-selector-match -Wno-undeclared-selector -Wno-deprecated-implementations -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk -Wprotocol -Wdeprecated-declarations -g -fvisibility=hidden -Wno-sign-conversion "-DIBOutlet=__attribute__((iboutlet))" "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))" "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=4.3 -iquote /Users/lucasgondolo/test/build/test.build/Release-iphoneos/test.build/test-generated-files.hmap -I/Users/lucasgondolo/test/build/test.build/Release-iphoneos/test.build/test-own-target-headers.hmap -I/Users/lucasgondolo/test/build/test.build/Release-iphoneos/test.build/test-all-target-headers.hmap -iquote /Users/lucasgondolo/test/build/test.build/Release-iphoneos/test.build/test-project-headers.hmap -I/Users/lucasgondolo/test/build/Release-iphoneos/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Users/lucasgondolo/test/build/test.build/Release-iphoneos/test.build/DerivedSources/armv7s -I/Users/lucasgondolo/test/build/test.build/Release-iphoneos/test.build/DerivedSources -F/Users/lucasgondolo/test/build/Release-iphoneos -DNS_BLOCK_ASSERTIONS=1 --serialize-diagnostics /var/folders/9r/_65jj9457bgb4n4nxcsm0xl80000gn/C/com.apple.Xcode.501/SharedPrecompiledHeaders/test-Prefix-esrzoamhgruxcxbhemvvlrjmmvoh/test-Prefix.pch.dia -c /Users/lucasgondolo/test/test/test-Prefix.pch -o /var/folders/9r/_65jj9457bgb4n4nxcsm0xl80000gn/C/com.apple.Xcode.501/SharedPrecompiledHeaders/test-Prefix-esrzoamhgruxcxbhemvvlrjmmvoh/test-Prefix.pch.pth -MMD -MT dependencies -MF /var/folders/9r/_65jj9457bgb4n4nxcsm0xl80000gn/C/com.apple.Xcode.501/SharedPrecompiledHeaders/test-Prefix-esrzoamhgruxcxbhemvvlrjmmvoh/test-Prefix.pch.d + diff --git a/platform/iphone/game_center.h b/platform/iphone/game_center.h new file mode 100644 index 0000000000..58a040eb6d --- /dev/null +++ b/platform/iphone/game_center.h @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* game_center.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. */ +/*************************************************************************/ +#ifdef GAME_CENTER_ENABLED + +#ifndef GAME_CENTER_H +#define GAME_CENTER_H + +#include "core/object.h" + +class GameCenter : public Object { + + OBJ_TYPE(GameCenter, Object); + + static GameCenter* instance; + static void _bind_methods(); + + List<Variant> pending_events; + + bool connected; + +public: + + Error connect(); + bool is_connected(); + + Error post_score(Variant p_score); + + Error award_achievement(Variant p_params); + + int get_pending_event_count(); + Variant pop_pending_event(); + + static GameCenter* get_singleton(); + + GameCenter(); + ~GameCenter(); +}; + + +#endif + +#endif diff --git a/platform/iphone/game_center.mm b/platform/iphone/game_center.mm new file mode 100644 index 0000000000..d517ddd105 --- /dev/null +++ b/platform/iphone/game_center.mm @@ -0,0 +1,163 @@ +/*************************************************************************/ +/* game_center.mm */ +/*************************************************************************/ +/* 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 GAME_CENTER_ENABLED + +#include "game_center.h" + +extern "C" { +#import <GameKit/GameKit.h> +}; + +GameCenter* GameCenter::instance = NULL; + +void GameCenter::_bind_methods() { + ObjectTypeDB::bind_method(_MD("connect"),&GameCenter::connect); + ObjectTypeDB::bind_method(_MD("is_connected"),&GameCenter::is_connected); + + ObjectTypeDB::bind_method(_MD("post_score"),&GameCenter::post_score); + ObjectTypeDB::bind_method(_MD("award_achievement"),&GameCenter::award_achievement); + + ObjectTypeDB::bind_method(_MD("get_pending_event_count"),&GameCenter::get_pending_event_count); + ObjectTypeDB::bind_method(_MD("pop_pending_event"),&GameCenter::pop_pending_event); +}; + + +Error GameCenter::connect() { + + GKLocalPlayer* player = [GKLocalPlayer localPlayer]; + [player authenticateWithCompletionHandler:^(NSError* error) { + + Dictionary ret; + ret["type"] = "authentication"; + if (player.isAuthenticated) { + ret["result"] = "ok"; + GameCenter::get_singleton()->connected = true; + } else { + ret["result"] = "error"; + ret["error_code"] = error.code; + ret["error_description"] = [error.localizedDescription UTF8String]; + GameCenter::get_singleton()->connected = false; + }; + + pending_events.push_back(ret); + }]; + return OK; +}; + +bool GameCenter::is_connected() { + return connected; +}; + +Error GameCenter::post_score(Variant p_score) { + + Dictionary params = p_score; + ERR_FAIL_COND_V(!params.has("score") || !params.has("category"), ERR_INVALID_PARAMETER); + float score = params["score"]; + String category = params["category"]; + + NSString* cat_str = [[[NSString alloc] initWithUTF8String:category.utf8().get_data()] autorelease]; + GKScore* reporter = [[[GKScore alloc] initWithCategory:cat_str] autorelease]; + reporter.value = score; + + [reporter reportScoreWithCompletionHandler:^(NSError* error) { + + Dictionary ret; + ret["type"] = "post_score"; + if (error == nil) { + ret["result"] = "ok"; + } else { + ret["result"] = "error"; + ret["error_code"] = error.code; + ret["error_description"] = [error.localizedDescription UTF8String]; + }; + + pending_events.push_back(ret); + }]; + + return OK; +}; + +Error GameCenter::award_achievement(Variant p_params) { + + Dictionary params = p_params; + ERR_FAIL_COND_V(!params.has("name") || !params.has("progress"), ERR_INVALID_PARAMETER); + String name = params["name"]; + float progress = params["progress"]; + + NSString* name_str = [[[NSString alloc] initWithUTF8String:name.utf8().get_data()] autorelease]; + GKAchievement* achievement = [[[GKAchievement alloc] initWithIdentifier: name_str] autorelease]; + ERR_FAIL_COND_V(!achievement, FAILED); + + achievement.percentComplete = progress; + [achievement reportAchievementWithCompletionHandler:^(NSError* error) { + + Dictionary ret; + ret["type"] = "award_achievement"; + if (error == nil) { + ret["result"] = "ok"; + } else { + ret["result"] = "error"; + ret["error_code"] = error.code; + }; + + pending_events.push_back(ret); + }]; + + return OK; +}; + +int GameCenter::get_pending_event_count() { + + return pending_events.size(); +}; + +Variant GameCenter::pop_pending_event() { + + Variant front = pending_events.front()->get(); + pending_events.pop_front(); + + return front; +}; + +GameCenter* GameCenter::get_singleton() { + return instance; +}; + +GameCenter::GameCenter() { + ERR_FAIL_COND(instance != NULL); + instance = this; + connected = false; +}; + + +GameCenter::~GameCenter() { + +}; + +#endif diff --git a/platform/iphone/gl_view.h b/platform/iphone/gl_view.h new file mode 100755 index 0000000000..0d95af9ab1 --- /dev/null +++ b/platform/iphone/gl_view.h @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* gl_view.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. */ +/*************************************************************************/ + +#import <UIKit/UIKit.h> +#import <OpenGLES/EAGL.h> +#import <OpenGLES/ES1/gl.h> +#import <OpenGLES/ES1/glext.h> + +@protocol GLViewDelegate; + +@interface GLView : UIView<UIKeyInput> +{ + @private + // The pixel dimensions of the backbuffer + GLint backingWidth; + GLint backingHeight; + + EAGLContext *context; + + // OpenGL names for the renderbuffer and framebuffers used to render to this view + GLuint viewRenderbuffer, viewFramebuffer; + + // OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist) + GLuint depthRenderbuffer; + + // An animation timer that, when animation is started, will periodically call -drawView at the given rate. + NSTimer *animationTimer; + NSTimeInterval animationInterval; + + // Delegate to do our drawing, called by -drawView, which can be called manually or via the animation timer. + id<GLViewDelegate> delegate; + + // Flag to denote that the -setupView method of a delegate has been called. + // Resets to NO whenever the delegate changes. + BOOL delegateSetup; + BOOL active; + float screen_scale; +} + +@property(nonatomic, assign) id<GLViewDelegate> delegate; + +-(void)startAnimation; +-(void)stopAnimation; +-(void)drawView; + +- (BOOL)canBecomeFirstResponder; + +- (void)open_keyboard; +- (void)hide_keyboard; +- (void)deleteBackward; +- (BOOL)hasText; +- (void)insertText:(NSString *)p_text; + +- (id)initGLES; +- (BOOL)createFramebuffer; +- (void)destroyFramebuffer; + +@property NSTimeInterval animationInterval; + +@end + +@protocol GLViewDelegate<NSObject> + +@required + +// Draw with OpenGL ES +-(void)drawView:(GLView*)view; + +@optional + +// Called whenever you need to do some initialization before rendering. +-(void)setupView:(GLView*)view; + +@end diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm new file mode 100755 index 0000000000..07f5b39fac --- /dev/null +++ b/platform/iphone/gl_view.mm @@ -0,0 +1,457 @@ +/*************************************************************************/ +/* gl_view.mm */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ + +#import <QuartzCore/QuartzCore.h> +#import <OpenGLES/EAGLDrawable.h> +#include "os_iphone.h" +#include "core/os/keyboard.h" + +#import "gl_view.h" + +/* +@interface GLView (private) + +- (id)initGLES; +- (BOOL)createFramebuffer; +- (void)destroyFramebuffer; +@end +*/ + +int gl_view_base_fb; +static String keyboard_text; +static GLView* _instance = NULL; + +void _show_keyboard(String p_existing) { + keyboard_text = p_existing; + printf("instance on show is %p\n", _instance); + [_instance open_keyboard]; +}; + +void _hide_keyboard() { + printf("instance on hide is %p\n", _instance); + [_instance hide_keyboard]; + keyboard_text = ""; +}; + +@implementation GLView + +@synthesize animationInterval; + +static const int max_touches = 8; +static UITouch* touches[max_touches]; + +static void init_touches() { + + for (int i=0; i<max_touches; i++) { + touches[i] = NULL; + }; +}; + +static int get_touch_id(UITouch* p_touch) { + + int first = -1; + for (int i=0; i<max_touches; i++) { + if (first == -1 && touches[i] == NULL) { + first = i; + continue; + }; + if (touches[i] == p_touch) + return i; + }; + + if (first != -1) { + touches[first] = p_touch; + return first; + }; + + return -1; +}; + +static int remove_touch(UITouch* p_touch) { + + int remaining = 0; + for (int i=0; i<max_touches; i++) { + + if (touches[i] == NULL) + continue; + if (touches[i] == p_touch) + touches[i] = NULL; + else + ++remaining; + }; + return remaining; +}; + +static int get_first_id(UITouch* p_touch) { + + for (int i=0; i<max_touches; i++) { + + if (touches[i] != NULL) + return i; + }; + return -1; +}; + +static void clear_touches() { + + for (int i=0; i<max_touches; i++) { + + touches[i] = NULL; + }; +}; + +// Implement this to override the default layer class (which is [CALayer class]). +// We do this so that our view will be backed by a layer that is capable of OpenGL ES rendering. ++ (Class) layerClass +{ + return [CAEAGLLayer class]; +} + +//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder: +- (id)initWithCoder:(NSCoder*)coder +{ + active = FALSE; + if((self = [super initWithCoder:coder])) + { + self = [self initGLES]; + } + return self; +} + +-(id)initGLES +{ + // Get our backing layer + CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer; + + // Configure it so that it is opaque, does not retain the contents of the backbuffer when displayed, and uses RGBA8888 color. + eaglLayer.opaque = YES; + eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking, + kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, + nil]; + + // Create our EAGLContext, and if successful make it current and create our framebuffer. +#ifdef GLES1_OVERRIDE + context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; +#else + context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; +#endif + + if(!context || ![EAGLContext setCurrentContext:context] || ![self createFramebuffer]) + { + [self release]; + return nil; + } + + // Default the animation interval to 1/60th of a second. + animationInterval = 1.0 / 60.0; + return self; +} + +-(id<GLViewDelegate>)delegate +{ + return delegate; +} + +// Update the delegate, and if it needs a -setupView: call, set our internal flag so that it will be called. +-(void)setDelegate:(id<GLViewDelegate>)d +{ + delegate = d; + delegateSetup = ![delegate respondsToSelector:@selector(setupView:)]; +} + +// If our view is resized, we'll be asked to layout subviews. +// This is the perfect opportunity to also update the framebuffer so that it is +// the same size as our display area. +-(void)layoutSubviews +{ + [EAGLContext setCurrentContext:context]; + [self destroyFramebuffer]; + [self createFramebuffer]; + [self drawView]; +} + +- (BOOL)createFramebuffer +{ + // Generate IDs for a framebuffer object and a color renderbuffer + UIScreen* mainscr = [UIScreen mainScreen]; + if (mainscr.currentMode.size.width == 640 || mainscr.currentMode.size.width == 960) // modern iphone, can go to 640x960 + self.contentScaleFactor = 2.0; + + glGenFramebuffersOES(1, &viewFramebuffer); + glGenRenderbuffersOES(1, &viewRenderbuffer); + + glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); + // This call associates the storage for the current render buffer with the EAGLDrawable (our CAEAGLLayer) + // allowing us to draw into a buffer that will later be rendered to screen whereever the layer is (which corresponds with our view). + [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self.layer]; + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer); + + glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); + glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); + + // For this sample, we also need a depth buffer, so we'll create and attach one via another renderbuffer. + glGenRenderbuffersOES(1, &depthRenderbuffer); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer); + glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight); + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer); + + if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) + { + NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); + return NO; + } + + if (OS::get_singleton()) { + OS::VideoMode vm; + vm.fullscreen = true; + vm.width = backingWidth; + vm.height = backingHeight; + vm.resizable = false; + OS::get_singleton()->set_video_mode(vm); + OSIPhone::get_singleton()->set_base_framebuffer(viewFramebuffer); + }; + gl_view_base_fb = viewFramebuffer; + + return YES; +} + +// Clean up any buffers we have allocated. +- (void)destroyFramebuffer +{ + glDeleteFramebuffersOES(1, &viewFramebuffer); + viewFramebuffer = 0; + glDeleteRenderbuffersOES(1, &viewRenderbuffer); + viewRenderbuffer = 0; + + if(depthRenderbuffer) + { + glDeleteRenderbuffersOES(1, &depthRenderbuffer); + depthRenderbuffer = 0; + } +} + +- (void)startAnimation +{ + if (active) + return; + active = TRUE; + printf("start animation!\n"); + animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES]; +} + +- (void)stopAnimation +{ + if (!active) + return; + active = FALSE; + printf("******** stop animation!\n"); + [animationTimer invalidate]; + animationTimer = nil; + clear_touches(); +} + +- (void)setAnimationInterval:(NSTimeInterval)interval +{ + animationInterval = interval; + + if(animationTimer) + { + [self stopAnimation]; + [self startAnimation]; + } +} + +// Updates the OpenGL view when the timer fires +- (void)drawView +{ + if (!active) { + printf("draw view not active!\n"); + return; + }; + + // Make sure that you are drawing to the current context + [EAGLContext setCurrentContext:context]; + + // If our drawing delegate needs to have the view setup, then call -setupView: and flag that it won't need to be called again. + if(!delegateSetup) + { + [delegate setupView:self]; + delegateSetup = YES; + } + + glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); + + [delegate drawView:self]; + + glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); + [context presentRenderbuffer:GL_RENDERBUFFER_OES]; + + GLenum err = glGetError(); + if(err) + NSLog(@"%x error", err); +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + NSArray* tlist = [[event allTouches] allObjects]; + for (unsigned int i=0; i< [tlist count]; i++) { + + if ( [touches containsObject:[tlist objectAtIndex:i]] ) { + + UITouch* touch = [tlist objectAtIndex:i]; + if (touch.phase != UITouchPhaseBegan) + continue; + int tid = get_touch_id(touch); + ERR_FAIL_COND(tid == -1); + CGPoint touchPoint = [touch locationInView:self]; + OSIPhone::get_singleton()->mouse_button(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1, tid == 0); + }; + }; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + + NSArray* tlist = [[event allTouches] allObjects]; + for (unsigned int i=0; i< [tlist count]; i++) { + + if ( [touches containsObject:[tlist objectAtIndex:i]] ) { + + UITouch* touch = [tlist objectAtIndex:i]; + if (touch.phase != UITouchPhaseMoved) + continue; + int tid = get_touch_id(touch); + ERR_FAIL_COND(tid == -1); + int first = get_first_id(touch); + CGPoint touchPoint = [touch locationInView:self]; + CGPoint prev_point = [touch previousLocationInView:self]; + OSIPhone::get_singleton()->mouse_move(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, first == tid); + }; + }; + +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + NSArray* tlist = [[event allTouches] allObjects]; + for (unsigned int i=0; i< [tlist count]; i++) { + + if ( [touches containsObject:[tlist objectAtIndex:i]] ) { + + UITouch* touch = [tlist objectAtIndex:i]; + if (touch.phase != UITouchPhaseEnded) + continue; + int tid = get_touch_id(touch); + ERR_FAIL_COND(tid == -1); + int rem = remove_touch(touch); + CGPoint touchPoint = [touch locationInView:self]; + OSIPhone::get_singleton()->mouse_button(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false, rem == 0); + }; + }; +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + + OSIPhone::get_singleton()->touches_cancelled(); + clear_touches(); +}; + +- (BOOL)canBecomeFirstResponder { + return YES; +}; + + +- (void)open_keyboard { + //keyboard_text = p_existing; + [self becomeFirstResponder]; +}; + +- (void)hide_keyboard { + //keyboard_text = p_existing; + [self resignFirstResponder]; +}; + +- (void)deleteBackward { + if (keyboard_text.length()) + keyboard_text.erase(keyboard_text.length() - 1, 1); + OSIPhone::get_singleton()->key(KEY_BACKSPACE, true); +}; + +- (BOOL)hasText { + return keyboard_text.length() ? YES : NO; +}; + +- (void)insertText:(NSString *)p_text { + String character; + character.parse_utf8([p_text UTF8String]); + keyboard_text = keyboard_text + character; + OSIPhone::get_singleton()->key(character[0] == 10 ? KEY_ENTER : character[0] , true); + printf("inserting text with character %i\n", character[0]); +}; + + +// When created via code however, we get initWithFrame +-(id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + _instance = self; + printf("after init super %p\n", self); + if(self != nil) + { + self = [self initGLES]; + printf("after init gles %p\n", self); + } + init_touches(); + self. multipleTouchEnabled = YES; + + //self.autoresizesSubviews = YES; + //[self setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleWidth]; + + return self; +} + +// Stop animating and release resources when they are no longer needed. +- (void)dealloc +{ + [self stopAnimation]; + + if([EAGLContext currentContext] == context) + { + [EAGLContext setCurrentContext:nil]; + } + + [context release]; + context = nil; + + [super dealloc]; +} + +@end diff --git a/platform/iphone/globals/global_defaults.cpp b/platform/iphone/globals/global_defaults.cpp new file mode 100644 index 0000000000..63463ab366 --- /dev/null +++ b/platform/iphone/globals/global_defaults.cpp @@ -0,0 +1,11 @@ + +#include "global_defaults.h" +#include "globals.h" + + +void register_iphone_global_defaults() { + + GLOBAL_DEF("rasterizer.iOS/use_fragment_lighting",false); + GLOBAL_DEF("display.iOS/driver","GLES2"); + Globals::get_singleton()->set_custom_property_info("display.iOS/driver",PropertyInfo(Variant::STRING,"display.iOS/driver",PROPERTY_HINT_ENUM,"GLES1,GLES2")); +} diff --git a/platform/iphone/globals/global_defaults.h b/platform/iphone/globals/global_defaults.h new file mode 100644 index 0000000000..25f1ae687e --- /dev/null +++ b/platform/iphone/globals/global_defaults.h @@ -0,0 +1,3 @@ + + +void register_iphone_global_defaults();
\ No newline at end of file diff --git a/platform/iphone/godot_iphone.cpp b/platform/iphone/godot_iphone.cpp new file mode 100644 index 0000000000..eb3f17736a --- /dev/null +++ b/platform/iphone/godot_iphone.cpp @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* godot_iphone.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 "os_iphone.h" +#include "main/main.h" + +#include <stdio.h> +#include <unistd.h> +#include <string.h> + +static OSIPhone* os = NULL; + +extern "C" { +int add_path(int p_argc, char** p_args); +}; + +int iphone_main(int width, int height, int argc, char** argv) { + + int len = strlen(argv[0]); + + while (len--){ + if (argv[0][len] == '/') break; + } + + if (len>=0) { + char path[512]; + memcpy(path, argv[0], len>sizeof(path)?sizeof(path):len); + path[len]=0; + printf("Path: %s\n", path); + chdir(path); + } + + printf("godot_iphone %s\n", argv[0]); + char cwd[512]; + getcwd(cwd, sizeof(cwd)); + printf("cwd %s\n", cwd); + os = new OSIPhone(width, height); + + char* fargv[64]; + for (int i=0; i<argc; i++) { + fargv[i] = argv[i]; + }; + fargv[argc] = NULL; + argc = add_path(argc, fargv); + + printf("os created\n"); + Error err = Main::setup(fargv[0], argc - 1, &fargv[1], false); + printf("setup %i\n", err); + if (err!=OK) + return 255; + + return 0; +}; + + +void iphone_finish() { + + printf("iphone_finish\n"); + Main::cleanup(); + delete os; +}; diff --git a/platform/iphone/in_app_store.h b/platform/iphone/in_app_store.h new file mode 100644 index 0000000000..dba1a1a5a1 --- /dev/null +++ b/platform/iphone/in_app_store.h @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* in_app_store.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. */ +/*************************************************************************/ +#ifdef STOREKIT_ENABLED + +#ifndef IN_APP_STORE_H +#define IN_APP_STORE_H + +#include "core/object.h" + +class InAppStore : public Object { + + OBJ_TYPE(InAppStore, Object); + + static InAppStore* instance; + static void _bind_methods(); + + List<Variant> pending_events; + +public: + + Error request_product_info(Variant p_params); + Error purchase(Variant p_params); + + int get_pending_event_count(); + Variant pop_pending_event(); + + void _post_event(Variant p_event); + void _record_purchase(String product_id); + + static InAppStore* get_singleton(); + + InAppStore(); + ~InAppStore(); +}; + +#endif + + +#endif + diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm new file mode 100644 index 0000000000..316e619e11 --- /dev/null +++ b/platform/iphone/in_app_store.mm @@ -0,0 +1,264 @@ +/*************************************************************************/ +/* in_app_store.mm */ +/*************************************************************************/ +/* 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 STOREKIT_ENABLED + +#include "in_app_store.h" + +extern "C" { +#import <StoreKit/StoreKit.h> +}; + +@interface SKProduct (LocalizedPrice) +@property (nonatomic, readonly) NSString *localizedPrice; +@end + +//----------------------------------// +// SKProduct extension +//----------------------------------// +@implementation SKProduct (LocalizedPrice) +- (NSString *)localizedPrice +{ + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; + [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; + [numberFormatter setLocale:self.priceLocale]; + NSString *formattedString = [numberFormatter stringFromNumber:self.price]; + [numberFormatter release]; + return formattedString; +} +@end + + +InAppStore* InAppStore::instance = NULL; + +void InAppStore::_bind_methods() { + ObjectTypeDB::bind_method(_MD("request_product_info"),&InAppStore::request_product_info); + ObjectTypeDB::bind_method(_MD("purchase"),&InAppStore::purchase); + + ObjectTypeDB::bind_method(_MD("get_pending_event_count"),&InAppStore::get_pending_event_count); + ObjectTypeDB::bind_method(_MD("pop_pending_event"),&InAppStore::pop_pending_event); +}; + +@interface ProductsDelegate : NSObject<SKProductsRequestDelegate> { + +}; + +@end + +@implementation ProductsDelegate + +- (void)productsRequest:(SKProductsRequest*)request didReceiveResponse:(SKProductsResponse*)response { + + NSArray* products = response.products; + Dictionary ret; + ret["type"] = "product_info"; + ret["result"] = "ok"; + StringArray titles; + StringArray descriptions; + RealArray prices; + StringArray ids; + StringArray localized_prices; + + for (int i=0; i<[products count]; i++) { + + SKProduct* product = [products objectAtIndex:i]; + + const char* str = [product.localizedTitle UTF8String]; + titles.push_back(String::utf8(str != NULL ? str : "")); + + str = [product.localizedDescription UTF8String]; + descriptions.push_back(String::utf8(str != NULL ? str : "")); + prices.push_back([product.price doubleValue]); + ids.push_back(String::utf8([product.productIdentifier UTF8String])); + localized_prices.push_back(String::utf8([product.localizedPrice UTF8String])); + }; + ret["titles"] = titles; + ret["descriptions"] = descriptions; + ret["prices"] = prices; + ret["ids"] = ids; + ret["localized_prices"] = localized_prices; + + StringArray invalid_ids; + + for (NSString* ipid in response.invalidProductIdentifiers) { + + invalid_ids.push_back(String::utf8([ipid UTF8String])); + }; + ret["invalid_ids"] = invalid_ids; + + InAppStore::get_singleton()->_post_event(ret); + + [request release]; +}; + +@end + +Error InAppStore::request_product_info(Variant p_params) { + + Dictionary params = p_params; + ERR_FAIL_COND_V(!params.has("product_ids"), ERR_INVALID_PARAMETER); + + StringArray pids = params["product_ids"]; + printf("************ request product info! %i\n", pids.size()); + + NSMutableArray* array = [[[NSMutableArray alloc] initWithCapacity:pids.size()] autorelease]; + for (int i=0; i<pids.size(); i++) { + printf("******** adding %ls to product list\n", pids[i].c_str()); + NSString* pid = [[[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()] autorelease]; + [array addObject:pid]; + }; + + NSSet* products = [[[NSSet alloc] initWithArray:array] autorelease]; + SKProductsRequest* request = [[SKProductsRequest alloc] initWithProductIdentifiers:products]; + + ProductsDelegate *delegate = [[ProductsDelegate alloc] init]; + + request.delegate = delegate; + [request start]; + + return OK; +}; + +@interface TransObserver : NSObject<SKPaymentTransactionObserver> { + +}; +@end + +@implementation TransObserver + +- (void)paymentQueue:(SKPaymentQueue*)queue updatedTransactions:(NSArray*) transactions { + + printf("transactions updated!\n"); + for (SKPaymentTransaction* transaction in transactions) { + + switch (transaction.transactionState) { + + case SKPaymentTransactionStatePurchased: { + printf("status purchased!\n"); + String pid = String::utf8([transaction.payment.productIdentifier UTF8String]); + InAppStore::get_singleton()->_record_purchase(pid); + Dictionary ret; + ret["type"] = "purchase"; + ret["result"] = "ok"; + ret["product_id"] = pid; + InAppStore::get_singleton()->_post_event(ret); + [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; + } break; + case SKPaymentTransactionStateFailed: { + printf("status transaction failed!\n"); + String pid = String::utf8([transaction.payment.productIdentifier UTF8String]); + Dictionary ret; + ret["type"] = "purchase"; + ret["result"] = "error"; + ret["product_id"] = pid; + InAppStore::get_singleton()->_post_event(ret); + [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; + } break; + case SKPaymentTransactionStateRestored: { + printf("status transaction restored!\n"); + String pid = String::utf8([transaction.originalTransaction.payment.productIdentifier UTF8String]); + InAppStore::get_singleton()->_record_purchase(pid); + [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; + } break; + + default: + printf("status default %i!\n", (int)transaction.transactionState); + + break; + }; + }; +}; + +@end + + +Error InAppStore::purchase(Variant p_params) { + + ERR_FAIL_COND_V(![SKPaymentQueue canMakePayments], ERR_UNAVAILABLE); + if (![SKPaymentQueue canMakePayments]) + return ERR_UNAVAILABLE; + + printf("purchasing!\n"); + Dictionary params = p_params; + ERR_FAIL_COND_V(!params.has("product_id"), ERR_INVALID_PARAMETER); + + NSString *pid = [[[NSString alloc] initWithUTF8String:String(params["product_id"]).utf8().get_data()] autorelease]; + SKPayment *payment = [SKPayment paymentWithProductIdentifier:pid]; + SKPaymentQueue* defq = [SKPaymentQueue defaultQueue]; + [defq addPayment:payment]; + printf("purchase sent!\n"); + + return OK; +}; + + +int InAppStore::get_pending_event_count() { + return pending_events.size(); +}; + +Variant InAppStore::pop_pending_event() { + + Variant front = pending_events.front()->get(); + pending_events.pop_front(); + + return front; +}; + +void InAppStore::_post_event(Variant p_event) { + + pending_events.push_back(p_event); +}; + +void InAppStore::_record_purchase(String product_id) { + + String skey = "purchased/"+product_id; + NSString* key = [[[NSString alloc] initWithUTF8String:skey.utf8().get_data()] autorelease]; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:key]; + [[NSUserDefaults standardUserDefaults] synchronize]; +}; + +InAppStore* InAppStore::get_singleton() { + + return instance; +}; + +InAppStore::InAppStore() { + ERR_FAIL_COND(instance != NULL); + instance = this; + + TransObserver* observer = [[TransObserver alloc] init]; + [[SKPaymentQueue defaultQueue] addTransactionObserver:observer]; +}; + +InAppStore::~InAppStore() { + +}; + + +#endif diff --git a/platform/iphone/logo.png b/platform/iphone/logo.png Binary files differnew file mode 100644 index 0000000000..d1851d7a5e --- /dev/null +++ b/platform/iphone/logo.png diff --git a/platform/iphone/main.m b/platform/iphone/main.m new file mode 100644 index 0000000000..055e6a63c8 --- /dev/null +++ b/platform/iphone/main.m @@ -0,0 +1,22 @@ +#import <UIKit/UIKit.h> +#import "app_delegate.h" +#include <stdio.h> + +int gargc; +char** gargv; + +int main(int argc, char *argv[]) +{ + printf("*********** main.m\n"); + gargc = argc; + gargv = argv; + + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + AppDelegate* app = [AppDelegate alloc]; + printf("running app main\n"); + UIApplicationMain(argc, argv, nil, @"AppDelegate"); + printf("main done, pool release\n"); + [pool release]; + return 0; +} + diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp new file mode 100644 index 0000000000..3b3b30cb86 --- /dev/null +++ b/platform/iphone/os_iphone.cpp @@ -0,0 +1,507 @@ +/*************************************************************************/ +/* os_iphone.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 IPHONE_ENABLED + +#include "os_iphone.h" + +#include "drivers/gles2/rasterizer_gles2.h" +#include "drivers/gles1/rasterizer_gles1.h" + +#include "servers/visual/visual_server_raster.h" +#include "servers/visual/visual_server_wrap_mt.h" + +#include "main/main.h" +#include "audio_driver_iphone.h" + +#include "core/os/dir_access.h" +#include "core/globals.h" + +#include "sem_iphone.h" + +int OSIPhone::get_video_driver_count() const { + + return 1; +}; + +const char * OSIPhone::get_video_driver_name(int p_driver) const { + + return "openglES"; +}; + +OSIPhone* OSIPhone::get_singleton() { + + return (OSIPhone*)OS::get_singleton(); +}; + + +OS::VideoMode OSIPhone::get_default_video_mode() const { + + return video_mode; +}; + +uint8_t OSIPhone::get_orientations() const { + + return supported_orientations; +}; + +extern int gl_view_base_fb; // from gl_view.mm + +void OSIPhone::set_data_dir(String p_dir) { + + DirAccess* da = DirAccess::open(p_dir); + + data_dir = da->get_current_dir(); + + memdelete(da); +}; + +void OSIPhone::set_unique_ID(String p_ID) { + + unique_ID = p_ID; +}; + +String OSIPhone::get_unique_ID() const { + + return unique_ID; +}; + +void OSIPhone::initialize_core() { + + OS_Unix::initialize_core(); + SemaphoreIphone::make_default(); +}; + +void OSIPhone::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + supported_orientations = 0; + supported_orientations |= ((GLOBAL_DEF("video_mode/allow_horizontal", true)?1:0) << LandscapeLeft); + supported_orientations |= ((GLOBAL_DEF("video_mode/allow_horizontal_flipped", false)?1:0) << LandscapeRight); + supported_orientations |= ((GLOBAL_DEF("video_mode/allow_vertical", false)?1:0) << PortraitDown); + supported_orientations |= ((GLOBAL_DEF("video_mode/allow_vertical_flipped", false)?1:0) << PortraitUp); + +#ifdef GLES1_OVERRIDE + rasterizer = memnew( RasterizerGLES1 ); +#else + rasterizer_gles22 = memnew( RasterizerGLES2(false, false, false) ); + rasterizer = rasterizer_gles22; + rasterizer_gles22->set_base_framebuffer(gl_view_base_fb); +#endif + + visual_server = memnew( VisualServerRaster(rasterizer) ); + if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { + + visual_server = memnew(VisualServerWrapMT(visual_server, false)); + }; + visual_server->init(); + + visual_server->init(); + visual_server->cursor_set_visible(false, 0); + + audio_driver = memnew(AudioDriverIphone); + audio_driver->set_singleton(); + audio_driver->init(); + + sample_manager = memnew( SampleManagerMallocSW ); + audio_server = memnew( AudioServerSW(sample_manager) ); + 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 ); + + /* +#ifdef IOS_SCORELOOP_ENABLED + scoreloop = memnew(ScoreloopIOS); + Globals::get_singleton()->add_singleton(Globals::Singleton("Scoreloop", scoreloop)); + scoreloop->connect(); +#endif + */ + +#ifdef GAME_CENTER_ENABLED + game_center = memnew(GameCenter); + Globals::get_singleton()->add_singleton(Globals::Singleton("GameCenter", game_center)); + game_center->connect(); +#endif + +#ifdef STOREKIT_ENABLED + store_kit = memnew(InAppStore); + Globals::get_singleton()->add_singleton(Globals::Singleton("InAppStore", store_kit)); +#endif +}; + +MainLoop *OSIPhone::get_main_loop() const { + + return main_loop; +}; + + +void OSIPhone::set_main_loop( MainLoop * p_main_loop ) { + + main_loop = p_main_loop; + + if (main_loop) { + input->set_main_loop(p_main_loop); + main_loop->init(); + } +}; + + +bool OSIPhone::iterate() { + + if (!main_loop) + return true; + + if (main_loop) { + for (int i=0; i<event_count; i++) { + + input->parse_input_event(event_queue[i]); + }; + }; + event_count = 0; + + return Main::iteration(); +}; + +void OSIPhone::key(uint32_t p_key, bool p_pressed) { + + InputEvent ev; + ev.type = InputEvent::KEY; + ev.ID = ++last_event_id; + ev.key.echo = false; + ev.key.pressed = p_pressed; + ev.key.scancode = p_key; + ev.key.unicode = p_key; + queue_event(ev); +}; + +void OSIPhone::mouse_button(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick, bool p_use_as_mouse) { + + InputEvent ev; + ev.type = InputEvent::SCREEN_TOUCH; + ev.ID = ++last_event_id; + ev.screen_touch.index=p_idx; + ev.screen_touch.pressed=p_pressed; + ev.screen_touch.x=p_x; + ev.screen_touch.y=p_y; + queue_event(ev); + + if (p_use_as_mouse) { + + InputEvent ev; + ev.type = InputEvent::MOUSE_BUTTON; + ev.device = 0; + ev.mouse_button.pointer_index = p_idx; + ev.ID = ++last_event_id; + + // swaped it for tilted screen + //ev.mouse_button.x = ev.mouse_button.global_x = video_mode.height - p_y; + //ev.mouse_button.y = ev.mouse_button.global_y = p_x; + ev.mouse_button.x = ev.mouse_button.global_x = p_x; + ev.mouse_button.y = ev.mouse_button.global_y = p_y; + + ev.mouse_button.button_index = BUTTON_LEFT; + ev.mouse_button.doubleclick = p_doubleclick; + ev.mouse_button.pressed = p_pressed; + + mouse_list.pressed[p_idx] = p_pressed; + + queue_event(ev); + }; +}; + +void OSIPhone::mouse_move(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, bool p_use_as_mouse) { + + InputEvent ev; + ev.type=InputEvent::SCREEN_DRAG; + ev.ID = ++last_event_id; + ev.screen_drag.index=p_idx; + ev.screen_drag.x=p_x; + ev.screen_drag.y=p_y; + ev.screen_drag.relative_x = p_x - p_prev_x; + ev.screen_drag.relative_y = p_y - p_prev_y; + queue_event(ev); + + if (p_use_as_mouse) { + InputEvent ev; + ev.type = InputEvent::MOUSE_MOTION; + ev.device = 0; + ev.mouse_motion.pointer_index = p_idx; + ev.ID = ++last_event_id; + + if (true) { // vertical + + ev.mouse_motion.x = ev.mouse_button.global_x = p_x; + ev.mouse_motion.y = ev.mouse_button.global_y = p_y; + ev.mouse_motion.relative_x = ev.mouse_motion.x - p_prev_x; + ev.mouse_motion.relative_y = ev.mouse_motion.y - p_prev_y; + + } else { // horizontal? + ev.mouse_motion.x = ev.mouse_button.global_x = video_mode.height - p_y; + ev.mouse_motion.y = ev.mouse_button.global_y = p_x; + ev.mouse_motion.relative_x = ev.mouse_motion.x - (video_mode.height - p_prev_x); + ev.mouse_motion.relative_y = ev.mouse_motion.y - p_prev_x; + }; + + 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.button_mask = 1; // pressed + + queue_event(ev); + }; +}; + +void OSIPhone::queue_event(const InputEvent& p_event) { + + ERR_FAIL_INDEX( event_count, MAX_EVENTS ); + + event_queue[event_count++] = p_event; +}; + +void OSIPhone::touches_cancelled() { + + for (int i=0; i<MAX_MOUSE_COUNT; i++) { + + if (mouse_list.pressed[i]) { + + // send a mouse_up outside the screen + mouse_button(i, -1, -1, false, false, false); + }; + }; +}; + +static const float ACCEL_RANGE = 1; + +void OSIPhone::update_accelerometer(float p_x, float p_y, float p_z) { + + input->set_accelerometer(Vector3(p_x / (float)ACCEL_RANGE, p_y / (float)ACCEL_RANGE, -p_z / (float)ACCEL_RANGE)); + + /* + if (p_x != last_accel.x) { + //printf("updating accel x %f\n", p_x); + InputEvent ev; + ev.type = InputEvent::JOYSTICK_MOTION; + ev.device = 0; + ev.joy_motion.axis = JOY_ANALOG_0_X; + ev.joy_motion.axis_value = (p_x / (float)ACCEL_RANGE); + ev.ID = ++last_event_id; + last_accel.x = p_x; + queue_event(ev); + }; + if (p_y != last_accel.y) { + //printf("updating accel y %f\n", p_y); + InputEvent ev; + ev.type = InputEvent::JOYSTICK_MOTION; + ev.device = 0; + ev.joy_motion.axis = JOY_ANALOG_0_Y; + ev.joy_motion.axis_value = (p_y / (float)ACCEL_RANGE); + ev.ID = ++last_event_id; + last_accel.y = p_y; + queue_event(ev); + }; + if (p_z != last_accel.z) { + //printf("updating accel z %f\n", p_z); + InputEvent ev; + ev.type = InputEvent::JOYSTICK_MOTION; + ev.device = 0; + ev.joy_motion.axis = JOY_ANALOG_1_X; + ev.joy_motion.axis_value = ( (1.0 - p_z) / (float)ACCEL_RANGE); + ev.ID = ++last_event_id; + last_accel.z = p_z; + queue_event(ev); + }; + */ +}; + + + +void OSIPhone::delete_main_loop() { + + if (main_loop) { + main_loop->finish(); + memdelete(main_loop); + }; + + main_loop = NULL; +}; + +void OSIPhone::finalize() { + + if(main_loop) // should not happen? + memdelete(main_loop); + + visual_server->finish(); + memdelete(visual_server); + memdelete(rasterizer); + + physics_server->finish(); + memdelete(physics_server); + + physics_2d_server->finish(); + memdelete(physics_2d_server); + + spatial_sound_server->finish(); + memdelete(spatial_sound_server); + + memdelete(input); + + spatial_sound_2d_server->finish(); + memdelete(spatial_sound_2d_server); + +}; + +void OSIPhone::set_mouse_show(bool p_show) { }; +void OSIPhone::set_mouse_grab(bool p_grab) { }; + +bool OSIPhone::is_mouse_grab_enabled() const { + + return true; +}; + +Point2 OSIPhone::get_mouse_pos() const { + + return Point2(); +}; + +int OSIPhone::get_mouse_button_state() const { + + return mouse_list.pressed[0]; +}; + +void OSIPhone::set_window_title(const String& p_title) { }; + +void OSIPhone::set_video_mode(const VideoMode& p_video_mode, int p_screen) { + + video_mode = p_video_mode; +}; + +OS::VideoMode OSIPhone::get_video_mode(int p_screen) const { + + return video_mode; +}; + +void OSIPhone::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const { + + p_list->push_back(video_mode); +}; + +bool OSIPhone::can_draw() const { + + return true; +}; + +int OSIPhone::set_base_framebuffer(int p_fb) { + + if (rasterizer_gles22) { + rasterizer_gles22->set_base_framebuffer(p_fb); + }; + return 0; +}; + +bool OSIPhone::has_virtual_keyboard() const { + return true; +}; + +extern void _show_keyboard(String p_existing); +extern void _hide_keyboard(); +extern Error _shell_open(String p_uri); + +void OSIPhone::show_virtual_keyboard(const String& p_existing_text,const Rect2& p_screen_rect) { + _show_keyboard(p_existing_text); +}; + +void OSIPhone::hide_virtual_keyboard() { + _hide_keyboard(); +}; + +Error OSIPhone::shell_open(String p_uri) { + return _shell_open(p_uri); +}; + + +void OSIPhone::set_cursor_shape(CursorShape p_shape) { + + +}; + +String OSIPhone::get_data_dir() const { + + return data_dir; +}; + +String OSIPhone::get_name() { + + return "iOS"; +}; + +bool OSIPhone::has_touchscreen_ui_hint() const { + + return true; +} + +void OSIPhone::set_locale(String p_locale) +{ + locale_code = p_locale; +} + +String OSIPhone::get_locale() const { + return locale_code; +} + +OSIPhone::OSIPhone(int width, int height) { + + rasterizer_gles22 = NULL; + main_loop = NULL; + visual_server = NULL; + rasterizer = NULL; + + VideoMode vm; + vm.fullscreen = true; + vm.width = width; + vm.height = height; + vm.resizable = false; + set_video_mode(vm); + event_count = 0; + last_event_id = 0; +}; + +OSIPhone::~OSIPhone() { + +} + +#endif diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h new file mode 100644 index 0000000000..a8ccba02b5 --- /dev/null +++ b/platform/iphone/os_iphone.h @@ -0,0 +1,193 @@ +/*************************************************************************/ +/* os_iphone.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. */ +/*************************************************************************/ +#ifdef IPHONE_ENABLED + +#ifndef OS_IPHONE_H +#define OS_IPHONE_H + +#include "os/input.h" +#include "drivers/unix/os_unix.h" + +#include "servers/visual_server.h" +#include "servers/visual/rasterizer.h" +#include "servers/physics/physics_server_sw.h" +#include "servers/physics_2d/physics_2d_server_sw.h" +#include "servers/audio/audio_server_sw.h" +#include "servers/audio/sample_manager_sw.h" +#include "servers/spatial_sound/spatial_sound_server_sw.h" +#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" +#include "game_center.h" +#include "in_app_store.h" + +class AudioDriverIphone; +class RasterizerGLES2; + +class OSIPhone : public OS_Unix { + +public: + + enum Orientations { + PortraitDown, + PortraitUp, + LandscapeLeft, + LandscapeRight, + }; +private: + + enum { + MAX_MOUSE_COUNT = 8, + MAX_EVENTS = 64, + }; + + uint8_t supported_orientations; + + Rasterizer *rasterizer; + + RasterizerGLES2* rasterizer_gles22; + + VisualServer *visual_server; + PhysicsServer* physics_server; + Physics2DServer *physics_2d_server; + + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSoundServerSW *spatial_sound_server; + SpatialSound2DServerSW *spatial_sound_2d_server; + AudioDriverIphone* audio_driver; + +#ifdef GAME_CENTER_ENABLED + GameCenter* game_center; +#endif +#ifdef STOREKIT_ENABLED + InAppStore* store_kit; +#endif + + MainLoop *main_loop; + + VideoMode video_mode; + + + 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 void initialize_core(); + 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 MainLoop *get_main_loop() const; + + virtual void delete_main_loop(); + + virtual void finalize(); + + struct MouseList { + + bool pressed[MAX_MOUSE_COUNT]; + MouseList() { + for (int i=0; i<MAX_MOUSE_COUNT; i++) + pressed[i] = false; + }; + }; + + MouseList mouse_list; + + Vector3 last_accel; + + InputEvent event_queue[MAX_EVENTS]; + int event_count; + int last_event_id; + void queue_event(const InputEvent& p_event); + + String data_dir; + String unique_ID; + String locale_code; + + InputDefault *input; + +public: + + bool iterate(); + + uint8_t get_orientations() const; + + void mouse_button(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick, bool p_use_as_mouse); + void mouse_move(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, bool p_use_as_mouse); + void touches_cancelled(); + void key(uint32_t p_key, bool p_pressed); + + int set_base_framebuffer(int p_fb); + + void update_accelerometer(float p_x, float p_y, float p_z); + + static OSIPhone* get_singleton(); + + 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_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 bool can_draw() const; + + virtual bool has_virtual_keyboard() const; + virtual void show_virtual_keyboard(const String& p_existing_text,const Rect2& p_screen_rect=Rect2()); + virtual void hide_virtual_keyboard(); + + virtual void set_cursor_shape(CursorShape p_shape); + + virtual bool has_touchscreen_ui_hint() const; + + void set_data_dir(String p_dir); + + virtual String get_name(); + + Error shell_open(String p_uri); + + String get_data_dir() const; + + void set_locale(String p_locale); + String get_locale() const; + + void set_unique_ID(String p_ID); + String get_unique_ID() const; + + OSIPhone(int width, int height); + ~OSIPhone(); +}; + +#endif // OS_IPHONE_H + +#endif diff --git a/platform/iphone/platform_config.h b/platform/iphone/platform_config.h new file mode 100644 index 0000000000..df50d30f40 --- /dev/null +++ b/platform/iphone/platform_config.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include <alloca.h> +#define GLES2_INCLUDE_H <ES2/gl.h> +#define GLES1_INCLUDE_H <ES1/gl.h> + diff --git a/platform/iphone/rasterizer_iphone.cpp b/platform/iphone/rasterizer_iphone.cpp new file mode 100644 index 0000000000..4674a5143c --- /dev/null +++ b/platform/iphone/rasterizer_iphone.cpp @@ -0,0 +1,2762 @@ +/*************************************************************************/ +/* rasterizer_iphone.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 IPHONE_ENABLED + +#include "rasterizer_iphone.h" +#include "os/os.h" +#include "globals.h" +#include <stdio.h> + +_FORCE_INLINE_ static void _gl_load_transform(const Transform& tr) { + + GLfloat matrix[16]={ /* build a 16x16 matrix */ + tr.basis.elements[0][0], + tr.basis.elements[1][0], + tr.basis.elements[2][0], + 0, + tr.basis.elements[0][1], + tr.basis.elements[1][1], + tr.basis.elements[2][1], + 0, + tr.basis.elements[0][2], + tr.basis.elements[1][2], + tr.basis.elements[2][2], + 0, + tr.origin.x, + tr.origin.y, + tr.origin.z, + 1 + }; + + glLoadMatrixf(matrix); +}; + +_FORCE_INLINE_ static void _gl_mult_transform(const Transform& tr) { + + GLfloat matrix[16]={ /* build a 16x16 matrix */ + tr.basis.elements[0][0], + tr.basis.elements[1][0], + tr.basis.elements[2][0], + 0, + tr.basis.elements[0][1], + tr.basis.elements[1][1], + tr.basis.elements[2][1], + 0, + tr.basis.elements[0][2], + tr.basis.elements[1][2], + tr.basis.elements[2][2], + 0, + tr.origin.x, + tr.origin.y, + tr.origin.z, + 1 + }; + + glMultMatrixf(matrix); +}; + +static const GLenum prim_type[]={GL_POINTS,GL_LINES,GL_TRIANGLES,GL_TRIANGLE_FAN}; + +static void _draw_primitive(int p_points, const float *p_vertices, const float *p_normals, const float* p_colors, const float *p_uvs,const Plane *p_tangents=NULL,int p_instanced=1) { + + ERR_FAIL_COND(!p_vertices); + ERR_FAIL_COND(p_points <1 || p_points>4); + + GLenum type = prim_type[p_points - 1]; + + + if (!p_colors) { + glColor4f(1, 1, 1, 1); + }; + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)p_vertices); + + if (p_normals) { + + glEnableClientState(GL_NORMAL_ARRAY); + glNormalPointer(GL_FLOAT, 0, (GLvoid*)p_normals); + }; + + if (p_colors) { + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4,GL_FLOAT, 0, p_colors); + }; + + if (p_uvs) { + + glClientActiveTexture(GL_TEXTURE0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, p_uvs); + }; + + glDrawArrays( type, 0, p_points); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +}; + + +/* TEXTURE API */ + +static Image _get_gl_image_and_format(const Image& p_image, Image::Format p_format, uint32_t p_flags,GLenum& r_gl_format,int &r_gl_components,bool &r_has_alpha_cache) { + + r_has_alpha_cache=false; + Image image=p_image; + + switch(p_format) { + + case Image::FORMAT_GRAYSCALE: { + r_gl_components=1; + r_gl_format=GL_LUMINANCE; + + } break; + case Image::FORMAT_INTENSITY: { + + image.convert(Image::FORMAT_RGBA); + r_gl_components=4; + r_gl_format=GL_RGBA; + r_has_alpha_cache=true; + } break; + case Image::FORMAT_GRAYSCALE_ALPHA: { + + image.convert(Image::FORMAT_RGBA); + r_gl_components=4; + r_gl_format=GL_RGBA; + r_has_alpha_cache=true; + } break; + + case Image::FORMAT_INDEXED: { + + image.convert(Image::FORMAT_RGB); + r_gl_components=3; + r_gl_format=GL_RGB; + + } break; + + case Image::FORMAT_INDEXED_ALPHA: { + + image.convert(Image::FORMAT_RGBA); + r_gl_components=4; + r_gl_format=GL_RGB; + r_has_alpha_cache=true; + + } break; + case Image::FORMAT_RGB: { + + r_gl_components=3; r_gl_format=GL_RGB; + } break; + case Image::FORMAT_RGBA: { + + r_gl_components=4; + r_gl_format=GL_RGBA; + r_has_alpha_cache=true; + } break; + default: { + + ERR_FAIL_V(Image()); + } + } + + return image; +} + +RID RasterizerIPhone::texture_create() { + + Texture *texture = memnew(Texture); + ERR_FAIL_COND_V(!texture,RID()); + glGenTextures(1, &texture->tex_id); + texture->active=false; + + return texture_owner.make_rid( texture ); + +} + +void RasterizerIPhone::texture_allocate(RID p_texture,int p_width, int p_height,Image::Format p_format,uint32_t p_flags) { + + bool has_alpha_cache; + int components; + GLenum format; + + Texture *texture = texture_owner.get( p_texture ); + ERR_FAIL_COND(!texture); + texture->width=p_width; + texture->height=p_height; + texture->format=p_format; + texture->flags=p_flags; + //texture->target = (p_flags & VS::TEXTURE_FLAG_CUBEMAP) ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + texture->target = GL_TEXTURE_2D; + + _get_gl_image_and_format(Image(),texture->format,texture->flags,format,components,has_alpha_cache); + + texture->gl_components_cache=components; + texture->gl_format_cache=format; + texture->format_has_alpha=has_alpha_cache; + texture->has_alpha=false; //by default it doesn't have alpha unless something with alpha is blitteds + + glBindTexture(texture->target, texture->tex_id); + + if (texture->flags&VS::TEXTURE_FLAG_MIPMAPS) { + glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE ); + } + + + if (texture->target==GL_TEXTURE_2D) { + glTexImage2D(texture->target, 0, format, texture->width, texture->height, 0, format, GL_UNSIGNED_BYTE,NULL); + } + + /* + else { + //cubemappor + for (int i=0;i<6;i++) + glTexImage2D(_cube_side_enum[i], 0, format, texture->width, texture->height, 0, format, GL_UNSIGNED_BYTE,NULL); + } + */ + + glTexParameteri(texture->target,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering + + if (texture->flags&VS::TEXTURE_FLAG_FILTER) { + + glTexParameteri(texture->target,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering + if (texture->flags&VS::TEXTURE_FLAG_MIPMAPS) { + //glTexParameteri(texture->target,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR); + }; + } + + + + if (texture->flags&VS::TEXTURE_FLAG_REPEAT /* && texture->target != GL_TEXTURE_CUBE_MAP*/) { + + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + } else { + + //glTexParameterf( texture->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE ); + glTexParameterf( texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + } + + texture->active=true; +} + +void RasterizerIPhone::texture_blit_rect(RID p_texture,int p_x,int p_y, const Image& p_image,VS::CubeMapSide p_cube_side) { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND(!texture); + ERR_FAIL_COND(!texture->active); + ERR_FAIL_COND(texture->format != p_image.get_format() ); + + int components; + GLenum format; + bool alpha; + + Image img = _get_gl_image_and_format(p_image, p_image.get_format(),texture->flags,format,components,alpha); + + if (img.detect_alpha()) + texture->has_alpha=true; + + GLenum blit_target = GL_TEXTURE_2D; //(texture->target == GL_TEXTURE_CUBE_MAP)?_cube_side_enum[p_cube_side]:GL_TEXTURE_2D; + + DVector<uint8_t>::Read read = img.get_data().read(); + + glBindTexture(texture->target, texture->tex_id); + glTexSubImage2D( blit_target, 0, p_x,p_y,img.get_width(),img.get_height(),format,GL_UNSIGNED_BYTE,read.ptr() ); + + //glGenerateMipmap( texture->target ); +} + +Image RasterizerIPhone::texture_get_rect(RID p_texture,int p_x,int p_y,int p_width, int p_height,VS::CubeMapSide p_cube_side) const { + + + return Image(); +} +void RasterizerIPhone::texture_set_flags(RID p_texture,uint32_t p_flags) { + + Texture *texture = texture_owner.get( p_texture ); + ERR_FAIL_COND(!texture); + + glBindTexture(texture->target, texture->tex_id); + uint32_t cube = texture->flags & VS::TEXTURE_FLAG_CUBEMAP; + texture->flags=p_flags|cube; // can't remove a cube from being a cube + + if (texture->flags&VS::TEXTURE_FLAG_REPEAT /*&& texture->target != GL_TEXTURE_CUBE_MAP*/) { + + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + } else { + //glTexParameterf( texture->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE ); + glTexParameterf( texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + + } + + + if (texture->flags&VS::TEXTURE_FLAG_FILTER) { + + glTexParameteri(texture->target,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering + if (texture->flags&VS::TEXTURE_FLAG_MIPMAPS) + glTexParameteri(texture->target,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR); + + } else { + + glTexParameteri(texture->target,GL_TEXTURE_MAG_FILTER,GL_NEAREST); // nearest + } +} +uint32_t RasterizerIPhone::texture_get_flags(RID p_texture) const { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture,0); + + return texture->flags; + +} +Image::Format RasterizerIPhone::texture_get_format(RID p_texture) const { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture,Image::FORMAT_GRAYSCALE); + + return texture->format; +} +uint32_t RasterizerIPhone::texture_get_width(RID p_texture) const { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture,0); + + return texture->width; +} +uint32_t RasterizerIPhone::texture_get_height(RID p_texture) const { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture,0); + + return texture->height; +} + +bool RasterizerIPhone::texture_has_alpha(RID p_texture) const { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture,0); + + return texture->has_alpha; + +} + +/* SHADER API */ + +RID RasterizerIPhone::shader_create() { + + return RID(); +} + +void RasterizerIPhone::shader_node_add(RID p_shader,VS::ShaderNodeType p_type,int p_id) { + + +} +void RasterizerIPhone::shader_node_remove(RID p_shader,int p_id) { + + +} +void RasterizerIPhone::shader_node_change_type(RID p_shader, int p_id, VS::ShaderNodeType p_type) { + + +} +void RasterizerIPhone::shader_node_set_param(RID p_shader, int p_id, const Variant& p_value) { + + +} + +void RasterizerIPhone::shader_get_node_list(RID p_shader,List<int> *p_node_list) const { + + +} +VS::ShaderNodeType RasterizerIPhone::shader_node_get_type(RID p_shader,int p_id) const { + + + return VS::NODE_ADD; +} +Variant RasterizerIPhone::shader_node_get_param(RID p_shader,int p_id) const { + + return Variant(); +} + +void RasterizerIPhone::shader_connect(RID p_shader,int p_src_id,int p_src_slot, int p_dst_id,int p_dst_slot) { + + +} +bool RasterizerIPhone::shader_is_connected(RID p_shader,int p_src_id,int p_src_slot, int p_dst_id,int p_dst_slot) const { + + return false; +} + +void RasterizerIPhone::shader_disconnect(RID p_shader,int p_src_id,int p_src_slot, int p_dst_id,int p_dst_slot) { + + +} + +void RasterizerIPhone::shader_get_connections(RID p_shader,List<VS::ShaderConnection> *p_connections) const { + + +} + +void RasterizerIPhone::shader_clear(RID p_shader) { + + +} + +/* COMMON MATERIAL API */ + +void RasterizerIPhone::material_set_param(RID p_material, const StringName& p_param, const Variant& p_value) { + + +} +Variant RasterizerIPhone::material_get_param(RID p_material, const StringName& p_param) const { + + return Variant(); +} +void RasterizerIPhone::material_get_param_list(RID p_material, List<String> *p_param_list) const { + + +} + +void RasterizerIPhone::material_set_flag(RID p_material, VS::MaterialFlag p_flag,bool p_enabled) { + + +} +bool RasterizerIPhone::material_get_flag(RID p_material,VS::MaterialFlag p_flag) const { + + return false; +} + +void RasterizerIPhone::material_set_blend_mode(RID p_material,VS::MaterialBlendMode p_mode) { + + +} +VS::MaterialBlendMode RasterizerIPhone::material_get_blend_mode(RID p_material) const { + + return VS::MATERIAL_BLEND_MODE_ADD; +} + +void RasterizerIPhone::material_set_line_width(RID p_material,float p_line_width) { + + +} +float RasterizerIPhone::material_get_line_width(RID p_material) const { + + return 0; +} + +/* FIXED MATERIAL */ + +RID RasterizerIPhone::material_create() { + + return material_owner.make_rid( memnew( Material ) ); +} + +void RasterizerIPhone::fixed_material_set_parameter(RID p_material, VS::FixedMaterialParam p_parameter, const Variant& p_value) { + + Material *m=material_owner.get( p_material ); + ERR_FAIL_COND(!m); + ERR_FAIL_INDEX(p_parameter, VisualServer::FIXED_MATERIAL_PARAM_MAX); + + m->parameters[p_parameter] = p_value; +} +Variant RasterizerIPhone::fixed_material_get_parameter(RID p_material,VS::FixedMaterialParam p_parameter) const { + + Material *m=material_owner.get( p_material ); + ERR_FAIL_COND_V(!m, Variant()); + ERR_FAIL_INDEX_V(p_parameter, VisualServer::FIXED_MATERIAL_PARAM_MAX, Variant()); + + return m->parameters[p_parameter]; +} + +void RasterizerIPhone::fixed_material_set_texture(RID p_material,VS::FixedMaterialParam p_parameter, RID p_texture) { + + Material *m=material_owner.get( p_material ); + ERR_FAIL_COND(!m); + ERR_FAIL_INDEX(p_parameter, VisualServer::FIXED_MATERIAL_PARAM_MAX); + + m->textures[p_parameter] = p_texture; +} +RID RasterizerIPhone::fixed_material_get_texture(RID p_material,VS::FixedMaterialParam p_parameter) const { + + Material *m=material_owner.get( p_material ); + ERR_FAIL_COND_V(!m, RID()); + ERR_FAIL_INDEX_V(p_parameter, VisualServer::FIXED_MATERIAL_PARAM_MAX, Variant()); + + return m->textures[p_parameter]; +} + +void RasterizerIPhone::fixed_material_set_detail_blend_mode(RID p_material,VS::MaterialBlendMode p_mode) { + + Material *m=material_owner.get( p_material ); + ERR_FAIL_COND(!m); + + m->detail_blend_mode = p_mode; +} +VS::MaterialBlendMode RasterizerIPhone::fixed_material_get_detail_blend_mode(RID p_material) const { + + Material *m=material_owner.get( p_material ); + ERR_FAIL_COND_V(!m, VS::MATERIAL_BLEND_MODE_MIX); + + return m->detail_blend_mode; +} + +void RasterizerIPhone::fixed_material_set_texcoord_mode(RID p_material,VS::FixedMaterialParam p_parameter, VS::FixedMaterialTexCoordMode p_mode) { + + Material *m=material_owner.get( p_material ); + ERR_FAIL_COND(!m); + ERR_FAIL_INDEX(p_parameter, VisualServer::FIXED_MATERIAL_PARAM_MAX); + + m->texcoord_mode[p_parameter] = p_mode; +} +VS::FixedMaterialTexCoordMode RasterizerIPhone::fixed_material_get_texcoord_mode(RID p_material,VS::FixedMaterialParam p_parameter) const { + + Material *m=material_owner.get( p_material ); + ERR_FAIL_COND_V(!m, VS::FIXED_MATERIAL_TEXCOORD_TEXGEN); + ERR_FAIL_INDEX_V(p_parameter, VisualServer::FIXED_MATERIAL_PARAM_MAX, VS::FIXED_MATERIAL_TEXCOORD_UV); + + return m->texcoord_mode[p_parameter]; // for now +} + +void RasterizerIPhone::fixed_material_set_texgen_mode(RID p_material,VS::FixedMaterialTexGenMode p_mode) { + + Material *m=material_owner.get( p_material ); + ERR_FAIL_COND(!m); + + m->texgen_mode = p_mode; +}; + +VS::FixedMaterialTexGenMode RasterizerIPhone::fixed_material_get_texgen_mode(RID p_material) const { + + Material *m=material_owner.get( p_material ); + ERR_FAIL_COND_V(!m, VS::FIXED_MATERIAL_TEXGEN_SPHERE); + + return m->texgen_mode; +}; + + +void RasterizerIPhone::fixed_material_set_uv_transform(RID p_material,const Transform& p_transform) { + + Material *m=material_owner.get( p_material ); + ERR_FAIL_COND(!m); + + m->uv_transform = p_transform; +} +Transform RasterizerIPhone::fixed_material_get_uv_transform(RID p_material) const { + + Material *m=material_owner.get( p_material ); + ERR_FAIL_COND_V(!m, Transform()); + + return m->uv_transform; +} + +/* SHADER MATERIAL */ + +RID RasterizerIPhone::shader_material_create() const { + + return RID(); +} + +void RasterizerIPhone::shader_material_set_vertex_shader(RID p_material,RID p_shader,bool p_owned) { + + +} +RID RasterizerIPhone::shader_material_get_vertex_shader(RID p_material) const { + + return RID(); +} + +void RasterizerIPhone::shader_material_set_fragment_shader(RID p_material,RID p_shader,bool p_owned) { + + +} +RID RasterizerIPhone::shader_material_get_fragment_shader(RID p_material) const { + + return RID(); +} + + +/* MESH API */ + +RID RasterizerIPhone::mesh_create() { + + + return mesh_owner.make_rid( memnew( Mesh ) ); +} + +void RasterizerIPhone::mesh_add_surface(RID p_mesh,VS::PrimitiveType p_primitive,uint32_t p_format,int p_array_len,int p_index_array_len) { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND(!mesh); + + ERR_FAIL_COND((p_format&VS::ARRAY_FORMAT_VERTEX)==0); // mandatory + ERR_FAIL_COND( p_array_len<=0 ); + ERR_FAIL_COND( p_index_array_len==0 ); + ERR_FAIL_INDEX( p_primitive, VS::PRIMITIVE_MAX ); + + Surface *surface = memnew( Surface ); + ERR_FAIL_COND( !surface ); + + int total_elem_size=0; + + bool use_VBO=true; //glGenBuffersARB!=NULL; // TODO detect if it's in there + if (p_format&VS::ARRAY_FORMAT_WEIGHTS) { + + use_VBO=false; + } + + + for (int i=0;i<VS::ARRAY_MAX;i++) { + + + Surface::ArrayData&ad=surface->array[i]; + ad.size=0; + ad.configured=false; + ad.ofs=0; + int elem_size=0; + int elem_count=0; + + if (!(p_format&(1<<i))) // no array + continue; + + + switch(i) { + + case VS::ARRAY_VERTEX: + case VS::ARRAY_NORMAL: { + + elem_size=3*sizeof(GLfloat); // vertex + elem_count=3; + } break; + case VS::ARRAY_TANGENT: { + elem_size=4*sizeof(GLfloat); // vertex + elem_count=4; + + } break; + case VS::ARRAY_COLOR: { + + elem_size=4; /* RGBA */ + elem_count=4; + } break; + case VS::ARRAY_TEX_UV: { + elem_size=2*sizeof(GLfloat); + elem_count=2; + + } break; + case VS::ARRAY_WEIGHTS: + case VS::ARRAY_BONES: { + + elem_size=VS::ARRAY_WEIGHTS_SIZE*sizeof(GLfloat); + elem_count=VS::ARRAY_WEIGHTS_SIZE; + + } break; + case VS::ARRAY_INDEX: { + + if (p_index_array_len<=0) { + ERR_PRINT("p_index_array_len==NO_INDEX_ARRAY"); + break; + } + /* determine wether using 8 or 16 bits indices */ + if (p_index_array_len>(1<<8)) { + + elem_size=2; + } else { + elem_size=1; + } + + if (use_VBO) { + + glGenBuffers(1,&surface->index_id); + ERR_FAIL_COND(surface->index_id==0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,surface->index_id); + glBufferData(GL_ELEMENT_ARRAY_BUFFER,p_index_array_len*elem_size,NULL,GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); //unbind + } else { + surface->index_array_local = (uint8_t*)memalloc(p_index_array_len*elem_size); + }; + + surface->index_array_len=p_index_array_len; // only way it can exist + ad.ofs=0; + ad.size=elem_size; + ad.configured=false; + ad.components=1; + + continue; + } break; + default: { + ERR_FAIL( ); + } + } + + ad.ofs=total_elem_size; + ad.size=elem_size; + ad.components=elem_count; + total_elem_size+=elem_size; + ad.configured=false; + } + + surface->stride=total_elem_size; + surface->array_len=p_array_len; + surface->format=p_format; + surface->primitive=p_primitive; + + + /* bind the bigass buffers */ + if (use_VBO) { + + glGenBuffers(1,&surface->vertex_id); + ERR_FAIL_COND(surface->vertex_id==0); + glBindBuffer(GL_ARRAY_BUFFER,surface->vertex_id); + glBufferData(GL_ARRAY_BUFFER,surface->array_len*surface->stride,NULL,GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER,0); //unbind + } else { + surface->array_local = (uint8_t*)memalloc(surface->array_len*surface->stride); + }; + + mesh->surfaces.push_back(surface); +} + +Error RasterizerIPhone::mesh_surface_set_array(RID p_mesh, int p_surface,VS::ArrayType p_type,const Variant& p_array) { + + ERR_FAIL_INDEX_V(p_type, VS::ARRAY_MAX, ERR_INVALID_PARAMETER ); + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), ERR_INVALID_PARAMETER ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND_V( !surface, ERR_INVALID_PARAMETER ); + + ERR_FAIL_COND_V( surface->array[p_type].size==0, ERR_INVALID_PARAMETER ); + + Surface::ArrayData &a=surface->array[p_type]; + + switch(p_type) { + + case VS::ARRAY_INDEX: { + ERR_FAIL_COND_V( surface->index_array_len<=0, ERR_INVALID_DATA ); + ERR_FAIL_COND_V( p_array.get_type() != Variant::INT_ARRAY, ERR_INVALID_PARAMETER ); + + DVector<int> indices = p_array; + ERR_FAIL_COND_V( indices.size() == 0, ERR_INVALID_PARAMETER ); + ERR_FAIL_COND_V( indices.size() != surface->index_array_len, ERR_INVALID_PARAMETER ); + + /* determine wether using 16 or 32 bits indices */ + + if (surface->index_array_local == 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,surface->index_id); + }; + + DVector<int>::Read read = indices.read(); + const int *src=read.ptr(); + + for (int i=0;i<surface->index_array_len;i++) { + + if (surface->index_array_local) { + + if (a.size<=(1<<8)) { + uint8_t v=src[i]; + + copymem(&surface->array_local[i*a.size], &v, a.size); + } else { + uint16_t v=src[i]; + + copymem(&surface->array_local[i*a.size], &v, a.size); + } + + } else { + if (a.size<=(1<<8)) { + uint8_t v=src[i]; + + glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, i*a.size, a.size, &v ); + } else { + uint16_t v=src[i]; + + glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, i*a.size, a.size, &v ); + + } + }; + } + if (surface->index_array_local == 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); + }; + a.configured=true; + return OK; + } break; + case VS::ARRAY_VERTEX: + case VS::ARRAY_NORMAL: { + + ERR_FAIL_COND_V( p_array.get_type() != Variant::VECTOR3_ARRAY, ERR_INVALID_PARAMETER ); + + + DVector<Vector3> array = p_array; + ERR_FAIL_COND_V( array.size() != surface->array_len, ERR_INVALID_PARAMETER ); + + if (surface->array_local == 0) { + glBindBuffer(GL_ARRAY_BUFFER,surface->vertex_id); + }; + + DVector<Vector3>::Read read = array.read(); + const Vector3* src=read.ptr(); + + // setting vertices means regenerating the AABB + if (p_type==VS::ARRAY_VERTEX) + surface->aabb=AABB(); + + for (int i=0;i<surface->array_len;i++) { + + + GLfloat vector[3]={ src[i].x, src[i].y, src[i].z }; + + if (surface->array_local == 0) { + glBufferSubData( GL_ARRAY_BUFFER, a.ofs+i*surface->stride, a.size , vector ); + } else { + copymem(&surface->array_local[a.ofs+i*surface->stride], vector, a.size); + } + + if (p_type==VS::ARRAY_VERTEX) { + + if (i==0) { + + surface->aabb=AABB(src[i],Vector3()); + } else { + + surface->aabb.expand_to( src[i] ); + } + } + } + + if (surface->array_local == 0) { + glBindBuffer(GL_ARRAY_BUFFER,0); + }; + + } break; + case VS::ARRAY_TANGENT: { + + ERR_FAIL_COND_V( p_array.get_type() != Variant::REAL_ARRAY, ERR_INVALID_PARAMETER ); + + DVector<real_t> array = p_array; + + ERR_FAIL_COND_V( array.size() != surface->array_len*4, ERR_INVALID_PARAMETER ); + + if (surface->array_local == 0) { + glBindBuffer(GL_ARRAY_BUFFER,surface->vertex_id); + }; + + + DVector<real_t>::Read read = array.read(); + const real_t* src = read.ptr(); + + for (int i=0;i<surface->array_len;i++) { + + GLfloat xyzw[4]={ + src[i*4+0], + src[i*4+1], + src[i*4+2], + src[i*4+3] + }; + + if (surface->array_local == 0) { + + glBufferSubData( GL_ARRAY_BUFFER, a.ofs+i*surface->stride, a.size , xyzw ); + } else { + + copymem(&surface->array_local[a.ofs+i*surface->stride], xyzw, a.size); + }; + + } + + if (surface->array_local == 0) { + glBindBuffer(GL_ARRAY_BUFFER,0); + }; + } break; + case VS::ARRAY_COLOR: { + + ERR_FAIL_COND_V( p_array.get_type() != Variant::COLOR_ARRAY, ERR_INVALID_PARAMETER ); + + + DVector<Color> array = p_array; + + ERR_FAIL_COND_V( array.size() != surface->array_len, ERR_INVALID_PARAMETER ); + + if (surface->array_local == 0) + glBindBuffer(GL_ARRAY_BUFFER,surface->vertex_id); + + + DVector<Color>::Read read = array.read(); + const Color* src = read.ptr(); + surface->has_alpha_cache=false; + + for (int i=0;i<surface->array_len;i++) { + + if (src[i].a<0.98) // tolerate alpha a bit, for crappy exporters + surface->has_alpha_cache=true; + uint8_t colors[4]={ src[i].r * 255.0 , src[i].g * 255.0, src[i].b * 255.0, src[i].a * 255.0 }; + // I'm not sure if this is correct, endianness-wise, i should re-check the GL spec + + if (surface->array_local == 0) + glBufferSubData( GL_ARRAY_BUFFER, a.ofs+i*surface->stride, a.size , colors ); + else + copymem(&surface->array_local[a.ofs+i*surface->stride], colors, a.size); + + } + + if (surface->array_local == 0) + glBindBuffer(GL_ARRAY_BUFFER,0); + + + } break; + case VS::ARRAY_TEX_UV: { + + ERR_FAIL_COND_V( p_array.get_type() != Variant::VECTOR3_ARRAY, ERR_INVALID_PARAMETER ); + + DVector<Vector3> array = p_array; + + ERR_FAIL_COND_V( array.size() != surface->array_len , ERR_INVALID_PARAMETER); + + if (surface->array_local == 0) + glBindBuffer(GL_ARRAY_BUFFER,surface->vertex_id); + + DVector<Vector3>::Read read = array.read(); + + const Vector3 * src=read.ptr(); + + for (int i=0;i<surface->array_len;i++) { + + GLfloat uv[2]={ src[i].x , src[i].y }; + + if (surface->array_local == 0) + glBufferSubData( GL_ARRAY_BUFFER, a.ofs+i*surface->stride, a.size , uv ); + else + copymem(&surface->array_local[a.ofs+i*surface->stride], uv, a.size); + + } + + if (surface->array_local == 0) + glBindBuffer(GL_ARRAY_BUFFER,0); + + } break; + case VS::ARRAY_BONES: + case VS::ARRAY_WEIGHTS: { + + + ERR_FAIL_COND_V( p_array.get_type() != Variant::REAL_ARRAY, ERR_INVALID_PARAMETER ); + + DVector<real_t> array = p_array; + + ERR_FAIL_COND_V( array.size() != surface->array_len*VS::ARRAY_WEIGHTS_SIZE, ERR_INVALID_PARAMETER ); + + if (surface->array_local == 0) + glBindBuffer(GL_ARRAY_BUFFER,surface->vertex_id); + + DVector<real_t>::Read read = array.read(); + + const real_t * src = read.ptr(); + + for (int i=0;i<surface->array_len;i++) { + + GLfloat data[VS::ARRAY_WEIGHTS_SIZE]; + for (int j=0;j<VS::ARRAY_WEIGHTS_SIZE;j++) + data[j]=src[i*VS::ARRAY_WEIGHTS_SIZE+j]; + + if (surface->array_local == 0) + glBufferSubData( GL_ARRAY_BUFFER, a.ofs+i*surface->stride, a.size , data ); + else + copymem(&surface->array_local[a.ofs+i*surface->stride], data, a.size); + + + } + + if (surface->array_local == 0) + glBindBuffer(GL_ARRAY_BUFFER,0); + } break; + default: { ERR_FAIL_V(ERR_INVALID_PARAMETER);} + } + + a.configured=true; + + return OK; +} +Variant RasterizerIPhone::mesh_surface_get_array(RID p_mesh, int p_surface,VS::ArrayType p_type) const { + + return Variant(); +} + +void RasterizerIPhone::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material,bool p_owned) { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX(p_surface, mesh->surfaces.size() ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND( !surface); + + if (surface->material_owned && surface->material.is_valid()) + free(surface->material); + + surface->material_owned=p_owned; + + surface->material=p_material; +} + +RID RasterizerIPhone::mesh_surface_get_material(RID p_mesh, int p_surface) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,RID()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), RID() ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND_V( !surface, RID() ); + + return surface->material; +} + +int RasterizerIPhone::mesh_surface_get_array_len(RID p_mesh, int p_surface) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,-1); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), -1 ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND_V( !surface, -1 ); + + return surface->array_len; +} +int RasterizerIPhone::mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,-1); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), -1 ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND_V( !surface, -1 ); + + return surface->index_array_len; +} +uint32_t RasterizerIPhone::mesh_surface_get_format(RID p_mesh, int p_surface) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,0); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), 0 ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND_V( !surface, 0 ); + + return surface->format; +} +VS::PrimitiveType RasterizerIPhone::mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,VS::PRIMITIVE_POINTS); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), VS::PRIMITIVE_POINTS ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND_V( !surface, VS::PRIMITIVE_POINTS ); + + return surface->primitive; +} + +void RasterizerIPhone::mesh_erase_surface(RID p_mesh,int p_index) { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX(p_index, mesh->surfaces.size() ); + Surface *surface = mesh->surfaces[p_index]; + ERR_FAIL_COND( !surface); + + memdelete( mesh->surfaces[p_index] ); + mesh->surfaces.remove(p_index); + +} +int RasterizerIPhone::mesh_get_surface_count(RID p_mesh) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,-1); + + return mesh->surfaces.size(); +} + +AABB RasterizerIPhone::mesh_get_aabb(RID p_mesh) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,AABB()); + + AABB aabb; + + for (int i=0;i<mesh->surfaces.size();i++) { + + if (i==0) + aabb=mesh->surfaces[i]->aabb; + else + aabb.merge_with(mesh->surfaces[i]->aabb); + } + + return aabb; +} + +/* MULTIMESH API */ + +RID RasterizerIPhone::multimesh_create() { + + return RID(); +} + +void RasterizerIPhone::multimesh_set_instance_count(RID p_multimesh,int p_count) { + + +} +int RasterizerIPhone::multimesh_get_instance_count(RID p_multimesh) const { + + return 0; +} + +void RasterizerIPhone::multimesh_set_mesh(RID p_multimesh,RID p_mesh) { + + +} +void RasterizerIPhone::multimesh_set_aabb(RID p_multimesh,const AABB& p_aabb) { + + +} +void RasterizerIPhone::multimesh_instance_set_transform(RID p_multimesh,int p_index,const Transform& p_transform) { + + +} +void RasterizerIPhone::multimesh_instance_set_color(RID p_multimesh,int p_index,const Color& p_color) { + + +} + +RID RasterizerIPhone::multimesh_get_mesh(RID p_multimesh) const { + + return RID(); +} +AABB RasterizerIPhone::multimesh_get_aabb(RID p_multimesh) const { + + return AABB(); +} + +Transform RasterizerIPhone::multimesh_instance_get_transform(RID p_multimesh,int p_index) const { + + return Transform(); +} +Color RasterizerIPhone::multimesh_instance_get_color(RID p_multimesh,int p_index) const { + + return Color(); +} + +/* POLY API */ + +RID RasterizerIPhone::poly_create() { + + return RID(); +} +void RasterizerIPhone::poly_set_material(RID p_poly, RID p_material,bool p_owned) { + + +} +void RasterizerIPhone::poly_add_primitive(RID p_poly, const Vector<Vector3>& p_points,const Vector<Vector3>& p_normals,const Vector<Color>& p_colors,const Vector<Vector3>& p_uvs) { + + +} +void RasterizerIPhone::poly_clear(RID p_poly) { + + +} + +AABB RasterizerIPhone::poly_get_aabb(RID p_poly) const { + + return AABB(); +} + + +/* PARTICLES API */ + +RID RasterizerIPhone::particles_create() { + + return RID(); +} + +void RasterizerIPhone::particles_set_amount(RID p_particles, int p_amount) { + + +} +int RasterizerIPhone::particles_get_amount(RID p_particles) const { + + return 0; +} + +void RasterizerIPhone::particles_set_emitting(RID p_particles, bool p_emitting) { + + +} + +bool RasterizerIPhone::particles_is_emitting(RID p_particles) const { + + return false; +} + +void RasterizerIPhone::particles_set_visibility_aabb(RID p_particles, const AABB& p_visibility) { + + +} +AABB RasterizerIPhone::particles_get_visibility_aabb(RID p_particles) const { + + return AABB(); +} + +void RasterizerIPhone::particles_set_emission_half_extents(RID p_particles, const Vector3& p_half_extents) { + + +} +Vector3 RasterizerIPhone::particles_get_emission_half_extents(RID p_particles) const { + + return Vector3(); +} + +void RasterizerIPhone::particles_set_gravity_normal(RID p_particles, const Vector3& p_normal) { + + +} +Vector3 RasterizerIPhone::particles_get_gravity_normal(RID p_particles) const { + + return Vector3(); +} + +void RasterizerIPhone::particles_set_variable(RID p_particles, VS::ParticleVariable p_variable,float p_value) { + + +} +float RasterizerIPhone::particles_get_variable(RID p_particles, VS::ParticleVariable p_variable) const { + + return 0; +} + +void RasterizerIPhone::particles_set_randomness(RID p_particles, VS::ParticleVariable p_variable,float p_randomness) { + + +} +float RasterizerIPhone::particles_get_randomness(RID p_particles, VS::ParticleVariable p_variable) const { + + return 0; +} + +void RasterizerIPhone::particles_set_color_phase_pos(RID p_particles, int p_phase, float p_pos) { + + +} +float RasterizerIPhone::particles_get_color_phase_pos(RID p_particles, int p_phase) const { + + return 0; +} + +void RasterizerIPhone::particles_set_color_phases(RID p_particles, int p_phases) { + + +} +int RasterizerIPhone::particles_get_color_phases(RID p_particles) const { + + return 0; +} + +void RasterizerIPhone::particles_set_color_phase_color(RID p_particles, int p_phase, const Color& p_color) { + + +} +Color RasterizerIPhone::particles_get_color_phase_color(RID p_particles, int p_phase) const { + + return Color(); +} + +void RasterizerIPhone::particles_set_attractors(RID p_particles, int p_attractors) { + + +} +int RasterizerIPhone::particles_get_attractors(RID p_particles) const { + + return 0; +} + +void RasterizerIPhone::particles_set_attractor_pos(RID p_particles, int p_attractor, const Vector3& p_pos) { + + +} +Vector3 RasterizerIPhone::particles_get_attractor_pos(RID p_particles,int p_attractor) const { + + return Vector3(); +} + +void RasterizerIPhone::particles_set_attractor_strength(RID p_particles, int p_attractor, float p_force) { + + +} +float RasterizerIPhone::particles_get_attractor_strength(RID p_particles,int p_attractor) const { + + return 0; +} + +void RasterizerIPhone::particles_set_material(RID p_particles, RID p_material,bool p_owned) { + + +} + +RID RasterizerIPhone::particles_get_material(RID p_particles) const { + + return RID(); +} + +AABB RasterizerIPhone::particles_get_aabb(RID p_particles) const { + + return AABB(); +} +/* BEAM API */ + +RID RasterizerIPhone::beam_create() { + + return RID(); +} + +void RasterizerIPhone::beam_set_point_count(RID p_beam, int p_count) { + + +} +int RasterizerIPhone::beam_get_point_count(RID p_beam) const { + + return 0; +} +void RasterizerIPhone::beam_clear(RID p_beam) { + + +} + +void RasterizerIPhone::beam_set_point(RID p_beam,int p_point,Vector3& p_pos) { + + +} +Vector3 RasterizerIPhone::beam_get_point(RID p_beam,int p_point) const { + + return Vector3(); +} + +void RasterizerIPhone::beam_set_primitive(RID p_beam,VS::BeamPrimitive p_primitive) { + + +} + +VS::BeamPrimitive RasterizerIPhone::beam_get_primitive(RID p_beam) const { + + return VS::BEAM_CUBIC; +} + +void RasterizerIPhone::beam_set_material(RID p_beam, RID p_material) { + + +} +RID RasterizerIPhone::beam_get_material(RID p_beam) const { + + return RID(); +} + +AABB RasterizerIPhone::beam_get_aabb(RID p_particles) const { + + return AABB(); +} +/* SKELETON API */ + +RID RasterizerIPhone::skeleton_create() { + + Skeleton *skeleton = memnew( Skeleton ); + ERR_FAIL_COND_V(!skeleton,RID()); + return skeleton_owner.make_rid( skeleton ); +} +void RasterizerIPhone::skeleton_resize(RID p_skeleton,int p_bones) { + + Skeleton *skeleton = skeleton_owner.get( p_skeleton ); + ERR_FAIL_COND(!skeleton); + if (p_bones == skeleton->bones.size()) { + return; + }; + ERR_FAIL_COND( p_bones<0 || p_bones>256); + + skeleton->bones.resize(p_bones); + +} +int RasterizerIPhone::skeleton_get_bone_count(RID p_skeleton) const { + + Skeleton *skeleton = skeleton_owner.get( p_skeleton ); + ERR_FAIL_COND_V(!skeleton, -1); + return skeleton->bones.size(); +} +void RasterizerIPhone::skeleton_bone_set_transform(RID p_skeleton,int p_bone, const Transform& p_transform) { + + Skeleton *skeleton = skeleton_owner.get( p_skeleton ); + ERR_FAIL_COND(!skeleton); + ERR_FAIL_INDEX( p_bone, skeleton->bones.size() ); + + skeleton->bones[p_bone] = p_transform; +} +Transform RasterizerIPhone::skeleton_bone_get_transform(RID p_skeleton,int p_bone) { + + Skeleton *skeleton = skeleton_owner.get( p_skeleton ); + ERR_FAIL_COND_V(!skeleton, Transform()); + ERR_FAIL_INDEX_V( p_bone, skeleton->bones.size(), Transform() ); + + // something + return skeleton->bones[p_bone]; +} + + +/* LIGHT API */ + +RID RasterizerIPhone::light_create(VS::LightType p_type) { + + Light *light = memnew( Light ); + light->type=p_type; + return light_owner.make_rid(light); +} + +VS::LightType RasterizerIPhone::light_get_type(RID p_light) const { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND_V(!light,VS::LIGHT_OMNI); + return light->type; +} + +void RasterizerIPhone::light_set_color(RID p_light,VS::LightColor p_type, const Color& p_color) { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND(!light); + ERR_FAIL_INDEX( p_type, 3 ); + light->colors[p_type]=p_color; +} +Color RasterizerIPhone::light_get_color(RID p_light,VS::LightColor p_type) const { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND_V(!light, Color()); + ERR_FAIL_INDEX_V( p_type, 3, Color() ); + return light->colors[p_type]; +} + +void RasterizerIPhone::light_set_shadow(RID p_light,bool p_enabled) { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND(!light); + light->shadow_enabled=p_enabled; +} + +bool RasterizerIPhone::light_has_shadow(RID p_light) const { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND_V(!light,false); + return light->shadow_enabled; +} + +void RasterizerIPhone::light_set_volumetric(RID p_light,bool p_enabled) { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND(!light); + light->volumetric_enabled=p_enabled; + +} +bool RasterizerIPhone::light_is_volumetric(RID p_light) const { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND_V(!light,false); + return light->volumetric_enabled; +} + +void RasterizerIPhone::light_set_projector(RID p_light,RID p_texture) { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND(!light); + light->projector=p_texture; +} +RID RasterizerIPhone::light_get_projector(RID p_light) const { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND_V(!light,RID()); + return light->projector; +} + +void RasterizerIPhone::light_set_var(RID p_light, VS::LightParam p_var, float p_value) { + + Light * light = light_owner.get( p_light ); + ERR_FAIL_COND(!light); + ERR_FAIL_INDEX( p_var, VS::LIGHT_PARAM_MAX ); + + light->vars[p_var]=p_value; +} +float RasterizerIPhone::light_get_var(RID p_light, VS::LightParam p_var) const { + + Light * light = light_owner.get( p_light ); + ERR_FAIL_COND_V(!light,0); + + ERR_FAIL_INDEX_V( p_var, VS::LIGHT_PARAM_MAX,0 ); + + return light->vars[p_var]; +} + +AABB RasterizerIPhone::light_get_aabb(RID p_light) const { + + Light *light = light_owner.get( p_light ); + ERR_FAIL_COND_V(!light,AABB()); + + switch( light->type ) { + + case VS::LIGHT_SPOT: { + + float len=light->vars[VS::LIGHT_PARAM_RADIUS]; + float size=Math::tan(Math::deg2rad(light->vars[VS::LIGHT_PARAM_SPOT_ANGLE]))*len; + return AABB( Vector3( -size,-size,-len ), Vector3( size*2, size*2, len ) ); + } break; + case VS::LIGHT_OMNI: { + + float r = light->vars[VS::LIGHT_PARAM_RADIUS]; + return AABB( -Vector3(r,r,r), Vector3(r,r,r)*2 ); + } break; + case VS::LIGHT_DIRECTIONAL: { + + return AABB(); + } break; + default: {} + } + + ERR_FAIL_V( AABB() ); +} + + +RID RasterizerIPhone::light_instance_create(RID p_light) { + + Light *light = light_owner.get( p_light ); + ERR_FAIL_COND_V(!light, RID()); + + LightInstance *light_instance = memnew( LightInstance ); + + light_instance->light=p_light; + light_instance->base=light; + light_instance->last_pass=0; + + return light_instance_owner.make_rid( light_instance ); +} +void RasterizerIPhone::light_instance_set_transform(RID p_light_instance,const Transform& p_transform) { + + LightInstance *lighti = light_instance_owner.get( p_light_instance ); + ERR_FAIL_COND(!lighti); + lighti->transform=p_transform; + +} + +void RasterizerIPhone::light_instance_set_active_hint(RID p_light_instance) { + + LightInstance *lighti = light_instance_owner.get( p_light_instance ); + ERR_FAIL_COND(!lighti); + lighti->last_pass=frame; + +} +bool RasterizerIPhone::light_instance_has_shadow(RID p_light_instance) const { + + return false; +} +bool RasterizerIPhone::light_instance_assign_shadow(RID p_light_instance) { + + return false; +} +Rasterizer::ShadowType RasterizerIPhone::light_instance_get_shadow_type(RID p_light_instance) const { + + return Rasterizer::SHADOW_CUBE; +} +int RasterizerIPhone::light_instance_get_shadow_passes(RID p_light_instance) const { + + return 0; +} +void RasterizerIPhone::light_instance_set_pssm_split_info(RID p_light_instance, int p_split, float p_near,float p_far, const CameraMatrix& p_camera, const Transform& p_transform) { + + +} + +/* PARTICLES INSTANCE */ + +RID RasterizerIPhone::particles_instance_create(RID p_particles) { + + return RID(); +} +void RasterizerIPhone::particles_instance_set_transform(RID p_particles_instance,const Transform& p_transform) { + + +} + +/* RENDER API */ +/* all calls (inside begin/end shadow) are always warranted to be in the following order: */ + +static GLfloat rtri; // Angle For The Triangle ( NEW ) +static GLfloat rquad; // Angle For The Quad ( NEW ) + +void RasterizerIPhone::begin_frame() { + + window_size = Size2( OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height ); + + double time = (OS::get_singleton()->get_ticks_usec()/1000); // get msec + time/=1000.0; // make secs + time_delta=time-last_time; + last_time=time; + frame++; + glClearColor(0,0,1,1); + glClear(GL_COLOR_BUFFER_BIT); + + /* nehe ?*/ + + #if 0 + glViewport(0,0,window_size.width,window_size.height); // Reset The Current Viewport + + glMatrixMode(GL_PROJECTION); // Select The Projection Matrix + glLoadIdentity(); // Reset The Projection Matrix + + // Calculate The Aspect Ratio Of The Window + gluPerspective(45.0f,(GLfloat)window_size.width/(GLfloat)window_size.height,0.1f,100.0f); + + glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix + glLoadIdentity(); // Reset The Modelview Matrix + + + + glShadeModel(GL_SMOOTH); // Enable Smooth Shading + glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background + glClearDepth(1.0f); // Depth Buffer Setup + glEnable(GL_DEPTH_TEST); // Enables Depth Testing + glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer + glLoadIdentity(); // Reset The Current Modelview Matrix + glTranslatef(-1.5f,0.0f,-6.0f); // Move Left 1.5 Units And Into The Screen 6.0 + glRotatef(rtri,0.0f,1.0f,0.0f); // Rotate The Triangle On The Y axis ( NEW ) + glBegin(GL_TRIANGLES); // Start Drawing A Triangle + glColor3f(1.0f,0.0f,0.0f); // Red + glVertex3f( 0.0f, 1.0f, 0.0f); // Top Of Triangle (Front) + glColor3f(0.0f,1.0f,0.0f); // Green + glVertex3f(-1.0f,-1.0f, 1.0f); // Left Of Triangle (Front) + glColor3f(0.0f,0.0f,1.0f); // Blue + glVertex3f( 1.0f,-1.0f, 1.0f); // Right Of Triangle (Front) + glColor3f(1.0f,0.0f,0.0f); // Red + glVertex3f( 0.0f, 1.0f, 0.0f); // Top Of Triangle (Right) + glColor3f(0.0f,0.0f,1.0f); // Blue + glVertex3f( 1.0f,-1.0f, 1.0f); // Left Of Triangle (Right) + glColor3f(0.0f,1.0f,0.0f); // Green + glVertex3f( 1.0f,-1.0f, -1.0f); // Right Of Triangle (Right) + glColor3f(1.0f,0.0f,0.0f); // Red + glVertex3f( 0.0f, 1.0f, 0.0f); // Top Of Triangle (Back) + glColor3f(0.0f,1.0f,0.0f); // Green + glVertex3f( 1.0f,-1.0f, -1.0f); // Left Of Triangle (Back) + glColor3f(0.0f,0.0f,1.0f); // Blue + glVertex3f(-1.0f,-1.0f, -1.0f); // Right Of Triangle (Back) + glColor3f(1.0f,0.0f,0.0f); // Red + glVertex3f( 0.0f, 1.0f, 0.0f); // Top Of Triangle (Left) + glColor3f(0.0f,0.0f,1.0f); // Blue + glVertex3f(-1.0f,-1.0f,-1.0f); // Left Of Triangle (Left) + glColor3f(0.0f,1.0f,0.0f); // Green + glVertex3f(-1.0f,-1.0f, 1.0f); // Right Of Triangle (Left) + glEnd(); // Done Drawing The Pyramid + + glLoadIdentity(); // Reset The Current Modelview Matrix + glTranslatef(1.5f,0.0f,-7.0f); // Move Right 1.5 Units And Into The Screen 7.0 + glRotatef(rquad,1.0f,1.0f,1.0f); // Rotate The Quad On The X axis ( NEW ) + glBegin(GL_QUADS); // Draw A Quad + glColor3f(0.0f,1.0f,0.0f); // Set The Color To Green + glVertex3f( 1.0f, 1.0f,-1.0f); // Top Right Of The Quad (Top) + glVertex3f(-1.0f, 1.0f,-1.0f); // Top Left Of The Quad (Top) + glVertex3f(-1.0f, 1.0f, 1.0f); // Bottom Left Of The Quad (Top) + glVertex3f( 1.0f, 1.0f, 1.0f); // Bottom Right Of The Quad (Top) + glColor3f(1.0f,0.5f,0.0f); // Set The Color To Orange + glVertex3f( 1.0f,-1.0f, 1.0f); // Top Right Of The Quad (Bottom) + glVertex3f(-1.0f,-1.0f, 1.0f); // Top Left Of The Quad (Bottom) + glVertex3f(-1.0f,-1.0f,-1.0f); // Bottom Left Of The Quad (Bottom) + glVertex3f( 1.0f,-1.0f,-1.0f); // Bottom Right Of The Quad (Bottom) + glColor3f(1.0f,0.0f,0.0f); // Set The Color To Red + glVertex3f( 1.0f, 1.0f, 1.0f); // Top Right Of The Quad (Front) + glVertex3f(-1.0f, 1.0f, 1.0f); // Top Left Of The Quad (Front) + glVertex3f(-1.0f,-1.0f, 1.0f); // Bottom Left Of The Quad (Front) + glVertex3f( 1.0f,-1.0f, 1.0f); // Bottom Right Of The Quad (Front) + glColor3f(1.0f,1.0f,0.0f); // Set The Color To Yellow + glVertex3f( 1.0f,-1.0f,-1.0f); // Top Right Of The Quad (Back) + glVertex3f(-1.0f,-1.0f,-1.0f); // Top Left Of The Quad (Back) + glVertex3f(-1.0f, 1.0f,-1.0f); // Bottom Left Of The Quad (Back) + glVertex3f( 1.0f, 1.0f,-1.0f); // Bottom Right Of The Quad (Back) + glColor3f(0.0f,0.0f,1.0f); // Set The Color To Blue + glVertex3f(-1.0f, 1.0f, 1.0f); // Top Right Of The Quad (Left) + glVertex3f(-1.0f, 1.0f,-1.0f); // Top Left Of The Quad (Left) + glVertex3f(-1.0f,-1.0f,-1.0f); // Bottom Left Of The Quad (Left) + glVertex3f(-1.0f,-1.0f, 1.0f); // Bottom Right Of The Quad (Left) + glColor3f(1.0f,0.0f,1.0f); // Set The Color To Violet + glVertex3f( 1.0f, 1.0f,-1.0f); // Top Right Of The Quad (Right) + glVertex3f( 1.0f, 1.0f, 1.0f); // Top Left Of The Quad (Right) + glVertex3f( 1.0f,-1.0f, 1.0f); // Bottom Left Of The Quad (Right) + glVertex3f( 1.0f,-1.0f,-1.0f); // Bottom Right Of The Quad (Right) + glEnd(); // Done Drawing The Quad + + rtri+=0.2f; // Increase The Rotation Variable For The Triangle ( NEW ) + rquad-=0.15f; // Decrease The Rotation Variable For The Quad ( NEW ) + + #endif + +} + +void RasterizerIPhone::set_viewport(const VS::ViewportRect& p_viewport) { + + + viewport=p_viewport; + canvas_transform=Transform(); + canvas_transform.translate(-(viewport.width / 2.0f), -(viewport.height / 2.0f), 0.0f); + canvas_transform.scale( Vector3( 2.0f / viewport.width, -2.0f / viewport.height, 1.0f ) ); + + glViewport( viewport.x, window_size.height-(viewport.height+viewport.y), viewport.width,viewport.height ); +} + +void RasterizerIPhone::begin_scene(RID p_fx,VS::ScenarioDebugMode p_debug) { + + opaque_render_list.clear(); + alpha_render_list.clear(); + light_instance_count=0; + scene_fx = p_fx.is_valid() ? fx_owner.get(p_fx) : NULL; +}; + +void RasterizerIPhone::begin_shadow_map( RID p_light_instance, int p_shadow_pass ) { + + +} + +void RasterizerIPhone::set_camera(const Transform& p_world,const CameraMatrix& p_projection) { + + camera_transform=p_world; + camera_transform_inverse=camera_transform.inverse(); + camera_projection=p_projection; + camera_plane = Plane( camera_transform.origin, camera_transform.basis.get_axis(2) ); + camera_z_near=camera_projection.get_z_near(); +camera_z_far=camera_projection.get_z_far(); + camera_projection.get_viewport_size(camera_vp_size.x,camera_vp_size.y); +} + +void RasterizerIPhone::add_light( RID p_light_instance ) { + +#define LIGHT_FADE_TRESHOLD 0.05 + + ERR_FAIL_COND( light_instance_count >= MAX_LIGHTS ); + + LightInstance *li = light_instance_owner.get(p_light_instance); + ERR_FAIL_COND(!li); + + /* make light hash */ + + // actually, not really a hash, but helps to sort the lights + // and avoid recompiling redudant shader versions + + li->hash_aux=li->base->type; + + if (li->base->shadow_enabled) + li->hash_aux|=(1<<3); + + if (li->base->projector.is_valid()) + li->hash_aux|=(1<<4); + + if (li->base->shadow_enabled && li->base->volumetric_enabled) + li->hash_aux|=(1<<5); + + switch(li->base->type) { + + case VisualServer::LIGHT_DIRECTIONAL: { + + Vector3 dir = li->transform.basis.get_axis(2); + li->light_vector.x=dir.x; + li->light_vector.y=dir.y; + li->light_vector.z=dir.z; + + } break; + case VisualServer::LIGHT_OMNI: { + + float radius = li->base->vars[VisualServer::LIGHT_PARAM_RADIUS]; + if (radius==0) + radius=0.0001; + li->linear_att=(1/LIGHT_FADE_TRESHOLD)/radius; + li->light_vector.x=li->transform.origin.x; + li->light_vector.y=li->transform.origin.y; + li->light_vector.z=li->transform.origin.z; + + } break; + case VisualServer::LIGHT_SPOT: { + + float radius = li->base->vars[VisualServer::LIGHT_PARAM_RADIUS]; + if (radius==0) + radius=0.0001; + li->linear_att=(1/LIGHT_FADE_TRESHOLD)/radius; + li->light_vector.x=li->transform.origin.x; + li->light_vector.y=li->transform.origin.y; + li->light_vector.z=li->transform.origin.z; + Vector3 dir = -li->transform.basis.get_axis(2); + li->spot_vector.x=dir.x; + li->spot_vector.y=dir.y; + li->spot_vector.z=dir.z; + + } break; + } + + light_instances[light_instance_count++]=li; +} + +void RasterizerIPhone::_add_geometry( const Geometry* p_geometry, const Transform& p_world, uint32_t p_vertex_format, const RID* p_light_instances, int p_light_count, const ParamOverrideMap* p_material_overrides,const Skeleton* p_skeleton, GeometryOwner *p_owner) { + + Material *m=NULL; + + if (p_geometry->material.is_valid()) + m=material_owner.get( p_geometry->material ); + + if (!m) { + m=material_owner.get( default_material ); + } + + ERR_FAIL_COND(!m); + + LightInstance *lights[RenderList::MAX_LIGHTS]; + int light_count=0; + + RenderList *render_list=&opaque_render_list; + if (p_geometry->has_alpha || m->detail_blend_mode!=VS::MATERIAL_BLEND_MODE_MIX) { + render_list = &alpha_render_list; + }; + + if (!m->flags[VS::MATERIAL_FLAG_UNSHADED]) { + + light_count=p_light_count; + for(int i=0;i<light_count;i++) { + lights[i]=light_instance_owner.get( p_light_instances[i] ); + } + } + + render_list->add_element( p_geometry, m, p_world, lights, light_count, p_material_overrides,p_skeleton, camera_plane.distance(p_world.origin), p_owner ); +} + +void RasterizerIPhone::add_mesh( RID p_mesh, const Transform* p_world, const RID* p_light_instances, int p_light_count, const ParamOverrideMap* p_material_overrides, RID p_skeleton) { + + Mesh *mesh = mesh_owner.get(p_mesh); + + int ssize = mesh->surfaces.size(); + + for (int i=0;i<ssize;i++) { + + Surface *s = mesh->surfaces[i]; + Skeleton *sk = p_skeleton.is_valid()?skeleton_owner.get(p_skeleton):NULL; + + _add_geometry(s,*p_world,s->format,p_light_instances,p_light_count,p_material_overrides,sk,NULL); + } + + mesh->last_pass=frame; +} + +void RasterizerIPhone::add_multimesh( RID p_multimesh, const Transform* p_world, const RID* p_light_instances, int p_light_count, const ParamOverrideMap* p_material_overrides) { + + +} + +void RasterizerIPhone::add_poly( RID p_poly, const Transform* p_world, const RID* p_light_instances, int p_light_count, const ParamOverrideMap* p_material_overrides) { + + Poly *p = poly_owner.get(p_poly); + if (!p->primitives.empty()) { + const Poly::Primitive *pp = &p->primitives[0]; + + uint32_t format=VisualServer::ARRAY_FORMAT_VERTEX; + + if (!pp->normals.empty()) + format|=VisualServer::ARRAY_FORMAT_NORMAL; + if (!pp->colors.empty()) + format|=VisualServer::ARRAY_FORMAT_COLOR; + if (!pp->uvs.empty()) + format|=VisualServer::ARRAY_TEX_UV; + + _add_geometry(p,*p_world,format,p_light_instances,p_light_count,p_material_overrides,NULL, NULL); + } +} + +void RasterizerIPhone::add_beam( RID p_beam, const Transform* p_world, const RID* p_light_instances, int p_light_count, const ParamOverrideMap* p_material_overrides) { + + +} + +void RasterizerIPhone::add_particles( RID p_particle_instance, const RID* p_light_instances, int p_light_count, const ParamOverrideMap* p_material_overrides) { + + +} + +void RasterizerIPhone::_setup_material(const Geometry *p_geometry,const Material *p_material) { + + if (p_material->flags[VS::MATERIAL_FLAG_DOUBLE_SIDED]) + glDisable(GL_CULL_FACE); + else { + glEnable(GL_CULL_FACE); + glCullFace( (p_material->flags[VS::MATERIAL_FLAG_INVERT_FACES])?GL_FRONT:GL_BACK); + } + + glEnable(GL_COLOR_MATERIAL); /* unused, unless color array */ + //glColorMaterial( GL_FRONT_AND_BACK, GL_DIFFUSE ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + + + ///ambient @TODO offer global ambient group option + float ambient_rgba[4]={ + 1, + 1, + 1, + 1.0 + }; + glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambient_rgba); + + ///diffuse + const Color &diffuse_color=p_material->parameters[VS::FIXED_MATERIAL_PARAM_DIFFUSE]; + float diffuse_rgba[4]={ + (float)diffuse_color.r, + (float)diffuse_color.g, + (float)diffuse_color.b, + (float)diffuse_color.a + }; + + glColor4f( diffuse_rgba[0],diffuse_rgba[1],diffuse_rgba[2],diffuse_rgba[3]); + + glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuse_rgba); + + //specular + + const Color &specular_color=p_material->parameters[VS::FIXED_MATERIAL_PARAM_SPECULAR]; + float specular_rgba[4]={ + (float)specular_color.r, + (float)specular_color.g, + (float)specular_color.b, + 1.0 + }; + + glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specular_rgba); + + const Color &emission_color=p_material->parameters[VS::FIXED_MATERIAL_PARAM_EMISSION]; + float emission_rgba[4]={ + (float)emission_color.r, + (float)emission_color.g, + (float)emission_color.b, + 1.0 + }; + + glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,emission_rgba); + + glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,p_material->parameters[VS::FIXED_MATERIAL_PARAM_SPECULAR_EXP]); + + if (p_material->flags[VS::MATERIAL_FLAG_UNSHADED]) { + glDisable(GL_LIGHTING); + } else { + glEnable(GL_LIGHTING); + glDisable(GL_LIGHTING); + } + + //depth test? + /* + if (p_material->flags[VS::MATERIAL_FLAG_WIREFRAME]) + glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); + else + glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + */ + if (p_material->textures[VS::FIXED_MATERIAL_PARAM_DIFFUSE]) { + + Texture *texture = texture_owner.get( p_material->textures[VS::FIXED_MATERIAL_PARAM_DIFFUSE] ); + ERR_FAIL_COND(!texture); + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_2D); + glBindTexture( GL_TEXTURE_2D,texture->tex_id ); + }; +}; + +void RasterizerIPhone::_setup_light(LightInstance* p_instance, int p_idx) { + + Light* ld = p_instance->base; + + int glid = GL_LIGHT0 + p_idx; + glLightfv(glid , GL_AMBIENT, ld->colors[VS::LIGHT_COLOR_AMBIENT].components ); + glLightfv(glid, GL_DIFFUSE, ld->colors[VS::LIGHT_COLOR_DIFFUSE].components ); + glLightfv(glid, GL_SPECULAR, ld->colors[VS::LIGHT_COLOR_SPECULAR].components ); + + + + switch(ld->type) { + + case VS::LIGHT_DIRECTIONAL: { + /* This doesnt have attenuation */ + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + Vector3 v(0.0,0.0,-1.0); // directional lights point up by default + v = p_instance->transform.get_basis().xform( v ); + v = camera_transform_inverse.get_basis().xform( v ); + v.normalize(); // this sucks, so it will be optimized at some point + v = -v; + float lightpos[4]={v.x,v.y,v.z,0.0}; + + glLightfv(glid,GL_POSITION,lightpos); //at modelview + + glPopMatrix(); + + } break; + case VS::LIGHT_OMNI: { + + glLightf(glid,GL_SPOT_CUTOFF,180.0); + glLightf(glid,GL_CONSTANT_ATTENUATION, ld->vars[VS::LIGHT_PARAM_ATTENUATION]); + glLightf(glid,GL_LINEAR_ATTENUATION, ld->vars[VS::LIGHT_PARAM_RADIUS]); + glLightf(glid,GL_QUADRATIC_ATTENUATION, ld->vars[VS::LIGHT_PARAM_ENERGY]); // wut? + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + Vector3 pos = p_instance->transform.get_origin(); + pos = camera_transform_inverse.xform(pos); + float lightpos[4]={pos.x,pos.y,pos.z,1.0}; + glLightfv(glid,GL_POSITION,lightpos); //at modelview + + glPopMatrix(); + + } break; + case VS::LIGHT_SPOT: { + + glLightf(glid,GL_SPOT_CUTOFF, ld->vars[VS::LIGHT_PARAM_SPOT_ANGLE]); + glLightf(glid,GL_SPOT_EXPONENT, ld->vars[VS::LIGHT_PARAM_SPOT_ATTENUATION]); + glLightf(glid,GL_CONSTANT_ATTENUATION, ld->vars[VS::LIGHT_PARAM_ATTENUATION]); + glLightf(glid,GL_LINEAR_ATTENUATION, ld->vars[VS::LIGHT_PARAM_RADIUS]); + glLightf(glid,GL_QUADRATIC_ATTENUATION, ld->vars[VS::LIGHT_PARAM_ENERGY]); // wut? + + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + Vector3 v(0.0,0.0,-1.0); // directional lights point up by default + v = p_instance->transform.get_basis().xform( v ); + v = camera_transform_inverse.get_basis().xform( v ); + v.normalize(); // this sucks, so it will be optimized at some point + float lightdir[4]={v.x, v.y, v.z, 1.0}; + glLightfv(glid,GL_SPOT_DIRECTION,lightdir); //at modelview + + v = p_instance->transform.get_origin(); + v = camera_transform_inverse.xform(v); + float lightpos[4]={v.x,v.y,v.z,1.0}; + glLightfv(glid,GL_POSITION,lightpos); //at modelview + + + glPopMatrix(); + + + } break; + default: break; + } +}; + +void RasterizerIPhone::_setup_lights(LightInstance **p_lights,int p_light_count) { + + for (int i=0; i<MAX_LIGHTS; i++) { + + if (i<p_light_count) { + glEnable(GL_LIGHT0 + i); + _setup_light(p_lights[i], i); + } else { + glDisable(GL_LIGHT0 + i); + } + } +} + +static const int gl_client_states[] = { + + GL_VERTEX_ARRAY, + GL_NORMAL_ARRAY, + -1, // ARRAY_TANGENT + GL_COLOR_ARRAY, + GL_TEXTURE_COORD_ARRAY, // ARRAY_TEX_UV + GL_TEXTURE_COORD_ARRAY, // ARRAY_TEX_UV2 + -1, // ARRAY_BONES + -1, // ARRAY_WEIGHTS + -1, // ARRAY_INDEX +}; + +void RasterizerIPhone::_setup_geometry(const Geometry *p_geometry, const Material* p_material) { + + + switch(p_geometry->type) { + + case Geometry::GEOMETRY_SURFACE: { + + Surface *surf = (Surface*)p_geometry; + uint8_t *base=0; + bool use_VBO = (surf->array_local==0); + + if (!use_VBO) { + + base = surf->array_local; + glBindBuffer(GL_ARRAY_BUFFER, 0); + + } else { + + glBindBuffer(GL_ARRAY_BUFFER, surf->vertex_id); + }; + + const Surface::ArrayData* a=surf->array; + for (int i=0;i<VS::ARRAY_MAX;i++) { + + const Surface::ArrayData& ad=surf->array[i]; + if (ad.size==0) { + if (gl_client_states[i] != -1) { + glDisableClientState(gl_client_states[i]); + }; + continue; // this one is disabled. + } + ERR_CONTINUE( !ad.configured ); + + if (gl_client_states[i] != -1) { + glEnableClientState(gl_client_states[i]); + }; + + switch (i) { + + case VS::ARRAY_VERTEX: + if (!use_VBO) + glVertexPointer(3,GL_FLOAT,surf->stride,(GLvoid*)&base[a->ofs]); + else + if (surf->array[VS::ARRAY_BONES].size) + glVertexPointer(3, GL_FLOAT, 0, skinned_buffer); + else + glVertexPointer(3,GL_FLOAT,surf->stride,(GLvoid*)a->ofs); + break; + + case VS::ARRAY_NORMAL: + if (use_VBO) + glNormalPointer(GL_FLOAT,surf->stride,(GLvoid*)a->ofs); + else + glNormalPointer(GL_FLOAT,surf->stride,(GLvoid*)&base[a->ofs]); + break; + case VS::ARRAY_TANGENT: + break; + case VS::ARRAY_COLOR: + if (use_VBO) + glColorPointer(4,GL_UNSIGNED_BYTE,surf->stride,(GLvoid*)a->ofs); + else + glColorPointer(4,GL_UNSIGNED_BYTE,surf->stride,(GLvoid*)&base[a->ofs]); + break; + case VS::ARRAY_TEX_UV: + case VS::ARRAY_TEX_UV2: + if (use_VBO) + glTexCoordPointer(2,GL_FLOAT,surf->stride,(GLvoid*)a->ofs); + else + glTexCoordPointer(2,GL_FLOAT,surf->stride,&base[a->ofs]); + break; + case VS::ARRAY_BONES: + case VS::ARRAY_WEIGHTS: + case VS::ARRAY_INDEX: + break; + }; + } + + + // process skeleton here + + } break; + + default: break; + + }; +}; + +static const GLenum gl_primitive[]={ + GL_POINTS, + GL_LINES, + GL_LINE_STRIP, + GL_LINE_LOOP, + GL_TRIANGLES, + GL_TRIANGLE_STRIP, + GL_TRIANGLE_FAN +}; + +void RasterizerIPhone::_render(const Geometry *p_geometry,const Material *p_material, const Skeleton* p_skeleton) { + + + switch(p_geometry->type) { + + case Geometry::GEOMETRY_SURFACE: { + + + Surface *s = (Surface*)p_geometry; + + if (s->index_array_len>0) { + + if (s->index_array_local) { + + glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->index_array_len>(1<<8))?GL_UNSIGNED_SHORT:GL_UNSIGNED_BYTE, s->index_array_local); + + } else { + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,s->index_id); + glDrawElements(gl_primitive[s->primitive],s->index_array_len, (s->index_array_len>(1<<8))?GL_UNSIGNED_SHORT:GL_UNSIGNED_BYTE,0); + } + + + } else { + + glDrawArrays(gl_primitive[s->primitive],0,s->array_len); + + }; + } break; + + default: break; + }; +}; + +void RasterizerIPhone::_render_list_forward(RenderList *p_render_list) { + + const Material *prev_material=NULL; + uint64_t prev_light_hash=0; + const Skeleton *prev_skeleton=NULL; + const Geometry *prev_geometry=NULL; + const ParamOverrideMap* prev_overrides=NULL; // make it diferent than NULL + + Geometry::Type prev_geometry_type=Geometry::GEOMETRY_INVALID; + + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(&camera_projection.matrix[0][0]); + + for (int i=0;i<p_render_list->element_count;i++) { + + RenderList::Element *e = p_render_list->elements[i]; + const Material *material = e->material; + uint64_t light_hash = e->light_hash; + const Skeleton *skeleton = e->skeleton; + const Geometry *geometry = e->geometry; + const ParamOverrideMap* material_overrides=e->material_overrides; + + if (material!=prev_material || geometry->type!=prev_geometry_type) { + _setup_material(e->geometry,material); + //_setup_material_overrides(e->material,NULL,material_overrides); + //_setup_material_skeleton(material,skeleton); + } else { + + if (material_overrides != prev_overrides) { + + //_setup_material_overrides(e->material,prev_overrides,material_overrides); + } + + if (prev_skeleton!=skeleton) { + //_setup_material_skeleton(material,skeleton); + }; + } + + if (geometry!=prev_geometry || geometry->type!=prev_geometry_type) { + + _setup_geometry(geometry, material); + }; + + if (i==0 || light_hash!=prev_light_hash) + _setup_lights(e->lights,e->light_count); + + glMatrixMode(GL_MODELVIEW); + _gl_load_transform(camera_transform_inverse); + _gl_mult_transform(e->transform); + + _render(geometry, material, skeleton); + + prev_material=material; + prev_skeleton=skeleton; + prev_geometry=geometry; + prev_light_hash=e->light_hash; + prev_geometry_type=geometry->type; + prev_overrides=material_overrides; + } +}; + +void RasterizerIPhone::end_scene() { + + glEnable(GL_BLEND); + glDepthMask(GL_FALSE); + + opaque_render_list.sort_mat_light(); + _render_list_forward(&opaque_render_list); + + glDisable(GL_BLEND); + glDepthMask(GL_TRUE); + + alpha_render_list.sort_z(); + _render_list_forward(&alpha_render_list); + + +} +void RasterizerIPhone::end_shadow_map() { + + +} + +void RasterizerIPhone::end_frame() { + + //ContextGL::get_singleton()->swap_buffers(); +} + +/* CANVAS API */ + +void RasterizerIPhone::canvas_begin() { + + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glLineWidth(1.0); + glDisable(GL_LIGHTING); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); +} +void RasterizerIPhone::canvas_set_transparency(float p_transparency) { + + +} + +void RasterizerIPhone::canvas_set_rect(const Rect2& p_rect, bool p_clip) { + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glScalef(2.0 / window_size.x, -2.0 / window_size.y, 0); + glTranslatef((-(window_size.x / 2.0)) + p_rect.pos.x, (-(window_size.y / 2.0)) + p_rect.pos.y, 0); + + if (p_clip) { + + glEnable(GL_SCISSOR_TEST); + glScissor(viewport.x+p_rect.pos.x,viewport.y+ (viewport.height-(p_rect.pos.y+p_rect.size.height)), + p_rect.size.width,p_rect.size.height); + } else { + + glDisable(GL_SCISSOR_TEST); + } + + +} +void RasterizerIPhone::canvas_draw_line(const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width) { + + glColor4f(1, 1, 1, 1); + + float verts[6]={ + p_from.x,p_from.y,0, + p_to.x,p_to.y,0 + }; + + float colors[]={ + p_color.r, p_color.g, p_color.b, p_color.a, + p_color.r, p_color.g, p_color.b, p_color.a, + }; + glLineWidth(p_width); + _draw_primitive(2,verts,0,colors,0); +} + +static void _draw_textured_quad(const Rect2& p_rect, const Rect2& p_src_region, const Size2& p_tex_size ) { + + float texcoords[]= { + p_src_region.pos.x/p_tex_size.width, + p_src_region.pos.y/p_tex_size.height, + + (p_src_region.pos.x+p_src_region.size.width)/p_tex_size.width, + p_src_region.pos.y/p_tex_size.height, + + (p_src_region.pos.x+p_src_region.size.width)/p_tex_size.width, + (p_src_region.pos.y+p_src_region.size.height)/p_tex_size.height, + + p_src_region.pos.x/p_tex_size.width, + (p_src_region.pos.y+p_src_region.size.height)/p_tex_size.height, + }; + + float coords[]= { + p_rect.pos.x, p_rect.pos.y, 0, + p_rect.pos.x+p_rect.size.width, p_rect.pos.y, 0, + p_rect.pos.x+p_rect.size.width, p_rect.pos.y+p_rect.size.height, 0, + p_rect.pos.x,p_rect.pos.y+p_rect.size.height, 0 + }; + + _draw_primitive(4,coords,0,0,texcoords); +} + +static void _draw_quad(const Rect2& p_rect) { + + float coords[]= { + p_rect.pos.x,p_rect.pos.y, 0, + p_rect.pos.x+p_rect.size.width,p_rect.pos.y, 0, + p_rect.pos.x+p_rect.size.width,p_rect.pos.y+p_rect.size.height, 0, + p_rect.pos.x,p_rect.pos.y+p_rect.size.height, 0 + }; + + _draw_primitive(4,coords,0,0,0); + +} + +void RasterizerIPhone::canvas_draw_rect(const Rect2& p_rect, bool p_region, const Rect2& p_source,bool p_tile,RID p_texture,const Color& p_modulate) { + + glColor4f(p_modulate.r, p_modulate.g, p_modulate.b, p_modulate.a); + + if ( p_texture.is_valid() ) { + + glEnable(GL_TEXTURE_2D); + Texture *texture = texture_owner.get( p_texture ); + ERR_FAIL_COND(!texture); + glActiveTexture(GL_TEXTURE0); + glBindTexture( GL_TEXTURE_2D,texture->tex_id ); + + if (!p_region) { + + Rect2 region = Rect2(0,0,texture->width,texture->height); + _draw_textured_quad(p_rect,region,region.size); + + } else { + + _draw_textured_quad(p_rect, p_source, Size2(texture->width,texture->height) ); + + } + } else { + + _draw_quad( p_rect ); + + } + + +} +void RasterizerIPhone::canvas_draw_style_box(const Rect2& p_rect, RID p_texture,const float *p_margin, bool p_draw_center) { + + glColor4f(1, 1, 1, 1); + + Texture *texture = texture_owner.get( p_texture ); + ERR_FAIL_COND(!texture); + + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glBindTexture( GL_TEXTURE_2D,texture->tex_id ); + + + /* CORNERS */ + + _draw_textured_quad( // top left + Rect2( p_rect.pos, Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_TOP])), + Rect2( Point2(), Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_TOP])), + Size2( texture->width, texture->height ) ); + + _draw_textured_quad( // top right + Rect2( Point2( p_rect.pos.x + p_rect.size.width - p_margin[MARGIN_RIGHT], p_rect.pos.y), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_TOP])), + Rect2( Point2(texture->width-p_margin[MARGIN_RIGHT],0), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_TOP])), + Size2( texture->width, texture->height ) ); + + + _draw_textured_quad( // bottom left + Rect2( Point2(p_rect.pos.x,p_rect.pos.y + p_rect.size.height - p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_BOTTOM])), + Rect2( Point2(0,texture->height-p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_BOTTOM])), + Size2( texture->width, texture->height ) ); + + _draw_textured_quad( // bottom right + Rect2( Point2( p_rect.pos.x + p_rect.size.width - p_margin[MARGIN_RIGHT], p_rect.pos.y + p_rect.size.height - p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_BOTTOM])), + Rect2( Point2(texture->width-p_margin[MARGIN_RIGHT],texture->height-p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_BOTTOM])), + Size2( texture->width, texture->height ) ); + + Rect2 rect_center( p_rect.pos+Point2( p_margin[MARGIN_LEFT], p_margin[MARGIN_TOP]), Size2( p_rect.size.width - p_margin[MARGIN_LEFT] - p_margin[MARGIN_RIGHT], p_rect.size.height - p_margin[MARGIN_TOP] - p_margin[MARGIN_BOTTOM] )); + + Rect2 src_center( Point2( p_margin[MARGIN_LEFT], p_margin[MARGIN_TOP]), Size2( texture->width - p_margin[MARGIN_LEFT] - p_margin[MARGIN_RIGHT], texture->height - p_margin[MARGIN_TOP] - p_margin[MARGIN_BOTTOM] )); + + + _draw_textured_quad( // top + Rect2( Point2(rect_center.pos.x,p_rect.pos.y),Size2(rect_center.size.width,p_margin[MARGIN_TOP])), + Rect2( Point2(p_margin[MARGIN_LEFT],0), Size2(src_center.size.width,p_margin[MARGIN_TOP])), + Size2( texture->width, texture->height ) ); + + _draw_textured_quad( // bottom + Rect2( Point2(rect_center.pos.x,rect_center.pos.y+rect_center.size.height),Size2(rect_center.size.width,p_margin[MARGIN_BOTTOM])), + Rect2( Point2(p_margin[MARGIN_LEFT],src_center.pos.y+src_center.size.height), Size2(src_center.size.width,p_margin[MARGIN_BOTTOM])), + Size2( texture->width, texture->height ) ); + + _draw_textured_quad( // left + Rect2( Point2(p_rect.pos.x,rect_center.pos.y),Size2(p_margin[MARGIN_LEFT],rect_center.size.height)), + Rect2( Point2(0,p_margin[MARGIN_TOP]), Size2(p_margin[MARGIN_LEFT],src_center.size.height)), + Size2( texture->width, texture->height ) ); + + _draw_textured_quad( // right + Rect2( Point2(rect_center.pos.x+rect_center.size.width,rect_center.pos.y),Size2(p_margin[MARGIN_RIGHT],rect_center.size.height)), + Rect2( Point2(src_center.pos.x+src_center.size.width,p_margin[MARGIN_TOP]), Size2(p_margin[MARGIN_RIGHT],src_center.size.height)), + Size2( texture->width, texture->height ) ); + + if (p_draw_center) { + + _draw_textured_quad( + rect_center, + src_center, + Size2( texture->width, texture->height )); + } + +} +void RasterizerIPhone::canvas_draw_primitive(const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, RID p_texture) { + + ERR_FAIL_COND(p_points.size()<1); + float verts[12]; + float uvs[8]; + float colors[16]; + + glColor4f(1, 1, 1, 1); + + int idx = 0; + for(int i=0;i<p_points.size();i++) { + + verts[idx++]=p_points[i].x; + verts[idx++]=p_points[i].y; + verts[idx++]=0; + } + + idx = 0; + for(int i=0;i<p_uvs.size();i++) { + + uvs[idx++] = p_uvs[i].x; + uvs[idx++] = p_uvs[i].y; + } + + idx = 0; + for (int i=0; i<p_colors.size(); i++) { + + colors[idx++] = p_colors[i].r; + colors[idx++] = p_colors[i].g; + colors[idx++] = p_colors[i].b; + colors[idx++] = p_colors[i].a; + }; + + if (p_texture.is_valid()) { + glEnable(GL_TEXTURE_2D); + Texture *texture = texture_owner.get( p_texture ); + if (texture) { + glActiveTexture(GL_TEXTURE0); + glBindTexture( GL_TEXTURE_2D,texture->tex_id ); + } + } + + _draw_primitive(p_points.size(),&verts[0],NULL,p_colors.size()?&colors[0]:NULL,p_uvs.size()?uvs:NULL); + +} + +/* FX */ + +RID RasterizerIPhone::fx_create() { + + return RID(); +} +void RasterizerIPhone::fx_get_effects(RID p_fx,List<String> *p_effects) const { + + +} +void RasterizerIPhone::fx_set_active(RID p_fx,const String& p_effect, bool p_active) { + + +} +bool RasterizerIPhone::fx_is_active(RID p_fx,const String& p_effect) const { + + return false; +} +void RasterizerIPhone::fx_get_effect_params(RID p_fx,const String& p_effect,List<PropertyInfo> *p_params) const { + + +} +Variant RasterizerIPhone::fx_get_effect_param(RID p_fx,const String& p_effect,const String& p_param) const { + + return Variant(); +} +void RasterizerIPhone::fx_set_effect_param(RID p_fx,const String& p_effect, const String& p_param, const Variant& p_pvalue) { + + +} + +/*MISC*/ + +bool RasterizerIPhone::is_texture(const RID& p_rid) const { + + return texture_owner.owns(p_rid); +} +bool RasterizerIPhone::is_material(const RID& p_rid) const { + + return material_owner.owns(p_rid); +} +bool RasterizerIPhone::is_mesh(const RID& p_rid) const { + + return mesh_owner.owns(p_rid); +} +bool RasterizerIPhone::is_multimesh(const RID& p_rid) const { + + return false; +} +bool RasterizerIPhone::is_poly(const RID& p_rid) const { + + return poly_owner.owns(p_rid); +} +bool RasterizerIPhone::is_particles(const RID &p_beam) const { + + return false; +} + +bool RasterizerIPhone::is_beam(const RID &p_beam) const { + + return false; +} + +bool RasterizerIPhone::is_light(const RID& p_rid) const { + + return light_owner.owns(p_rid); +} +bool RasterizerIPhone::is_light_instance(const RID& p_rid) const { + + return light_instance_owner.owns(p_rid); +} +bool RasterizerIPhone::is_particles_instance(const RID& p_rid) const { + + return false; +} +bool RasterizerIPhone::is_skeleton(const RID& p_rid) const { + + return skeleton_owner.owns(p_rid); +} +bool RasterizerIPhone::is_fx(const RID& p_rid) const { + + return fx_owner.owns(p_rid); +} +bool RasterizerIPhone::is_shader(const RID& p_rid) const { + + return false; +} + +void RasterizerIPhone::free(const RID& p_rid) const { + + if (texture_owner.owns(p_rid)) { + + // delete the texture + Texture *texture = texture_owner.get(p_rid); + + glDeleteTextures( 1,&texture->tex_id ); + + texture_owner.free(p_rid); + memdelete(texture); + + } else if (material_owner.owns(p_rid)) { + + Material *material = material_owner.get( p_rid ); + ERR_FAIL_COND(!material); + + material_owner.free(p_rid); + memdelete(material); + + } else if (mesh_owner.owns(p_rid)) { + + Mesh *mesh = mesh_owner.get(p_rid); + ERR_FAIL_COND(!mesh); + for (int i=0;i<mesh->surfaces.size();i++) { + + Surface *surface = mesh->surfaces[i]; + if (surface->array_local != 0) { + memfree(surface->array_local); + }; + if (surface->index_array_local != 0) { + memfree(surface->index_array_local); + }; + + if (surface->vertex_id) + glDeleteBuffers(1,&surface->vertex_id); + if (surface->index_id) + glDeleteBuffers(1,&surface->index_id); + + memdelete( surface ); + }; + + mesh->surfaces.clear(); + + mesh_owner.free(p_rid); + memdelete(mesh); + + } else if (skeleton_owner.owns(p_rid)) { + + Skeleton *skeleton = skeleton_owner.get( p_rid ); + ERR_FAIL_COND(!skeleton) + + skeleton_owner.free(p_rid); + memdelete(skeleton); + + } else if (light_owner.owns(p_rid)) { + + Light *light = light_owner.get( p_rid ); + ERR_FAIL_COND(!light) + + light_owner.free(p_rid); + memdelete(light); + + } else if (light_instance_owner.owns(p_rid)) { + + LightInstance *light_instance = light_instance_owner.get( p_rid ); + ERR_FAIL_COND(!light_instance); + + light_instance_owner.free(p_rid); + memdelete( light_instance ); + + } else if (fx_owner.owns(p_rid)) { + + FX *fx = fx_owner.get( p_rid ); + ERR_FAIL_COND(!fx); + + fx_owner.free(p_rid); + memdelete( fx ); + + }; +} + +void RasterizerIPhone::init() { + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glFrontFace(GL_CW); + + glEnable(GL_TEXTURE_2D); +} + +void RasterizerIPhone::finish() { + +} + + int RasterizerIPhone::get_render_info(VS::RenderInfo p_info) { + + return false; +} + +RasterizerIPhone::RasterizerIPhone() { + + frame = 0; +}; + +RasterizerIPhone::~RasterizerIPhone() { + +}; + + +#endif diff --git a/platform/iphone/rasterizer_iphone.h b/platform/iphone/rasterizer_iphone.h new file mode 100644 index 0000000000..41dbbdaa5a --- /dev/null +++ b/platform/iphone/rasterizer_iphone.h @@ -0,0 +1,894 @@ +/*************************************************************************/ +/* rasterizer_iphone.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. */ +/*************************************************************************/ +#ifdef IPHONE_ENABLED + +#ifndef RASTERIZER_IPHONE_H +#define RASTERIZER_IPHONE_H + +#include "servers/visual/rasterizer.h" + +#include "image.h" +#include "rid.h" +#include "servers/visual_server.h" +#include "list.h" +#include "map.h" +#include "camera_matrix.h" +#include "sort.h" +#include <ES1/gl.h> + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +class RasterizerIPhone : public Rasterizer { + + enum { + SKINNED_BUFFER_SIZE = 1024 * 128, // 10k vertices + MAX_LIGHTS = 8, + }; + + uint8_t skinned_buffer[SKINNED_BUFFER_SIZE]; + + struct Texture { + + uint32_t flags; + int width,height; + Image::Format format; + + GLenum target; + GLenum gl_format_cache; + int gl_components_cache; + bool has_alpha; + bool format_has_alpha; + + bool active; + GLuint tex_id; + bool mipmap_dirty; + + Texture() { + + flags=width=height=0; + tex_id=0; + format=Image::FORMAT_GRAYSCALE; + gl_components_cache=0; + format_has_alpha=false; + has_alpha=false; + active=false; + mipmap_dirty=true; + } + + ~Texture() { + + if (tex_id!=0) { + + glDeleteTextures(1,&tex_id); + } + } + }; + + mutable RID_Owner<Texture> texture_owner; + + struct Material { + + bool flags[VS::MATERIAL_FLAG_MAX]; + Variant parameters[VisualServer::FIXED_MATERIAL_PARAM_MAX]; + RID textures[VisualServer::FIXED_MATERIAL_PARAM_MAX]; + + Transform uv_transform; + VS::FixedMaterialTexCoordMode texcoord_mode[VisualServer::FIXED_MATERIAL_PARAM_MAX]; + + VS::MaterialBlendMode detail_blend_mode; + + VS::FixedMaterialTexGenMode texgen_mode; + + Material() { + + flags[VS::MATERIAL_FLAG_VISIBLE]=true; + flags[VS::MATERIAL_FLAG_DOUBLE_SIDED]=false; + flags[VS::MATERIAL_FLAG_INVERT_FACES]=false; + flags[VS::MATERIAL_FLAG_UNSHADED]=false; + flags[VS::MATERIAL_FLAG_WIREFRAME]=false; + + parameters[VS::FIXED_MATERIAL_PARAM_DIFFUSE] = Color(0.8, 0.8, 0.8); + parameters[VS::FIXED_MATERIAL_PARAM_SPECULAR_EXP] = 12; + + for (int i=0; i<VisualServer::FIXED_MATERIAL_PARAM_MAX; i++) { + texcoord_mode[i] = VS::FIXED_MATERIAL_TEXCOORD_UV; + }; + detail_blend_mode = VS::MATERIAL_BLEND_MODE_MIX; + texgen_mode = VS::FIXED_MATERIAL_TEXGEN_SPHERE; + } + }; + mutable RID_Owner<Material> material_owner; + + + struct Geometry { + + enum Type { + GEOMETRY_INVALID, + GEOMETRY_SURFACE, + GEOMETRY_POLY, + GEOMETRY_PARTICLES, + GEOMETRY_BEAM, + GEOMETRY_DETAILER, + }; + + Type type; + RID material; + bool has_alpha; + bool material_owned; + + Vector3 scale; + Vector3 uv_scale; + + Geometry() : scale(1, 1, 1) { has_alpha=false; material_owned = false; } + virtual ~Geometry() {}; + }; + + struct GeometryOwner { + + virtual ~GeometryOwner() {} + }; + + + struct Surface : public Geometry { + + struct ArrayData { + + uint32_t ofs,size; + bool configured; + int components; + ArrayData() { ofs=0; size=0; configured=false; } + }; + + ArrayData array[VS::ARRAY_MAX]; + // support for vertex array objects + GLuint array_object_id; + // support for vertex buffer object + GLuint vertex_id; // 0 means, unconfigured + GLuint index_id; // 0 means, unconfigured + // no support for the above, array in localmem. + uint8_t *array_local; + uint8_t *index_array_local; + + AABB aabb; + + int array_len; + int index_array_len; + + VS::PrimitiveType primitive; + + uint32_t format; + + int stride; + + bool active; + + Point2 uv_min; + Point2 uv_max; + + bool has_alpha_cache; + + Surface() { + + array_len=0; + type=GEOMETRY_SURFACE; + primitive=VS::PRIMITIVE_POINTS; + index_array_len=VS::NO_INDEX_ARRAY; + format=0; + stride=0; + + array_local = index_array_local = 0; + vertex_id = index_id = 0; + + active=false; + } + + ~Surface() { + + } + }; + + + struct Mesh { + + bool active; + Vector<Surface*> surfaces; + + mutable uint64_t last_pass; + Mesh() { + last_pass=0; + active=false; + } + }; + mutable RID_Owner<Mesh> mesh_owner; + + struct Poly : public Geometry { + + struct Primitive { + + Vector<Vector3> vertices; + Vector<Vector3> normals; + Vector<Vector3> uvs; + Vector<Color> colors; + + }; + + AABB aabb; + List<Primitive> primitives; + Poly() { + + type=GEOMETRY_POLY; + } + }; + + mutable RID_Owner<Poly> poly_owner; + + + struct Skeleton { + + Vector<Transform> bones; + + }; + + mutable RID_Owner<Skeleton> skeleton_owner; + + + struct Light { + + VS::LightType type; + float vars[VS::LIGHT_PARAM_MAX]; + Color colors[3]; + bool shadow_enabled; + RID projector; + bool volumetric_enabled; + Color volumetric_color; + + + Light() { + + vars[VS::LIGHT_PARAM_SPOT_ATTENUATION]=1; + vars[VS::LIGHT_PARAM_SPOT_ANGLE]=45; + vars[VS::LIGHT_PARAM_ATTENUATION]=1.0; + vars[VS::LIGHT_PARAM_ENERGY]=1.0; + vars[VS::LIGHT_PARAM_RADIUS]=1.0; + colors[VS::LIGHT_COLOR_AMBIENT]=Color(0,0,0); + colors[VS::LIGHT_COLOR_DIFFUSE]=Color(1,1,1); + colors[VS::LIGHT_COLOR_SPECULAR]=Color(1,1,1); + shadow_enabled=false; + volumetric_enabled=false; + } + }; + + struct ShadowBuffer; + + struct LightInstance { + + struct SplitInfo { + + CameraMatrix camera; + Transform transform; + float near; + float far; + }; + + RID light; + Light *base; + uint64_t last_pass; + Transform transform; + + + CameraMatrix projection; + Vector<SplitInfo> splits; + + Vector3 light_vector; + Vector3 spot_vector; + float linear_att; + + uint64_t hash_aux; + }; + mutable RID_Owner<Light> light_owner; + mutable RID_Owner<LightInstance> light_instance_owner; + + LightInstance *light_instances[MAX_LIGHTS]; + int light_instance_count; + + struct RenderList { + + enum { + MAX_ELEMENTS=4096, + MAX_LIGHTS=4 + }; + + struct Element { + + float depth; + const Skeleton *skeleton; + Transform transform; + LightInstance* lights[MAX_LIGHTS]; + int light_count; + const Geometry *geometry; + const Material *material; + uint64_t light_hash; + GeometryOwner *owner; + const ParamOverrideMap* material_overrides; + }; + + + Element _elements[MAX_ELEMENTS]; + Element *elements[MAX_ELEMENTS]; + int element_count; + + void clear() { + + element_count=0; + } + + struct SortZ { + + _FORCE_INLINE_ bool operator()(const Element* A, const Element* B ) const { + + return A->depth > B->depth; + } + }; + + void sort_z() { + + SortArray<Element*,SortZ> sorter; + sorter.sort(elements,element_count); + } + + struct SortSkel { + + _FORCE_INLINE_ bool operator()(const Element* A, const Element* B ) const { + + if (A->geometry < B->geometry) + return true; + else if (A->geometry > B->geometry) + return false; + else return (!A->skeleton && B->skeleton); + } + }; + + void sort_skel() { + + SortArray<Element*,SortSkel> sorter; + sorter.sort(elements,element_count); + } + + struct SortMat { + + _FORCE_INLINE_ bool operator()(const Element* A, const Element* B ) const { + + if (A->geometry == B->geometry) { + + if (A->material == B->material) { + + + return (A->material_overrides < B->material_overrides); + } else { + + return (A->material < B->material); + } + } else { + + return (A->geometry < B->geometry); + } + } + }; + + void sort_mat() { + + SortArray<Element*,SortMat> sorter; + sorter.sort(elements,element_count); + } + + struct SortMatLight { + + _FORCE_INLINE_ bool operator()(const Element* A, const Element* B ) const { + + if (A->geometry == B->geometry) { + + if (A->material == B->material) { + + if (A->light_hash == B->light_hash) + return (A->material_overrides < B->material_overrides); + else + return A->light_hash<B->light_hash; + } else { + + return (A->material < B->material); + } + } else { + + return (A->geometry < B->geometry); + } + } + }; + + void sort_mat_light() { + + SortArray<Element*,SortMatLight> sorter; + sorter.sort(elements,element_count); + } + + struct LISort { + + _FORCE_INLINE_ bool operator ()(const LightInstance *A, const LightInstance *B) const { + + return (A->hash_aux < B->hash_aux); + } + }; + + _FORCE_INLINE_ void add_element( const Geometry *p_geometry, const Material* p_material,const Transform& p_transform, LightInstance **p_light_instances, int p_light_count, const ParamOverrideMap* p_material_overrides, const Skeleton *p_skeleton, float p_depth, GeometryOwner *p_owner=NULL) { + + + ERR_FAIL_COND( element_count >= MAX_ELEMENTS ); + Element *e = elements[element_count++]; + + e->geometry=p_geometry; + e->material=p_material; + e->transform=p_transform; + e->skeleton=p_skeleton; + e->light_hash=0; + e->light_count=p_light_count; + e->owner=p_owner; + e->material_overrides=p_material_overrides; + + if (e->light_count>0) { + + SortArray<LightInstance*,LISort> light_sort; + light_sort.sort(p_light_instances,p_light_count); + //@TODO OPTIOMIZE + + for (int i=0;i<p_light_count;i++) { + + e->lights[i]=p_light_instances[i]; + + if (i==0) + e->light_hash=hash_djb2_one_64( make_uint64_t(e->lights[i]) ); + else + e->light_hash=hash_djb2_one_64( make_uint64_t(e->lights[i]),e->light_hash); + } + + } + + } + + RenderList() { + + for (int i=0;i<MAX_ELEMENTS;i++) + elements[i]=&_elements[i]; // assign elements + } + }; + + RenderList opaque_render_list; + RenderList alpha_render_list; + + RID default_material; + + struct FX { + + bool bgcolor_active; + Color bgcolor; + + bool skybox_active; + RID skybox_cubemap; + + bool antialias_active; + float antialias_tolerance; + + bool glow_active; + int glow_passes; + float glow_attenuation; + float glow_bloom; + + bool ssao_active; + float ssao_attenuation; + float ssao_radius; + float ssao_max_distance; + float ssao_range_max; + float ssao_range_min; + bool ssao_only; + + bool fog_active; + float fog_distance; + float fog_attenuation; + Color fog_color_near; + Color fog_color_far; + bool fog_bg; + + bool toon_active; + float toon_treshold; + float toon_soft; + + bool edge_active; + Color edge_color; + float edge_size; + + FX(); + + }; + mutable RID_Owner<FX> fx_owner; + + + FX *scene_fx; + CameraMatrix camera_projection; + Transform camera_transform; + Transform camera_transform_inverse; + float camera_z_near; + float camera_z_far; + Size2 camera_vp_size; + + Plane camera_plane; + + void _add_geometry( const Geometry* p_geometry, const Transform& p_world, uint32_t p_vertex_format, const RID* p_light_instances, int p_light_count, const ParamOverrideMap* p_material_overrides,const Skeleton* p_skeleton,GeometryOwner *p_owner); + void _render_list_forward(RenderList *p_render_list); + + void _setup_light(LightInstance* p_instance, int p_idx); + void _setup_lights(LightInstance **p_lights,int p_light_count); + void _setup_material(const Geometry *p_geometry,const Material *p_material); + + void _setup_geometry(const Geometry *p_geometry, const Material* p_material); + void _render(const Geometry *p_geometry,const Material *p_material, const Skeleton* p_skeleton); + + + /*********/ + /* FRAME */ + /*********/ + + Size2 window_size; + VS::ViewportRect viewport; + Transform canvas_transform; + double last_time; + double time_delta; + uint64_t frame; + +public: + + /* TEXTURE API */ + + virtual RID texture_create(); + virtual void texture_allocate(RID p_texture,int p_width, int p_height,Image::Format p_format,uint32_t p_flags=VS::TEXTURE_FLAGS_DEFAULT); + virtual void texture_blit_rect(RID p_texture,int p_x,int p_y, const Image& p_image,VS::CubeMapSide p_cube_side=VS::CUBEMAP_LEFT); + virtual Image texture_get_rect(RID p_texture,int p_x,int p_y,int p_width, int p_height,VS::CubeMapSide p_cube_side=VS::CUBEMAP_LEFT) const; + virtual void texture_set_flags(RID p_texture,uint32_t p_flags); + virtual uint32_t texture_get_flags(RID p_texture) const; + virtual Image::Format texture_get_format(RID p_texture) const; + virtual uint32_t texture_get_width(RID p_texture) const; + virtual uint32_t texture_get_height(RID p_texture) const; + virtual bool texture_has_alpha(RID p_texture) const; + + /* SHADER API */ + + virtual RID shader_create(); + + virtual void shader_node_add(RID p_shader,VS::ShaderNodeType p_type,int p_id); + virtual void shader_node_remove(RID p_shader,int p_id); + virtual void shader_node_change_type(RID p_shader, int p_id, VS::ShaderNodeType p_type); + virtual void shader_node_set_param(RID p_shader, int p_id, const Variant& p_value); + + virtual void shader_get_node_list(RID p_shader,List<int> *p_node_list) const; + virtual VS::ShaderNodeType shader_node_get_type(RID p_shader,int p_id) const; + virtual Variant shader_node_get_param(RID p_shader,int p_id) const; + + virtual void shader_connect(RID p_shader,int p_src_id,int p_src_slot, int p_dst_id,int p_dst_slot); + virtual bool shader_is_connected(RID p_shader,int p_src_id,int p_src_slot, int p_dst_id,int p_dst_slot) const; + virtual void shader_disconnect(RID p_shader,int p_src_id,int p_src_slot, int p_dst_id,int p_dst_slot); + + virtual void shader_get_connections(RID p_shader,List<VS::ShaderConnection> *p_connections) const; + + virtual void shader_clear(RID p_shader); + + /* COMMON MATERIAL API */ + + virtual void material_set_param(RID p_material, const StringName& p_param, const Variant& p_value); + virtual Variant material_get_param(RID p_material, const StringName& p_param) const; + virtual void material_get_param_list(RID p_material, List<String> *p_param_list) const; + + virtual void material_set_flag(RID p_material, VS::MaterialFlag p_flag,bool p_enabled); + virtual bool material_get_flag(RID p_material,VS::MaterialFlag p_flag) const; + + virtual void material_set_blend_mode(RID p_material,VS::MaterialBlendMode p_mode); + virtual VS::MaterialBlendMode material_get_blend_mode(RID p_material) const; + + virtual void material_set_line_width(RID p_material,float p_line_width); + virtual float material_get_line_width(RID p_material) const; + + /* FIXED MATERIAL */ + + virtual RID material_create(); + + virtual void fixed_material_set_parameter(RID p_material, VS::FixedMaterialParam p_parameter, const Variant& p_value); + virtual Variant fixed_material_get_parameter(RID p_material,VS::FixedMaterialParam p_parameter) const; + + virtual void fixed_material_set_texture(RID p_material,VS::FixedMaterialParam p_parameter, RID p_texture); + virtual RID fixed_material_get_texture(RID p_material,VS::FixedMaterialParam p_parameter) const; + + virtual void fixed_material_set_detail_blend_mode(RID p_material,VS::MaterialBlendMode p_mode); + virtual VS::MaterialBlendMode fixed_material_get_detail_blend_mode(RID p_material) const; + + virtual void fixed_material_set_texgen_mode(RID p_material,VS::FixedMaterialTexGenMode p_mode); + virtual VS::FixedMaterialTexGenMode fixed_material_get_texgen_mode(RID p_material) const; + + virtual void fixed_material_set_texcoord_mode(RID p_material,VS::FixedMaterialParam p_parameter, VS::FixedMaterialTexCoordMode p_mode); + virtual VS::FixedMaterialTexCoordMode fixed_material_get_texcoord_mode(RID p_material,VS::FixedMaterialParam p_parameter) const; + + virtual void fixed_material_set_uv_transform(RID p_material,const Transform& p_transform); + virtual Transform fixed_material_get_uv_transform(RID p_material) const; + + /* SHADER MATERIAL */ + + virtual RID shader_material_create() const; + + virtual void shader_material_set_vertex_shader(RID p_material,RID p_shader,bool p_owned=false); + virtual RID shader_material_get_vertex_shader(RID p_material) const; + + virtual void shader_material_set_fragment_shader(RID p_material,RID p_shader,bool p_owned=false); + virtual RID shader_material_get_fragment_shader(RID p_material) const; + + + /* MESH API */ + + virtual RID mesh_create(); + + virtual void mesh_add_surface(RID p_mesh,VS::PrimitiveType p_primitive,uint32_t p_format,int p_array_len,int p_index_array_len=VS::NO_INDEX_ARRAY); + + virtual Error mesh_surface_set_array(RID p_mesh, int p_surface,VS::ArrayType p_type,const Variant& p_array) ; + virtual Variant mesh_surface_get_array(RID p_mesh, int p_surface,VS::ArrayType p_type) const; + + virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material,bool p_owned=false); + virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const; + + virtual int mesh_surface_get_array_len(RID p_mesh, int p_surface) const; + virtual int mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const; + virtual uint32_t mesh_surface_get_format(RID p_mesh, int p_surface) const; + virtual VS::PrimitiveType mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const; + + virtual void mesh_erase_surface(RID p_mesh,int p_index); + virtual int mesh_get_surface_count(RID p_mesh) const; + + virtual AABB mesh_get_aabb(RID p_mesh) const; + + /* MULTIMESH API */ + + virtual RID multimesh_create(); + + virtual void multimesh_set_instance_count(RID p_multimesh,int p_count); + virtual int multimesh_get_instance_count(RID p_multimesh) const; + + virtual void multimesh_set_mesh(RID p_multimesh,RID p_mesh); + virtual void multimesh_set_aabb(RID p_multimesh,const AABB& p_aabb); + virtual void multimesh_instance_set_transform(RID p_multimesh,int p_index,const Transform& p_transform); + virtual void multimesh_instance_set_color(RID p_multimesh,int p_index,const Color& p_color); + + virtual RID multimesh_get_mesh(RID p_multimesh) const; + virtual AABB multimesh_get_aabb(RID p_multimesh) const;; + + virtual Transform multimesh_instance_get_transform(RID p_multimesh,int p_index) const; + virtual Color multimesh_instance_get_color(RID p_multimesh,int p_index) const; + + /* POLY API */ + + virtual RID poly_create(); + virtual void poly_set_material(RID p_poly, RID p_material,bool p_owned=false); + virtual void poly_add_primitive(RID p_poly, const Vector<Vector3>& p_points,const Vector<Vector3>& p_normals,const Vector<Color>& p_colors,const Vector<Vector3>& p_uvs); + virtual void poly_clear(RID p_poly); + + virtual AABB poly_get_aabb(RID p_poly) const; + + + /* PARTICLES API */ + + virtual RID particles_create(); + + virtual void particles_set_amount(RID p_particles, int p_amount); + virtual int particles_get_amount(RID p_particles) const; + + virtual void particles_set_emitting(RID p_particles, bool p_emitting); + virtual bool particles_is_emitting(RID p_particles) const; + + virtual void particles_set_visibility_aabb(RID p_particles, const AABB& p_visibility); + virtual AABB particles_get_visibility_aabb(RID p_particles) const; + + virtual void particles_set_emission_half_extents(RID p_particles, const Vector3& p_half_extents); + virtual Vector3 particles_get_emission_half_extents(RID p_particles) const; + + virtual void particles_set_gravity_normal(RID p_particles, const Vector3& p_normal); + virtual Vector3 particles_get_gravity_normal(RID p_particles) const; + + virtual void particles_set_variable(RID p_particles, VS::ParticleVariable p_variable,float p_value); + virtual float particles_get_variable(RID p_particles, VS::ParticleVariable p_variable) const; + + virtual void particles_set_randomness(RID p_particles, VS::ParticleVariable p_variable,float p_randomness); + virtual float particles_get_randomness(RID p_particles, VS::ParticleVariable p_variable) const; + + virtual void particles_set_color_phase_pos(RID p_particles, int p_phase, float p_pos); + virtual float particles_get_color_phase_pos(RID p_particles, int p_phase) const; + + virtual void particles_set_color_phases(RID p_particles, int p_phases); + virtual int particles_get_color_phases(RID p_particles) const; + + virtual void particles_set_color_phase_color(RID p_particles, int p_phase, const Color& p_color); + virtual Color particles_get_color_phase_color(RID p_particles, int p_phase) const; + + virtual void particles_set_attractors(RID p_particles, int p_attractors); + virtual int particles_get_attractors(RID p_particles) const; + + virtual void particles_set_attractor_pos(RID p_particles, int p_attractor, const Vector3& p_pos); + virtual Vector3 particles_get_attractor_pos(RID p_particles,int p_attractor) const; + + virtual void particles_set_attractor_strength(RID p_particles, int p_attractor, float p_force); + virtual float particles_get_attractor_strength(RID p_particles,int p_attractor) const; + + virtual void particles_set_material(RID p_particles, RID p_material,bool p_owned=false); + virtual RID particles_get_material(RID p_particles) const; + + virtual AABB particles_get_aabb(RID p_particles) const; + /* BEAM API */ + + virtual RID beam_create(); + + virtual void beam_set_point_count(RID p_beam, int p_count); + virtual int beam_get_point_count(RID p_beam) const; + virtual void beam_clear(RID p_beam); + + virtual void beam_set_point(RID p_beam,int p_point,Vector3& p_pos); + virtual Vector3 beam_get_point(RID p_beam,int p_point) const; + + virtual void beam_set_primitive(RID p_beam,VS::BeamPrimitive p_primitive); + virtual VS::BeamPrimitive beam_get_primitive(RID p_beam) const; + + virtual void beam_set_material(RID p_beam, RID p_material); + virtual RID beam_get_material(RID p_beam) const; + + virtual AABB beam_get_aabb(RID p_particles) const; + /* SKELETON API */ + + virtual RID skeleton_create(); + virtual void skeleton_resize(RID p_skeleton,int p_bones); + virtual int skeleton_get_bone_count(RID p_skeleton) const; + virtual void skeleton_bone_set_transform(RID p_skeleton,int p_bone, const Transform& p_transform); + virtual Transform skeleton_bone_get_transform(RID p_skeleton,int p_bone); + + + /* LIGHT API */ + + virtual RID light_create(VS::LightType p_type); + virtual VS::LightType light_get_type(RID p_light) const; + + virtual void light_set_color(RID p_light,VS::LightColor p_type, const Color& p_color); + virtual Color light_get_color(RID p_light,VS::LightColor p_type) const; + + virtual void light_set_shadow(RID p_light,bool p_enabled); + virtual bool light_has_shadow(RID p_light) const; + + virtual void light_set_volumetric(RID p_light,bool p_enabled); + virtual bool light_is_volumetric(RID p_light) const; + + virtual void light_set_projector(RID p_light,RID p_texture); + virtual RID light_get_projector(RID p_light) const; + + virtual void light_set_var(RID p_light, VS::LightParam p_var, float p_value); + virtual float light_get_var(RID p_light, VS::LightParam p_var) const; + + virtual AABB light_get_aabb(RID p_poly) const; + + + virtual RID light_instance_create(RID p_light); + virtual void light_instance_set_transform(RID p_light_instance,const Transform& p_transform); + + virtual void light_instance_set_active_hint(RID p_light_instance); + virtual bool light_instance_has_shadow(RID p_light_instance) const; + virtual bool light_instance_assign_shadow(RID p_light_instance); + virtual ShadowType light_instance_get_shadow_type(RID p_light_instance) const; + virtual int light_instance_get_shadow_passes(RID p_light_instance) const; + virtual void light_instance_set_pssm_split_info(RID p_light_instance, int p_split, float p_near,float p_far, const CameraMatrix& p_camera, const Transform& p_transform); + + /* PARTICLES INSTANCE */ + + virtual RID particles_instance_create(RID p_particles); + virtual void particles_instance_set_transform(RID p_particles_instance,const Transform& p_transform); + + /* RENDER API */ + /* all calls (inside begin/end shadow) are always warranted to be in the following order: */ + + virtual void begin_frame(); + + virtual void set_viewport(const VS::ViewportRect& p_viewport); + + virtual void begin_scene(RID p_fx=RID(),VS::ScenarioDebugMode p_debug=VS::SCENARIO_DEBUG_DISABLED); + virtual void begin_shadow_map( RID p_light_instance, int p_shadow_pass ); + + virtual void set_camera(const Transform& p_world,const CameraMatrix& p_projection); + + virtual void add_light( RID p_light_instance ); ///< all "add_light" calls happen before add_geometry calls + + typedef Map<StringName,Variant> ParamOverrideMap; + + virtual void add_mesh( RID p_mesh, const Transform* p_world, const RID* p_light_instances, int p_light_count, const ParamOverrideMap* p_material_overrides = NULL, RID p_skeleton=RID()); + virtual void add_multimesh( RID p_multimesh, const Transform* p_world, const RID* p_light_instances, int p_light_count, const ParamOverrideMap* p_material_overrides = NULL); + virtual void add_poly( RID p_poly, const Transform* p_world, const RID* p_light_instances, int p_light_count, const ParamOverrideMap* p_material_overrides = NULL); + virtual void add_beam( RID p_beam, const Transform* p_world, const RID* p_light_instances, int p_light_count, const ParamOverrideMap* p_material_overrides = NULL); + virtual void add_particles( RID p_particle_instance, const RID* p_light_instances, int p_light_count, const ParamOverrideMap* p_material_overrides = NULL); + + virtual void end_scene(); + virtual void end_shadow_map(); + + virtual void end_frame(); + + /* CANVAS API */ + + virtual void canvas_begin(); + virtual void canvas_set_transparency(float p_transparency); + virtual void canvas_set_rect(const Rect2& p_rect, bool p_clip);; + virtual void canvas_draw_line(const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width); + virtual void canvas_draw_rect(const Rect2& p_rect, bool p_region, const Rect2& p_source,bool p_tile,RID p_texture,const Color& p_modulate); + virtual void canvas_draw_style_box(const Rect2& p_rect, RID p_texture,const float *p_margins, bool p_draw_center=true); + virtual void canvas_draw_primitive(const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, RID p_texture); + + /* FX */ + + virtual RID fx_create(); + virtual void fx_get_effects(RID p_fx,List<String> *p_effects) const; + virtual void fx_set_active(RID p_fx,const String& p_effect, bool p_active); + virtual bool fx_is_active(RID p_fx,const String& p_effect) const; + virtual void fx_get_effect_params(RID p_fx,const String& p_effect,List<PropertyInfo> *p_params) const; + virtual Variant fx_get_effect_param(RID p_fx,const String& p_effect,const String& p_param) const; + virtual void fx_set_effect_param(RID p_fx,const String& p_effect, const String& p_param, const Variant& p_pvalue); + + /*MISC*/ + + virtual bool is_texture(const RID& p_rid) const; + virtual bool is_material(const RID& p_rid) const; + virtual bool is_mesh(const RID& p_rid) const; + virtual bool is_multimesh(const RID& p_rid) const; + virtual bool is_poly(const RID& p_rid) const; + virtual bool is_particles(const RID &p_beam) const; + virtual bool is_beam(const RID &p_beam) const; + + virtual bool is_light(const RID& p_rid) const; + virtual bool is_light_instance(const RID& p_rid) const; + virtual bool is_particles_instance(const RID& p_rid) const; + virtual bool is_skeleton(const RID& p_rid) const; + virtual bool is_fx(const RID& p_rid) const; + virtual bool is_shader(const RID& p_rid) const; + + virtual void free(const RID& p_rid) const; + + virtual void init(); + virtual void finish(); + + virtual int get_render_info(VS::RenderInfo p_info); + + RasterizerIPhone(); + virtual ~RasterizerIPhone(); +}; + +#endif +#endif diff --git a/platform/iphone/sem_iphone.cpp b/platform/iphone/sem_iphone.cpp new file mode 100644 index 0000000000..5afaa7b308 --- /dev/null +++ b/platform/iphone/sem_iphone.cpp @@ -0,0 +1,115 @@ +/*************************************************************************/ +/* sem_iphone.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 "sem_iphone.h" + +#include <unistd.h> +#include <fcntl.h> + +void cgsem_init(cgsem_t *cgsem) +{ + int flags, fd, i; + + pipe(cgsem->pipefd); + + /* Make the pipes FD_CLOEXEC to allow them to close should we call + * execv on restart. */ + for (i = 0; i < 2; i++) { + fd = cgsem->pipefd[i]; + flags = fcntl(fd, F_GETFD, 0); + flags |= FD_CLOEXEC; + fcntl(fd, F_SETFD, flags); + } +} + +void cgsem_post(cgsem_t *cgsem) +{ + const char buf = 1; + + write(cgsem->pipefd[1], &buf, 1); +} + +void cgsem_wait(cgsem_t *cgsem) +{ + char buf; + + read(cgsem->pipefd[0], &buf, 1); +} + +void cgsem_destroy(cgsem_t *cgsem) +{ + close(cgsem->pipefd[1]); + close(cgsem->pipefd[0]); +} + + +#include "os/memory.h" +#include <errno.h> + + +Error SemaphoreIphone::wait() { + + cgsem_wait(&sem); + return OK; +} + +Error SemaphoreIphone::post() { + + cgsem_post(&sem); + + return OK; +} +int SemaphoreIphone::get() const { + + return 0; +} + + +Semaphore *SemaphoreIphone::create_semaphore_iphone() { + + return memnew( SemaphoreIphone ); +} + +void SemaphoreIphone::make_default() { + + create_func=create_semaphore_iphone; +} + +SemaphoreIphone::SemaphoreIphone() { + + cgsem_init(&sem); +} + + +SemaphoreIphone::~SemaphoreIphone() { + + cgsem_destroy(&sem); +} + + + diff --git a/platform/iphone/sem_iphone.h b/platform/iphone/sem_iphone.h new file mode 100644 index 0000000000..7a57b5dd99 --- /dev/null +++ b/platform/iphone/sem_iphone.h @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* sem_iphone.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 SEM_IPHONE_H +#define SEM_IPHONE_H + +struct cgsem { + int pipefd[2]; +}; + +typedef struct cgsem cgsem_t; + +#include "os/semaphore.h" + +class SemaphoreIphone : public Semaphore { + + mutable cgsem_t sem; + + static Semaphore *create_semaphore_iphone(); + +public: + + virtual Error wait(); + virtual Error post(); + virtual int get() const; + + static void make_default(); + SemaphoreIphone(); + + ~SemaphoreIphone(); + +}; + + +#endif diff --git a/platform/iphone/view_controller.h b/platform/iphone/view_controller.h new file mode 100644 index 0000000000..ac94d7ed3a --- /dev/null +++ b/platform/iphone/view_controller.h @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* view_controller.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. */ +/*************************************************************************/ +#import <UIKit/UIKit.h> + +@interface ViewController : UIViewController { + +}; + +- (BOOL)shouldAutorotateToInterfaceOrientation: + (UIInterfaceOrientation)p_orientation; + +- (void)didReceiveMemoryWarning; + +@end diff --git a/platform/iphone/view_controller.mm b/platform/iphone/view_controller.mm new file mode 100644 index 0000000000..8af20358b9 --- /dev/null +++ b/platform/iphone/view_controller.mm @@ -0,0 +1,107 @@ +/*************************************************************************/ +/* view_controller.mm */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ +#import "view_controller.h" + +#include "os_iphone.h" + +extern "C" { + +int add_path(int p_argc, char** p_args) { + + NSString* str = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_path"]; + if (!str) + return p_argc; + + p_args[p_argc++] = "-path"; + [str retain]; // memory leak lol (maybe make it static here and delete it in ViewController destructor? @todo + p_args[p_argc++] = (char*)[str cString]; + p_args[p_argc] = NULL; + + return p_argc; +}; + +}; + +@interface ViewController () + +@end + +@implementation ViewController + +- (void)didReceiveMemoryWarning { + + printf("*********** did receive memory warning!\n"); +}; + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)p_orientation { + + if (/*OSIPhone::get_singleton() == NULL*/TRUE) { + + printf("checking on info.plist\n"); + NSArray* arr = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UISupportedInterfaceOrientations"]; + switch(p_orientation) { + + case UIInterfaceOrientationLandscapeLeft: + return [arr indexOfObject:@"UIInterfaceOrientationLandscapeLeft"] != NSNotFound ? YES : NO; + + case UIInterfaceOrientationLandscapeRight: + return [arr indexOfObject:@"UIInterfaceOrientationLandscapeRight"] != NSNotFound ? YES : NO; + + case UIInterfaceOrientationPortrait: + return [arr indexOfObject:@"UIInterfaceOrientationPortrait"] != NSNotFound ? YES : NO; + + case UIInterfaceOrientationPortraitUpsideDown: + return [arr indexOfObject:@"UIInterfaceOrientationPortraitUpsideDown"] != NSNotFound ? YES : NO; + + default: + return NO; + } + }; + + uint8_t supported = OSIPhone::get_singleton()->get_orientations(); + switch(p_orientation) { + + case UIInterfaceOrientationLandscapeLeft: + return supported & (1<<OSIPhone::LandscapeLeft) ? YES : NO; + + case UIInterfaceOrientationLandscapeRight: + return supported & (1<<OSIPhone::LandscapeRight) ? YES : NO; + + case UIInterfaceOrientationPortrait: + return supported & (1<<OSIPhone::PortraitDown) ? YES : NO; + + case UIInterfaceOrientationPortraitUpsideDown: + return supported & (1<<OSIPhone::PortraitUp) ? YES : NO; + + default: + return NO; + } +}; + +@end diff --git a/platform/isim/SCsub b/platform/isim/SCsub new file mode 100644 index 0000000000..6fb568465e --- /dev/null +++ b/platform/isim/SCsub @@ -0,0 +1,30 @@ +Import('env') + +iphone_lib = [ + + '#platform/iphone/os_iphone.cpp', + #'#platform/iphone/rasterizer_iphone.cpp', + '#platform/iphone/audio_driver_iphone.cpp', + '#platform/iphone/gl_view.mm', + '#platform/iphone/main.m', + '#platform/iphone/app_delegate.mm', + '#platform/iphone/view_controller.mm', + '#platform/iphone/scoreloop_ios.mm', +] + + +#env.Depends('#core/math/vector3.h', 'vector3_psp.h') + +#iphone_lib = env.Library('iphone', iphone_lib) + +obj = env.Object('#platform/iphone/godot_iphone.cpp') + +prog = None +if env["target"]=="release": + prog = env.Program('#bin/godot_iphone_opt', [obj] + iphone_lib) + #action = "dsymutil "+File(prog)[0].path+" -o ../build/script_exec/build/Debug-iphoneos/script_exec.app.dSYM" + #env.AddPostAction(prog, action) +else: + prog = env.Program('#bin/godot', [obj] + iphone_lib) + #action = "dsymutil "+File(prog)[0].path+" -o ../build/script_exec/build/Debug-iphoneos/script_exec.app.dSYM" + #env.AddPostAction(prog, action) diff --git a/platform/isim/detect.py b/platform/isim/detect.py new file mode 100644 index 0000000000..8deabbf311 --- /dev/null +++ b/platform/isim/detect.py @@ -0,0 +1,98 @@ +import os +import sys + + +def is_active(): + return False + +def get_name(): + return "iSIM" + + +def can_build(): + + import sys + if sys.platform == 'darwin': + return True + + return False + +def get_opts(): + + return [ + ('ISIMPLATFORM', 'name of the iphone platform', 'iPhoneSimulator'), + ('ISIMPATH', 'the path to iphone toolchain', '/Developer/Platforms/${ISIMPLATFORM}.platform'), + ('ISIMSDK', 'path to the iphone SDK', '$ISIMPATH/Developer/SDKs/${ISIMPLATFORM}4.3.sdk'), + ] + +def get_flags(): + + return [ + ('lua', 'no'), + ('tools', 'yes'), + ('nedmalloc', 'no'), + ] + + + +def configure(env): + + env.Append(CPPPATH=['#platform/iphone']) + + env['OBJSUFFIX'] = ".isim.o" + env['LIBSUFFIX'] = ".isim.a" + env['PROGSUFFIX'] = ".isim" + + env['ENV']['PATH'] = env['ISIMPATH']+"/Developer/usr/bin/:"+env['ENV']['PATH'] + + env['CC'] = '$ISIMPATH/Developer/usr/bin/gcc' + env['CXX'] = '$ISIMPATH/Developer/usr/bin/g++' + env['AR'] = 'ar' + + import string + env['CCFLAGS'] = string.split('-arch i386 -fobjc-abi-version=2 -fobjc-legacy-dispatch -fmessage-length=0 -fpascal-strings -fasm-blocks -Wall -D__IPHONE_OS_VERSION_MIN_REQUIRED=40100 -isysroot $ISIMSDK -mmacosx-version-min=10.6 -DCUSTOM_MATRIX_TRANSFORM_H=\\\"build/iphone/matrix4_iphone.h\\\" -DCUSTOM_VECTOR3_TRANSFORM_H=\\\"build/iphone/vector3_iphone.h\\\"') + + env.Append(LINKFLAGS=['-arch', 'i386', + #'-miphoneos-version-min=2.2.1', + '-isysroot', '$ISIMSDK', + '-mmacosx-version-min=10.6', + '-Xlinker', + '-objc_abi_version', + '-Xlinker', '2', + '-framework', 'Foundation', + '-framework', 'UIKit', + '-framework', 'IOKit', + '-framework', 'CoreGraphics', + '-framework', 'OpenGLES', + '-framework', 'QuartzCore', + '-framework', 'AudioToolbox', + '-F$ISIMSDK', + ]) + + env.Append(CPPPATH = ['$ISIMSDK/System/Library/Frameworks/OpenGLES.framework/Headers']) + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['-O3', '-ffast-math']) + env.Append(LINKFLAGS=['-O3', '-ffast-math']) + env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX'] + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-DDEBUG', '-D_DEBUG', '-gdwarf-2', '-Wall', '-O0', '-DDEBUG_ENABLED']) + env.Append(CPPFLAGS=['-DDEBUG_MEMORY_ALLOC']) + + elif (env["target"]=="profile"): + + env.Append(CCFLAGS=['-g','-pg']) + env.Append(LINKFLAGS=['-pg']) + + + env['ENV']['MACOSX_DEPLOYMENT_TARGET'] = '10.6' + env['ENV']['CODESIGN_ALLOCATE'] = '/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate' + env.Append(CPPFLAGS=['-DIPHONE_ENABLED', '-DUNIX_ENABLED', '-DGLES2_ENABLED', '-fno-exceptions']) + + if env['lua'] == "yes": + env.Append(CCFLAGS=['-DLUA_USE_FLOAT']) + diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub new file mode 100644 index 0000000000..fd1bb39e90 --- /dev/null +++ b/platform/javascript/SCsub @@ -0,0 +1,25 @@ +Import('env') + +javascript_files = [ + "os_javascript.cpp", + "audio_driver_javascript.cpp", + "javascript_main.cpp" +] + +#env.Depends('#core/math/vector3.h', 'vector3_psp.h') + +#obj = env.SharedObject('godot_javascript.cpp') + +env_javascript = env.Clone() +if env['target'] == "profile": + env_javascript.Append(CPPFLAGS=['-DPROFILER_ENABLED']) + +javascript_objects=[] +for x in javascript_files: + javascript_objects.append( env_javascript.Object( x ) ) + +prog = None + +#env_javascript.SharedLibrary("#platform/javascript/libgodot_javascript.so",[javascript_objects]) + +env.Program('#bin/godot.html', javascript_objects) diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp new file mode 100644 index 0000000000..2a77806fb3 --- /dev/null +++ b/platform/javascript/audio_driver_javascript.cpp @@ -0,0 +1,91 @@ +/*************************************************************************/ +/* audio_driver_javascript.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_javascript.h" +#include <string.h> + + + + + + +#define MAX_NUMBER_INTERFACES 3 +#define MAX_NUMBER_OUTPUT_DEVICES 6 + +/* Structure for passing information to callback function */ + + + +//AudioDriverJavaScript* AudioDriverJavaScript::s_ad=NULL; + +const char* AudioDriverJavaScript::get_name() const { + + return "JavaScript"; +} + +Error AudioDriverJavaScript::init(){ + + return OK; + +} +void AudioDriverJavaScript::start(){ + + + +} +int AudioDriverJavaScript::get_mix_rate() const { + + return 44100; +} +AudioDriverSW::OutputFormat AudioDriverJavaScript::get_output_format() const{ + + return OUTPUT_STEREO; +} +void AudioDriverJavaScript::lock(){ + + //if (active && mutex) + // mutex->lock(); + +} +void AudioDriverJavaScript::unlock() { + + //if (active && mutex) + // mutex->unlock(); + +} +void AudioDriverJavaScript::finish(){ + +} + + +AudioDriverJavaScript::AudioDriverJavaScript() +{ +} + + + diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h new file mode 100644 index 0000000000..9fe93c56ac --- /dev/null +++ b/platform/javascript/audio_driver_javascript.h @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* audio_driver_javascript.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_JAVASCRIPT_H +#define AUDIO_DRIVER_JAVASCRIPT_H + + +#include "servers/audio/audio_server_sw.h" +#include "os/mutex.h" + +class AudioDriverJavaScript : public AudioDriverSW { +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(); + + + AudioDriverJavaScript(); +}; + + +#endif diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py new file mode 100644 index 0000000000..da92c0869b --- /dev/null +++ b/platform/javascript/detect.py @@ -0,0 +1,102 @@ +import os +import sys +import string + +def is_active(): + return True + +def get_name(): + return "JavaScript" + +def can_build(): + + import os + if (not os.environ.has_key("EMSCRIPTEN_ROOT")): + return False + return True + +def get_opts(): + + return [ + ['compress','Compress JS Executable','no'] + ] + +def get_flags(): + + return [ + ('lua', 'no'), + ('tools', 'no'), + ('nedmalloc', 'no'), + ('theora', 'no'), + ('tools', 'no'), + ('nedmalloc', 'no'), + ('vorbis', 'yes'), + ('musepack', 'no'), + ('squirrel', 'no'), + ('squish', 'no'), + ('speex', 'no'), + ('old_scenes', 'no'), +# ('default_gui_theme', 'no'), + + #('builtin_zlib', 'no'), + ] + + + +def configure(env): + + + env.Append(CPPPATH=['#platform/android']) + + env['OBJSUFFIX'] = ".js.o" + env['LIBSUFFIX'] = ".js.a" + env['PROGSUFFIX'] = ".html" + + em_path=os.environ["EMSCRIPTEN_ROOT"] + + env['ENV']['PATH'] = em_path+":"+env['ENV']['PATH'] + + env['CC'] = em_path+'/emcc' + env['CXX'] = em_path+'/emcc' + env['AR'] = em_path+"/emar" + env['RANLIB'] = em_path+"/emranlib" + +# env.Append(LIBS=['c','m','stdc++','log','GLESv1_CM','GLESv2']) + +# env["LINKFLAGS"]= string.split(" -g --sysroot="+ld_sysroot+" -Wl,--no-undefined -Wl,-z,noexecstack ") + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['-O2']) + env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX'] + + elif (env["target"]=="release_debug"): + + env.Append(CCFLAGS=['-O2','-DDEBUG_ENABLED']) + env['OBJSUFFIX'] = "_optd"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_optd"+env['LIBSUFFIX'] + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-D_DEBUG', '-Wall', '-O2', '-DDEBUG_ENABLED']) + env.Append(CPPFLAGS=['-DDEBUG_MEMORY_ALLOC']) + + env.Append(CPPFLAGS=["-fno-exceptions",'-DNO_SAFE_CAST','-fno-rtti']) + env.Append(CPPFLAGS=['-DJAVASCRIPT_ENABLED', '-DUNIX_ENABLED', '-DNO_FCNTL','-DMPC_FIXED_POINT','-DTYPED_METHOD_BIND','-DNO_THREADS']) + env.Append(CPPFLAGS=['-DGLES2_ENABLED']) + env.Append(CPPFLAGS=['-DGLES_NO_CLIENT_ARRAYS']) + env.Append(CPPFLAGS=['-s','ASM_JS=1']) + env.Append(CPPFLAGS=['-s','FULL_ES2=1']) +# env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED','-DMPC_FIXED_POINT']) + if (env["compress"]=="yes"): + lzma_binpath = em_path+"/third_party/lzma.js/lzma-native" + lzma_decoder = em_path+"/third_party/lzma.js/lzma-decoder.js" + lzma_dec = "LZMA.decompress" + + env.Append(LINKFLAGS=['--compression',lzma_binpath+","+lzma_decoder+","+lzma_dec]) + + env.Append(LINKFLAGS=['-s','ASM_JS=1']) + env.Append(LINKFLAGS=['-O2']) + + diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp new file mode 100644 index 0000000000..a81fabe085 --- /dev/null +++ b/platform/javascript/export/export.cpp @@ -0,0 +1,452 @@ +/*************************************************************************/ +/* export.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 "version.h" +#include "export.h" +#include "tools/editor/editor_settings.h" +#include "tools/editor/editor_import_export.h" +#include "tools/editor/editor_node.h" +#include "io/zip_io.h" +#include "io/marshalls.h" +#include "globals.h" +#include "os/file_access.h" +#include "os/os.h" +#include "platform/javascript/logo.h" +#include "string.h" +class EditorExportPlatformJavaScript : public EditorExportPlatform { + + OBJ_TYPE( EditorExportPlatformJavaScript,EditorExportPlatform ); + + String custom_release_package; + String custom_debug_package; + + enum PackMode { + PACK_SINGLE_FILE, + PACK_MULTIPLE_FILES + }; + + + PackMode pack_mode; + + bool show_run; + + int max_memory; + int version_code; + + Ref<ImageTexture> logo; + + static Error save_pack_file_js(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total); + +protected: + + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + +public: + + virtual String get_name() const { return "HTML5"; } + virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_BC; } + virtual Ref<Texture> get_logo() const { return logo; } + + + virtual bool poll_devices() { return show_run?true:false;} + virtual int get_device_count() const { return show_run?1:0; }; + virtual String get_device_name(int p_device) const { return "Run in Browser"; } + virtual String get_device_info(int p_device) const { return "Run exported HTML in the system's default browser."; } + virtual Error run(int p_device); + + virtual bool requieres_password(bool p_debug) const { return false; } + virtual String get_binary_extension() const { return "html"; } + virtual Error export_project(const String& p_path,bool p_debug,const String& p_password=""); + + virtual bool can_export(String *r_error=NULL) const; + + EditorExportPlatformJavaScript(); + ~EditorExportPlatformJavaScript(); +}; + +bool EditorExportPlatformJavaScript::_set(const StringName& p_name, const Variant& p_value) { + + String n=p_name; + + if (n=="custom_package/debug") + custom_debug_package=p_value; + else if (n=="custom_package/release") + custom_release_package=p_value; + else if (n=="browser/enable_run") + show_run=p_value; + else if (n=="options/memory_size") + max_memory=p_value; + else if (n=="options/pack_mode") + pack_mode=PackMode(int(p_value)); + else + return false; + + return true; +} + +bool EditorExportPlatformJavaScript::_get(const StringName& p_name,Variant &r_ret) const{ + + String n=p_name; + + if (n=="custom_package/debug") + r_ret=custom_debug_package; + else if (n=="custom_package/release") + r_ret=custom_release_package; + else if (n=="browser/enable_run") + r_ret=show_run; + else if (n=="options/memory_size") + r_ret=max_memory; + else if (n=="options/pack_mode") + r_ret=pack_mode; + else + return false; + + return true; +} +void EditorExportPlatformJavaScript::_get_property_list( List<PropertyInfo> *p_list) const{ + + p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/debug", PROPERTY_HINT_FILE,"zip")); + p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/release", PROPERTY_HINT_FILE,"zip")); + p_list->push_back( PropertyInfo( Variant::INT, "options/pack_mode",PROPERTY_HINT_ENUM,"Single File, Multiple Files")); + p_list->push_back( PropertyInfo( Variant::INT, "options/memory_size",PROPERTY_HINT_ENUM,"32mb,64mb,128mb,256mb,512mb,1024mb")); + p_list->push_back( PropertyInfo( Variant::BOOL, "browser/enable_run")); + + //p_list->push_back( PropertyInfo( Variant::INT, "resources/pack_mode", PROPERTY_HINT_ENUM,"Copy,Single Exec.,Pack (.pck),Bundles (Optical)")); + +} + + +static const char* files_pre=""\ +"var Module;\n"\ +"if (typeof Module === 'undefined') Module = eval('(function() { try { return Module || {} } catch(e) { return {} } })()');\n"\ +"if (!Module.expectedDataFileDownloads) {\n"\ +" Module.expectedDataFileDownloads = 0;\n"\ +" Module.finishedDataFileDownloads = 0;\n"\ +"}\n"\ +"Module.expectedDataFileDownloads++;\n"\ +"(function() {\n"\ +"\n"\ +" function runWithFS() {\n"\ +"function assert(check, msg) {\n"\ +" if (!check) throw msg + new Error().stack;\n"\ +"} \n"; + +static const char* files_post=""\ +"}\n"\ +"if (Module['calledRun']) {\n"\ +" runWithFS();\n"\ +"} else {\n"\ +" if (!Module['preRun']) Module['preRun'] = [];\n"\ +" Module[\"preRun\"].push(runWithFS); // FS is not initialized yet, wait for it\n"\ +"}\n"\ +"})();"; + + +static void _fix_html(Vector<uint8_t>& html,const String& name,int max_memory) { + + + String str; + String strnew; + str.parse_utf8((const char*)html.ptr(),html.size()); + Vector<String> lines=str.split("\n"); + for(int i=0;i<lines.size();i++) { + if (lines[i].find("godot.js")!=-1) { + strnew+="<script type=\"text/javascript\" src=\""+name+"_files.js\"></script>\n"; + strnew+="<script async type=\"text/javascript\" src=\""+name+".js\"></script>\n"; + } else if (lines[i].find("var Module")!=-1) { + strnew+=lines[i]; + strnew+="TOTAL_MEMORY:"+itos(max_memory*1024*1024)+","; + } else { + strnew+=lines[i]+"\n"; + } + } + + CharString cs = strnew.utf8(); + html.resize(cs.size()); + for(int i=9;i<cs.size();i++) { + html[i]=cs[i]; + } +} + + +struct JSExportData { + + EditorProgress *ep; + FileAccess *f; + +}; + + +static void store_file_buffer(FileAccess*f,const String& p_path,const Vector<uint8_t>& p_data) { + + + String pre = "Module['FS_createDataFile']('/', '"+p_path.replace("res://","")+"',["; + CharString cs = pre.utf8(); + f->store_buffer((const uint8_t*)cs.ptr(),cs.length()); + for(int i=0;i<p_data.size();i++) { + + + uint8_t c=','; + if (i>0) + f->store_buffer(&c,1); + + uint8_t str[4]; + uint8_t d = p_data[i]; + if (d<10) { + str[0]='0'+d; + str[1]=0; + f->store_buffer(str,1); + } else if (d<100) { + + str[0]='0'+d/10; + str[1]='0'+d%10; + str[2]=0; + f->store_buffer(str,2); + + } else { + str[0]='0'+d/100; + str[1]='0'+(d/10)%10; + str[2]='0'+d%10; + str[3]=0; + f->store_buffer(str,3); + } + } + String post = "],true,true);\n"; + cs = post.utf8(); + f->store_buffer((const uint8_t*)cs.ptr(),cs.length()); +} + + +Error EditorExportPlatformJavaScript::save_pack_file_js(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total) { + + JSExportData *ed=(JSExportData*)p_userdata; + + FileAccess *f=(FileAccess *)p_userdata; + store_file_buffer(ed->f,p_path,p_data); + ed->ep->step("File: "+p_path,3+p_file*100/p_total); + return OK; + +} + +Error EditorExportPlatformJavaScript::export_project(const String& p_path,bool p_debug,const String& p_password) { + + + String src_template; + + EditorProgress ep("export","Exporting for javascript",104); + + String template_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + if (p_debug) { + + src_template=custom_debug_package!=""?custom_debug_package:template_path+"javascript_debug.zip"; + } else { + + src_template=custom_release_package!=""?custom_release_package:template_path+"javascript_release.zip"; + + } + + + FileAccess *src_f=NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + ep.step("Exporting to HTML5",0); + + unzFile pkg = unzOpen2(src_template.utf8().get_data(), &io); + if (!pkg) { + + EditorNode::add_io_error("Could not find template HTML5 to export:\n"+src_template); + return ERR_FILE_NOT_FOUND; + } + + ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(pkg); + + + while(ret==UNZ_OK) { + + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(pkg,&info,fname,16384,NULL,0,NULL,0); + + String file=fname; + + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg,data.ptr(),data.size()); + unzCloseCurrentFile(pkg); + + //write + + if (file=="godot.html") { + + _fix_html(data,p_path.get_file().basename(),1<<(max_memory+5)); + file=p_path.get_file(); + } + if (file=="godot.js") { + + //_fix_godot(data); + file=p_path.get_file().basename()+".js"; + } + + String dst = p_path.get_base_dir().plus_file(file); + FileAccess *f=FileAccess::open(dst,FileAccess::WRITE); + if (!f) { + EditorNode::add_io_error("Could not create file for writing:\n"+dst); + unzClose(pkg); + return ERR_FILE_CANT_WRITE; + } + f->store_buffer(data.ptr(),data.size()); + memdelete(f); + + + ret = unzGoToNextFile(pkg); + } + + + ep.step("Finding Files..",1); + + Vector<String> remaps; + + FileAccess *f=FileAccess::open(p_path.basename()+"_files.js",FileAccess::WRITE); + if (!f) { + EditorNode::add_io_error("Could not create file for writing:\n"+p_path.basename()+"_files.js"); + return ERR_FILE_CANT_WRITE; + } + + f->store_buffer((const uint8_t*)files_pre,strlen(files_pre)); + + if (pack_mode==PACK_SINGLE_FILE) { + + String ftmp = EditorSettings::get_singleton()->get_settings_path()+"/tmp/webpack.pck"; + FileAccess *f2 = FileAccess::open(ftmp,FileAccess::WRITE); + if (!f2) { + memdelete(f); + return ERR_CANT_CREATE; + } + Error err = save_pack(f2,false); + memdelete(f2); + if (err) { + memdelete(f); + return ERR_CANT_CREATE; + } + + Vector<uint8_t> data = FileAccess::get_file_as_array(ftmp); + store_file_buffer(f,"data.pck",data); + + + } else { + JSExportData ed; + ed.ep=&ep; + ed.f=f; + + Error err =export_project_files(save_pack_file_js,&ed,false); + if (err) + return err; + } + f->store_buffer((const uint8_t*)files_post,strlen(files_post)); + memdelete(f); + + + return OK; + +} + + +Error EditorExportPlatformJavaScript::run(int p_device) { + + String path = EditorSettings::get_singleton()->get_settings_path()+"/tmp/tmp_export.html"; + Error err = export_project(path,true,""); + if (err) + return err; + + OS::get_singleton()->shell_open(path); + + return OK; +} + + +EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { + + show_run=false; + Image img( _javascript_logo ); + logo = Ref<ImageTexture>( memnew( ImageTexture )); + logo->create_from_image(img); + max_memory=3; + pack_mode=PACK_SINGLE_FILE; +} + +bool EditorExportPlatformJavaScript::can_export(String *r_error) const { + + + bool valid=true; + String err; + String exe_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + if (!FileAccess::exists(exe_path+"javascript_debug.zip") || !FileAccess::exists(exe_path+"javascript_release.zip")) { + valid=false; + err+="No export templates found.\nDownload and install export templates.\n"; + } + + if (custom_debug_package!="" && !FileAccess::exists(custom_debug_package)) { + valid=false; + err+="Custom debug package not found.\n"; + } + + if (custom_release_package!="" && !FileAccess::exists(custom_release_package)) { + valid=false; + err+="Custom release package not found.\n"; + } + + if (r_error) + *r_error=err; + + return valid; +} + + +EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() { + +} + + +void register_javascript_exporter() { + + + Ref<EditorExportPlatformJavaScript> exporter = Ref<EditorExportPlatformJavaScript>( memnew(EditorExportPlatformJavaScript) ); + EditorImportExport::get_singleton()->add_export_platform(exporter); + + +} + diff --git a/platform/javascript/export/export.h b/platform/javascript/export/export.h new file mode 100644 index 0000000000..2b575c9149 --- /dev/null +++ b/platform/javascript/export/export.h @@ -0,0 +1,29 @@ +/*************************************************************************/ +/* export.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. */ +/*************************************************************************/ +void register_javascript_exporter(); diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp new file mode 100644 index 0000000000..81485bab8e --- /dev/null +++ b/platform/javascript/javascript_main.cpp @@ -0,0 +1,251 @@ +/*************************************************************************/ +/* javascript_main.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 <GL/glut.h> +#include "os_javascript.h" +#include "main/main.h" +#include "io/resource_loader.h" +#include "os/keyboard.h" +OS_JavaScript *os=NULL; + +static void _gfx_init(void *ud,bool gl2,int w, int h,bool fs) { + + glutInitWindowSize(w, h); + glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); + glutCreateWindow("godot"); + +} + + +static void _glut_skey(bool pressed,int key) { + + InputEvent ev; + ev.type=InputEvent::KEY; + ev.key.pressed=pressed; + switch(key) { + case GLUT_KEY_F1: ev.key.scancode=KEY_F1; break; + case GLUT_KEY_F2: ev.key.scancode=KEY_F2; break; + case GLUT_KEY_F3: ev.key.scancode=KEY_F3; break; + case GLUT_KEY_F4: ev.key.scancode=KEY_F4; break; + case GLUT_KEY_F5: ev.key.scancode=KEY_F5; break; + case GLUT_KEY_F6: ev.key.scancode=KEY_F6; break; + case GLUT_KEY_F7: ev.key.scancode=KEY_F7; break; + case GLUT_KEY_F8: ev.key.scancode=KEY_F8; break; + case GLUT_KEY_F9: ev.key.scancode=KEY_F9; break; + case GLUT_KEY_F10: ev.key.scancode=KEY_F10; break; + case GLUT_KEY_F11: ev.key.scancode=KEY_F11; break; + case GLUT_KEY_F12: ev.key.scancode=KEY_F12; break; + case GLUT_KEY_LEFT: ev.key.scancode=KEY_LEFT; break; + case GLUT_KEY_UP: ev.key.scancode=KEY_UP; break; + case GLUT_KEY_RIGHT: ev.key.scancode=KEY_RIGHT; break; + case GLUT_KEY_DOWN: ev.key.scancode=KEY_DOWN; break; + case GLUT_KEY_PAGE_UP: ev.key.scancode=KEY_PAGEUP; break; + case GLUT_KEY_PAGE_DOWN: ev.key.scancode=KEY_PAGEDOWN; break; + case GLUT_KEY_HOME: ev.key.scancode=KEY_HOME; break; + case GLUT_KEY_END: ev.key.scancode=KEY_END; break; + case GLUT_KEY_INSERT: ev.key.scancode=KEY_INSERT; break; + } + + + uint32_t m = glutGetModifiers(); + ev.key.mod.alt=(m&GLUT_ACTIVE_ALT)!=0; + ev.key.mod.shift=(m&GLUT_ACTIVE_SHIFT)!=0; + ev.key.mod.control=(m&GLUT_ACTIVE_CTRL)!=0; + + os->push_input(ev); +} + +static void _glut_skey_up(int key, int x, int y) { + + _glut_skey(false,key); +} + +static void _glut_skey_down(int key, int x, int y) { + + _glut_skey(true,key); +} + +static void _glut_key(bool pressed,unsigned char key) { + + InputEvent ev; + ev.type=InputEvent::KEY; + ev.key.pressed=pressed; + switch(key) { + case '\n': ev.key.scancode=KEY_RETURN; break; + case 0x1b: ev.key.scancode=KEY_ESCAPE; break; + case 8: ev.key.scancode=KEY_BACKSPACE; break; + case 0x7f: ev.key.scancode=KEY_DELETE; break; + case 0x20: ev.key.scancode=KEY_SPACE; ev.key.unicode=key; break; + default: { + ev.key.unicode=key; + } + } + + + uint32_t m = glutGetModifiers(); + ev.key.mod.alt=(m&GLUT_ACTIVE_ALT)!=0; + ev.key.mod.shift=(m&GLUT_ACTIVE_SHIFT)!=0; + ev.key.mod.control=(m&GLUT_ACTIVE_CTRL)!=0; + + os->push_input(ev); + +} + +static void _glut_key_up(unsigned char key, int x, int y) { + + _glut_key(false,key); +} + +static void _glut_key_down(unsigned char key, int x, int y) { + + _glut_key(true,key); +} + +static uint32_t _mouse_button_mask=0; + +static void _glut_mouse_button(int button, int state, int x, int y) { + + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + switch(button) { + case GLUT_LEFT_BUTTON: ev.mouse_button.button_index=BUTTON_LEFT; break; + case GLUT_MIDDLE_BUTTON: ev.mouse_button.button_index=BUTTON_MIDDLE; break; + case GLUT_RIGHT_BUTTON: ev.mouse_button.button_index=BUTTON_RIGHT; break; + case 3: ev.mouse_button.button_index=BUTTON_WHEEL_UP; break; + case 4: ev.mouse_button.button_index=BUTTON_WHEEL_DOWN; break; + } + + + ev.mouse_button.pressed=state==GLUT_DOWN; + ev.mouse_button.x=x; + ev.mouse_button.y=y; + ev.mouse_button.global_x=x; + ev.mouse_button.global_y=y; + + if (ev.mouse_button.button_index<4) { + if (ev.mouse_button.pressed) { + _mouse_button_mask|=1<<ev.mouse_button.button_index; + } else { + _mouse_button_mask&=~(1<<ev.mouse_button.button_index); + } + } + + uint32_t m = glutGetModifiers(); + ev.mouse_button.mod.alt=(m&GLUT_ACTIVE_ALT)!=0; + ev.mouse_button.mod.shift=(m&GLUT_ACTIVE_SHIFT)!=0; + ev.mouse_button.mod.control=(m&GLUT_ACTIVE_CTRL)!=0; + + os->push_input(ev); + +} + + +static int _glut_prev_x=0; +static int _glut_prev_y=0; + +static void _glut_mouse_motion(int x, int y) { + + InputEvent ev; + ev.type=InputEvent::MOUSE_MOTION; + ev.mouse_motion.button_mask=_mouse_button_mask; + ev.mouse_motion.x=x; + ev.mouse_motion.y=y; + ev.mouse_motion.global_x=x; + ev.mouse_motion.global_y=y; + ev.mouse_motion.relative_x=x-_glut_prev_x; + ev.mouse_motion.relative_y=y-_glut_prev_y; + _glut_prev_x=x; + _glut_prev_y=y; + + uint32_t m = glutGetModifiers(); + ev.mouse_motion.mod.alt=(m&GLUT_ACTIVE_ALT)!=0; + ev.mouse_motion.mod.shift=(m&GLUT_ACTIVE_SHIFT)!=0; + ev.mouse_motion.mod.control=(m&GLUT_ACTIVE_CTRL)!=0; + + os->push_input(ev); + +} + +static void _gfx_idle() { + + glutPostRedisplay(); +} + +static void _godot_draw(void) { + + os->main_loop_iterate(); + glutSwapBuffers(); +} + +int main(int argc, char *argv[]) { + /* Initialize the window */ + + printf("let it go!\n"); + glutInit(&argc, argv); + os = new OS_JavaScript(_gfx_init,NULL,NULL,NULL,NULL); +#if 0 + char *args[]={"-test","gui","-v",NULL}; + Error err = Main::setup("apk",3,args); +#else +// char *args[]={"-v",NULL};// +// Error err = Main::setup("",1,args); + Error err = Main::setup("",0,NULL); + +#endif + ResourceLoader::set_abort_on_missing_resources(false); //ease up compatibility + Main::start(); + + glutSpecialUpFunc(_glut_skey_up); + glutSpecialFunc(_glut_skey_down); + glutKeyboardUpFunc(_glut_key_up); + glutKeyboardFunc(_glut_key_down); + glutMouseFunc(_glut_mouse_button); + glutMotionFunc(_glut_mouse_motion); + glutMotionFunc(_glut_mouse_motion); + glutPassiveMotionFunc(_glut_mouse_motion); + + + + /* Set up glut callback functions */ + glutIdleFunc (_gfx_idle); +// glutReshapeFunc(gears_reshape); + glutDisplayFunc(_godot_draw); + //glutSpecialFunc(gears_special); + os->main_loop_begin(); + + glutMainLoop(); + + return 0; +} + + +/* + * + *09] <azakai|2__> reduz: yes, define TOTAL_MEMORY on Module. for example var Module = { TOTAL_MEMORY: 12345.. }; before the main + * + */ diff --git a/platform/javascript/logo.png b/platform/javascript/logo.png Binary files differnew file mode 100644 index 0000000000..07e0a41292 --- /dev/null +++ b/platform/javascript/logo.png diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp new file mode 100644 index 0000000000..c131a7e84b --- /dev/null +++ b/platform/javascript/os_javascript.cpp @@ -0,0 +1,593 @@ +/*************************************************************************/ +/* os_javascript.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 "os_javascript.h" +#include "drivers/gles2/rasterizer_gles2.h" +#include "core/io/file_access_buffered_fa.h" +#include "drivers/unix/file_access_unix.h" +#include "drivers/unix/dir_access_unix.h" + +#include "servers/visual/visual_server_raster.h" + +#include "main/main.h" + +#include "core/globals.h" + +int OS_JavaScript::get_video_driver_count() const { + + return 1; +} +const char * OS_JavaScript::get_video_driver_name(int p_driver) const { + + return "GLES2"; +} + +OS::VideoMode OS_JavaScript::get_default_video_mode() const { + + return OS::VideoMode(); +} + +int OS_JavaScript::get_audio_driver_count() const { + + return 1; +} + +const char * OS_JavaScript::get_audio_driver_name(int p_driver) const { + + return "JavaScript"; +} + +void OS_JavaScript::initialize_core() { + + OS_Unix::initialize_core(); + FileAccess::make_default<FileAccessBufferedFA<FileAccessUnix> >(FileAccess::ACCESS_RESOURCES); + +} + +void OS_JavaScript::set_opengl_extensions(const char* p_gl_extensions) { + + ERR_FAIL_COND(!p_gl_extensions); + gl_extensions=p_gl_extensions; +} + +void OS_JavaScript::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + print_line("Init OS"); + + if (gfx_init_func) + gfx_init_func(gfx_init_ud,use_gl2,p_desired.width,p_desired.height,p_desired.fullscreen); + + default_videomode=p_desired; + + print_line("Init Audio"); + + AudioDriverManagerSW::add_driver(&audio_driver_javascript); + + if (true) { + RasterizerGLES2 *rasterizer_gles22=memnew( RasterizerGLES2(false,false,false,false) );; + rasterizer_gles22->set_use_framebuffers(false); //not supported by emscripten + if (gl_extensions) + rasterizer_gles22->set_extensions(gl_extensions); + rasterizer = rasterizer_gles22; + } else { +// rasterizer = memnew( RasterizerGLES1(true, false) ); + } + + print_line("Init VS"); + + 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."); + } + + print_line("Init SM"); + + sample_manager = memnew( SampleManagerMallocSW ); + audio_server = memnew( AudioServerSW(sample_manager) ); + + print_line("Init Mixer"); + + audio_server->set_mixer_params(AudioMixerSW::INTERPOLATION_LINEAR,false); + audio_server->init(); + + print_line("Init SoundServer"); + + spatial_sound_server = memnew( SpatialSoundServerSW ); + spatial_sound_server->init(); + + print_line("Init SpatialSoundServer"); + + spatial_sound_2d_server = memnew( SpatialSound2DServerSW ); + spatial_sound_2d_server->init(); + + // + print_line("Init Physicsserver"); + + physics_server = memnew( PhysicsServerSW ); + physics_server->init(); + physics_2d_server = memnew( Physics2DServerSW ); + physics_2d_server->init(); + + input = memnew( InputDefault ); + +} + +void OS_JavaScript::set_main_loop( MainLoop * p_main_loop ) { + + main_loop=p_main_loop; + input->set_main_loop(p_main_loop); + +} + +void OS_JavaScript::delete_main_loop() { + + memdelete( main_loop ); +} + +void OS_JavaScript::finalize() { + + memdelete(input); +} + + +void OS_JavaScript::vprint(const char* p_format, va_list p_list, bool p_stderr) { + + if (p_stderr) { + + vfprintf(stderr,p_format,p_list); + fflush(stderr); + } else { + + vprintf(p_format,p_list); + fflush(stdout); + } +} + +void OS_JavaScript::print(const char *p_format, ... ) { + + va_list argp; + va_start(argp, p_format); + vprintf(p_format, argp ); + va_end(argp); + +} + +void OS_JavaScript::alert(const String& p_alert) { + + print("ALERT: %s\n",p_alert.utf8().get_data()); +} + + +void OS_JavaScript::set_mouse_show(bool p_show) { + + //javascript has no mouse... +} + +void OS_JavaScript::set_mouse_grab(bool p_grab) { + + //it really has no mouse...! +} + +bool OS_JavaScript::is_mouse_grab_enabled() const { + + //*sigh* technology has evolved so much since i was a kid.. + return false; +} +Point2 OS_JavaScript::get_mouse_pos() const { + + return Point2(); +} +int OS_JavaScript::get_mouse_button_state() const { + + return 0; +} +void OS_JavaScript::set_window_title(const String& p_title) { + + +} + +//interesting byt not yet +//void set_clipboard(const String& p_text); +//String get_clipboard() const; + +void OS_JavaScript::set_video_mode(const VideoMode& p_video_mode,int p_screen) { + + +} + +OS::VideoMode OS_JavaScript::get_video_mode(int p_screen) const { + + return default_videomode; +} +void OS_JavaScript::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const { + + p_list->push_back(default_videomode); +} + +String OS_JavaScript::get_name() { + + return "HTML5"; +} + +MainLoop *OS_JavaScript::get_main_loop() const { + + return main_loop; +} + +bool OS_JavaScript::can_draw() const { + + return true; //always? +} + +void OS_JavaScript::set_cursor_shape(CursorShape p_shape) { + + //javascript really really really has no mouse.. how amazing.. +} + +void OS_JavaScript::main_loop_begin() { + + if (main_loop) + main_loop->init(); +} +bool OS_JavaScript::main_loop_iterate() { + + if (!main_loop) + return false; + return Main::iteration(); +} + +void OS_JavaScript::main_loop_end() { + + if (main_loop) + main_loop->finish(); + +} + +void OS_JavaScript::main_loop_focusout() { + + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); + //audio_driver_javascript.set_pause(true); + +} + +void OS_JavaScript::main_loop_focusin(){ + + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); + //audio_driver_javascript.set_pause(false); + +} + +void OS_JavaScript::push_input(const InputEvent& p_ev) { + + InputEvent ev = p_ev; + ev.ID=last_id++; + input->parse_input_event(p_ev); +} + +void OS_JavaScript::process_touch(int p_what,int p_pointer, const Vector<TouchPos>& p_points) { + +// print_line("ev: "+itos(p_what)+" pnt: "+itos(p_pointer)+" pointc: "+itos(p_points.size())); + + 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; + input->parse_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; + input->parse_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; + input->parse_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; + input->parse_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; + input->parse_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.relative_x=p_points[idx].pos.x - touch[i].pos.x; + ev.screen_drag.relative_y=p_points[idx].pos.y - touch[i].pos.y; + input->parse_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; + input->parse_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; + input->parse_input_event(ev); + + } + touch.clear(); + } + + } 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; + input->parse_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; + input->parse_input_event(ev); + touch.remove(i); + i--; + } + } + + } break; + + } + +} + +void OS_JavaScript::process_accelerometer(const Vector3& p_accelerometer) { + + input->set_accelerometer(p_accelerometer); +} + +bool OS_JavaScript::has_touchscreen_ui_hint() const { + + return true; +} + +void OS_JavaScript::main_loop_request_quit() { + + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); +} + +void OS_JavaScript::set_display_size(Size2 p_size) { + + default_videomode.width=p_size.x; + default_videomode.height=p_size.y; +} + +void OS_JavaScript::reload_gfx() { + + if (gfx_init_func) + gfx_init_func(gfx_init_ud,use_gl2,default_videomode.width,default_videomode.height,default_videomode.fullscreen); + if (rasterizer) + rasterizer->reload_vram(); +} + +Error OS_JavaScript::shell_open(String p_uri) { + + if (open_uri_func) + return open_uri_func(p_uri)?ERR_CANT_OPEN:OK; + return ERR_UNAVAILABLE; +}; + +String OS_JavaScript::get_resource_dir() const { + + return "/"; //javascript has it's own filesystem for resources inside the APK +} + +String OS_JavaScript::get_locale() const { + + if (get_locale_func) + return get_locale_func(); + return OS_Unix::get_locale(); +} + + +String OS_JavaScript::get_data_dir() const { + + if (get_data_dir_func) + return get_data_dir_func(); + return "/"; + //return Globals::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); +}; + + + + +OS_JavaScript::OS_JavaScript(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func,GetLocaleFunc p_get_locale_func) { + + + default_videomode.width=800; + default_videomode.height=600; + default_videomode.fullscreen=true; + default_videomode.resizable=false; + + gfx_init_func=p_gfx_init_func; + gfx_init_ud=p_gfx_init_ud; + main_loop=NULL; + last_id=1; + gl_extensions=NULL; + rasterizer=NULL; + + open_uri_func=p_open_uri_func; + get_data_dir_func=p_get_data_dir_func; + get_locale_func=p_get_locale_func; + + +} + +OS_JavaScript::~OS_JavaScript() { + + +} diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h new file mode 100644 index 0000000000..dfc93d3ff0 --- /dev/null +++ b/platform/javascript/os_javascript.h @@ -0,0 +1,164 @@ +/*************************************************************************/ +/* os_javascript.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 OS_JAVASCRIPT_H +#define OS_JAVASCRIPT_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_javascript.h" + +typedef void (*GFXInitFunc)(void *ud,bool gl2,int w, int h, bool fs); +typedef int (*OpenURIFunc)(const String&); +typedef String (*GetDataDirFunc)(); +typedef String (*GetLocaleFunc)(); + +class OS_JavaScript : public OS_Unix { +public: + + struct TouchPos { + int id; + Point2 pos; + }; + +private: + + Vector<TouchPos> touch; + + Point2 last_mouse; + unsigned int last_id; + GFXInitFunc gfx_init_func; + void*gfx_init_ud; + + bool use_gl2; + + Rasterizer *rasterizer; + VisualServer *visual_server; + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSoundServerSW *spatial_sound_server; + SpatialSound2DServerSW *spatial_sound_2d_server; + PhysicsServer *physics_server; + Physics2DServer *physics_2d_server; + AudioDriverJavaScript audio_driver_javascript; + const char* gl_extensions; + + InputDefault *input; + VideoMode default_videomode; + MainLoop * main_loop; + + OpenURIFunc open_uri_func; + GetDataDirFunc get_data_dir_func; + GetLocaleFunc get_locale_func; + +public: + + // 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_core(); + 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_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_request_quit(); + void main_loop_end(); + void main_loop_focusout(); + void main_loop_focusin(); + + virtual bool has_touchscreen_ui_hint() const; + + void set_opengl_extensions(const char* p_gl_extensions); + void set_display_size(Size2 p_size); + + void reload_gfx(); + + virtual Error shell_open(String p_uri); + virtual String get_data_dir() const; + virtual String get_resource_dir() const; + virtual String get_locale() const; + + void process_accelerometer(const Vector3& p_accelerometer); + void process_touch(int p_what,int p_pointer, const Vector<TouchPos>& p_points); + void push_input(const InputEvent& p_ev); + OS_JavaScript(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func,GetLocaleFunc p_get_locale_func); + ~OS_JavaScript(); + +}; + +#endif diff --git a/platform/javascript/platform_config.h b/platform/javascript/platform_config.h new file mode 100644 index 0000000000..38fc934ae4 --- /dev/null +++ b/platform/javascript/platform_config.h @@ -0,0 +1,29 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include <alloca.h> diff --git a/platform/nacl/SCsub b/platform/nacl/SCsub new file mode 100644 index 0000000000..ac01752dbb --- /dev/null +++ b/platform/nacl/SCsub @@ -0,0 +1,30 @@ +Import('env') + +nacl_lib = [ + + 'os_nacl.cpp', + 'audio_driver_nacl.cpp', + 'godot_nacl.cpp', + #'pepper_main.cpp', + 'opengl_context.cpp', + 'godot_module.cpp', + 'geturl_handler.cpp', +] + +nacl_posix = [ + + '#drivers/unix/thread_posix.cpp', + '#drivers/unix/mutex_posix.cpp', + '#drivers/unix/semaphore_posix.cpp', +] + +posix_lib = [] +for f in nacl_posix: + posix_lib.append(env.Object(f, CPPFLAGS = env['CPPFLAGS']+['-DUNIX_ENABLED'], OBJSUFFIX = '.posix'+env['OBJSUFFIX'])) + +prog = env.Program('#bin/godot_nacl', nacl_lib + posix_lib) + +if (env['nacl_arch'] == 'i686'): + env.Alias("nacl_32", prog) +if (env['nacl_arch'] == 'x86_64'): + env.Alias("nacl_64", prog) diff --git a/platform/nacl/audio_driver_nacl.cpp b/platform/nacl/audio_driver_nacl.cpp new file mode 100644 index 0000000000..8f1861eee3 --- /dev/null +++ b/platform/nacl/audio_driver_nacl.cpp @@ -0,0 +1,106 @@ +/*************************************************************************/ +/* audio_driver_nacl.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_nacl.h" + +#include "ppapi/cpp/instance.h" + +extern pp::Instance* godot_instance; + +const char* AudioDriverNacl::get_name() const { + + return "Nacl"; +} + +void AudioDriverNacl::output_callback(void* samples, uint32_t buffer_size, void* data) { + + AudioDriverNacl* ad = (AudioDriverNacl*)data; + int16_t* out = (int16_t*)samples; + + ad->lock(); + ad->audio_server_process(ad->sample_frame_count_, ad->samples_in); + ad->unlock(); + + for (int i=0; i<ad->sample_count; i++) { + + out[i] = ad->samples_in[i]>>16; + }; + +}; + +Error AudioDriverNacl::init(){ + + int frame_size = 4096; + sample_frame_count_ = pp::AudioConfig::RecommendSampleFrameCount(godot_instance,PP_AUDIOSAMPLERATE_44100, frame_size); + sample_count = sample_frame_count_ * 2; + + audio_ = pp::Audio(godot_instance, + pp::AudioConfig(godot_instance, + PP_AUDIOSAMPLERATE_44100, + sample_frame_count_), + &AudioDriverNacl::output_callback, + this); + + samples_in = memnew_arr(int32_t, sample_frame_count_ * 2); + + return OK; +} +void AudioDriverNacl::start(){ + + audio_.StartPlayback(); +} +int AudioDriverNacl::get_mix_rate() const { + + return 44100; +} +AudioDriverSW::OutputFormat AudioDriverNacl::get_output_format() const{ + + return OUTPUT_STEREO; +} +void AudioDriverNacl::lock(){ + +} +void AudioDriverNacl::unlock() { + + +} +void AudioDriverNacl::finish(){ + + audio_.StopPlayback(); +} + + +AudioDriverNacl::AudioDriverNacl() { +} + +AudioDriverNacl::~AudioDriverNacl() { + + memdelete_arr(samples_in); +} + + diff --git a/platform/nacl/audio_driver_nacl.h b/platform/nacl/audio_driver_nacl.h new file mode 100644 index 0000000000..4a45d0bb4b --- /dev/null +++ b/platform/nacl/audio_driver_nacl.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* audio_driver_nacl.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_NACL_H +#define AUDIO_DRIVER_NACL_H + +#include "servers/audio/audio_server_sw.h" + +#include "ppapi/cpp/audio.h" + +class AudioDriverNacl : public AudioDriverSW { + + static void output_callback(void* samples, uint32_t buffer_size, void* data); + + int32_t* samples_in; + int sample_frame_count_; + int sample_count; + pp::Audio audio_; + +public: + + 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(); + + + AudioDriverNacl(); + ~AudioDriverNacl(); +}; + +#endif // AUDIO_DRIVER_NACL_H + diff --git a/platform/nacl/context_gl_nacl.cpp b/platform/nacl/context_gl_nacl.cpp new file mode 100644 index 0000000000..70cf01a16e --- /dev/null +++ b/platform/nacl/context_gl_nacl.cpp @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* context_gl_nacl.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 "context_gl_nacl.h" + +#include <nacl/npapi.h> +#include <nacl/npruntime.h> +#include <nacl/npapi_extensions.h> +#include <nacl/npupp.h> +#include <pgl/pgl.h> +#include <GLES2/gl2.h> + +void ContextGLNacl::make_current() { + +}; + +int ContextGLNacl::get_window_width() { + +}; + +int ContextGLNacl::get_window_height() { + +}; + +void ContextGLNacl::swap_buffers() { + +}; + +Error ContextGLNacl::initialize() { + +}; + + +ContextGLNacl::ContextGLNacl() { + + +}; + +ContextGLNacl::~ContextGLNacl() { + +}; + diff --git a/platform/nacl/context_gl_nacl.h b/platform/nacl/context_gl_nacl.h new file mode 100644 index 0000000000..a5cf7a728c --- /dev/null +++ b/platform/nacl/context_gl_nacl.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* context_gl_nacl.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 CONTEXT_GL_NACL_H +#define CONTEXT_GL_NACL_H + +#include "drivers/gl_context/context_gl.h" + +class NPDevice; + +class ContextGLNacl : public ContextGL { + + enum { + COMMAND_BUFFER_SIZE = 1024 * 1024, + }; + + NPDevice* device3d_; + +public: + + virtual void make_current(); + + virtual int get_window_width(); + virtual int get_window_height(); + virtual void swap_buffers(); + + virtual Error initialize(); + + ContextGLNacl(); + ~ContextGLNacl(); +}; + +#endif // CONTEXT_GL_NACL_H diff --git a/platform/nacl/detect.py b/platform/nacl/detect.py new file mode 100644 index 0000000000..f8849cfd25 --- /dev/null +++ b/platform/nacl/detect.py @@ -0,0 +1,71 @@ +import os +import sys + +def is_active(): + return True + +def get_name(): + return "NaCl" + +def can_build(): + + import os + if not os.environ.has_key("NACLPATH"): + return False + return True + +def get_opts(): + + return [ + ('NACLPATH', 'the path to nacl', os.environ.get("NACLPATH", 0)), + ('nacl_arch', 'The architecture for Nacl build (can be i686 or x86_64', 'i686'), + ] + +def get_flags(): + + return [ + ('nedmalloc', 'no'), + ('tools', 'no'), + ] + + + +def configure(env): + + env.Append(CPPPATH=['#platform/nacl']) + + env['OBJSUFFIX'] = ".nacl.${nacl_arch}.o" + env['LIBSUFFIX'] = ".nacl.${nacl_arch}.a" + env['PROGSUFFIX'] = ".${nacl_arch}.nexe" + + env['ENV']['PATH'] = env['ENV']['PATH']+":"+env['NACLPATH']+"/toolchain/linux_x86_newlib/bin" + + env['CC'] = '${nacl_arch}-nacl-gcc' + env['CXX'] = '${nacl_arch}-nacl-g++' + env['AR'] = '${nacl_arch}-nacl-ar' + + env.Append(CCFLAGS=['-fexceptions', '-Wno-long-long', '-pthread', '-DXP_UNIX']) + + env.Append(CPPPATH=env['NACLPATH']) + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['-O2','-ffast-math','-fomit-frame-pointer', '-ffunction-sections', '-fdata-sections', '-fno-default-inline']) + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-g', '-O0', '-Wall','-DDEBUG_ENABLED']) + + + elif (env["target"]=="profile"): + + env.Append(CCFLAGS=['-g','-pg']) + env.Append(LINKFLAGS=['-pg']) + + env.Append(CCFLAGS=['-DNACL_ENABLED', '-DGLES2_ENABLED']) + + env.Append(LIBFLAGS=['m32']) + env.Append(LIBS=env.Split('ppapi ppapi_cpp pthread srpc ppapi_gles22')) + + import methods + env.Append( BUILDERS = { 'GLSL120GLES' : env.Builder(action = methods.build_gles2_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) diff --git a/platform/nacl/geturl_handler.cpp b/platform/nacl/geturl_handler.cpp new file mode 100644 index 0000000000..4873d691ce --- /dev/null +++ b/platform/nacl/geturl_handler.cpp @@ -0,0 +1,150 @@ +/*************************************************************************/ +/* geturl_handler.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 "geturl_handler.h" + +#include "core/os/copymem.h" + +#include <stdio.h> +#include <stdlib.h> +#include "ppapi/c/pp_errors.h" +#include "ppapi/c/ppb_instance.h" +#include "ppapi/cpp/module.h" +#include "ppapi/cpp/var.h" + +void GetURLHandler::Start() { + pp::CompletionCallback cc = + cc_factory_.NewCallback(&GetURLHandler::OnOpen); + url_loader_.Open(url_request_, cc); +} + +void GetURLHandler::OnOpen(int32_t result) { + if (result != PP_OK) { + status = STATUS_ERROR; + return; + } + // Here you would process the headers. A real program would want to at least + // check the HTTP code and potentially cancel the request. + // pp::URLResponseInfo response = loader_.GetResponseInfo(); + + // Start streaming. + ReadBody(); +} + +void GetURLHandler::AppendDataBytes(const char* buffer, int32_t num_bytes) { + if (num_bytes <= 0) + return; + // Make sure we don't get a buffer overrun. + num_bytes = std::min(READ_BUFFER_SIZE, num_bytes); + int ofs = data.size(); + data.resize(ofs + num_bytes); + copymem(&data[ofs], buffer, num_bytes); +} + +void GetURLHandler::OnRead(int32_t result) { + if (result == PP_OK) { + // Streaming the file is complete. + status = STATUS_COMPLETED; + instance_->HandleMessage("package_finished"); + instance_->HandleMessage(0); + printf("completed!\n"); + } else if (result > 0) { + // The URLLoader just filled "result" number of bytes into our buffer. + // Save them and perform another read. + AppendDataBytes(buffer_, result); + ReadBody(); + } else { + // A read error occurred. + status = STATUS_ERROR; + ERR_FAIL_COND(result < 0); + } +} + +void GetURLHandler::ReadBody() { + // Note that you specifically want an "optional" callback here. This will + // allow ReadBody() to return synchronously, ignoring your completion + // callback, if data is available. For fast connections and large files, + // reading as fast as we can will make a large performance difference + // However, in the case of a synchronous return, we need to be sure to run + // the callback we created since the loader won't do anything with it. + pp::CompletionCallback cc = + cc_factory_.NewOptionalCallback(&GetURLHandler::OnRead); + int32_t result = PP_OK; + do { + result = url_loader_.ReadResponseBody(buffer_, sizeof(buffer_), cc); + // Handle streaming data directly. Note that we *don't* want to call + // OnRead here, since in the case of result > 0 it will schedule + // another call to this function. If the network is very fast, we could + // end up with a deeply recursive stack. + if (result > 0) { + AppendDataBytes(buffer_, result); + } + } while (result > 0); + + if (result != PP_OK_COMPLETIONPENDING) { + // Either we reached the end of the stream (result == PP_OK) or there was + // an error. We want OnRead to get called no matter what to handle + // that case, whether the error is synchronous or asynchronous. If the + // result code *is* COMPLETIONPENDING, our callback will be called + // asynchronously. + cc.Run(result); + } +} + +GetURLHandler::Status GetURLHandler::get_status() const { + + return status; +}; + +Vector<uint8_t> GetURLHandler::get_data() const { + + return data; +}; + +int GetURLHandler::get_bytes_read() const { + + return data.size(); +}; + +GetURLHandler::GetURLHandler(pp::Instance* instance, + const String& url) + : instance_(instance), + url_(url), + url_request_(instance), + url_loader_(instance), + cc_factory_(this) { + url_request_.SetURL(std::string(url.utf8().get_data())); + url_request_.SetMethod("GET"); + status = STATUS_NONE; + printf("url handler for url %ls!\n", url.c_str()); +} + +GetURLHandler::~GetURLHandler() { +} + + diff --git a/platform/nacl/geturl_handler.h b/platform/nacl/geturl_handler.h new file mode 100644 index 0000000000..44086ae7c1 --- /dev/null +++ b/platform/nacl/geturl_handler.h @@ -0,0 +1,115 @@ +/*************************************************************************/ +/* geturl_handler.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 EXAMPLES_GETURL_GETURL_HANDLER_H_ +#define EXAMPLES_GETURL_GETURL_HANDLER_H_ + +#include "core/ustring.h" +#include "core/vector.h" +#include "ppapi/cpp/completion_callback.h" +#include "ppapi/cpp/url_loader.h" +#include "ppapi/cpp/url_request_info.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/utility/completion_callback_factory.h" + +#define READ_BUFFER_SIZE 32768 + +// GetURLHandler is used to download data from |url|. When download is +// finished or when an error occurs, it posts a message back to the browser +// with the results encoded in the message as a string and self-destroys. +// +// EXAMPLE USAGE: +// GetURLHandler* handler* = GetURLHandler::Create(instance,url); +// handler->Start(); +// +class GetURLHandler { + +public: + + enum Status { + + STATUS_NONE, + STATUS_IN_PROGRESS, + STATUS_COMPLETED, + STATUS_ERROR, + }; + +private: + + Status status; + + // Callback fo the pp::URLLoader::Open(). + // Called by pp::URLLoader when response headers are received or when an + // error occurs (in response to the call of pp::URLLoader::Open()). + // Look at <ppapi/c/ppb_url_loader.h> and + // <ppapi/cpp/url_loader.h> for more information about pp::URLLoader. + void OnOpen(int32_t result); + + // Callback fo the pp::URLLoader::ReadResponseBody(). + // |result| contains the number of bytes read or an error code. + // Appends data from this->buffer_ to this->url_response_body_. + void OnRead(int32_t result); + + // Reads the response body (asynchronously) into this->buffer_. + // OnRead() will be called when bytes are received or when an error occurs. + void ReadBody(); + + // Append data bytes read from the URL onto the internal buffer. Does + // nothing if |num_bytes| is 0. + void AppendDataBytes(const char* buffer, int32_t num_bytes); + + pp::Instance* instance_; // Weak pointer. + String url_; // URL to be downloaded. + pp::URLRequestInfo url_request_; + pp::URLLoader url_loader_; // URLLoader provides an API to download URLs. + char buffer_[READ_BUFFER_SIZE]; // Temporary buffer for reads. + Vector<uint8_t> data; // Contains accumulated downloaded data. + pp::CompletionCallbackFactory<GetURLHandler> cc_factory_; + bool complete; + + GetURLHandler(const GetURLHandler&); + void operator=(const GetURLHandler&); + +public: + // Creates instance of GetURLHandler on the heap. + // GetURLHandler objects shall be created only on the heap (they + // self-destroy when all data is in). + // Initiates page (URL) download. + void Start(); + + Status get_status() const; + Vector<uint8_t> get_data() const; + + int get_bytes_read() const; + + GetURLHandler(pp::Instance* instance_, const String& url); + ~GetURLHandler(); +}; + +#endif // EXAMPLES_GETURL_GETURL_HANDLER_H_ + diff --git a/platform/nacl/godot_module.cpp b/platform/nacl/godot_module.cpp new file mode 100644 index 0000000000..b5a049d9bf --- /dev/null +++ b/platform/nacl/godot_module.cpp @@ -0,0 +1,332 @@ +/*************************************************************************/ +/* godot_module.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 "opengl_context.h" + +#include <cstdlib> +#include <cstring> +#include <string> +#include <vector> + +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/module.h" +#include "ppapi/gles2/gl2ext_ppapi.h" + +#include "ppapi/cpp/rect.h" +#include "ppapi/cpp/size.h" +#include "ppapi/cpp/var.h" +#include "geturl_handler.h" + +#include "core/variant.h" +#include "os_nacl.h" + +extern int nacl_main(int argc, const char** argn, const char** argv); +extern void nacl_cleanup(); + +static String pkg_url; + +pp::Instance* godot_instance = NULL; + +struct StateData { + int arg_count; + Array args; + String method; +}; + +extern OSNacl* os_nacl; + +class GodotInstance : public pp::Instance { + + enum State { + STATE_METHOD, + STATE_PARAM_COUNT, + STATE_PARAMS, + STATE_CALL, + }; + + State state; + StateData* sd; + SharedOpenGLContext opengl_context_; + int width; + int height; + + #define MAX_ARGS 64 + uint32_t init_argc; + char* init_argn[MAX_ARGS]; + char* init_argv[MAX_ARGS]; + + bool package_loaded; + GetURLHandler* package_pending; + +public: + explicit GodotInstance(PP_Instance instance) : pp::Instance(instance) { + printf("GodotInstance!\n"); + state = STATE_METHOD; + RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_KEYBOARD | PP_INPUTEVENT_CLASS_WHEEL | PP_INPUTEVENT_CLASS_TOUCH); + sd = NULL; + package_pending = NULL; + package_loaded = false; + godot_instance = this; + } + virtual ~GodotInstance() { + + nacl_cleanup(); + } + + /// Called by the browser to handle the postMessage() call in Javascript. + /// Detects which method is being called from the message contents, and + /// calls the appropriate function. Posts the result back to the browser + /// asynchronously. + /// @param[in] var_message The message posted by the browser. The possible + /// messages are 'fortyTwo' and 'reverseText:Hello World'. Note that + /// the 'reverseText' form contains the string to reverse following a ':' + /// separator. + virtual void HandleMessage(const pp::Var& var_message); + + bool HandleInputEvent(const pp::InputEvent& event); + + bool Init(uint32_t argc, const char* argn[], const char* argv[]) { + + printf("******* init! %i, %p, %p\n", argc, argn, argv); + fflush(stdout); + if (opengl_context_ == NULL) { + opengl_context_.reset(new OpenGLContext(this)); + }; + opengl_context_->InvalidateContext(this); + opengl_context_->ResizeContext(pp::Size(0, 0)); + int current = opengl_context_->MakeContextCurrent(this); + printf("current is %i\n", current); + + os_nacl = new OSNacl; + + pkg_url = ""; + for (uint32_t i=0; i<argc; i++) { + if (strcmp(argn[i], "package") == 0) { + pkg_url = argv[i]; + }; + }; + + sd = memnew(StateData); + + if (pkg_url == "") { + nacl_main(argc, argn, argv); + } else { + printf("starting package %ls\n", pkg_url.c_str()); + init_argc = MIN(argc, MAX_ARGS-1); + for (uint32_t i=0; i<argc; i++) { + + int nlen = strlen(argn[i]); + init_argn[i] = (char*)memalloc(nlen+1); + strcpy(init_argn[i], argn[i]); + init_argn[i+1] = NULL; + + int len = strlen(argv[i]); + init_argv[i] = (char*)memalloc(len+1); + strcpy(init_argv[i], argv[i]); + init_argv[i+1] = NULL; + }; + package_pending = memnew(GetURLHandler(this, pkg_url)); + package_pending->Start(); + }; + return true; + }; + + // Called whenever the in-browser window changes size. + virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) { + + if (position.size().width() == width && + position.size().height() == height) + return; // Size didn't change, no need to update anything. + + if (opengl_context_ == NULL) { + opengl_context_.reset(new OpenGLContext(this)); + }; + opengl_context_->InvalidateContext(this); + opengl_context_->ResizeContext(position.size()); + if (!opengl_context_->MakeContextCurrent(this)) + return; + + width = position.size().width(); + height = position.size().height(); + // init gl here? + OS::VideoMode vm; + vm.width = width; + vm.height = height; + vm.resizable = false; + vm.fullscreen = true; + OS::get_singleton()->set_video_mode(vm, 0); + + DrawSelf(); + }; + + // Called to draw the contents of the module's browser area. + void DrawSelf() { + + if (opengl_context_ == NULL) + return; + + opengl_context_->FlushContext(); + }; +}; + +static Variant to_variant(const pp::Var& p_var) { + + if (p_var.is_undefined() || p_var.is_null()) + return Variant(); + if (p_var.is_bool()) + return Variant(p_var.AsBool()); + if (p_var.is_double()) + return Variant(p_var.AsDouble()); + if (p_var.is_int()) + return Variant((int64_t)p_var.AsInt()); + if (p_var.is_string()) + return Variant(String::utf8(p_var.AsString().c_str())); + + return Variant(); +}; + +void GodotInstance::HandleMessage(const pp::Var& var_message) { + + switch (state) { + + case STATE_METHOD: { + + ERR_FAIL_COND(!var_message.is_string()); + sd->method = var_message.AsString().c_str(); + state = STATE_PARAM_COUNT; + } break; + case STATE_PARAM_COUNT: { + + ERR_FAIL_COND(!var_message.is_number()); + sd->arg_count = var_message.AsInt(); + state = sd->arg_count>0?STATE_PARAMS:STATE_CALL; + + } break; + case STATE_PARAMS: { + + Variant p = to_variant(var_message); + sd->args.push_back(p); + if (sd->args.size() >= sd->arg_count) + state = STATE_CALL; + } break; + default: + break; + }; + + if (state == STATE_CALL) { + + // call + state = STATE_METHOD; + + + if (sd->method == "package_finished") { + + GetURLHandler::Status status = package_pending->get_status(); + printf("status is %i, %i, %i\n", status, GetURLHandler::STATUS_ERROR, GetURLHandler::STATUS_COMPLETED); + if (status == GetURLHandler::STATUS_ERROR) { + printf("Error fetching package!\n"); + }; + if (status == GetURLHandler::STATUS_COMPLETED) { + + OSNacl* os = (OSNacl*)OS::get_singleton(); + os->add_package(pkg_url, package_pending->get_data()); + }; + memdelete(package_pending); + package_pending = NULL; + + package_loaded = true; + + opengl_context_->MakeContextCurrent(this); + nacl_main(init_argc, (const char**)init_argn, (const char**)init_argv); + for (uint32_t i=0; i<init_argc; i++) { + memfree(init_argn[i]); + memfree(init_argv[i]); + }; + }; + + if (sd->method == "get_package_status") { + + if (package_loaded) { + // post "loaded" + PostMessage("loaded"); + } else if (package_pending == NULL) { + // post "none" + PostMessage("none"); + } else { + // post package_pending->get_bytes_read(); + PostMessage(package_pending->get_bytes_read()); + }; + }; + }; +} + +bool GodotInstance::HandleInputEvent(const pp::InputEvent& event) { + + OSNacl* os = (OSNacl*)OS::get_singleton(); + os->handle_event(event); + return true; +}; + +class GodotModule : public pp::Module { + public: + GodotModule() : pp::Module() {} + virtual ~GodotModule() { + glTerminatePPAPI(); + } + + /// Create and return a GodotInstance object. + /// @param[in] instance a handle to a plug-in instance. + /// @return a newly created GodotInstance. + /// @note The browser is responsible for calling @a delete when done. + virtual pp::Instance* CreateInstance(PP_Instance instance) { + printf("CreateInstance! %x\n", instance); + return new GodotInstance(instance); + } + + /// Called by the browser when the module is first loaded and ready to run. + /// This is called once per module, not once per instance of the module on + /// the page. + virtual bool Init() { + printf("GodotModule::init!\n"); + return glInitializePPAPI(get_browser_interface()); + } +}; + +namespace pp { +/// Factory function called by the browser when the module is first loaded. +/// The browser keeps a singleton of this module. It calls the +/// CreateInstance() method on the object you return to make instances. There +/// is one instance per <embed> tag on the page. This is the main binding +/// point for your NaCl module with the browser. +/// @return new GodotModule. +/// @note The browser is responsible for deleting returned @a Module. +Module* CreateModule() { + printf("CreateModule!\n"); + return new GodotModule(); +} +} // namespace pp diff --git a/platform/nacl/godot_nacl.cpp b/platform/nacl/godot_nacl.cpp new file mode 100644 index 0000000000..753f4124f8 --- /dev/null +++ b/platform/nacl/godot_nacl.cpp @@ -0,0 +1,80 @@ +/*************************************************************************/ +/* godot_nacl.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 "os_nacl.h" +#include "main/main.h" + +#include <stdio.h> +#include <string.h> + +OSNacl* os_nacl = NULL; + +int nacl_main(int argc, const char** argn, const char** argv) { + + // os is created in GodotModule::Init for the nacl module + printf("called with %i args, %p, %p\n", argc, argn, argv); + char* nargv[64]; + int nargc = 1; + nargv[0] = (char*)argv[0]; + for (int i=1; i<argc; i++) { + + printf("arg %i is %s, %s\n", i, argn[i], argv[i]); + if (strncmp(argn[i], "arg", 3) == 0) { + + printf("using arg %i, %s\n", nargc, argv[i]); + nargv[nargc++] = (char*)argv[i]; + }; + }; + printf("total %i\n", nargc); + nargv[nargc] = 0; + + printf("godot_nacl\n"); + for (int i=0; i<argc; i++) { + printf("arg %i: %s\n", i, argv[i]); + }; + + printf("os created\n"); + Error err = Main::setup("", nargc-1, &nargv[1]); + printf("setup %i\n", err); + if (err!=OK) + return 255; + + printf("calling start\n"); + Main::start(); + + return 0; +}; + + +void nacl_cleanup() { + + Main::cleanup(); + delete os_nacl; +}; + + diff --git a/platform/nacl/html/check_browser.js b/platform/nacl/html/check_browser.js new file mode 100644 index 0000000000..9636ad0384 --- /dev/null +++ b/platform/nacl/html/check_browser.js @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2011 The Native Client Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/** + * @fileoverview This file provides a BrowserChecker Javascript class. + * Users can create a BrowserChecker object, invoke checkBrowser(|version|), + * and then use getIsValidBrowser() and getBrowserSupportStatus() + * to determine if the browser version is greater than |version| + * and if the Native Client plugin is found. + */ + +// Create a namespace object +var browser_version = browser_version || {}; + +/** + * Class to provide checking for version and NativeClient. + * @param {integer} arg1 An argument that indicates major version of Chrome we + * require, such as 14. + */ + +/** + * Constructor for the BrowserChecker. Sets the major version of + * Chrome that is required to |minChromeVersion|. + * @param minChromeVersion The earliest major version of chrome that + * is supported. If the Chrome browser version is less than + * |minChromeVersion| then |isValidBrowswer| will be set to false. + * @param opt_maxChromeVersion Ignored. Retained for backwards compatibility. + * @param appVersion The application version string. + * @param plugins The plugins that exist in the browser. + * @constructor + */ +browser_version.BrowserChecker = function(minChromeVersion, + appVersion, plugins, + opt_maxChromeVersion) { + /** + * Version specified by the user. This class looks to see if the browser + * version is >= |minChromeVersion_|. + * @type {integer} + * @private + */ + this.minChromeVersion_ = minChromeVersion; + + /** + * List of Browser plugin objects. + * @type {Ojbect array} + * @private + */ + this.plugins_ = plugins; + + /** + * Application version string from the Browser. + * @type {integer} + * @private + */ + this.appVersion_ = appVersion; + + /** + * Flag used to indicate if the browser has Native Client and is if the + * browser version is recent enough. + * @type {boolean} + * @private + */ + this.isValidBrowser_ = false; + + /** + * Actual major version of Chrome -- found by querying the browser. + * @type {integer} + * @private + */ + this.chromeVersion_ = null; + + /** + * Browser support status. This allows the user to get a detailed status + * rather than using this.browserSupportMessage. + */ + this.browserSupportStatus_ = + browser_version.BrowserChecker.StatusValues.UNKNOWN; +} + +/** + * The values used for BrowserChecker status to indicate success or + * a specific error. + * @enum {id} + */ +browser_version.BrowserChecker.StatusValues = { + UNKNOWN: 0, + NACL_ENABLED: 1, + UNKNOWN_BROWSER: 2, + CHROME_VERSION_TOO_OLD: 3, + NACL_NOT_ENABLED: 4, + NOT_USING_SERVER: 5 +}; + +/** + * Determines if the plugin with name |name| exists in the browser. + * @param {string} name The name of the plugin. + * @param {Object array} plugins The plugins in this browser. + * @return {bool} |true| if the plugin is found. + */ +browser_version.BrowserChecker.prototype.pluginExists = function(name, + plugins) { + for (var index=0; index < plugins.length; index++) { + var plugin = this.plugins_[index]; + var plugin_name = plugin['name']; + // If the plugin is not found, you can use the Javascript console + // to see the names of the plugins that were found when debugging. + if (plugin_name.indexOf(name) != -1) { + return true; + } + } + return false; +} + +/** + * Returns browserSupportStatus_ which indicates if the browser supports + * Native Client. Values are defined as literals in + * browser_version.BrowserChecker.StatusValues. + * @ return {int} Level of NaCl support. + */ +browser_version.BrowserChecker.prototype.getBrowserSupportStatus = function() { + return this.browserSupportStatus_; +} + +/** + * Returns isValidBrowser (true/false) to indicate if the browser supports + * Native Client. + * @ return {bool} If this browser has NativeClient and correct version. + */ +browser_version.BrowserChecker.prototype.getIsValidBrowser = function() { + return this.isValidBrowser_; +} + +/** + * Checks to see if this browser can support Native Client applications. + * For Chrome browsers, checks to see if the "Native Client" plugin is + * enabled. + */ +browser_version.BrowserChecker.prototype.checkBrowser = function() { + var versionPatt = /Chrome\/(\d+)\.(\d+)\.(\d+)\.(\d+)/; + var result = this.appVersion_.match(versionPatt); + + // |result| stores the Chrome version number. + if (!result) { + this.isValidBrowser_ = false; + this.browserSupportStatus_ = + browser_version.BrowserChecker.StatusValues.UNKNOWN_BROWSER; + } else { + this.chromeVersion_ = result[1]; + // We know we have Chrome, check version and/or plugin named Native Client + if (this.chromeVersion_ >= this.minChromeVersion_) { + var found_nacl = this.pluginExists('Native Client', this.plugins_); + if (found_nacl) { + this.isValidBrowser_ = true; + this.browserSupportStatus_ = + browser_version.BrowserChecker.StatusValues.NACL_ENABLED; + } else { + this.isValidBrowser_ = false; + this.browserSupportStatus_ = + browser_version.BrowserChecker.StatusValues.NACL_NOT_ENABLED; + } + } else { + // We are in a version that is less than |minChromeVersion_| + this.isValidBrowser_ = false; + this.browserSupportStatus_ = + browser_version.BrowserChecker.StatusValues.CHROME_VERSION_TOO_OLD; + } + } + var my_protocol = window.location.protocol; + if (my_protocol.indexOf('file') == 0) { + this.isValidBrowser_ = false; + this.browserSupportStatus_ = + browser_version.BrowserChecker.StatusValues.NOT_USING_SERVER; + } +} + diff --git a/platform/nacl/html/godot_nacl.nmf b/platform/nacl/html/godot_nacl.nmf new file mode 100644 index 0000000000..eca039ec1e --- /dev/null +++ b/platform/nacl/html/godot_nacl.nmf @@ -0,0 +1,6 @@ +{ + "program": { + "x86-64": {"url": "godot_nacl.x86_64.nexe"}, + "x86-32": {"url": "godot_nacl.i686.nexe"} + } +} diff --git a/platform/nacl/html/icon_128.png b/platform/nacl/html/icon_128.png Binary files differnew file mode 100644 index 0000000000..1793aa7e7a --- /dev/null +++ b/platform/nacl/html/icon_128.png diff --git a/platform/nacl/html/icon_16.png b/platform/nacl/html/icon_16.png Binary files differnew file mode 100644 index 0000000000..09de19e418 --- /dev/null +++ b/platform/nacl/html/icon_16.png diff --git a/platform/nacl/html/index.html b/platform/nacl/html/index.html new file mode 100644 index 0000000000..e8be6f69b9 --- /dev/null +++ b/platform/nacl/html/index.html @@ -0,0 +1,258 @@ +<!DOCTYPE html> +<html> + <!-- + Copyright (c) 2011 The Native Client Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. + --> +<head> + <title>Load Progress Example</title> + <script type="text/javascript" src="check_browser.js"></script> + <script type="text/javascript"> + // Check for Native Client support in the browser before the DOM loads. + var isValidBrowser = false; + var browserSupportStatus = 0; + var checker = new browser_version.BrowserChecker( + 14, // Minumum Chrome version. + navigator["appVersion"], + navigator["plugins"]); + checker.checkBrowser(); + + loadProgressModule = null; // Global application object. + statusText = 'NO-STATUS'; + + isValidBrowser = checker.getIsValidBrowser(); + browserSupportStatus = checker.getBrowserSupportStatus(); + + // Handler that gets called when the NaCl module starts loading. This + // event is always triggered when an <EMBED> tag has a MIME type of + // application/x-nacl. + function moduleDidStartLoad() { + appendToEventLog('loadstart'); + } + + // Progress event handler. |event| contains a couple of interesting + // properties that are used in this example: + // total The size of the NaCl module in bytes. Note that this value + // is 0 until |lengthComputable| is true. In particular, this + // value is 0 for the first 'progress' event. + // loaded The number of bytes loaded so far. + // lengthComputable A boolean indicating that the |total| field + // represents a valid length. + // + // event The ProgressEvent that triggered this handler. + function moduleLoadProgress(event) { + var loadPercent = 0.0; + var loadPercentString; + if (event.lengthComputable && event.total > 0) { + loadPercent = event.loaded / event.total * 100.0; + loadPercentString = loadPercent + '%'; + } else { + // The total length is not yet known. + loadPercent = -1.0; + loadPercentString = 'Computing...'; + } + //appendToEventLog('progress: ' + loadPercentString + ' (' + event.loaded + ' of ' + event.total + ' bytes)'); + updateStatus("Loading module : "+ Math.floor(event.loaded / event.total * 100.0) + "% complete"); + } + + // Handler that gets called if an error occurred while loading the NaCl + // module. Note that the event does not carry any meaningful data about + // the error, you have to check lastError on the <EMBED> element to find + // out what happened. + function moduleLoadError() { + appendToEventLog('error: ' + loadProgressModule.lastError); + } + + // Handler that gets called if the NaCl module load is aborted. + function moduleLoadAbort() { + appendToEventLog('abort'); + } + + // When the NaCl module has loaded indicate success. + function moduleDidLoad() { + loadProgressModule = document.getElementById('load_progress'); + appendToEventLog('load'); + updateStatus('SUCCESS'); + } + + function check_dl_status() { + var module = document.getElementById('load_progress'); + module.postMessage("get_package_status"); + module.postMessage(0); + }; + + // Handler that gets called when the NaCl module loading has completed. + // You will always get one of these events, regardless of whether the NaCl + // module loaded successfully or not. For example, if there is an error + // during load, you will get an 'error' event and a 'loadend' event. Note + // that if the NaCl module loads successfully, you will get both a 'load' + // event and a 'loadend' event. + function moduleDidEndLoad() { + appendToEventLog('loadend'); + var lastError = event.target.lastError; + if (lastError == undefined || lastError.length == 0) { + lastError = '<none>'; + window.setTimeout(check_dl_status, 1000); + } + appendToEventLog('lastError: ' + lastError); + } + + + // Handle a message coming from the NaCl module. + function handleMessage(message_event) { + var data = message_event.data; + if (data == "loaded") { + updateStatus("Data Loaded!"); + } else if (data == "none") { + // nothing + updateStatus("Downloading package: connecting"); + window.setTimeout(check_dl_status, 1000); + } else { + updateStatus("Downloading package: " + data + " bytes"); + window.setTimeout(check_dl_status, 1000); + }; + } + + // Set the global status message. Updates the 'status_field' element with + // the new text. + // opt_message The message text. If this is null or undefined, then + // attempt to set the element with id 'status_field' to the value of + // |statusText|. + function updateStatus(opt_message) { + if (opt_message) + statusText = opt_message; + var statusField = document.getElementById('status_field'); + if (statusField) { + statusField.innerHTML = statusText; + } + } + + // Append an event name to the 'event_log_field' element. Event names + // are separated by a <br> tag so they get listed one per line. + // logMessage The message to append to the log. + function appendToEventLog(logMessage) { + var eventLogField = document.getElementById('event_log_field'); + if (eventLogField.innerHTML.length == 0) { + eventLogField.innerHTML = logMessage; + } else { + eventLogField.innerHTML = eventLogField.innerHTML + + '<br />' + + logMessage; + } + } + + </script> +</head> +<body bgcolor=#000000> + +<font color=#FFFFFF> + +<h1>Native Client Load Event Example</h1> + +<h2>Status</h2> +<div id="status_field">NO-STATUS</div> + +<div id="listener"> + <script type="text/javascript"> + var listener = document.getElementById('listener'); + listener.addEventListener('loadstart', moduleDidStartLoad, true); + listener.addEventListener('progress', moduleLoadProgress, true); + listener.addEventListener('error', moduleLoadError, true); + listener.addEventListener('abort', moduleLoadAbort, true); + listener.addEventListener('load', moduleDidLoad, true); + listener.addEventListener('loadend', moduleDidEndLoad, true); + listener.addEventListener('message', handleMessage, true); + + switch (browserSupportStatus) { + case browser_version.BrowserChecker.StatusValues.NACL_ENABLED: + appendToEventLog('Native Client plugin enabled.'); + break; + case browser_version.BrowserChecker.StatusValues.UNKNOWN_BROWSER: + updateStatus('UNKNOWN BROWSER'); + break; + case browser_version.BrowserChecker.StatusValues.CHROME_VERSION_TOO_OLD: + appendToEventLog( + 'Chrome too old: You must use Chrome version 14 or later.'); + updateStatus('NEED CHROME 14 OR LATER'); + break; + case browser_version.BrowserChecker.StatusValues.NACL_NOT_ENABLED: + appendToEventLog( + 'NaCl disabled: Native Client is not enabled.<br>' + + 'Please go to <b>chrome://plugins</b> and enable Native Client ' + + 'plugin.'); + updateStatus('NaCl NOT ENABLED'); + break; + case browser_version.BrowserChecker.StatusValues.NOT_USING_SERVER: + appendToEventLog( + 'file: URL detected, please use a web server to host Native ' + + 'Client applications.'); + updateStatus('NaCl NOT ENABLED'); + default: + appendToEventLog('Unknown error: Unable to detect browser and/or ' + + 'Native Client support.'); + updateStatus('UNKNOWN ERROR'); + break; + } + </script> + + <!-- Load the published .nexe. This includes the 'src' attribute which + shows how to load multi-architecture modules. Each entry in the "nexes" + object in the .nmf manifest file is a key-value pair: the key is the runtime + ('x86-32', 'x86-64', etc.); the value is a URL for the desired NaCl module. + To load the debug versions of your .nexes, set the 'src' attribute to the + _dbg.nmf version of the manifest file. + + Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load' + and a 'message' event listener attached. This wrapping method is used + instead of attaching the event listeners directly to the <EMBED> element to + ensure that the listeners are active before the NaCl module 'load' event + fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or + pp::Instance.PostMessage() (in C++) from within the initialization code in + your NaCl module. + --> + <embed name="nacl_module" + id="load_progress" + width=1024 height=600 + src="godot_nacl.nmf" + type="application/x-nacl" + package="data.pcz" + arg1="-path" arg2="." + no_arg1="-test" no_arg2="gui" /> + + <script type="text/javascript"> + loadProgressModule = document.getElementById('load_progress'); + // Futher diagnose NaCl loading. + if (loadProgressModule == null || + typeof loadProgressModule.readyState == 'undefined') { + switch (browserSupportStatus) { + case browser_version.BrowserChecker.StatusValues.NACL_ENABLED: + // The NaCl plugin is enabled and running, it's likely that the flag + // isn't set. + appendToEventLog( + 'NaCl flag disabled: The Native Client flag is not enabled.<br>' + + 'Please go to <b>chrome://flags</b> enable Native Client and ' + + 'relaunch your browser. See also: ' + + '<a href="http://code.google.com/chrome/nativeclient/docs/' + + 'running.html">Running Web Applications that Use Native Client' + + '</a>'); + updateStatus('NaCl NOT ENABLED'); + break; + case browser_version.BrowserChecker.StatusValues.UNKNOWN_BROWSER: + appendToEventLog('Native Client applications are not supported by ' + + 'this browser.'); + break; + default: + appendToEventLog('Unknown error when loading Native Client ' + + 'application.'); + } + } + </script> +</div> + +<h2>Event Log</h2> +<div id="event_log_field">event log?</div> + +</body> +</html> + diff --git a/platform/nacl/html/manifest.json b/platform/nacl/html/manifest.json new file mode 100644 index 0000000000..6e271507a9 --- /dev/null +++ b/platform/nacl/html/manifest.json @@ -0,0 +1,19 @@ +{ + "name": "Capsuleman's Castle Adventure", + "description": "Capsuleman's Castle Adventure", + "version": "1", + "app": { + "launch": { + "local_path": "index.html" + } + }, + "icons": { + "16": "icon_16.png", + "128": "icon_128.png" + }, + "permissions": [ + "unlimitedStorage", + "notifications", + "experimental" + ] +} diff --git a/platform/nacl/logo.png b/platform/nacl/logo.png Binary files differnew file mode 100644 index 0000000000..aac72c01b2 --- /dev/null +++ b/platform/nacl/logo.png diff --git a/platform/nacl/nacl_keycodes.h b/platform/nacl/nacl_keycodes.h new file mode 100644 index 0000000000..1cbf379078 --- /dev/null +++ b/platform/nacl/nacl_keycodes.h @@ -0,0 +1,422 @@ +/*************************************************************************/ +/* nacl_keycodes.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) 2006 Michael Emmel mike.emmel@gmail.com. All rights reserved. + * Copyright (C) 2008, 2009 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF USE, DATA, OR + * PROFITS, OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_KEYBOARD_CODES_POSIX_H_ +#define BASE_KEYBOARD_CODES_POSIX_H_ +#pragma once + +#include "core/os/keyboard.h" + +enum { + VKEY_BACK = 0x08, + VKEY_TAB = 0x09, + VKEY_CLEAR = 0x0C, + VKEY_RETURN = 0x0D, + VKEY_SHIFT = 0x10, + VKEY_CONTROL = 0x11, + VKEY_MENU = 0x12, + VKEY_PAUSE = 0x13, + VKEY_CAPITAL = 0x14, + VKEY_KANA = 0x15, + VKEY_HANGUL = 0x15, + VKEY_JUNJA = 0x17, + VKEY_FINAL = 0x18, + VKEY_HANJA = 0x19, + VKEY_KANJI = 0x19, + VKEY_ESCAPE = 0x1B, + VKEY_CONVERT = 0x1C, + VKEY_NONCONVERT = 0x1D, + VKEY_ACCEPT = 0x1E, + VKEY_MODECHANGE = 0x1F, + VKEY_SPACE = 0x20, + VKEY_PRIOR = 0x21, + VKEY_NEXT = 0x22, + VKEY_END = 0x23, + VKEY_HOME = 0x24, + VKEY_LEFT = 0x25, + VKEY_UP = 0x26, + VKEY_RIGHT = 0x27, + VKEY_DOWN = 0x28, + VKEY_SELECT = 0x29, + VKEY_PRINT = 0x2A, + VKEY_EXECUTE = 0x2B, + VKEY_SNAPSHOT = 0x2C, + VKEY_INSERT = 0x2D, + VKEY_DELETE = 0x2E, + VKEY_HELP = 0x2F, + VKEY_0 = 0x30, + VKEY_1 = 0x31, + VKEY_2 = 0x32, + VKEY_3 = 0x33, + VKEY_4 = 0x34, + VKEY_5 = 0x35, + VKEY_6 = 0x36, + VKEY_7 = 0x37, + VKEY_8 = 0x38, + VKEY_9 = 0x39, + VKEY_A = 0x41, + VKEY_B = 0x42, + VKEY_C = 0x43, + VKEY_D = 0x44, + VKEY_E = 0x45, + VKEY_F = 0x46, + VKEY_G = 0x47, + VKEY_H = 0x48, + VKEY_I = 0x49, + VKEY_J = 0x4A, + VKEY_K = 0x4B, + VKEY_L = 0x4C, + VKEY_M = 0x4D, + VKEY_N = 0x4E, + VKEY_O = 0x4F, + VKEY_P = 0x50, + VKEY_Q = 0x51, + VKEY_R = 0x52, + VKEY_S = 0x53, + VKEY_T = 0x54, + VKEY_U = 0x55, + VKEY_V = 0x56, + VKEY_W = 0x57, + VKEY_X = 0x58, + VKEY_Y = 0x59, + VKEY_Z = 0x5A, + VKEY_LWIN = 0x5B, + VKEY_COMMAND = VKEY_LWIN, // Provide the Mac name for convenience. + VKEY_RWIN = 0x5C, + VKEY_APPS = 0x5D, + VKEY_SLEEP = 0x5F, + VKEY_NUMPAD0 = 0x60, + VKEY_NUMPAD1 = 0x61, + VKEY_NUMPAD2 = 0x62, + VKEY_NUMPAD3 = 0x63, + VKEY_NUMPAD4 = 0x64, + VKEY_NUMPAD5 = 0x65, + VKEY_NUMPAD6 = 0x66, + VKEY_NUMPAD7 = 0x67, + VKEY_NUMPAD8 = 0x68, + VKEY_NUMPAD9 = 0x69, + VKEY_MULTIPLY = 0x6A, + VKEY_ADD = 0x6B, + VKEY_SEPARATOR = 0x6C, + VKEY_SUBTRACT = 0x6D, + VKEY_DECIMAL = 0x6E, + VKEY_DIVIDE = 0x6F, + VKEY_F1 = 0x70, + VKEY_F2 = 0x71, + VKEY_F3 = 0x72, + VKEY_F4 = 0x73, + VKEY_F5 = 0x74, + VKEY_F6 = 0x75, + VKEY_F7 = 0x76, + VKEY_F8 = 0x77, + VKEY_F9 = 0x78, + VKEY_F10 = 0x79, + VKEY_F11 = 0x7A, + VKEY_F12 = 0x7B, + VKEY_F13 = 0x7C, + VKEY_F14 = 0x7D, + VKEY_F15 = 0x7E, + VKEY_F16 = 0x7F, + VKEY_F17 = 0x80, + VKEY_F18 = 0x81, + VKEY_F19 = 0x82, + VKEY_F20 = 0x83, + VKEY_F21 = 0x84, + VKEY_F22 = 0x85, + VKEY_F23 = 0x86, + VKEY_F24 = 0x87, + VKEY_NUMLOCK = 0x90, + VKEY_SCROLL = 0x91, + VKEY_LSHIFT = 0xA0, + VKEY_RSHIFT = 0xA1, + VKEY_LCONTROL = 0xA2, + VKEY_RCONTROL = 0xA3, + VKEY_LMENU = 0xA4, + VKEY_RMENU = 0xA5, + VKEY_BROWSER_BACK = 0xA6, + VKEY_BROWSER_FORWARD = 0xA7, + VKEY_BROWSER_REFRESH = 0xA8, + VKEY_BROWSER_STOP = 0xA9, + VKEY_BROWSER_SEARCH = 0xAA, + VKEY_BROWSER_FAVORITES = 0xAB, + VKEY_BROWSER_HOME = 0xAC, + VKEY_VOLUME_MUTE = 0xAD, + VKEY_VOLUME_DOWN = 0xAE, + VKEY_VOLUME_UP = 0xAF, + VKEY_MEDIA_NEXT_TRACK = 0xB0, + VKEY_MEDIA_PREV_TRACK = 0xB1, + VKEY_MEDIA_STOP = 0xB2, + VKEY_MEDIA_PLAY_PAUSE = 0xB3, + VKEY_MEDIA_LAUNCH_MAIL = 0xB4, + VKEY_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5, + VKEY_MEDIA_LAUNCH_APP1 = 0xB6, + VKEY_MEDIA_LAUNCH_APP2 = 0xB7, + VKEY_OEM_1 = 0xBA, + VKEY_OEM_PLUS = 0xBB, + VKEY_OEM_COMMA = 0xBC, + VKEY_OEM_MINUS = 0xBD, + VKEY_OEM_PERIOD = 0xBE, + VKEY_OEM_2 = 0xBF, + VKEY_OEM_3 = 0xC0, + VKEY_OEM_4 = 0xDB, + VKEY_OEM_5 = 0xDC, + VKEY_OEM_6 = 0xDD, + VKEY_OEM_7 = 0xDE, + VKEY_OEM_8 = 0xDF, + VKEY_OEM_102 = 0xE2, + VKEY_PROCESSKEY = 0xE5, + VKEY_PACKET = 0xE7, + VKEY_ATTN = 0xF6, + VKEY_CRSEL = 0xF7, + VKEY_EXSEL = 0xF8, + VKEY_EREOF = 0xF9, + VKEY_PLAY = 0xFA, + VKEY_ZOOM = 0xFB, + VKEY_NONAME = 0xFC, + VKEY_PA1 = 0xFD, + VKEY_OEM_CLEAR = 0xFE, + VKEY_UNKNOWN = 0 +}; + +static uint32_t godot_key(uint32_t p_key, bool& is_char) { + + is_char = false; + + switch (p_key) { + + case VKEY_BACK: return KEY_BACKSPACE; + case VKEY_TAB: return KEY_TAB; + case VKEY_CLEAR: return KEY_CLEAR; + case VKEY_RETURN: return KEY_RETURN; + case VKEY_SHIFT: return KEY_SHIFT; + case VKEY_CONTROL: return KEY_CONTROL; + case VKEY_MENU: return KEY_MENU; + case VKEY_PAUSE: return KEY_PAUSE; +// case VKEY_CAPITAL: return KEY_CAPITAL; +// case VKEY_KANA: return KEY_KANA; +// case VKEY_HANGUL: return KEY_HANGUL; +// case VKEY_JUNJA: return KEY_JUNJA; +// case VKEY_FINAL: return KEY_FINAL; +// case VKEY_HANJA: return KEY_HANJA; +// case VKEY_KANJI: return KEY_KANJI; + case VKEY_ESCAPE: return KEY_ESCAPE; +// case VKEY_CONVERT: return KEY_CONVERT; +// case VKEY_NONCONVERT: return KEY_NONCONVERT; +// case VKEY_ACCEPT: return KEY_ACCEPT; +// case VKEY_MODECHANGE: return KEY_MODECHANGE; +// case VKEY_PRIOR: return KEY_PRIOR; +// case VKEY_NEXT: return KEY_NEXT; + case VKEY_END: return KEY_END; + case VKEY_HOME: return KEY_HOME; + case VKEY_LEFT: return KEY_LEFT; + case VKEY_UP: return KEY_UP; + case VKEY_RIGHT: return KEY_RIGHT; + case VKEY_DOWN: return KEY_DOWN; +// case VKEY_SELECT: return KEY_SELECT; + case VKEY_PRINT: return KEY_PRINT; +// case VKEY_EXECUTE: return KEY_EXECUTE; +// case VKEY_SNAPSHOT: return KEY_SNAPSHOT; + case VKEY_INSERT: return KEY_INSERT; + case VKEY_DELETE: return KEY_DELETE; + case VKEY_HELP: return KEY_HELP; +// case VKEY_LWIN: return KEY_LWIN; +// case VKEY_RWIN: return KEY_RWIN; +// case VKEY_APPS: return KEY_APPS; +// case VKEY_SLEEP: return KEY_SLEEP; + case VKEY_NUMPAD0: return KEY_KP_0; + case VKEY_NUMPAD1: return KEY_KP_1; + case VKEY_NUMPAD2: return KEY_KP_2; + case VKEY_NUMPAD3: return KEY_KP_3; + case VKEY_NUMPAD4: return KEY_KP_4; + case VKEY_NUMPAD5: return KEY_KP_5; + case VKEY_NUMPAD6: return KEY_KP_6; + case VKEY_NUMPAD7: return KEY_KP_7; + case VKEY_NUMPAD8: return KEY_KP_8; + case VKEY_NUMPAD9: return KEY_KP_9; + case VKEY_MULTIPLY: return KEY_KP_MULTIPLY; + case VKEY_ADD: return KEY_KP_ADD; +// case VKEY_SEPARATOR: return KEY_SEPARATOR; + case VKEY_SUBTRACT: return KEY_KP_SUBSTRACT; + case VKEY_DECIMAL: return KEY_KP_PERIOD; + case VKEY_DIVIDE: return KEY_KP_DIVIDE; + case VKEY_F1: return KEY_F1; + case VKEY_F2: return KEY_F2; + case VKEY_F3: return KEY_F3; + case VKEY_F4: return KEY_F4; + case VKEY_F5: return KEY_F5; + case VKEY_F6: return KEY_F6; + case VKEY_F7: return KEY_F7; + case VKEY_F8: return KEY_F8; + case VKEY_F9: return KEY_F9; + case VKEY_F10: return KEY_F10; + case VKEY_F11: return KEY_F11; + case VKEY_F12: return KEY_F12; + case VKEY_F13: return KEY_F13; + case VKEY_F14: return KEY_F14; + case VKEY_F15: return KEY_F15; + case VKEY_F16: return KEY_F16; + /* + case VKEY_F17: return KEY_F17; + case VKEY_F18: return KEY_F18; + case VKEY_F19: return KEY_F19; + case VKEY_F20: return KEY_F20; + case VKEY_F21: return KEY_F21; + case VKEY_F22: return KEY_F22; + case VKEY_F23: return KEY_F23; + case VKEY_F24: return KEY_F24; + */ + case VKEY_NUMLOCK: return KEY_NUMLOCK; + case VKEY_SCROLL: return KEY_SCROLLLOCK; + case VKEY_LSHIFT: return KEY_SHIFT; + case VKEY_RSHIFT: return KEY_SHIFT; + case VKEY_LCONTROL: return KEY_CONTROL; + case VKEY_RCONTROL: return KEY_CONTROL; + case VKEY_LMENU: return KEY_MENU; + case VKEY_RMENU: return KEY_MENU; + case VKEY_BROWSER_BACK: return KEY_BACK; + case VKEY_BROWSER_FORWARD: return KEY_FORWARD; + case VKEY_BROWSER_REFRESH: return KEY_REFRESH; + case VKEY_BROWSER_STOP: return KEY_STOP; + case VKEY_BROWSER_SEARCH: return KEY_SEARCH; + case VKEY_BROWSER_FAVORITES: return KEY_FAVORITES; + case VKEY_BROWSER_HOME: return KEY_HOMEPAGE; + case VKEY_VOLUME_MUTE: return KEY_VOLUMEMUTE; + case VKEY_VOLUME_DOWN: return KEY_VOLUMEDOWN; + case VKEY_VOLUME_UP: return KEY_VOLUMEUP; + case VKEY_MEDIA_NEXT_TRACK: return KEY_MEDIANEXT; + case VKEY_MEDIA_PREV_TRACK: return KEY_MEDIAPREVIOUS; + case VKEY_MEDIA_STOP: return KEY_MEDIASTOP; + case VKEY_MEDIA_PLAY_PAUSE: return KEY_MEDIAPLAY; + case VKEY_MEDIA_LAUNCH_MAIL: return KEY_LAUNCHMAIL; + case VKEY_MEDIA_LAUNCH_MEDIA_SELECT: return KEY_LAUNCHMEDIA; // FUCKING USELESS KEYS, HOW DO THEY WORK? + case VKEY_MEDIA_LAUNCH_APP1: return KEY_LAUNCH0; + case VKEY_MEDIA_LAUNCH_APP2: return KEY_LAUNCH0; +// case VKEY_OEM_102: return KEY_OEM_102; +// case VKEY_PROCESSKEY: return KEY_PROCESSKEY; +// case VKEY_PACKET: return KEY_PACKET; +// case VKEY_ATTN: return KEY_ATTN; +// case VKEY_CRSEL: return KEY_CRSEL; +// case VKEY_EXSEL: return KEY_EXSEL; +// case VKEY_EREOF: return KEY_EREOF; +// case VKEY_PLAY: return KEY_PLAY; +// case VKEY_ZOOM: return KEY_ZOOM; +// case VKEY_NONAME: return KEY_NONAME; +// case VKEY_PA1: return KEY_PA1; +// case VKEY_OEM_CLEAR: return KEY_OEM_CLEAR; + + default: break; + }; + + is_char = true; + + switch (p_key) { + + case VKEY_SPACE: return KEY_SPACE; + case VKEY_0: return KEY_0; + case VKEY_1: return KEY_1; + case VKEY_2: return KEY_2; + case VKEY_3: return KEY_3; + case VKEY_4: return KEY_4; + case VKEY_5: return KEY_5; + case VKEY_6: return KEY_6; + case VKEY_7: return KEY_7; + case VKEY_8: return KEY_8; + case VKEY_9: return KEY_9; + case VKEY_A: return KEY_A; + case VKEY_B: return KEY_B; + case VKEY_C: return KEY_C; + case VKEY_D: return KEY_D; + case VKEY_E: return KEY_E; + case VKEY_F: return KEY_F; + case VKEY_G: return KEY_G; + case VKEY_H: return KEY_H; + case VKEY_I: return KEY_I; + case VKEY_J: return KEY_J; + case VKEY_K: return KEY_K; + case VKEY_L: return KEY_L; + case VKEY_M: return KEY_M; + case VKEY_N: return KEY_N; + case VKEY_O: return KEY_O; + case VKEY_P: return KEY_P; + case VKEY_Q: return KEY_Q; + case VKEY_R: return KEY_R; + case VKEY_S: return KEY_S; + case VKEY_T: return KEY_T; + case VKEY_U: return KEY_U; + case VKEY_V: return KEY_V; + case VKEY_W: return KEY_W; + case VKEY_X: return KEY_X; + case VKEY_Y: return KEY_Y; + case VKEY_Z: return KEY_Z; + /* + case VKEY_OEM_PLUS: return KEY_PLUS; + case VKEY_OEM_COMMA: return KEY_COMMA; + case VKEY_OEM_MINUS: return KEY_MINUS; + case VKEY_OEM_PERIOD: return KEY_PERIOD; + case VKEY_OEM_1: return KEY_OEM_1; + case VKEY_OEM_2: return KEY_OEM_2; + case VKEY_OEM_3: return KEY_OEM_3; + case VKEY_OEM_4: return KEY_OEM_4; + case VKEY_OEM_5: return KEY_OEM_5; + case VKEY_OEM_6: return KEY_OEM_6; + case VKEY_OEM_7: return KEY_OEM_7; + case VKEY_OEM_8: return KEY_OEM_8; + */ + default: break; + + }; + + return 0; +}; + +#endif // BASE_KEYBOARD_CODES_POSIX_H_ diff --git a/platform/nacl/opengl_context.cpp b/platform/nacl/opengl_context.cpp new file mode 100644 index 0000000000..8dafe959a3 --- /dev/null +++ b/platform/nacl/opengl_context.cpp @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* opengl_context.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 "opengl_context.h" + +#include <pthread.h> +#include "ppapi/gles2/gl2ext_ppapi.h" +#include "os_nacl.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/module.h" +#include "ppapi/cpp/completion_callback.h" +#include "ppapi/utility/completion_callback_factory.h" + + +namespace { +// This is called by the brower when the 3D context has been flushed to the +// browser window. +void FlushCallback(void* data, int32_t result) { + static_cast<OpenGLContext*>(data)->set_flush_pending(false); + static_cast<OpenGLContext*>(data)->FlushContext(); +} +} // namespace + +OpenGLContext::OpenGLContext(pp::Instance* p_instance) + : pp::Graphics3DClient(p_instance), + flush_pending_(false) { + + instance = p_instance; + pp::Module* module = pp::Module::Get(); + assert(module); + gles2_interface_ = static_cast<const struct PPB_OpenGLES2_Dev*>( + module->GetBrowserInterface(PPB_OPENGLES2_INTERFACE)); + assert(gles2_interface_); +} + +OpenGLContext::~OpenGLContext() { + glSetCurrentContextPPAPI(0); +} + +bool OpenGLContext::MakeContextCurrent(pp::Instance* instance) { + + if (instance == NULL) { + glSetCurrentContextPPAPI(0); + return false; + } + // Lazily create the Pepper context. + if (context_.is_null()) { + int32_t attribs[] = { + PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, + PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24, + PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 8, + PP_GRAPHICS3DATTRIB_SAMPLES, 0, + PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0, + PP_GRAPHICS3DATTRIB_WIDTH, width, + PP_GRAPHICS3DATTRIB_HEIGHT, height, + PP_GRAPHICS3DATTRIB_NONE + }; + + context_ = pp::Graphics3D(instance, pp::Graphics3D(), attribs); + if (context_.is_null()) { + glSetCurrentContextPPAPI(0); + return false; + } + instance->BindGraphics(context_); + } + glSetCurrentContextPPAPI(context_.pp_resource()); + return true; +} + +void OpenGLContext::ResizeContext(const pp::Size& size) { + + width = size.width(); + height = size.height(); + + if (!context_.is_null()) { + context_.ResizeBuffers(size.width(), size.height()); + } +} + + +void OpenGLContext::InvalidateContext(pp::Instance* instance) { + glSetCurrentContextPPAPI(0); +} + +void OpenGLContext::FlushContext() { + if (flush_pending()) { + // A flush is pending so do nothing; just drop this flush on the floor. + return; + } + set_flush_pending(true); + + OSNacl* os = (OSNacl*)OS::get_singleton(); + MakeContextCurrent(instance); + os->iterate(); + + context_.SwapBuffers(pp::CompletionCallback(&FlushCallback, this)); +} + diff --git a/platform/nacl/opengl_context.h b/platform/nacl/opengl_context.h new file mode 100644 index 0000000000..b0431f29bf --- /dev/null +++ b/platform/nacl/opengl_context.h @@ -0,0 +1,124 @@ +/*************************************************************************/ +/* opengl_context.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 EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_ +#define EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_ + +/// +/// @file +/// OpenGLContext manages the OpenGL context in the browser that is associated +/// with a @a pp::Instance instance. +/// + +#include <pthread.h> + +#include <algorithm> +#include <string> + +#include "ppapi/c/ppb_opengles2.h" +//#include "ppapi/cpp/dev/context_3d_dev.h" +#include "ppapi/cpp/graphics_3d_client.h" +#include "ppapi/cpp/graphics_3d.h" +//#include "ppapi/cpp/dev/surface_3d_dev.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/size.h" + +// A convenience wrapper for a shared OpenGLContext pointer type. As other +// smart pointer types are needed, add them here. + +#include <tr1/memory> + +class OpenGLContext; + +typedef std::tr1::shared_ptr<OpenGLContext> SharedOpenGLContext; + +/// OpenGLContext manages an OpenGL rendering context in the browser. +/// +class OpenGLContext : public pp::Graphics3DClient { + public: + explicit OpenGLContext(pp::Instance* instance); + + /// Release all the in-browser resources used by this context, and make this + /// context invalid. + virtual ~OpenGLContext(); + + /// The Graphics3DClient interfcace. + virtual void Graphics3DContextLost() { + assert(!"Unexpectedly lost graphics context"); + } + + /// Make @a this the current 3D context in @a instance. + /// @param instance The instance of the NaCl module that will receive the + /// the current 3D context. + /// @return success. + bool MakeContextCurrent(pp::Instance* instance); + + /// Flush the contents of this context to the browser's 3D device. + void FlushContext(); + + /// Make the underlying 3D device invalid, so that any subsequent rendering + /// commands will have no effect. The next call to MakeContextCurrent() will + /// cause the underlying 3D device to get rebound and start receiving + /// receiving rendering commands again. Use InvalidateContext(), for + /// example, when resizing the context's viewing area. + void InvalidateContext(pp::Instance* instance); + + void ResizeContext(const pp::Size& size); + + /// The OpenGL ES 2.0 interface. + const struct PPB_OpenGLES2_Dev* gles2() const { + return gles2_interface_; + } + + /// The PP_Resource needed to make GLES2 calls through the Pepper interface. + const PP_Resource gl_context() const { + return context_.pp_resource(); + } + + /// Indicate whether a flush is pending. This can only be called from the + /// main thread; it is not thread safe. + bool flush_pending() const { + return flush_pending_; + } + void set_flush_pending(bool flag) { + flush_pending_ = flag; + } + + private: + pp::Graphics3D context_; + bool flush_pending_; + + int width, height; + + pp::Instance* instance; + + const struct PPB_OpenGLES2_Dev* gles2_interface_; +}; + +#endif // EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_ + diff --git a/platform/nacl/os_nacl.cpp b/platform/nacl/os_nacl.cpp new file mode 100644 index 0000000000..d97195c50d --- /dev/null +++ b/platform/nacl/os_nacl.cpp @@ -0,0 +1,529 @@ +/*************************************************************************/ +/* os_nacl.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 "os_nacl.h" + +#include "drivers/unix/memory_pool_static_malloc.h" +#include "os/memory_pool_dynamic_static.h" +#include "main/main.h" +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> +#include <time.h> + +#include "io/file_access_memory.h" +#include "core/io/file_access_pack.h" +#include "scene/io/scene_loader.h" +#include "scene/main/scene_main_loop.h" + +#include "servers/visual/visual_server_raster.h" + +#include "drivers/gles2/rasterizer_gles2.h" +#include "nacl_keycodes.h" + +#include "core/globals.h" +#include "core/input_map.h" + +#include <ppapi/cpp/point.h> +#include <ppapi/cpp/var.h> + +#define UNIX_ENABLED +#include "drivers/unix/thread_posix.h" +#include "drivers/unix/semaphore_posix.h" +#include "drivers/unix/mutex_posix.h" + + +int OSNacl::get_video_driver_count() const { + + return 1; +}; +const char * OSNacl::get_video_driver_name(int p_driver) const { + + return "gles2"; +}; + +OS::VideoMode OSNacl::get_default_video_mode() const { + + return OS::VideoMode(800,600,false); +}; + +int OSNacl::get_audio_driver_count() const { + + return 1; +}; + +const char * OSNacl::get_audio_driver_name(int p_driver) const { + + return "nacl_audio"; +}; + +static MemoryPoolStaticMalloc *mempool_static=NULL; +static MemoryPoolDynamicStatic *mempool_dynamic=NULL; + +void OSNacl::initialize_core() { + + ticks_start=0; + ticks_start=get_ticks_usec(); +}; + +void OSNacl::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + rasterizer = memnew( RasterizerGLES2 ); + visual_server = memnew( VisualServerRaster(rasterizer) ); + visual_server->init(); + visual_server->cursor_set_visible(false, 0); + + audio_driver = memnew(AudioDriverNacl); + audio_driver->set_singleton(); + audio_driver->init(); + + 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 OSNacl::set_main_loop( MainLoop * p_main_loop ) { + + main_loop = p_main_loop; + input->set_main_loop(p_main_loop); + main_loop->init(); +}; + +void OSNacl::delete_main_loop() { + + if (main_loop) + memdelete(main_loop); +}; + +void OSNacl::finalize() { + + +}; +void OSNacl::finalize_core() { + + if (mempool_dynamic) + memdelete( mempool_dynamic ); + if (mempool_static) + delete mempool_static; + +}; + +void OSNacl::alert(const String& p_alert,const String& p_title) { + + fprintf(stderr,"ERROR: %s\n",p_alert.utf8().get_data()); +}; + +void OSNacl::vprint(const char* p_format, va_list p_list, bool p_strerr) { + + vprintf(p_format,p_list); + fflush(stdout); +} + + +String OSNacl::get_stdin_string(bool p_block) { + + char buff[1024]; + return fgets(buff,1024,stdin); +}; + +void OSNacl::set_mouse_show(bool p_show) { + +}; + +void OSNacl::set_mouse_grab(bool p_grab) { + +}; + +bool OSNacl::is_mouse_grab_enabled() const { + + return false; +}; + +int OSNacl::get_mouse_button_state() const { + + return mouse_mask; +}; + + +Point2 OSNacl::get_mouse_pos() const { + + return Point2(); +}; + +void OSNacl::set_window_title(const String& p_title) { +}; + +void OSNacl::set_video_mode(const VideoMode& p_video_mode, int p_screen) { + + video_mode = p_video_mode; +}; + +OS::VideoMode OSNacl::get_video_mode(int p_screen) const { + + return video_mode; +}; + +void OSNacl::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const { + +}; + +Error OSNacl::execute(const String& p_path, const List<String>& p_arguments,bool p_blocking, OS::ProcessID *r_child_id, String* r_pipe, int *r_exitcode) { + + return ERR_UNAVAILABLE; +}; + +Error OSNacl::kill(const ProcessID& p_pid) { + + return ERR_UNAVAILABLE; +}; + +bool OSNacl::has_environment(const String& p_var) const { + + return getenv(p_var.utf8().get_data())!=NULL; +}; + +String OSNacl::get_environment(const String& p_var) const { + + if (getenv(p_var.utf8().get_data())) + return getenv(p_var.utf8().get_data()); + return ""; +}; + +String OSNacl::get_name() { + + return "NaCl"; +}; + +MainLoop *OSNacl::get_main_loop() const { + + return main_loop; +}; + +OS::Date OSNacl::get_date() const { + + time_t t=time(NULL); + struct tm *lt=localtime(&t); + Date ret; + ret.year=lt->tm_year; + ret.month=(Month)lt->tm_mon; + ret.day=lt->tm_mday; + ret.weekday=(Weekday)lt->tm_wday; + ret.dst=lt->tm_isdst; + + return ret; +}; + +OS::Time OSNacl::get_time() const { + + time_t t=time(NULL); + struct tm *lt=localtime(&t); + Time ret; + ret.hour=lt->tm_hour; + ret.min=lt->tm_min; + ret.sec=lt->tm_sec; + return ret; +}; + +void OSNacl::delay_usec(uint32_t p_usec) const { + + //usleep(p_usec); +}; + +uint64_t OSNacl::get_ticks_usec() const { + + struct timeval tv_now; + gettimeofday(&tv_now,NULL); + + uint64_t longtime = (uint64_t)tv_now.tv_usec + (uint64_t)tv_now.tv_sec*1000000L; + longtime-=ticks_start; + + return longtime; +}; + +bool OSNacl::can_draw() const { + + return minimized != true; +}; + +void OSNacl::queue_event(const InputEvent& p_event) { + + ERR_FAIL_INDEX( event_count, MAX_EVENTS ); + + event_queue[event_count++] = p_event; +}; + +void OSNacl::add_package(String p_name, Vector<uint8_t> p_data) { + + FileAccessMemory::register_file(p_name, p_data); + FileAccess::make_default<FileAccessMemory>(FileAccess::ACCESS_RESOURCES); + FileAccess::make_default<FileAccessMemory>(FileAccess::ACCESS_USERDATA); + FileAccess::make_default<FileAccessMemory>(FileAccess::ACCESS_FILESYSTEM); + + if (!PackedData::get_singleton()) + memnew(PackedData); + + printf("adding package %ls, %x\n", p_name.c_str(), PackedData::get_singleton()); + PackedData::get_singleton()->set_disabled(true); + PackedData::get_singleton()->add_pack(p_name); + PackedData::get_singleton()->set_disabled(false); + printf("added\n"); +}; + +void OSNacl::set_cursor_shape(CursorShape p_shape) { + + +}; + +String OSNacl::get_resource_dir() const { + + return "."; +}; + +static int mouse_button(int p_nacl_but) { + + switch (p_nacl_but) { + + case PP_INPUTEVENT_MOUSEBUTTON_LEFT: + return BUTTON_LEFT; + case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE: + return BUTTON_MIDDLE; + case PP_INPUTEVENT_MOUSEBUTTON_RIGHT: + return BUTTON_RIGHT; + }; + + return 0; +}; + +static InputModifierState modifier(uint32_t p_mod) { + + InputModifierState mod_mask; + + mod_mask.shift = p_mod & PP_INPUTEVENT_MODIFIER_SHIFTKEY; + mod_mask.alt = p_mod & PP_INPUTEVENT_MODIFIER_ALTKEY; + mod_mask.control = p_mod & PP_INPUTEVENT_MODIFIER_CONTROLKEY; + mod_mask.meta = p_mod & PP_INPUTEVENT_MODIFIER_METAKEY; + + return mod_mask; +}; + + + +void OSNacl::handle_event(const pp::InputEvent& p_event) { + + int type = p_event.GetType(); + switch (type) { + + case PP_INPUTEVENT_TYPE_MOUSEDOWN: + case PP_INPUTEVENT_TYPE_MOUSEUP: + case PP_INPUTEVENT_TYPE_WHEEL: { + + InputEvent event; + event.ID=++event_id; + event.type = InputEvent::MOUSE_BUTTON; + event.device=0; + + pp::MouseInputEvent mevent(p_event); + if (type == PP_INPUTEVENT_TYPE_WHEEL) { + + pp::WheelInputEvent wevent(p_event);; + float ticks = wevent.GetTicks().y(); + if (ticks == 0) + break; // whut? + + event.mouse_button.pressed = true; + event.mouse_button.button_index = ticks > 0 ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN; + event.mouse_button.doubleclick = false; + + } else { + + event.mouse_button.pressed = (type == PP_INPUTEVENT_TYPE_MOUSEDOWN); + event.mouse_button.button_index = mouse_button(mevent.GetButton()); + event.mouse_button.doubleclick = (mevent.GetClickCount() % 2) == 0; + + mouse_mask &= ~(1<< (event.mouse_button.button_index - 1)); + mouse_mask |= (event.mouse_button.pressed << (event.mouse_button.button_index - 1)); + }; + pp::Point pos = mevent.GetPosition(); + event.mouse_button.button_mask = mouse_mask; + event.mouse_button.global_x = pos.x(); + event.mouse_button.x = pos.x(); + event.mouse_button.global_y = pos.y(); + event.mouse_button.y = pos.y(); + event.mouse_button.pointer_index = 0; + event.mouse_button.mod = modifier(p_event.GetModifiers()); + queue_event(event); + + } break; + + case PP_INPUTEVENT_TYPE_MOUSEMOVE: { + + pp::MouseInputEvent mevent(p_event); + pp::Point pos = mevent.GetPosition(); + + InputEvent event; + event.ID=++event_id; + event.type = InputEvent::MOUSE_MOTION; + event.mouse_motion.pointer_index = 0; + event.mouse_motion.global_x = pos.x(); + event.mouse_motion.global_y = pos.y(); + event.mouse_motion.x = pos.x(); + event.mouse_motion.y = pos.y(); + event.mouse_motion.button_mask = mouse_mask; + event.mouse_motion.mod = modifier(p_event.GetModifiers()); + + event.mouse_motion.relative_x = pos.x() - mouse_last_x; + event.mouse_motion.relative_y = pos.y() - mouse_last_y; + mouse_last_x = pos.x(); + mouse_last_y = pos.y(); + + queue_event(event); + + } break; + + case PP_INPUTEVENT_TYPE_RAWKEYDOWN: + case PP_INPUTEVENT_TYPE_KEYDOWN: + case PP_INPUTEVENT_TYPE_KEYUP: { + + pp::KeyboardInputEvent kevent(p_event); + bool is_char; + uint32_t key = godot_key(kevent.GetKeyCode(), is_char); + if (type != PP_INPUTEVENT_TYPE_KEYUP && is_char) { + + last_scancode = key; + break; + }; + + InputEvent event; + event.ID=++event_id; + event.type = InputEvent::KEY; + event.key.pressed = (type != PP_INPUTEVENT_TYPE_KEYUP); + event.key.scancode = key; + event.key.unicode = key; + + event.key.echo = p_event.GetModifiers() & PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT; + event.key.mod = modifier(p_event.GetModifiers()); + queue_event(event); + } break; + + case PP_INPUTEVENT_TYPE_CHAR: { + + pp::KeyboardInputEvent kevent(p_event); + InputEvent event; + event.ID = ++event_id; + event.type = InputEvent::KEY; + event.key.pressed = true; + event.key.scancode = last_scancode; + event.key.unicode = kevent.GetCharacterText().AsString().c_str()[0]; + event.key.mod = modifier(p_event.GetModifiers()); + event.key.echo = p_event.GetModifiers() & PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT; + queue_event(event); + + } break; + + /* + case NPEventType_Minimize: { + + minimized = p_event->u.minimize.value == 1; + + } break; + + + case NPEventType_Focus: { + + if (p_event->u.focus.value == 1) { + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); + } else { + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); + }; + } break; + + */ + + default: + ; + }; +}; + +bool OSNacl::iterate() { + + if (!main_loop) { + event_count = 0; + return true; + }; + + for (int i=0; i<event_count; i++) { + + input->parse_input_event(event_queue[i]); + }; + + event_count = 0; + + return Main::iteration(); +}; + + +OSNacl::OSNacl() { + + main_loop=NULL; + mempool_dynamic = NULL; + mempool_static = NULL; + mouse_last_x = 0; + mouse_last_y = 0; + event_count = 0; + event_id = 0; + mouse_mask = 0; + video_mode = get_default_video_mode(); + last_scancode = 0; + minimized = false; + + ThreadPosix::make_default(); + SemaphorePosix::make_default(); + MutexPosix::make_default(); + mempool_static = new MemoryPoolStaticMalloc; + mempool_dynamic = memnew( MemoryPoolDynamicStatic ); +}; + +OSNacl::~OSNacl() { + +}; diff --git a/platform/nacl/os_nacl.h b/platform/nacl/os_nacl.h new file mode 100644 index 0000000000..b8173ef61c --- /dev/null +++ b/platform/nacl/os_nacl.h @@ -0,0 +1,156 @@ +/*************************************************************************/ +/* os_nacl.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 OS_NACL_H +#define OS_NACL_H + +#include "core/os/os.h" + +#include "servers/visual_server.h" +#include "servers/visual/rasterizer.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 "audio_driver_nacl.h" +#include "os/input.h" + + +#include <ppapi/cpp/input_event.h> + +class OSNacl : OS { + + uint64_t ticks_start; + +protected: + + enum { + MAX_EVENTS = 64, + }; + + MainLoop *main_loop; + + Rasterizer *rasterizer; + VisualServer *visual_server; + PhysicsServer *physics_server; + SpatialSoundServerSW *spatial_sound_server; + + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSound2DServerSW *spatial_sound_2d_server; + Physics2DServer *physics_2d_server; + AudioDriverNacl* audio_driver; + + + // 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; + + void vprint(const char* p_format, va_list p_list, bool p_stderr); + + virtual void initialize_core(); + 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(); + virtual void finalize_core(); + + int mouse_last_x, mouse_last_y; + + InputEvent event_queue[MAX_EVENTS]; + int event_count; + void queue_event(const InputEvent& p_event); + + int event_id; + uint32_t mouse_mask; + + uint32_t last_scancode; + + bool minimized; + + VideoMode video_mode; + + InputDefault *input; + +public: + + void add_package(String p_name, Vector<uint8_t> p_data); + + void handle_event(const pp::InputEvent& p_event); + + virtual void alert(const String& p_alert,const String& p_title); + virtual String get_stdin_string(bool p_block); + + 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_video_mode(const VideoMode& p_video_mode,int p_screen); + virtual VideoMode get_video_mode(int p_screen) const; + virtual void get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const; + + virtual Error execute(const String& p_path, const List<String>& p_arguments,bool p_blocking,ProcessID *r_child_id=NULL,String* r_pipe=NULL,int *r_exitcode=NULL); + virtual Error kill(const ProcessID& p_pid); + + virtual bool has_environment(const String& p_var) const; + virtual String get_environment(const String& p_var) const; + + virtual void set_cursor_shape(CursorShape p_shape); + + virtual String get_name(); + + virtual MainLoop *get_main_loop() const; + + virtual Date get_date() const; + virtual Time get_time() const; + + virtual void delay_usec(uint32_t p_usec) const; + virtual uint64_t get_ticks_usec() const; + + virtual String get_resource_dir() const; + + virtual bool can_draw() const; + + bool iterate(); + + OSNacl(); + ~OSNacl(); +}; + +#endif // OS_NACL_H diff --git a/platform/nacl/pepper_main.cpp b/platform/nacl/pepper_main.cpp new file mode 100644 index 0000000000..6a1f4acbe7 --- /dev/null +++ b/platform/nacl/pepper_main.cpp @@ -0,0 +1,541 @@ +/*************************************************************************/ +/* pepper_main.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. */ +/*************************************************************************/ + IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and + your use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive license, + under Apple's copyrights in this original Apple software + (the "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following text and + disclaimers in all such redistributions of the Apple Software. Neither the + name, trademarks, service marks or logos of Apple Computer, Inc. may be used + to endorse or promote products derived from the Apple Software without + specific prior written permission from Apple. Except as expressly stated in + this notice, no other rights or licenses, express or implied, are granted by + Apple herein, including but not limited to any patent rights that may be + infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION + AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER + THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR + OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdlib.h> + +#include "os_nacl.h" +#include <GLES2/gl2.h> + +#include <nacl/npupp.h> +#include <pgl/pgl.h> +#include <string.h> +#include <stdint.h> + +static NPNetscapeFuncs kBrowserFuncs = { 0 }; +static NPNetscapeFuncs* browser = &kBrowserFuncs; + +static NPDevice* device3d_ = NULL; +static PGLContext pgl_context_; +static NPDeviceContext3D context3d_; +static int width_; +static int height_; + +extern int nacl_main(int argc, char** argn, char** argv); +extern void nacl_cleanup(); + +NPExtensions* extensions = NULL; +static NPP npp_; + +const int32_t kCommandBufferSize = 1024 * 1024; + +// Plugin entry points +extern "C" { + +// Plugin entry points + +// Entrypoints ----------------------------------------------------------------- + +NPError NP_GetEntryPoints(NPPluginFuncs* plugin_funcs) { + plugin_funcs->version = 11; + plugin_funcs->size = sizeof(plugin_funcs); + plugin_funcs->newp = NPP_New; + plugin_funcs->destroy = NPP_Destroy; + plugin_funcs->setwindow = NPP_SetWindow; + plugin_funcs->newstream = NPP_NewStream; + plugin_funcs->destroystream = NPP_DestroyStream; + plugin_funcs->asfile = NPP_StreamAsFile; + plugin_funcs->writeready = NPP_WriteReady; + plugin_funcs->write = (NPP_WriteUPP)NPP_Write; + plugin_funcs->print = NPP_Print; + plugin_funcs->event = NPP_HandleEvent; + plugin_funcs->urlnotify = NPP_URLNotify; + plugin_funcs->getvalue = NPP_GetValue; + plugin_funcs->setvalue = NPP_SetValue; + + return NPERR_NO_ERROR; +} + +NPError NP_Shutdown() { + pglTerminate(); + return NPERR_NO_ERROR; +} + +NPError NP_GetValue(void* instance, NPPVariable variable, void* value); +char* NP_GetMIMEDescription(); + +NPError NP_Initialize(NPNetscapeFuncs* browser_funcs, + NPPluginFuncs* plugin_funcs) { + printf("NPP_Initialize\n"); + memcpy(&kBrowserFuncs, browser_funcs, sizeof(kBrowserFuncs)); + pglInitialize(); + return NP_GetEntryPoints(plugin_funcs); +} + + +} // extern "C" + +void Initialize3D() { + // Initialize a 3D context. + NPDeviceContext3DConfig config; + config.commandBufferSize = kCommandBufferSize; + NPError err = device3d_->initializeContext(npp_, &config, &context3d_); + if (err != NPERR_NO_ERROR) { + printf("Failed to initialize 3D context\n"); + exit(1); + } + + // Create a PGL context. + pgl_context_ = pglCreateContext(npp_, device3d_, &context3d_); + + // Initialize the demo GL state. + //pglMakeCurrent(pgl_context_); + //GLFromCPPInit(); + //pglMakeCurrent(NULL); +} + +NPError NPP_New(NPMIMEType pluginType, + NPP instance, + uint16_t mode, + int16_t argc, char* argn[], char* argv[], + NPSavedData* saved) { + printf("NPP_New\n"); + if (browser->version >= 14) { + + npp_ = instance; + if (!extensions) { + browser->getvalue(npp_, NPNVPepperExtensions, + reinterpret_cast<void*>(&extensions)); + // CHECK(extensions); + } + + printf("%s: %i\n", __FUNCTION__, __LINE__); + + device3d_ = extensions->acquireDevice(npp_, NPPepper3DDevice); + if (device3d_ == NULL) { + printf("Failed to acquire 3DDevice\n"); + exit(1); + } + printf("%s: %i\n", __FUNCTION__, __LINE__); + + /* + deviceaudio_ = extensions->acquireDevice(npp_, NPPepperAudioDevice); + if (deviceaudio_ == NULL) { + printf("Failed to acquire AudioDevice\n"); + exit(1); + } + */ + Initialize3D(); + pglMakeCurrent(pgl_context_); + nacl_main(argc, argn, argv); + pglMakeCurrent(NULL); + }; + + return NPERR_NO_ERROR; +} + +NPError NPP_Destroy(NPP instance, NPSavedData** save) { + + nacl_cleanup(); + + return NPERR_NO_ERROR; +} + +void Destroy3D() { + printf("destroy 3d\n"); + // Destroy the PGL context. + pglDestroyContext(pgl_context_); + pgl_context_ = NULL; + + // Destroy the Device3D context. + device3d_->destroyContext(npp_, &context3d_); +} + +static void iteration(void* data) { + + (void)data; + OSNacl* os = (OSNacl*)OS::get_singleton(); + + if (!pglMakeCurrent(pgl_context_) && pglGetError() == PGL_CONTEXT_LOST) { + printf("******* Lost context! :O\n"); + Destroy3D(); + Initialize3D(); + pglMakeCurrent(pgl_context_); + } + + glViewport(0, 0, width_, height_); + + os->iterate(); + + pglSwapBuffers(); + pglMakeCurrent(NULL); + + browser->pluginthreadasynccall(npp_, iteration, NULL); +}; + +NPError NPP_SetWindow(NPP instance, NPWindow* window) { + + width_ = window->width; + height_ = window->height; + + if (!pgl_context_) + Initialize3D(); + + // Schedule the first call to Draw. + OSNacl* os = (OSNacl*)OS::get_singleton(); + OS::VideoMode vm; + vm.width = width_; + vm.height = height_; + vm.resizable = false; + vm.fullscreen = false; + os->set_video_mode(vm); + + browser->pluginthreadasynccall(npp_, iteration, NULL); + + return NPERR_NO_ERROR; +} + +NPError NPP_NewStream(NPP instance, + NPMIMEType type, + NPStream* stream, + NPBool seekable, + uint16_t* stype) { + *stype = NP_ASFILEONLY; + return NPERR_NO_ERROR; +} + +NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason) { + return NPERR_NO_ERROR; +} + +void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname) { +} + +int32_t NPP_Write(NPP instance, + NPStream* stream, + int32_t offset, + int32_t len, + void* buffer) { + return 0; +} + +int32_t NPP_WriteReady(NPP instance, NPStream* stream) { + return 0; +} + +void NPP_Print(NPP instance, NPPrint* platformPrint) { +} + +int16_t NPP_HandleEvent(NPP instance, void* event) { + + OSNacl* os = (OSNacl*)OS::get_singleton(); + os->handle_event(event); + return 1; +} + +void NPP_URLNotify(NPP instance, + const char* url, + NPReason reason, + void* notify_data) { + // PluginObject* obj = static_cast<PluginObject*>(instance->pdata); +} + +static NPObject* Allocate(NPP npp, NPClass* npclass) { + return new NPObject; +} + +static void Deallocate(NPObject* object) { + delete object; +} + +// Return |true| if |method_name| is a recognized method. +static bool HasMethod(NPObject* obj, NPIdentifier method_name) { + + char *name = NPN_UTF8FromIdentifier(method_name); + bool is_method = false; + if (strcmp((const char *)name, "start_package") == 0) { + is_method = true; + } else if (strcmp((const char*)name, "add_package_chunk") == 0) { + is_method = true; + } else if (strcmp((const char*)name, "end_package") == 0) { + is_method = true; + } else if (strcmp((const char*)name, "start_scene") == 0) { + is_method = true; + } + NPN_MemFree(name); + return is_method; +} + +// I don't know what this is +static bool InvokeDefault(NPObject *obj, const NPVariant *args, + uint32_t argCount, NPVariant *result) { + if (result) { + NULL_TO_NPVARIANT(*result); + } + return true; +} + +static uint8_t* mem = NULL; +static int pkg_size = 0; +static String pkgname; + +static bool variant_is_number(const NPVariant& v) { + + switch (v.type) { + + case NPVariantType_Int32: + case NPVariantType_Double: + return true; + + default: + return false; + } + return false; +}; + +static double variant_as_number(const NPVariant& v) { + + switch (v.type) { + + case NPVariantType_Int32: + return (double)v.value.intValue; + case NPVariantType_Double: + return (double)v.value.doubleValue; + default: + return 0; + } + + return 0; +}; + +// Invoke() is called by the browser to invoke a function object whose name +// is |method_name|. +static bool Invoke(NPObject* obj, + NPIdentifier method_name, + const NPVariant *args, + uint32_t arg_count, + NPVariant *result) { + NULL_TO_NPVARIANT(*result); + char *name = NPN_UTF8FromIdentifier(method_name); + if (name == NULL) + return false; + bool rval = false; + + OSNacl* os = (OSNacl*)OS::get_singleton(); + + if (strcmp(name, "start_package") == 0) { + + printf("arg count is %i\n", arg_count); + for (int i=0; i<arg_count; i++) { + printf("type for %i is %i\n", i, args[i].type); + } + if (arg_count != 2) { + return false; // assuming "false" means error + }; + + if (args[0].type != NPVariantType_String) { + printf("arg 0 not string type %i\n", args[1].type); + return false; + }; + + if (!variant_is_number(args[1])) { + + printf("arg 1 not number, type %i\n", args[1].type); + return false; + }; + + pkgname = String::utf8(args[0].value.stringValue.UTF8Characters, args[0].value.stringValue.UTF8Length); + pkg_size = (int)variant_as_number(args[1]); + mem = (uint8_t*)malloc(pkg_size); + + printf("args %ls, %lf\n", pkgname.c_str(), variant_as_number(args[1])); + + return true; + }; + + if (strcmp(name, "add_package_chunk") == 0) { + + if (arg_count != 3) { // assuming arg_count starts from 1 + return false; // assuming "false" means error + }; + + if (!variant_is_number(args[0])) return false; + if (args[1].type != NPVariantType_String) return false; + if (!variant_is_number(args[2])) return false; + + if (!mem) + return false; + + int ofs = variant_as_number(args[0]); + int len = variant_as_number(args[2]); + + String s; + if (s.parse_utf8(args[1].value.stringValue.UTF8Characters, args[1].value.stringValue.UTF8Length)) { + printf("error parsing?\n"); + }; + uint8_t* dst = mem + ofs; + for (int i=0; i<len; i++) { + + dst[i] = s[i]; + }; + + //memcpy(mem + ofs, args[1].value.stringValue.UTF8Characters, len); + return true; + }; + + if (strcmp(name, "end_package") == 0) { + + os->add_package(pkgname, mem, pkg_size); + return true; + }; + + + if (strcmp(name, "start_scene") == 0) { +printf("start_scene!\n"); + if (arg_count != 1) { + return false; + }; + + if (args[0].type != NPVariantType_String) return false; +printf("calling with param %s\n", args[0].value.stringValue.UTF8Characters); + + printf("pepper iteration\n"); + if (!pglMakeCurrent(pgl_context_) && pglGetError() == PGL_CONTEXT_LOST) { + printf("******* Lost context! :O\n"); + Destroy3D(); + Initialize3D(); + pglMakeCurrent(pgl_context_); + } + os->start_scene(String::utf8(args[0].value.stringValue.UTF8Characters)); + pglSwapBuffers(); + pglMakeCurrent(NULL); + +printf("returning true\n"); + return true; + }; + + NPN_MemFree(name); + + return rval; +} + + +static NPClass GodotClass = { + NP_CLASS_STRUCT_VERSION, + Allocate, + Deallocate, + NULL, // Invalidate is not implemented + HasMethod, + Invoke, + InvokeDefault, + NULL, // HasProperty is not implemented + NULL, // GetProperty is not implemented + NULL, // SetProperty is not implemented +}; + +static NPObject* npobject = NULL; + +NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value) { + NPError err = NPERR_NO_ERROR; + + switch (variable) { + case NPPVpluginNameString: + *(reinterpret_cast<const char**>(value)) = "Pepper Test PlugIn"; + break; + case NPPVpluginDescriptionString: + *(reinterpret_cast<const char**>(value)) = + "Simple Pepper plug-in for manual testing."; + break; + case NPPVpluginNeedsXEmbed: + *(reinterpret_cast<NPBool*>(value)) = 1; + break; + case NPPVpluginScriptableNPObject: { + if (npobject == NULL) { + npobject = NPN_CreateObject(instance, &GodotClass); + } else { + NPN_RetainObject(npobject); + }; + void** v = reinterpret_cast<void**>(value); + *v = npobject; + } break; + default: + fprintf(stderr, "Unhandled variable to NPP_GetValue\n"); + err = NPERR_GENERIC_ERROR; + break; + } + + return err; +} + +NPError NPP_SetValue(NPP instance, NPNVariable variable, void* value) { + return NPERR_GENERIC_ERROR; +} + +NPError NP_GetValue(void* instance, NPPVariable variable, void* value) { + return NPP_GetValue(reinterpret_cast<NPP>(instance), variable, value); +} + +char* NP_GetMIMEDescription() { + return const_cast<char*>("pepper-application/x-pepper-test-plugin;"); +} diff --git a/platform/nacl/platform_config.h b/platform/nacl/platform_config.h new file mode 100644 index 0000000000..38fc934ae4 --- /dev/null +++ b/platform/nacl/platform_config.h @@ -0,0 +1,29 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include <alloca.h> diff --git a/platform/osx/SCsub b/platform/osx/SCsub new file mode 100644 index 0000000000..d7839d7d65 --- /dev/null +++ b/platform/osx/SCsub @@ -0,0 +1,11 @@ +Import('env') + +files = [ + 'os_osx.mm', + 'godot_main_osx.mm', + 'audio_driver_osx.cpp', + 'sem_osx.cpp', +# 'context_gl_osx.cpp', + ] + +env.Program('#bin/godot_osx',files) diff --git a/platform/osx/audio_driver_osx.cpp b/platform/osx/audio_driver_osx.cpp new file mode 100644 index 0000000000..8f28e8ff63 --- /dev/null +++ b/platform/osx/audio_driver_osx.cpp @@ -0,0 +1,160 @@ +/*************************************************************************/ +/* audio_driver_osx.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 OSX_ENABLED + +#include "audio_driver_osx.h" + +Error AudioDriverOSX::init() { + + active = false; + channels = 2; + + AudioStreamBasicDescription strdesc; + strdesc.mFormatID = kAudioFormatLinearPCM; + strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; + strdesc.mChannelsPerFrame = channels; + strdesc.mSampleRate = 44100; + strdesc.mFramesPerPacket = 1; + strdesc.mBitsPerChannel = 16; + strdesc.mBytesPerFrame = + strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8; + strdesc.mBytesPerPacket = + strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; + + OSStatus result = noErr; + AURenderCallbackStruct callback; + AudioComponentDescription desc; + AudioComponent comp = NULL; + const AudioUnitElement output_bus = 0; + const AudioUnitElement bus = output_bus; + const AudioUnitScope scope = kAudioUnitScope_Input; + + zeromem(&desc, sizeof(desc)); + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = 0; /* !!! FIXME: ? */ + comp = AudioComponentFindNext(NULL, &desc); + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + + result = AudioComponentInstanceNew(comp, &audio_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + ERR_FAIL_COND_V(comp == NULL, FAILED); + + result = AudioUnitSetProperty(audio_unit, + kAudioUnitProperty_StreamFormat, + scope, bus, &strdesc, sizeof(strdesc)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + zeromem(&callback, sizeof(AURenderCallbackStruct)); + callback.inputProc = &AudioDriverOSX::output_callback; + callback.inputProcRefCon = this; + result = AudioUnitSetProperty(audio_unit, + kAudioUnitProperty_SetRenderCallback, + scope, bus, &callback, sizeof(callback)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + result = AudioUnitInitialize(audio_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + + result = AudioOutputUnitStart(audio_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + + const int samples = 1024; + samples_in = memnew_arr(int32_t, samples); // whatever + buffer_frames = samples / channels; + + return OK; +}; + +OSStatus AudioDriverOSX::output_callback(void *inRefCon, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList * ioData) { + + + AudioBuffer *abuf; + AudioDriverOSX* ad = (AudioDriverOSX*)inRefCon; + + if (!ad->active) { + for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { + abuf = &ioData->mBuffers[i]; + zeromem(abuf->mData, abuf->mDataByteSize); + }; + return 0; + }; + + int frames_left; + + for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { + + abuf = &ioData->mBuffers[i]; + frames_left = inNumberFrames; + int16_t* out = (int16_t*)abuf->mData; + + while (frames_left) { + + int frames = MIN(frames_left, ad->buffer_frames); + ad->lock(); + ad->audio_server_process(frames, ad->samples_in); + ad->unlock(); + + for(int i = 0; i < frames * ad->channels; i++) { + + out[i] = ad->samples_in[i]>>16; + } + + frames_left -= frames; + out += frames * ad->channels; + }; + }; + + return 0; +}; + +void AudioDriverOSX::start() { + active = true; +}; + +int AudioDriverOSX::get_mix_rate() const { + return 44100; +}; + +AudioDriverSW::OutputFormat AudioDriverOSX::get_output_format() const { + return OUTPUT_STEREO; +}; + +void AudioDriverOSX::lock() {}; +void AudioDriverOSX::unlock() {}; + +void AudioDriverOSX::finish() { + + memdelete_arr(samples_in); +}; + +#endif diff --git a/platform/osx/audio_driver_osx.h b/platform/osx/audio_driver_osx.h new file mode 100644 index 0000000000..daa388fb86 --- /dev/null +++ b/platform/osx/audio_driver_osx.h @@ -0,0 +1,71 @@ +/*************************************************************************/ +/* audio_driver_osx.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. */ +/*************************************************************************/ +#ifdef OSX_ENABLED + +#ifndef AUDIO_DRIVER_OSX_H +#define AUDIO_DRIVER_OSX_H + +#include "servers/audio/audio_server_sw.h" + +#include <AudioUnit/AudioUnit.h> + +class AudioDriverOSX : public AudioDriverSW { + + AudioComponentInstance audio_unit; + bool active; + + int channels; + int32_t* samples_in; + int buffer_frames; + + static OSStatus output_callback(void *inRefCon, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList * ioData); + + +public: + + const char* get_name() const { + return "AudioUnit"; + }; + + 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(); +}; + +#endif + +#endif diff --git a/platform/osx/context_gl_osx.cpp b/platform/osx/context_gl_osx.cpp new file mode 100644 index 0000000000..9c2cd16b49 --- /dev/null +++ b/platform/osx/context_gl_osx.cpp @@ -0,0 +1,104 @@ +/*************************************************************************/ +/* context_gl_osx.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 "context_gl_osx.h" + +#ifdef OSX_ENABLED +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 + +void ContextGL_OSX::release_current() { + + aglSetCurrentContext( context ); +} + +void ContextGL_OSX::make_current() { + + aglSetCurrentContext( NULL ); +} +void ContextGL_OSX::swap_buffers() { + + aglSwapBuffers( context ); +} + +Error ContextGL_OSX::initialize() { + + if ( (Ptr) kUnresolvedCFragSymbolAddress == (Ptr) aglChoosePixelFormat ) + return FAILED; + + GLint attributes[] = { AGL_RGBA, + AGL_DOUBLEBUFFER, + AGL_DEPTH_SIZE, 32, + AGL_NO_RECOVERY, + AGL_NONE, + AGL_NONE }; + + AGLPixelFormat format = NULL; + + format = aglChoosePixelFormat( NULL, 0, attributes ); + + if ( !format ) + return FAILED; + + context = aglCreateContext( format, 0 ); + + if ( !context ) + return FAILED; + + aglDestroyPixelFormat( format ); + + aglSetWindowRef( context, window ); + + GLint swapInterval = 1; + aglSetInteger( context, AGL_SWAP_INTERVAL, &swapInterval ); + + aglSetCurrentContext( context ); + + return OK; +} + +ContextGL_OSX::ContextGL_OSX(WindowRef p_window) { + + window = p_window; +} + + +ContextGL_OSX::~ContextGL_OSX() { + + if (context) + aglDestroyContext(context); +} + + +#endif +#endif diff --git a/platform/osx/context_gl_osx.h b/platform/osx/context_gl_osx.h new file mode 100644 index 0000000000..54da42b331 --- /dev/null +++ b/platform/osx/context_gl_osx.h @@ -0,0 +1,65 @@ +/*************************************************************************/ +/* context_gl_osx.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 CONTEXT_GL_OSX_H +#define CONTEXT_GL_OSX_H + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +#ifdef OSX_ENABLED + +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) + +#include "os/os.h" +#include "drivers/gl_context/context_gl.h" +#include <AGL/agl.h> +#include <Carbon/Carbon.h> + +class ContextGL_OSX : public ContextGL { + + AGLContext context; + WindowRef window; + +public: + + virtual void release_current(); + virtual void make_current(); + virtual void swap_buffers(); + + virtual Error initialize(); + + ContextGL_OSX(WindowRef window); + ~ContextGL_OSX(); + +}; + +#endif + +#endif +#endif diff --git a/platform/osx/detect.py b/platform/osx/detect.py new file mode 100644 index 0000000000..5337416074 --- /dev/null +++ b/platform/osx/detect.py @@ -0,0 +1,107 @@ + +import os +import sys + + +def is_active(): + return True + +def get_name(): + return "OSX" + +def can_build(): + + if (sys.platform != "darwin"): + return False + + return True # osx enabled + +def get_opts(): + + return [ + ('force_64_bits','Force 64 bits binary','no'), + + ] + +def get_flags(): + + return [ + ('opengl', 'no'), + ('legacygl', 'yes'), + ('builtin_zlib', 'no'), + ('freetype','builtin'), #use builtin freetype + ] + + + +def configure(env): + + env.Append(CPPPATH=['#platform/osx']) + + if (env["tools"]=="no"): + #no tools suffix + env['OBJSUFFIX'] = ".nt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".nt"+env['LIBSUFFIX'] + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['-O2','-ffast-math','-fomit-frame-pointer','-ftree-vectorize','-msse2']) + env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX'] + + elif (env["target"]=="release_debug"): + + env.Append(CCFLAGS=['-O2','-DDEBUG_ENABLED']) + env['OBJSUFFIX'] = "_optd"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_optd"+env['LIBSUFFIX'] + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-g3', '-Wall','-DDEBUG_ENABLED','-DDEBUG_MEMORY_ENABLED']) + + + elif (env["target"]=="profile"): + env.Append(CCFLAGS=['-g','-pg']) + env.Append(LINKFLAGS=['-pg']) + + if (env["freetype"]!="no"): + env.Append(CCFLAGS=['-DFREETYPE_ENABLED']) + env.Append(CPPPATH=['#tools/freetype']) + env.Append(CPPPATH=['#tools/freetype/freetype/include']) + + if (env["force_64_bits"]!="no"): + env.Append(CCFLAGS=['-arch', 'x86_64']) + env.Append(LINKFLAGS=['-arch', 'x86_64']) + env['OBJSUFFIX'] = ".64"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".64"+env['LIBSUFFIX'] + else: + env.Append(CCFLAGS=['-arch', 'i386']) + env.Append(LINKFLAGS=['-arch', 'i386']) + +# env.Append(CPPPATH=['#platform/osx/include/freetype2', '#platform/osx/include']) +# env.Append(LIBPATH=['#platform/osx/lib']) + + + #if env['opengl'] == 'yes': + # env.Append(CPPFLAGS=['-DOPENGL_ENABLED','-DGLEW_ENABLED']) + + env.Append(CPPFLAGS=["-DAPPLE_STYLE_KEYS"]) + env.Append(CPPFLAGS=['-DUNIX_ENABLED','-DGLES2_ENABLED','-DGLEW_ENABLED', '-DOSX_ENABLED']) + env.Append(LIBS=['pthread']) + #env.Append(CPPFLAGS=['-F/Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks', '-isysroot', '/Developer/SDKs/MacOSX10.4u.sdk', '-mmacosx-version-min=10.4']) + #env.Append(LINKFLAGS=['-mmacosx-version-min=10.4', '-isysroot', '/Developer/SDKs/MacOSX10.4u.sdk', '-Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk']) + env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit','-lz']) + + if (env["CXX"]=="clang++"): + env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) + env["CC"]="clang" + env["LD"]="clang++" + + 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') } ) + #env.Append( BUILDERS = { 'HLSL9' : env.Builder(action = methods.build_hlsl_dx9_headers, suffix = 'hlsl.h',src_suffix = '.hlsl') } ) + + diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp new file mode 100644 index 0000000000..f55e901794 --- /dev/null +++ b/platform/osx/export/export.cpp @@ -0,0 +1,504 @@ +#include "version.h" +#include "export.h" +#include "tools/editor/editor_settings.h" +#include "tools/editor/editor_import_export.h" +#include "tools/editor/editor_node.h" +#include "io/zip_io.h" +#include "io/marshalls.h" +#include "io/resource_saver.h" +#include "globals.h" +#include "os/file_access.h" +#include "os/os.h" +#include "platform/osx/logo.h" +#include "string.h" + + +class EditorExportPlatformOSX : public EditorExportPlatform { + + OBJ_TYPE( EditorExportPlatformOSX,EditorExportPlatform ); + + String custom_release_package; + String custom_debug_package; + + + int version_code; + + String app_name; + String info; + String icon; + String identifier; + String short_version; + String version; + String signature; + String copyright; + bool use64; + bool high_resolution; + + Ref<ImageTexture> logo; + + void _fix_plist(Vector<uint8_t>& plist, const String &p_binary); + void _make_icon(const Image& p_icon,Vector<uint8_t>& data); + + +protected: + + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + +public: + + virtual String get_name() const { return "Mac OSX"; } + virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_BC; } + virtual Ref<Texture> get_logo() const { return logo; } + + + virtual bool poll_devices() { return false;} + virtual int get_device_count() const { return 0; }; + virtual String get_device_name(int p_device) const { return String(); } + virtual String get_device_info(int p_device) const { return String(); } + virtual Error run(int p_device); + + virtual bool requieres_password(bool p_debug) const { return false; } + virtual String get_binary_extension() const { return "zip"; } + virtual Error export_project(const String& p_path,bool p_debug,const String& p_password=""); + + virtual bool can_export(String *r_error=NULL) const; + + EditorExportPlatformOSX(); + ~EditorExportPlatformOSX(); +}; + +bool EditorExportPlatformOSX::_set(const StringName& p_name, const Variant& p_value) { + + String n=p_name; + + if (n=="custom_package/debug") + custom_debug_package=p_value; + else if (n=="custom_package/release") + custom_release_package=p_value; + else if (n=="application/name") + app_name=p_value; + else if (n=="application/info") + info=p_value; + else if (n=="application/icon") + icon=p_value; + else if (n=="application/identifier") + identifier=p_value; + else if (n=="application/signature") + signature=p_value; + else if (n=="application/short_version") + short_version=p_value; + else if (n=="application/version") + version=p_value; + else if (n=="application/copyright") + copyright=p_value; + else if (n=="application/64_bits") + use64=p_value; + else if (n=="display/high_res") + high_resolution=p_value; + else + return false; + + return true; +} + +bool EditorExportPlatformOSX::_get(const StringName& p_name,Variant &r_ret) const{ + + String n=p_name; + + if (n=="custom_package/debug") + r_ret=custom_debug_package; + else if (n=="custom_package/release") + r_ret=custom_release_package; + else if (n=="application/name") + r_ret=app_name; + else if (n=="application/info") + r_ret=info; + else if (n=="application/icon") + r_ret=icon; + else if (n=="application/identifier") + r_ret=identifier; + else if (n=="application/signature") + r_ret=signature; + else if (n=="application/short_version") + r_ret=short_version; + else if (n=="application/version") + r_ret=version; + else if (n=="application/copyright") + r_ret=copyright; + else if (n=="application/64_bits") + r_ret=use64; + else if (n=="display/high_res") + r_ret=high_resolution; + else + return false; + + return true; +} +void EditorExportPlatformOSX::_get_property_list( List<PropertyInfo> *p_list) const{ + + p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/debug", PROPERTY_HINT_FILE,"zip")); + p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/release", PROPERTY_HINT_FILE,"zip")); + + p_list->push_back( PropertyInfo( Variant::STRING, "application/name") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/info") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/icon",PROPERTY_HINT_FILE,"png") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/identifier") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/signature") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/short_version") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/version") ); + p_list->push_back( PropertyInfo( Variant::STRING, "application/copyright") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "application/64_bits") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "display/high_res") ); + + + //p_list->push_back( PropertyInfo( Variant::INT, "resources/pack_mode", PROPERTY_HINT_ENUM,"Copy,Single Exec.,Pack (.pck),Bundles (Optical)")); + +} + +void EditorExportPlatformOSX::_make_icon(const Image& p_icon,Vector<uint8_t>& icon) { + + + Ref<ImageTexture> it = memnew( ImageTexture ); + int size=512; + + Vector<uint8_t> data; + + data.resize(8); + data[0]='i'; + data[1]='c'; + data[2]='n'; + data[3]='s'; + + const char *name[]={"ic09","ic08","ic07","icp6","icp5","icp4"}; + int index=0; + + while(size>=16) { + + Image copy = p_icon; + copy.convert(Image::FORMAT_RGBA); + copy.resize(size,size); + it->create_from_image(copy); + String path = EditorSettings::get_singleton()->get_settings_path()+"/tmp/icon.png"; + ResourceSaver::save(path,it); + + FileAccess *f = FileAccess::open(path,FileAccess::READ); + ERR_FAIL_COND(!f); + + int ofs = data.size(); + uint32_t len = f->get_len(); + data.resize(data.size()+len+8); + f->get_buffer(&data[ofs+8],len); + memdelete(f); + len+=8; + len=BSWAP32(len); + copymem(&data[ofs],name[index],4); + encode_uint32(len,&data[ofs+4]); + index++; + size/=2; + } + + uint32_t total_len = data.size(); + total_len = BSWAP32(total_len); + encode_uint32(total_len,&data[4]); + + icon=data; +} + + +void EditorExportPlatformOSX::_fix_plist(Vector<uint8_t>& plist,const String& p_binary) { + + + String str; + String strnew; + str.parse_utf8((const char*)plist.ptr(),plist.size()); + Vector<String> lines=str.split("\n"); + for(int i=0;i<lines.size();i++) { + if (lines[i].find("$binary")!=-1) { + strnew+=lines[i].replace("$binary",p_binary)+"\n"; + } else if (lines[i].find("$name")!=-1) { + strnew+=lines[i].replace("$name",p_binary)+"\n"; + } else if (lines[i].find("$info")!=-1) { + strnew+=lines[i].replace("$info",info)+"\n"; + } else if (lines[i].find("$identifier")!=-1) { + strnew+=lines[i].replace("$identifier",identifier)+"\n"; + } else if (lines[i].find("$short_version")!=-1) { + strnew+=lines[i].replace("$short_version",short_version)+"\n"; + } else if (lines[i].find("$version")!=-1) { + strnew+=lines[i].replace("$version",version)+"\n"; + } else if (lines[i].find("$signature")!=-1) { + strnew+=lines[i].replace("$signature",signature)+"\n"; + } else if (lines[i].find("$copyright")!=-1) { + strnew+=lines[i].replace("$copyright",copyright)+"\n"; + } else if (lines[i].find("$highres")!=-1) { + strnew+=lines[i].replace("$highres",high_resolution?"<true/>":"<false/>")+"\n"; + } else { + strnew+=lines[i]+"\n"; + } + } + + CharString cs = strnew.utf8(); + plist.resize(cs.size()); + for(int i=9;i<cs.size();i++) { + plist[i]=cs[i]; + } +} + +Error EditorExportPlatformOSX::export_project(const String& p_path,bool p_debug,const String& p_password) { + + String src_pkg; + + EditorProgress ep("export","Exporting for OSX",104); + + String pkg_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/osx.zip"; + + if (p_debug) { + + src_pkg=custom_debug_package!=""?custom_debug_package:pkg_path; + } else { + + src_pkg=custom_release_package!=""?custom_release_package:pkg_path; + + } + + + FileAccess *src_f=NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + ep.step("Creating app",0); + + unzFile pkg = unzOpen2(src_pkg.utf8().get_data(), &io); + if (!pkg) { + + EditorNode::add_io_error("Could not find template app to export:\n"+src_pkg); + return ERR_FILE_NOT_FOUND; + } + + ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(pkg); + + zlib_filefunc_def io2=io; + FileAccess *dst_f=NULL; + io2.opaque=&dst_f; + zipFile dpkg=zipOpen2(p_path.utf8().get_data(),APPEND_STATUS_CREATE,NULL,&io2); + + String binary_to_use="godot_osx_"+String(p_debug?"debug":"release")+"."+String(use64?"64":"32"); + + print_line("binary: "+binary_to_use); + String pkg_name; + if (app_name!="") + pkg_name=app_name; + else if (String(Globals::get_singleton()->get("application/name"))!="") + pkg_name=String(Globals::get_singleton()->get("application/name")); + else + pkg_name="Unnamed"; + + + while(ret==UNZ_OK) { + + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(pkg,&info,fname,16384,NULL,0,NULL,0); + + String file=fname; + + print_line("READ: "+file); + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg,data.ptr(),data.size()); + unzCloseCurrentFile(pkg); + + //write + + file = file.replace_first("osx_template.app/",""); + + + if (file=="Contents/Info.plist") { + print_line("parse plist"); + _fix_plist(data,pkg_name); + } + + if (file.begins_with("Contents/MacOS/godot_")) { + if (file!="Contents/MacOS/"+binary_to_use) { + ret = unzGoToNextFile(pkg); + continue; //ignore! + } + file="Contents/MacOS/"+pkg_name; + } + + if (file=="Contents/Resources/icon.icns") { + //see if there is an icon + String iconpath = Globals::get_singleton()->get("application/icon"); + print_line("icon? "+iconpath); + if (iconpath!="") { + Image icon; + icon.load(iconpath); + if (!icon.empty()) { + print_line("loaded?"); + _make_icon(icon,data); + } + } + //bleh? + } + + file=pkg_name+".app/"+file; + + if (data.size()>0) { + print_line("ADDING: "+file+" size: "+itos(data.size())); + + zip_fileinfo fi; + fi.tmz_date.tm_hour=info.tmu_date.tm_hour; + fi.tmz_date.tm_min=info.tmu_date.tm_min; + fi.tmz_date.tm_sec=info.tmu_date.tm_sec; + fi.tmz_date.tm_mon=info.tmu_date.tm_mon; + fi.tmz_date.tm_mday=info.tmu_date.tm_mday; + fi.tmz_date.tm_year=info.tmu_date.tm_year; + fi.dosDate=info.dosDate; + fi.internal_fa=info.internal_fa; + fi.external_fa=info.external_fa; + + int err = zipOpenNewFileInZip(dpkg, + file.utf8().get_data(), + &fi, + NULL, + 0, + NULL, + 0, + NULL, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + print_line("OPEN ERR: "+itos(err)); + err = zipWriteInFileInZip(dpkg,data.ptr(),data.size()); + print_line("WRITE ERR: "+itos(err)); + zipCloseFileInZip(dpkg); + } + + ret = unzGoToNextFile(pkg); + } + + + ep.step("Making PKG",1); + + String pack_path=EditorSettings::get_singleton()->get_settings_path()+"/tmp/data.pck"; + FileAccess *pfs = FileAccess::open(pack_path,FileAccess::WRITE); + Error err = save_pack(pfs); + memdelete(pfs); + + if (err) { + zipClose(dpkg,NULL); + unzClose(pkg); + return err; + + } + + { + //write datapack + + int err = zipOpenNewFileInZip(dpkg, + (pkg_name+".app/Contents/Resources/data.pck").utf8().get_data(), + NULL, + NULL, + 0, + NULL, + 0, + NULL, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + + FileAccess *pf = FileAccess::open(pack_path,FileAccess::READ); + ERR_FAIL_COND_V(!pf,ERR_CANT_OPEN); + const int BSIZE = 16384; + uint8_t buf[BSIZE]; + + while(true) { + + int r = pf->get_buffer(buf,BSIZE); + if (r<=0) + break; + zipWriteInFileInZip(dpkg,buf,r); + + } + zipCloseFileInZip(dpkg); + memdelete(pf); + + } + + zipClose(dpkg,NULL); + unzClose(pkg); + + return OK; +} + + +Error EditorExportPlatformOSX::run(int p_device) { + + return OK; +} + + +EditorExportPlatformOSX::EditorExportPlatformOSX() { + + Image img( _osx_logo ); + logo = Ref<ImageTexture>( memnew( ImageTexture )); + logo->create_from_image(img); + + info="This Game is Nice"; + identifier="com.godot.macgame"; + signature="godotmacgame"; + short_version="1.0"; + version="1.0"; + use64=false; + high_resolution=false; + +} + +bool EditorExportPlatformOSX::can_export(String *r_error) const { + + + bool valid=true; + String err; + String exe_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + if (!FileAccess::exists(exe_path+"osx.zip")) { + valid=false; + err+="No export templates found.\nDownload and install export templates.\n"; + } + + if (custom_debug_package!="" && !FileAccess::exists(custom_debug_package)) { + valid=false; + err+="Custom debug package not found.\n"; + } + + if (custom_release_package!="" && !FileAccess::exists(custom_release_package)) { + valid=false; + err+="Custom release package not found.\n"; + } + + if (r_error) + *r_error=err; + + return valid; +} + + +EditorExportPlatformOSX::~EditorExportPlatformOSX() { + +} + + +void register_osx_exporter() { + + + Ref<EditorExportPlatformOSX> exporter = Ref<EditorExportPlatformOSX>( memnew(EditorExportPlatformOSX) ); + EditorImportExport::get_singleton()->add_export_platform(exporter); + + +} + diff --git a/platform/osx/export/export.h b/platform/osx/export/export.h new file mode 100644 index 0000000000..b149e482c9 --- /dev/null +++ b/platform/osx/export/export.h @@ -0,0 +1,3 @@ + + +void register_osx_exporter(); diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm new file mode 100644 index 0000000000..3546c1e71a --- /dev/null +++ b/platform/osx/godot_main_osx.mm @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* godot_main_osx.mm */ +/*************************************************************************/ +/* 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 "os_osx.h" +#include "main/main.h" + +#include <string.h> +#include <unistd.h> + +//#define main godot_main + +int main(int argc, char** argv) { + + + + if (argc>=1 && argv[0][0]=='/') { + //potentially launched from finder + int len = strlen(argv[0]); + while (len--) { + if (argv[0][len] == '/') break; + } + if (len>=0) { + char *path = (char *)malloc(len+1); + memcpy(path, argv[0], len); + path[len]=0; + + char *pathinfo = (char*)malloc(strlen(path)+strlen("/../Info.plist")+1); + //in real code you would check for errors in malloc here + strcpy(pathinfo, path); + strcat(pathinfo, "/../Info.plist"); + + FILE*f=fopen(pathinfo,"rb"); + if (f) { + //running from app bundle, as Info.plist was found + fclose(f); + chdir(path); + chdir("../Resources"); //data.pck, or just the files are here + + } + + free(path); + free(pathinfo); + } + + + + } + + OS_OSX os; + + + Error err = Main::setup(argv[0],argc-1,&argv[1]); + if (err!=OK) + return 255; + + if (Main::start()) + os.run(); // it is actually the OS that decides how to run + Main::cleanup(); + + return 0; +}; diff --git a/platform/osx/godot_osx.h b/platform/osx/godot_osx.h new file mode 100644 index 0000000000..ebac475089 --- /dev/null +++ b/platform/osx/godot_osx.h @@ -0,0 +1,37 @@ +/*************************************************************************/ +/* godot_osx.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 GODOT_OSX_H +#define GODOT_OSX_H + +#import <Cocoa/Cocoa.h> + +@interface GodotMain : NSObject +@end + +#endif diff --git a/platform/osx/godot_osx.mm b/platform/osx/godot_osx.mm new file mode 100644 index 0000000000..fb3c5808c7 --- /dev/null +++ b/platform/osx/godot_osx.mm @@ -0,0 +1,215 @@ +/*************************************************************************/ +/* godot_osx.mm */ +/*************************************************************************/ +/* 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 <sys/param.h> /* for MAXPATHLEN */ +#include <unistd.h> +#include "godot_osx.h" + +/* For some reaon, Apple removed setAppleMenu from the headers in 10.4, + but the method still is there and works. To avoid warnings, we declare + it ourselves here. */ +@interface NSApplication() +- (void)setAppleMenu:(NSMenu *)menu; +@end + +static int global_argc; +static char **global_argv; +static BOOL gCalledAppMainline = FALSE; + +static NSString *getApplicationName(void) +{ + const NSDictionary *dict; + NSString *appName = 0; + + /* Determine the application name */ + dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); + if (dict) + appName = [dict objectForKey: @"CFBundleName"]; + + if (![appName length]) + appName = [[NSProcessInfo processInfo] processName]; + + return appName; +} + +/* The main class of the application, the application's delegate */ +@implementation GodotMain + +static void setApplicationMenu(void) +{ + /* warning: this code is very odd */ + NSMenu *appleMenu; + NSMenuItem *menuItem; + NSString *title; + NSString *appName; + + appName = getApplicationName(); + appleMenu = [[NSMenu alloc] initWithTitle:@""]; + + /* Add menu items */ + title = [@"About " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Hide " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; + + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Quit " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + + + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:appleMenu]; + [[NSApp mainMenu] addItem:menuItem]; + + /* Tell the application object that this is now the application menu */ + [NSApp setAppleMenu:appleMenu]; + + /* Finally give up our references to the objects */ + [appleMenu release]; + [menuItem release]; +} + +/* Create a window menu */ +static void setupWindowMenu(void) +{ + NSMenu *windowMenu; + NSMenuItem *windowMenuItem; + NSMenuItem *menuItem; + + windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + + /* "Minimize" item */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; + [windowMenu addItem:menuItem]; + [menuItem release]; + + /* Put menu into the menubar */ + windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; + [windowMenuItem setSubmenu:windowMenu]; + [[NSApp mainMenu] addItem:windowMenuItem]; + + /* Tell the application object that this is now the window menu */ + [NSApp setWindowsMenu:windowMenu]; + + /* Finally give up our references to the objects */ + [windowMenu release]; + [windowMenuItem release]; +} + +/* Replacement for NSApplicationMain */ +static void CustomApplicationMain (int argc, char **argv) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + GodotMain *main; + + /* Ensure the application object is initialised */ + [NSApplication sharedApplication]; + + /* Set up the menubar */ + [NSApp setMainMenu:[[NSMenu alloc] init]]; + setApplicationMenu(); + setupWindowMenu(); + + main = [[main alloc] init]; + [NSApp setDelegate:main]; + + /* Start the main event loop */ + [NSApp run]; + + [main release]; + [pool release]; +} + +extern int godot_main(int argc, char** argv); + +/* Called when the internal event loop has just started running */ +- (void) applicationDidFinishLaunching: (NSNotification *) note +{ + int status; + + /* Hand off to main application code */ + gCalledAppMainline = TRUE; + + int ret = godot_main(global_argc, global_argv); + + exit(ret); +} +@end + +#ifdef main +# undef main +#endif + + +int main (int argc, char **argv) +{ + /* Copy the arguments into a global variable */ + /* This is passed if we are launched by double-clicking */ + if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { + global_argv = (char **) malloc(sizeof (char *) * 2); + global_argv[0] = argv[0]; + global_argv[1] = NULL; + global_argc = 1; + + // chdir to binary's dir when launched from finder + int len = strlen(global_argv[0]); + + while (len--){ + if (global_argv[0][len] == '/') break; + } + + if (len>=0) { + char *path = (char *)malloc(len+1); + memcpy(path, global_argv[0], len); + path[len]=0; + printf("Path: %s\n", path); + chdir(path); + } + + } else { + int i; + global_argc = argc; + global_argv = (char **) malloc(sizeof (char *) * (argc+1)); + for (i = 0; i <= argc; i++) + global_argv[i] = argv[i]; + } + + CustomApplicationMain (argc, argv); + return 0; +} + diff --git a/platform/osx/logo.png b/platform/osx/logo.png Binary files differnew file mode 100644 index 0000000000..2bcd3aa72e --- /dev/null +++ b/platform/osx/logo.png diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h new file mode 100644 index 0000000000..b9e381d6ec --- /dev/null +++ b/platform/osx/os_osx.h @@ -0,0 +1,166 @@ +/*************************************************************************/ +/* os_osx.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 OS_OSX_H +#define OS_OSX_H + + +#include "os/input.h" +#include "drivers/unix/os_unix.h" + +#include "servers/visual_server.h" +#include "servers/visual/visual_server_wrap_mt.h" +#include "servers/visual/rasterizer.h" +#include "servers/physics_server.h" +#include "servers/audio/audio_server_sw.h" +#include "servers/audio/sample_manager_sw.h" +#include "servers/spatial_sound/spatial_sound_server_sw.h" +#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" +#include "drivers/rtaudio/audio_driver_rtaudio.h" +#include "drivers/alsa/audio_driver_alsa.h" +#include "servers/physics_2d/physics_2d_server_sw.h" +#include "platform/osx/audio_driver_osx.h" +#include <ApplicationServices/ApplicationServices.h> + +//bitch +#undef CursorShape +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class OS_OSX : public OS_Unix { +public: + bool force_quit; + Rasterizer *rasterizer; + VisualServer *visual_server; + VideoMode current_videomode; + List<String> args; + MainLoop *main_loop; + unsigned int event_id; + + PhysicsServer *physics_server; + Physics2DServer *physics_2d_server; + + IP_Unix *ip_unix; + + AudioDriverOSX audio_driver_osx; + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSoundServerSW *spatial_sound_server; + SpatialSound2DServerSW *spatial_sound_2d_server; + + InputDefault *input; + + /* objc */ + + CGEventSourceRef eventSource; + + void process_events(); + + void* framework; +// pthread_key_t current; + bool mouse_grab; + Point2 mouse_pos; + uint32_t last_id; + + id delegate; + id window_delegate; + id window_object; + id window_view; + id autoreleasePool; + id cursor; + id pixelFormat; + id context; + + CursorShape cursor_shape; + +protected: + + 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 void initialize_core(); + virtual void initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver); + virtual void finalize(); + + virtual void set_main_loop( MainLoop * p_main_loop ); + virtual void delete_main_loop(); + + +public: + + + + + + + static OS_OSX* singleton; + + virtual String get_name(); + + virtual void set_cursor_shape(CursorShape p_shape); + + 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_icon(const Image& p_icon); + + virtual MainLoop *get_main_loop() const; + + virtual bool can_draw() const; + + virtual void set_clipboard(const String& p_text); + virtual String get_clipboard() const; + + virtual void release_rendering_thread(); + virtual void make_rendering_thread(); + virtual void swap_buffers(); + + Error shell_open(String p_uri); + void push_input(const InputEvent& p_event); + + 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_executable_path() const; + + virtual void move_window_to_foreground(); + + void run(); + + + OS_OSX(); +}; + +#endif diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm new file mode 100644 index 0000000000..86d1dbb4c2 --- /dev/null +++ b/platform/osx/os_osx.mm @@ -0,0 +1,1323 @@ +/*************************************************************************/ +/* os_osx.mm */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ +#import <Cocoa/Cocoa.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/hid/IOHIDLib.h> +#include <IOKit/hid/IOHIDKeys.h> + +#include "sem_osx.h" +#include "servers/visual/visual_server_raster.h" +//#include "drivers/opengl/rasterizer_gl.h" +//#include "drivers/gles2/rasterizer_gles2.h" +#include "os_osx.h" +#include <stdio.h> +#include <stdlib.h> +#include "print_string.h" +#include "servers/physics/physics_server_sw.h" +#include "drivers/gles2/rasterizer_instance_gles2.h" +#include "servers/visual/visual_server_wrap_mt.h" +#include "main/main.h" +#include "os/keyboard.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <libproc.h> +//uses portions of glfw + +//======================================================================== +// GLFW 3.0 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund <elmindreda@elmindreda.org> +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +static NSRect convertRectToBacking(NSRect contentRect) { + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) + return [OS_OSX::singleton->window_view convertRectToBacking:contentRect]; + else +#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + return contentRect; + +} + +static InputModifierState translateFlags(NSUInteger flags) +{ + InputModifierState mod; + + + mod.shift = (flags & NSShiftKeyMask); + mod.control = (flags & NSControlKeyMask); + mod.alt = (flags & NSAlternateKeyMask); + mod.meta = (flags & NSCommandKeyMask); + + return mod; +} + +static int mouse_x=0; +static int mouse_y=0; +static int prev_mouse_x=0; +static int prev_mouse_y=0; +static int button_mask=0; + + +@interface GodotApplication : NSApplication +@end + +@implementation GodotApplication + +// From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost +// This works around an AppKit bug, where key up events while holding +// down the command key don't get sent to the key window. +- (void)sendEvent:(NSEvent *)event +{ + if ([event type] == NSKeyUp && ([event modifierFlags] & NSCommandKeyMask)) + [[self keyWindow] sendEvent:event]; + else + [super sendEvent:event]; +} + +@end + +@interface GodotApplicationDelegate : NSObject +@end + +@implementation GodotApplicationDelegate + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ +/* _Godotwindow* window; + + for (window = _Godot.windowListHead; window; window = window->next) + _GodotInputWindowCloseRequest(window); +*/ + return NSTerminateCancel; +} + +- (void)applicationDidHide:(NSNotification *)notification +{ + /* _Godotwindow* window; + + for (window = _Godot.windowListHead; window; window = window->next) + _GodotInputWindowVisibility(window, GL_FALSE); + */ +} + +- (void)applicationDidUnhide:(NSNotification *)notification +{ + /* + _Godotwindow* window; + + for (window = _Godot.windowListHead; window; window = window->next) + { + if ([window_object isVisible]) + _GodotInputWindowVisibility(window, GL_TRUE); + } + */ +} + +- (void)applicationDidChangeScreenParameters:(NSNotification *) notification +{ + //_GodotInputMonitorChange(); +} + +@end + +@interface GodotWindowDelegate : NSObject +{ + // _Godotwindow* window; +} + +@end + +@implementation GodotWindowDelegate + + +- (BOOL)windowShouldClose:(id)sender +{ + //_GodotInputWindowCloseRequest(window); + if (OS_OSX::singleton->get_main_loop()) + OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); + return NO; +} + + + + +- (void)windowDidResize:(NSNotification *)notification +{ + [OS_OSX::singleton->context update]; + + const NSRect contentRect = [OS_OSX::singleton->window_view frame]; + const NSRect fbRect = convertRectToBacking(contentRect); + + OS_OSX::singleton->current_videomode.width=fbRect.size.width; + OS_OSX::singleton->current_videomode.height=fbRect.size.height; + + + // _GodotInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); + // _GodotInputWindowSize(window, contentRect.size.width, contentRect.size.height); + //_GodotInputWindowDamage(window); + + //if (window->cursorMode == Godot_CURSOR_DISABLED) + // centerCursor(window); +} + +- (void)windowDidMove:(NSNotification *)notification +{ + // [window->nsgl.context update]; + + // int x, y; + // _GodotPlatformGetWindowPos(window, &x, &y); + // _GodotInputWindowPos(window, x, y); + + //if (window->cursorMode == Godot_CURSOR_DISABLED) + // centerCursor(window); +} + +- (void)windowDidMiniaturize:(NSNotification *)notification +{ + // _GodotInputWindowIconify(window, GL_TRUE); +} + +- (void)windowDidDeminiaturize:(NSNotification *)notification +{ + //if (window->monitor) +// enterFullscreenMode(window); + + // _GodotInputWindowIconify(window, GL_FALSE); +} + +- (void)windowDidBecomeKey:(NSNotification *)notification +{ + // _GodotInputWindowFocus(window, GL_TRUE); + // _GodotPlatformSetCursorMode(window, window->cursorMode); + if (OS_OSX::singleton->get_main_loop()) + OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); +} + +- (void)windowDidResignKey:(NSNotification *)notification +{ + // _GodotInputWindowFocus(window, GL_FALSE); + // _GodotPlatformSetCursorMode(window, Godot_CURSOR_NORMAL); + if (OS_OSX::singleton->get_main_loop()) + OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); +} + +@end + +@interface GodotContentView : NSView +{ + NSTrackingArea* trackingArea; +} + + + +@end + +@implementation GodotContentView + ++ (void)initialize +{ + if (self == [GodotContentView class]) + { + /* if (_glfw.ns.cursor == nil) + { + NSImage* data = [[NSImage alloc] initWithSize:NSMakeSize(1, 1)]; + _glfw.ns.cursor = [[NSCursor alloc] initWithImage:data + hotSpot:NSZeroPoint]; + [data release]; + }*/ + } +} + +- (id)init +{ + self = [super init]; + trackingArea = nil; + [self updateTrackingAreas]; + + return self; +} + +-(void)dealloc +{ + [trackingArea release]; + [super dealloc]; +} + +- (BOOL)isOpaque +{ + return YES; +} + +- (BOOL)canBecomeKeyView +{ + return YES; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (void)cursorUpdate:(NSEvent *)event +{ + // setModeCursor(window, window->cursorMode); +} + +- (void)mouseDown:(NSEvent *)event +{ + + print_line("mouse down:"); + button_mask|=BUTTON_MASK_LEFT; + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=BUTTON_LEFT; + ev.mouse_button.pressed=true; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + ev.mouse_button.doubleclick = [event clickCount]==2; + ev.mouse_button.mod = translateFlags([event modifierFlags]); + OS_OSX::singleton->push_input(ev); + + + /* _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_LEFT, + GLFW_PRESS, + translateFlags([event modifierFlags]));*/ +} + +- (void)mouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)mouseUp:(NSEvent *)event +{ + + button_mask&=~BUTTON_MASK_LEFT; + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=BUTTON_LEFT; + ev.mouse_button.pressed=false; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + ev.mouse_button.mod = translateFlags([event modifierFlags]); + OS_OSX::singleton->push_input(ev); + + /* _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_LEFT, + GLFW_RELEASE, + translateFlags([event modifierFlags]));*/ +} + +- (void)mouseMoved:(NSEvent *)event +{ + + InputEvent ev; + ev.type=InputEvent::MOUSE_MOTION; + ev.mouse_motion.button_mask=button_mask; + prev_mouse_x=mouse_x; + prev_mouse_y=mouse_y; + const NSRect contentRect = [OS_OSX::singleton->window_view frame]; + const NSPoint p = [event locationInWindow]; + mouse_x = p.x; + mouse_y = contentRect.size.height - p.y; + ev.mouse_motion.x=mouse_x; + ev.mouse_motion.y=mouse_y; + ev.mouse_motion.global_x=mouse_x; + ev.mouse_motion.global_y=mouse_y; + ev.mouse_motion.relative_x=mouse_x - prev_mouse_x; + ev.mouse_motion.relative_y=mouse_y - prev_mouse_y; + ev.mouse_motion.mod = translateFlags([event modifierFlags]); + + +// ev.mouse_motion.relative_x=[event deltaX]; +// ev.mouse_motion.relative_y=[event deltaY]; + + OS_OSX::singleton->push_input(ev); + + + /* if (window->cursorMode == GLFW_CURSOR_DISABLED) + _glfwInputCursorMotion(window, [event deltaX], [event deltaY]); + else + { + const NSRect contentRect = [window->ns.view frame]; + const NSPoint p = [event locationInWindow]; + + _glfwInputCursorMotion(window, p.x, contentRect.size.height - p.y); + }*/ +} + +- (void)rightMouseDown:(NSEvent *)event +{ + + button_mask|=BUTTON_MASK_RIGHT; + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=BUTTON_RIGHT; + ev.mouse_button.pressed=true; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + ev.mouse_button.mod = translateFlags([event modifierFlags]); + OS_OSX::singleton->push_input(ev); + + /* _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_RIGHT, + GLFW_PRESS, + translateFlags([event modifierFlags]));*/ +} + +- (void)rightMouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)rightMouseUp:(NSEvent *)event +{ + + button_mask&=~BUTTON_MASK_RIGHT; + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=BUTTON_RIGHT; + ev.mouse_button.pressed=false; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + ev.mouse_button.mod = translateFlags([event modifierFlags]); + OS_OSX::singleton->push_input(ev); + + /*_glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_RIGHT, + GLFW_RELEASE, + translateFlags([event modifierFlags]));*/ +} + +- (void)otherMouseDown:(NSEvent *)event +{ + + if ((int) [event buttonNumber]!=2) + return; + + button_mask|=BUTTON_MASK_MIDDLE; + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=BUTTON_MIDDLE; + ev.mouse_button.pressed=true; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + ev.mouse_button.mod = translateFlags([event modifierFlags]); + OS_OSX::singleton->push_input(ev); + + /*_glfwInputMouseClick(window, + (int) [event buttonNumber], + GLFW_PRESS, + translateFlags([event modifierFlags]));*/ +} + +- (void)otherMouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)otherMouseUp:(NSEvent *)event +{ + + if ((int) [event buttonNumber]!=2) + return; + + button_mask&=~BUTTON_MASK_MIDDLE; + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=BUTTON_MIDDLE; + ev.mouse_button.pressed=false; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + ev.mouse_button.mod = translateFlags([event modifierFlags]); + OS_OSX::singleton->push_input(ev); + /* _glfwInputMouseClick(window, + (int) [event buttonNumber], + GLFW_RELEASE, + translateFlags([event modifierFlags]));*/ +} + +- (void)mouseExited:(NSEvent *)event +{ + // _glfwInputCursorEnter(window, GL_FALSE); +} + +- (void)mouseEntered:(NSEvent *)event +{ + // _glfwInputCursorEnter(window, GL_TRUE); +} + +- (void)viewDidChangeBackingProperties +{ + /* const NSRect contentRect = [window->ns.view frame]; + const NSRect fbRect = convertRectToBacking(window, contentRect); + + _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);*/ +} + +- (void)updateTrackingAreas +{ + if (trackingArea != nil) + { + [self removeTrackingArea:trackingArea]; + [trackingArea release]; + } + + NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow | + NSTrackingCursorUpdate | + NSTrackingInVisibleRect; + + trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] + options:options + owner:self + userInfo:nil]; + + [self addTrackingArea:trackingArea]; + [super updateTrackingAreas]; +} + +// Translates a OS X keycode to a Godot keycode +// +static int translateKey(unsigned int key) +{ + // Keyboard symbol translation table + static const unsigned int table[128] = + { + /* 00 */ KEY_A, + /* 01 */ KEY_S, + /* 02 */ KEY_D, + /* 03 */ KEY_F, + /* 04 */ KEY_H, + /* 05 */ KEY_G, + /* 06 */ KEY_Z, + /* 07 */ KEY_X, + /* 08 */ KEY_C, + /* 09 */ KEY_V, + /* 0a */ KEY_UNKNOWN, + /* 0b */ KEY_B, + /* 0c */ KEY_Q, + /* 0d */ KEY_W, + /* 0e */ KEY_E, + /* 0f */ KEY_R, + /* 10 */ KEY_Y, + /* 11 */ KEY_T, + /* 12 */ KEY_1, + /* 13 */ KEY_2, + /* 14 */ KEY_3, + /* 15 */ KEY_4, + /* 16 */ KEY_6, + /* 17 */ KEY_5, + /* 18 */ KEY_EQUAL, + /* 19 */ KEY_9, + /* 1a */ KEY_7, + /* 1b */ KEY_MINUS, + /* 1c */ KEY_8, + /* 1d */ KEY_0, + /* 1e */ KEY_BRACERIGHT, + /* 1f */ KEY_O, + /* 20 */ KEY_U, + /* 21 */ KEY_BRACELEFT, + /* 22 */ KEY_I, + /* 23 */ KEY_P, + /* 24 */ KEY_RETURN, + /* 25 */ KEY_L, + /* 26 */ KEY_J, + /* 27 */ KEY_APOSTROPHE, + /* 28 */ KEY_K, + /* 29 */ KEY_SEMICOLON, + /* 2a */ KEY_BACKSLASH, + /* 2b */ KEY_COMMA, + /* 2c */ KEY_SLASH, + /* 2d */ KEY_N, + /* 2e */ KEY_M, + /* 2f */ KEY_PERIOD, + /* 30 */ KEY_TAB, + /* 31 */ KEY_SPACE, + /* 32 */ KEY_QUOTELEFT, + /* 33 */ KEY_BACKSPACE, + /* 34 */ KEY_UNKNOWN, + /* 35 */ KEY_ESCAPE, + /* 36 */ KEY_META, + /* 37 */ KEY_META, + /* 38 */ KEY_SHIFT, + /* 39 */ KEY_CAPSLOCK, + /* 3a */ KEY_ALT, + /* 3b */ KEY_CONTROL, + /* 3c */ KEY_SHIFT, + /* 3d */ KEY_ALT, + /* 3e */ KEY_CONTROL, + /* 3f */ KEY_UNKNOWN, /* Function */ + /* 40 */ KEY_UNKNOWN, + /* 41 */ KEY_KP_PERIOD, + /* 42 */ KEY_UNKNOWN, + /* 43 */ KEY_KP_MULTIPLY, + /* 44 */ KEY_UNKNOWN, + /* 45 */ KEY_KP_ADD, + /* 46 */ KEY_UNKNOWN, + /* 47 */ KEY_NUMLOCK, /* Really KeypadClear... */ + /* 48 */ KEY_UNKNOWN, /* VolumeUp */ + /* 49 */ KEY_UNKNOWN, /* VolumeDown */ + /* 4a */ KEY_UNKNOWN, /* Mute */ + /* 4b */ KEY_KP_DIVIDE, + /* 4c */ KEY_KP_ENTER, + /* 4d */ KEY_UNKNOWN, + /* 4e */ KEY_KP_SUBSTRACT, + /* 4f */ KEY_UNKNOWN, + /* 50 */ KEY_UNKNOWN, + /* 51 */ KEY_EQUAL, //wtf equal? + /* 52 */ KEY_KP_0, + /* 53 */ KEY_KP_1, + /* 54 */ KEY_KP_2, + /* 55 */ KEY_KP_3, + /* 56 */ KEY_KP_4, + /* 57 */ KEY_KP_5, + /* 58 */ KEY_KP_6, + /* 59 */ KEY_KP_7, + /* 5a */ KEY_UNKNOWN, + /* 5b */ KEY_KP_8, + /* 5c */ KEY_KP_9, + /* 5d */ KEY_UNKNOWN, + /* 5e */ KEY_UNKNOWN, + /* 5f */ KEY_UNKNOWN, + /* 60 */ KEY_F5, + /* 61 */ KEY_F6, + /* 62 */ KEY_F7, + /* 63 */ KEY_F3, + /* 64 */ KEY_F8, + /* 65 */ KEY_F9, + /* 66 */ KEY_UNKNOWN, + /* 67 */ KEY_F11, + /* 68 */ KEY_UNKNOWN, + /* 69 */ KEY_F13, + /* 6a */ KEY_F16, + /* 6b */ KEY_F14, + /* 6c */ KEY_UNKNOWN, + /* 6d */ KEY_F10, + /* 6e */ KEY_UNKNOWN, + /* 6f */ KEY_F12, + /* 70 */ KEY_UNKNOWN, + /* 71 */ KEY_F15, + /* 72 */ KEY_INSERT, /* Really Help... */ + /* 73 */ KEY_HOME, + /* 74 */ KEY_PAGEUP, + /* 75 */ KEY_DELETE, + /* 76 */ KEY_F4, + /* 77 */ KEY_END, + /* 78 */ KEY_F2, + /* 79 */ KEY_PAGEDOWN, + /* 7a */ KEY_F1, + /* 7b */ KEY_LEFT, + /* 7c */ KEY_RIGHT, + /* 7d */ KEY_DOWN, + /* 7e */ KEY_UP, + /* 7f */ KEY_UNKNOWN, + }; + + if (key >= 128) + return KEY_UNKNOWN; + + return table[key]; +} +- (void)keyDown:(NSEvent *)event +{ + InputEvent ev; + ev.type=InputEvent::KEY; + ev.key.pressed=true; + ev.key.mod=translateFlags([event modifierFlags]); + ev.key.scancode = translateKey([event keyCode]); + ev.key.echo = [event isARepeat]; + + NSString* characters = [event characters]; + NSUInteger i, length = [characters length]; + + + if (length>0 && keycode_has_unicode(ev.key.scancode)) { + + + for (i = 0; i < length; i++) { + ev.key.unicode=[characters characterAtIndex:i]; + OS_OSX::singleton->push_input(ev); + ev.key.scancode=0; + } + + } else { + OS_OSX::singleton->push_input(ev); + } +} + +- (void)flagsChanged:(NSEvent *)event +{ + /* int action; + unsigned int newModifierFlags = + [event modifierFlags] & NSDeviceIndependentModifierFlagsMask; + + if (newModifierFlags > window->ns.modifierFlags) + action = GLFW_PRESS; + else + action = GLFW_RELEASE; + + window->ns.modifierFlags = newModifierFlags; + + const int key = translateKey([event keyCode]); + const int mods = translateFlags([event modifierFlags]); + _glfwInputKey(window, key, [event keyCode], action, mods);*/ +} + +- (void)keyUp:(NSEvent *)event +{ + + InputEvent ev; + ev.type=InputEvent::KEY; + ev.key.pressed=false; + ev.key.mod=translateFlags([event modifierFlags]); + ev.key.scancode = translateKey([event keyCode]); + OS_OSX::singleton->push_input(ev); + + + /* const int key = translateKey([event keyCode]); + const int mods = translateFlags([event modifierFlags]); + _glfwInputKey(window, key, [event keyCode], GLFW_RELEASE, mods);*/ +} + +- (void)scrollWheel:(NSEvent *)event +{ + + double deltaX, deltaY; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) + { + deltaX = [event scrollingDeltaX]; + deltaY = [event scrollingDeltaY]; + + if ([event hasPreciseScrollingDeltas]) + { + deltaX *= 0.1; + deltaY *= 0.1; + } + } + else +#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + { + deltaX = [event deltaX]; + deltaY = [event deltaY]; + } + + + if (fabs(deltaY)) { + + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.mouse_button.button_index=deltaY >0 ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN; + ev.mouse_button.pressed=true; + ev.mouse_button.x=mouse_x; + ev.mouse_button.y=mouse_y; + ev.mouse_button.global_x=mouse_x; + ev.mouse_button.global_y=mouse_y; + ev.mouse_button.button_mask=button_mask; + OS_OSX::singleton->push_input(ev); + ev.mouse_button.pressed=false; + OS_OSX::singleton->push_input(ev); + } + +} + +@end + +@interface GodotWindow : NSWindow {} +@end + +@implementation GodotWindow + +- (BOOL)canBecomeKeyWindow +{ + // Required for NSBorderlessWindowMask windows + return YES; +} + +@end + + +int OS_OSX::get_video_driver_count() const { + + return 1; +} +const char * OS_OSX::get_video_driver_name(int p_driver) const { + + return "GLES2"; +} + +OS::VideoMode OS_OSX::get_default_video_mode() const { + + VideoMode vm; + vm.width=800; + vm.height=600; + vm.fullscreen=false; + vm.resizable=true; + return vm; +} + + +void OS_OSX::initialize_core() { + + OS_Unix::initialize_core(); + SemaphoreOSX::make_default(); + +} + +void OS_OSX::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + /*** OSX INITIALIZATION ***/ + /*** OSX INITIALIZATION ***/ + /*** OSX INITIALIZATION ***/ + + current_videomode=p_desired; + window_delegate = [[GodotWindowDelegate alloc] init]; + + // Don't use accumulation buffer support; it's not accelerated + // Aux buffers probably aren't accelerated either + + unsigned int styleMask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | (current_videomode.resizable?NSResizableWindowMask:0); + + + window_object = [[GodotWindow alloc] + initWithContentRect:NSMakeRect(0, 0, current_videomode.width,current_videomode.height) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + + ERR_FAIL_COND( window_object==nil ); + + window_view = [[GodotContentView alloc] init]; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) { + [window_view setWantsBestResolutionOpenGLSurface:YES]; + if (current_videomode.resizable) + [window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + } +#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + +// [window_object setTitle:[NSString stringWithUTF8String:"GodotEnginies"]]; + [window_object setContentView:window_view]; + [window_object setDelegate:window_delegate]; + [window_object setAcceptsMouseMovedEvents:YES]; + [window_object center]; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) + [window_object setRestorable:NO]; +#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + + unsigned int attributeCount = 0; + + // OS X needs non-zero color size, so set resonable values + int colorBits = 24; + + // Fail if a robustness strategy was requested + + +#define ADD_ATTR(x) { attributes[attributeCount++] = x; } +#define ADD_ATTR2(x, y) { ADD_ATTR(x); ADD_ATTR(y); } + + // Arbitrary array size here + NSOpenGLPixelFormatAttribute attributes[40]; + + ADD_ATTR(NSOpenGLPFADoubleBuffer); + ADD_ATTR(NSOpenGLPFAClosestPolicy); + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (false/* use gl3*/) + ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); +#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + + ADD_ATTR2(NSOpenGLPFAColorSize, colorBits); + + /* if (fbconfig->alphaBits > 0) + ADD_ATTR2(NSOpenGLPFAAlphaSize, fbconfig->alphaBits);*/ + + ADD_ATTR2(NSOpenGLPFADepthSize, 24); + + ADD_ATTR2(NSOpenGLPFAStencilSize, 8); + + /*if (fbconfig->stereo) + ADD_ATTR(NSOpenGLPFAStereo);*/ + + /* if (fbconfig->samples > 0) + { + ADD_ATTR2(NSOpenGLPFASampleBuffers, 1); + ADD_ATTR2(NSOpenGLPFASamples, fbconfig->samples); + }*/ + + // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB + // frambuffer, so there's no need (and no way) to request it + + ADD_ATTR(0); + +#undef ADD_ATTR +#undef ADD_ATTR2 + + pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + ERR_FAIL_COND( pixelFormat == nil); + + + context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat + shareContext:nil]; + + ERR_FAIL_COND(context==nil); + + + [context setView:window_view]; + + [context makeCurrentContext]; + + [NSApp activateIgnoringOtherApps:YES]; + + [window_object makeKeyAndOrderFront:nil]; + + + /*** END OSX INITIALIZATION ***/ + /*** END OSX INITIALIZATION ***/ + /*** END OSX INITIALIZATION ***/ + + bool use_gl2=p_video_driver!=1; + + + + AudioDriverManagerSW::add_driver(&audio_driver_osx); + + + rasterizer = instance_RasterizerGLES2(); + + visual_server = memnew( VisualServerRaster(rasterizer) ); + + if (get_render_thread_mode()!=RENDER_THREAD_UNSAFE) { + + visual_server =memnew(VisualServerWrapMT(visual_server,get_render_thread_mode()==RENDER_SEPARATE_THREAD)); + } + 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 ); + + _ensure_data_dir(); + + +} +void OS_OSX::finalize() { + +} + +void OS_OSX::set_main_loop( MainLoop * p_main_loop ) { + + main_loop=p_main_loop; + input->set_main_loop(p_main_loop); + +} + +void OS_OSX::delete_main_loop() { + + memdelete(main_loop); + main_loop=NULL; +} + + +String OS_OSX::get_name() { + + return "OSX"; +} + +void OS_OSX::set_cursor_shape(CursorShape p_shape) { + + if (cursor_shape==p_shape) + return; + + switch(p_shape) { + case CURSOR_ARROW: [[NSCursor arrowCursor] set]; break; + case CURSOR_IBEAM: [[NSCursor IBeamCursor] set]; break; + case CURSOR_POINTING_HAND: [[NSCursor pointingHandCursor] set]; break; + case CURSOR_CROSS: [[NSCursor crosshairCursor] set]; break; + case CURSOR_WAIT: [[NSCursor arrowCursor] set]; break; + case CURSOR_BUSY: [[NSCursor arrowCursor] set]; break; + case CURSOR_DRAG: [[NSCursor closedHandCursor] set]; break; + case CURSOR_CAN_DROP: [[NSCursor openHandCursor] set]; break; + case CURSOR_FORBIDDEN: [[NSCursor arrowCursor] set]; break; + case CURSOR_VSIZE: [[NSCursor resizeUpDownCursor] set]; break; + case CURSOR_HSIZE: [[NSCursor resizeLeftRightCursor] set]; break; + case CURSOR_BDIAGSIZE: [[NSCursor arrowCursor] set]; break; + case CURSOR_FDIAGSIZE: [[NSCursor arrowCursor] set]; break; + case CURSOR_MOVE: [[NSCursor arrowCursor] set]; break; + case CURSOR_VSPLIT: [[NSCursor resizeUpDownCursor] set]; break; + case CURSOR_HSPLIT: [[NSCursor resizeLeftRightCursor] set]; break; + case CURSOR_HELP: [[NSCursor arrowCursor] set]; break; + default: {}; + } + + cursor_shape=p_shape; +} + +void OS_OSX::set_mouse_show(bool p_show) { + +} +void OS_OSX::set_mouse_grab(bool p_grab) { + +} +bool OS_OSX::is_mouse_grab_enabled() const { + + return mouse_grab; +} +Point2 OS_OSX::get_mouse_pos() const { + + return Vector2(mouse_x,mouse_y); +} +int OS_OSX::get_mouse_button_state() const { + return button_mask; +} +void OS_OSX::set_window_title(const String& p_title) { + + [window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]]; + +} + +void OS_OSX::set_icon(const Image& p_icon) { + + Image img=p_icon; + img.convert(Image::FORMAT_RGBA); + NSBitmapImageRep *imgrep= [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL + pixelsWide: p_icon.get_width() + pixelsHigh: p_icon.get_height() + bitsPerSample: 8 + samplesPerPixel: 4 + hasAlpha: YES + isPlanar: NO + colorSpaceName: NSDeviceRGBColorSpace + bytesPerRow: p_icon.get_width()*4 + bitsPerPixel: 32] autorelease]; + ERR_FAIL_COND(imgrep==nil); + uint8_t *pixels = [imgrep bitmapData]; + + int len = img.get_width()*img.get_height(); + DVector<uint8_t> data = img.get_data(); + DVector<uint8_t>::Read r = data.read(); + + /* Premultiply the alpha channel */ + for (int i = 0; i<len ; i++) { + uint8_t alpha = r[i*4+3]; + pixels[i*4+0] = (uint8_t)(((uint16_t)r[i*4+0] * alpha) / 255); + pixels[i*4+1] = (uint8_t)(((uint16_t)r[i*4+1] * alpha) / 255); + pixels[i*4+2] = (uint8_t)(((uint16_t)r[i*4+2] * alpha) / 255); + pixels[i*4+3] = alpha; + + } + + NSImage *nsimg = [[[NSImage alloc] initWithSize: NSMakeSize(img.get_width(),img.get_height())] autorelease]; + ERR_FAIL_COND(nsimg == nil); + [nsimg addRepresentation: imgrep]; + + [NSApp setApplicationIconImage:nsimg]; + +} + +MainLoop *OS_OSX::get_main_loop() const { + + return main_loop; +} + +bool OS_OSX::can_draw() const { + + return true; +} + +void OS_OSX::set_clipboard(const String& p_text) { + + NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil]; + + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard declareTypes:types owner:nil]; + [pasteboard setString:[NSString stringWithUTF8String:p_text.utf8().get_data()] + forType:NSStringPboardType]; +} +String OS_OSX::get_clipboard() const { + + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + + if (![[pasteboard types] containsObject:NSStringPboardType]) + { + return ""; + } + + NSString* object = [pasteboard stringForType:NSStringPboardType]; + if (!object) + { + return ""; + } + + char *utfs = strdup([object UTF8String]); + String ret; + ret.parse_utf8(utfs); + free(utfs); + + return ret; +} + +void OS_OSX::release_rendering_thread() { + + [NSOpenGLContext clearCurrentContext]; + +} +void OS_OSX::make_rendering_thread() { + + [context makeCurrentContext]; + +} + +Error OS_OSX::shell_open(String p_uri) { + + [[NSWorkspace sharedWorkspace] openURL:[[NSURL alloc] initWithString:[NSString stringWithUTF8String:p_uri.utf8().get_data()]]]; + return OK; +} + +void OS_OSX::swap_buffers() { + + [context flushBuffer]; + +} + + + +void OS_OSX::set_video_mode(const VideoMode& p_video_mode,int p_screen) { + +} + +OS::VideoMode OS_OSX::get_video_mode(int p_screen) const { + + return current_videomode; +} +void OS_OSX::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const { + +} + +void OS_OSX::move_window_to_foreground() { + + [window_object orderFrontRegardless]; +} + +String OS_OSX::get_executable_path() const { + + int ret; + pid_t pid; + char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; + + pid = getpid(); + ret = proc_pidpath (pid, pathbuf, sizeof(pathbuf)); + if ( ret <= 0 ) { + return OS::get_executable_path(); + } else { + String path; + path.parse_utf8(pathbuf); + + return path; + } + +} + + +void OS_OSX::process_events() { + + while (true) { + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event == nil) + break; + + [NSApp sendEvent:event]; + } + + [autoreleasePool drain]; + autoreleasePool = [[NSAutoreleasePool alloc] init]; +} + + + +void OS_OSX::push_input(const InputEvent& p_event) { + + InputEvent ev=p_event; + ev.ID=last_id++; + //print_line("EV: "+String(ev)); + input->parse_input_event(ev); +} + +void OS_OSX::run() { + + force_quit = false; + + if (!main_loop) + return; + + main_loop->init(); + +// uint64_t last_ticks=get_ticks_usec(); + +// int frames=0; +// uint64_t frame=0; + + while (!force_quit) { + + process_events(); // get rid of pending events +// process_joysticks(); + if (Main::iteration()==true) + break; + }; + + main_loop->finish(); +} + + +OS_OSX* OS_OSX::singleton=NULL; + +OS_OSX::OS_OSX() { + + singleton=this; + autoreleasePool = [[NSAutoreleasePool alloc] init]; + + eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); + ERR_FAIL_COND(!eventSource); + + CGEventSourceSetLocalEventsSuppressionInterval(eventSource, 0.0); + + + /*if (pthread_key_create(&_Godot.nsgl.current, NULL) != 0) + { + _GodotInputError(Godot_PLATFORM_ERROR, + "NSGL: Failed to create context TLS"); + return GL_FALSE; + }*/ + + framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + ERR_FAIL_COND(!framework); + + // Implicitly create shared NSApplication instance + [GodotApplication sharedApplication]; + + // In case we are unbundled, make us a proper UI application + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + + #if 0 + // Menu bar setup must go between sharedApplication above and + // finishLaunching below, in order to properly emulate the behavior + // of NSApplicationMain + createMenuBar(); + #endif + + [NSApp finishLaunching]; + + delegate = [[GodotApplicationDelegate alloc] init]; + ERR_FAIL_COND(!delegate); + [NSApp setDelegate:delegate]; + + + last_id=1; + cursor_shape=CURSOR_ARROW; + + +} diff --git a/platform/osx/platform_config.h b/platform/osx/platform_config.h new file mode 100644 index 0000000000..da4265f3cf --- /dev/null +++ b/platform/osx/platform_config.h @@ -0,0 +1,31 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include <alloca.h> +#define GLES2_INCLUDE_H "gl_context/glew.h" +#define GLES1_INCLUDE_H "gl_context/glew.h" diff --git a/platform/osx/sem_osx.cpp b/platform/osx/sem_osx.cpp new file mode 100644 index 0000000000..be70bedf84 --- /dev/null +++ b/platform/osx/sem_osx.cpp @@ -0,0 +1,115 @@ +/*************************************************************************/ +/* sem_osx.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 "sem_osx.h" + +#include <unistd.h> +#include <fcntl.h> + +void cgsem_init(cgsem_t *cgsem) +{ + int flags, fd, i; + + pipe(cgsem->pipefd); + + /* Make the pipes FD_CLOEXEC to allow them to close should we call + * execv on restart. */ + for (i = 0; i < 2; i++) { + fd = cgsem->pipefd[i]; + flags = fcntl(fd, F_GETFD, 0); + flags |= FD_CLOEXEC; + fcntl(fd, F_SETFD, flags); + } +} + +void cgsem_post(cgsem_t *cgsem) +{ + const char buf = 1; + + write(cgsem->pipefd[1], &buf, 1); +} + +void cgsem_wait(cgsem_t *cgsem) +{ + char buf; + + read(cgsem->pipefd[0], &buf, 1); +} + +void cgsem_destroy(cgsem_t *cgsem) +{ + close(cgsem->pipefd[1]); + close(cgsem->pipefd[0]); +} + + +#include "os/memory.h" +#include <errno.h> + + +Error SemaphoreOSX::wait() { + + cgsem_wait(&sem); + return OK; +} + +Error SemaphoreOSX::post() { + + cgsem_post(&sem); + + return OK; +} +int SemaphoreOSX::get() const { + + return 0; +} + + +Semaphore *SemaphoreOSX::create_semaphore_osx() { + + return memnew( SemaphoreOSX ); +} + +void SemaphoreOSX::make_default() { + + create_func=create_semaphore_osx; +} + +SemaphoreOSX::SemaphoreOSX() { + + cgsem_init(&sem); +} + + +SemaphoreOSX::~SemaphoreOSX() { + + cgsem_destroy(&sem); +} + + + diff --git a/platform/osx/sem_osx.h b/platform/osx/sem_osx.h new file mode 100644 index 0000000000..65bed7397f --- /dev/null +++ b/platform/osx/sem_osx.h @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* sem_osx.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 SEM_OSX_H +#define SEM_OSX_H + +struct cgsem { + int pipefd[2]; +}; + +typedef struct cgsem cgsem_t; + +#include "os/semaphore.h" + +class SemaphoreOSX : public Semaphore { + + mutable cgsem_t sem; + + static Semaphore *create_semaphore_osx(); + +public: + + virtual Error wait(); + virtual Error post(); + virtual int get() const; + + static void make_default(); + SemaphoreOSX(); + + ~SemaphoreOSX(); + +}; + + +#endif diff --git a/platform/server/SCsub b/platform/server/SCsub new file mode 100644 index 0000000000..3dda6b4395 --- /dev/null +++ b/platform/server/SCsub @@ -0,0 +1,8 @@ +Import('env') + + +common_server=[\ + "os_server.cpp",\ +] + +env.Program('#bin/godot_server',['godot_server.cpp']+common_server) diff --git a/platform/server/detect.py b/platform/server/detect.py new file mode 100644 index 0000000000..1ce8c23229 --- /dev/null +++ b/platform/server/detect.py @@ -0,0 +1,91 @@ + +import os +import sys + + +def is_active(): + return True + +def get_name(): + return "Server" + + +def can_build(): + + if (os.name!="posix"): + return False + + return True # enabled + +def get_opts(): + + return [ + ('use_llvm','Use llvm compiler','no'), + ('force_32_bits','Force 32 bits binary','no') + ] + +def get_flags(): + + return [ + ('builtin_zlib', 'no'), + ] + + + +def configure(env): + + env.Append(CPPPATH=['#platform/server']) + if (env["use_llvm"]=="yes"): + env["CC"]="clang" + env["CXX"]="clang++" + env["LD"]="clang++" + + env['OBJSUFFIX'] = ".srv"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".srv"+env['LIBSUFFIX'] + + if (env["force_32_bits"]!="no"): + env['OBJSUFFIX'] = ".32"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".32"+env['LIBSUFFIX'] + + + if (env["tools"]=="no"): + #no tools suffix + env['OBJSUFFIX'] = ".nt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".nt"+env['LIBSUFFIX'] + + + 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"]=="debug"): + + env.Append(CCFLAGS=['-g2', '-Wall','-DDEBUG_ENABLED','-DDEBUG_MEMORY_ENABLED']) + + elif (env["target"]=="profile"): + + env.Append(CCFLAGS=['-g','-pg']) + env.Append(LINKFLAGS=['-pg']) + + + env.Append(CPPFLAGS=['-DSERVER_ENABLED','-DUNIX_ENABLED']) + env.Append(LIBS=['pthread','z']) #TODO detect linux/BSD! + + if (env["force_32_bits"]=="yes"): + env.Append(CPPFLAGS=['-m32']) + env.Append(LINKFLAGS=['-m32','-L/usr/lib/i386-linux-gnu']) + + if (env["CXX"]=="clang++"): + env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) + env["CC"]="clang" + env["LD"]="clang++" + diff --git a/platform/server/godot_server.cpp b/platform/server/godot_server.cpp new file mode 100644 index 0000000000..3f817c7237 --- /dev/null +++ b/platform/server/godot_server.cpp @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* godot_server.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 "main/main.h" +#include "os_server.h" + +int main(int argc, char* argv[]) { + + OS_Server os; + + Error err = Main::setup(argv[0],argc-1,&argv[1]); + if (err!=OK) + return 255; + + if (Main::start()) + os.run(); // it is actually the OS that decides how to run + Main::cleanup(); + + return os.get_exit_code(); +} diff --git a/platform/server/logo.h b/platform/server/logo.h new file mode 100644 index 0000000000..bb383145f5 --- /dev/null +++ b/platform/server/logo.h @@ -0,0 +1,2 @@ + /* AUTOGENERATED FILE, DO NOT EDIT */ + static const unsigned char _server_logo[]={0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0x73,0x7a,0x7a,0xf4,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x12,0x0,0x0,0xb,0x12,0x1,0xd2,0xdd,0x7e,0xfc,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdd,0x9,0x8,0x10,0x20,0x27,0x86,0x78,0xdd,0x76,0x0,0x0,0x7,0xff,0x49,0x44,0x41,0x54,0x58,0xc3,0xe5,0x97,0x5b,0x6c,0x5c,0xc5,0x19,0xc7,0x7f,0x67,0xe6,0xac,0xf7,0x7e,0xb1,0xbd,0x4e,0x7c,0x49,0xe2,0x24,0xb6,0x43,0x1c,0xc,0xa6,0x20,0x94,0x4,0xda,0x26,0x5,0x51,0x28,0xb4,0x5c,0x2a,0x55,0x50,0xb5,0x85,0xa6,0xea,0x13,0x8,0xa9,0xa5,0x2f,0x54,0xaa,0x8a,0xd4,0xdb,0x4b,0xdb,0x27,0xaa,0x88,0x16,0x1a,0x50,0x8a,0x84,0xd4,0xf6,0xa1,0x2,0x24,0x6e,0x89,0x80,0x8,0x8,0x71,0x2e,0x26,0xc1,0x71,0x12,0x27,0xbe,0x67,0xd7,0xb7,0xf5,0xae,0xd7,0xf6,0xee,0xd9,0x3d,0x7b,0x66,0xa6,0xf,0x6b,0x8c,0x93,0x42,0xc,0x42,0xed,0x4b,0x47,0x1a,0x9d,0xf3,0x30,0x33,0xdf,0xff,0xfb,0x7f,0xff,0x6f,0xe6,0xfb,0xe0,0xff,0x7d,0x58,0x9f,0x77,0xc3,0xbd,0xf7,0xdd,0xdf,0xf4,0xd8,0x63,0x8f,0xee,0xac,0xab,0xab,0xbb,0x49,0x79,0xaa,0xa1,0xe2,0x55,0xd2,0xe5,0x72,0x39,0x35,0x3f,0x3f,0x9f,0x4e,0xa5,0xd2,0xa9,0x43,0xef,0xbe,0x97,0xda,0xbf,0xef,0xe9,0x14,0x60,0xbe,0x30,0x80,0xef,0x3d,0xb8,0x67,0xdd,0xf,0x1f,0xfa,0xc1,0x8e,0x86,0x86,0xe4,0xce,0x40,0x4d,0xe0,0x26,0x21,0xe5,0x8e,0x78,0x3c,0x4a,0xc5,0xad,0x50,0x2a,0x97,0x50,0x9e,0x42,0x6b,0x85,0xa7,0x14,0x46,0x6b,0xb4,0xd6,0xb8,0xae,0xcb,0xc2,0xc2,0x22,0x8b,0x8b,0x8b,0xb8,0xae,0x9b,0xf1,0x94,0x97,0x5a,0x58,0x28,0xa4,0xa,0x85,0xe2,0xd3,0x3f,0xfd,0xc9,0xa3,0xff,0xba,0xdc,0x86,0xfd,0xd1,0xcf,0x9f,0xf6,0x3e,0x15,0xa,0x6,0x2,0x77,0x77,0x77,0x77,0x6f,0xf7,0x7,0xfc,0x3b,0x7d,0xb6,0xbd,0xbd,0xb6,0xb6,0x16,0x30,0x68,0x6d,0xd0,0xc6,0xa0,0x95,0xc2,0x71,0x1c,0x94,0x52,0x68,0xa5,0x51,0x5a,0xa1,0x95,0x42,0x79,0xa,0xa5,0x3c,0x5c,0xd7,0xc5,0xf3,0x3c,0x2a,0x95,0xa,0x42,0xa,0x4a,0x65,0x37,0xd9,0x73,0xa4,0x27,0xd9,0x77,0xfa,0x74,0x77,0x3a,0x95,0x7e,0xe5,0x93,0x9c,0x5c,0x6,0xb0,0x7e,0x5d,0x73,0x2c,0x3f,0x57,0x78,0x61,0x72,0x62,0x8a,0xc6,0xa6,0x35,0x84,0x6a,0x13,0xb8,0x6e,0x19,0xad,0x34,0x58,0x16,0x9e,0xe7,0xe1,0xb3,0xab,0xcb,0x8d,0x31,0x68,0xa3,0xd1,0x4a,0x61,0x8c,0x1,0xc,0x13,0x93,0x53,0x4c,0x4f,0xcf,0x30,0x3a,0x3a,0xc6,0xc9,0x93,0xbd,0x7c,0xd0,0xdb,0xcb,0xc8,0xc8,0x30,0xc1,0x60,0x98,0x58,0x2c,0x4e,0x20,0x10,0xf2,0xad,0x1a,0x82,0x7,0xee,0xff,0xb1,0x9,0x5,0x23,0x18,0xa3,0xb1,0x6d,0x1b,0x7f,0xc0,0x47,0x34,0x16,0x21,0x14,0xc,0x10,0x89,0x86,0xf0,0xf9,0x6c,0x42,0xe1,0x0,0xc1,0x80,0x9f,0xba,0xba,0x3a,0x36,0xb4,0x6e,0xe0,0xf8,0xb1,0x13,0xbc,0xfe,0xc6,0x1b,0xc,0x9c,0x3b,0xc7,0xc0,0xc0,0x59,0xb2,0xd9,0xec,0xf2,0x79,0x42,0x48,0x62,0xd1,0x18,0x9d,0x9d,0xd7,0x21,0xa5,0xec,0x7c,0xe7,0xdd,0x3,0x67,0x3f,0x95,0x1,0x0,0xaf,0xe2,0x22,0x22,0x2,0xa3,0x2d,0x94,0x56,0x14,0xa,0x1e,0x8b,0x5,0x7,0x8c,0xa9,0x7a,0xad,0xf5,0x92,0xc7,0x10,0xa,0x7,0x19,0x1f,0x1b,0xe0,0x95,0x57,0x5f,0xfe,0x44,0xfd,0xb4,0xb4,0xb4,0xd2,0xde,0xb6,0x15,0x21,0x24,0xd3,0xd3,0x13,0xb8,0x95,0xf2,0x5d,0xc0,0x95,0x1,0x54,0xbc,0x45,0x66,0x67,0xb,0xd8,0xb6,0xf,0x29,0x6d,0x22,0x91,0x38,0xb9,0xec,0x3c,0xe1,0x48,0xc,0xcb,0x18,0x8c,0x56,0x60,0x9,0x8c,0x31,0xa8,0x8a,0x62,0x72,0x72,0xea,0x92,0xc3,0xe2,0xf1,0x5a,0xb6,0x75,0x5e,0x83,0x14,0x92,0x91,0xf1,0x21,0x8e,0x1c,0x39,0x44,0x24,0x1a,0x23,0x91,0xa8,0x27,0xe0,0xf,0x9c,0xb8,0xa2,0x6,0x0,0x84,0xc,0x13,0x9,0x47,0x11,0x42,0x60,0xdb,0x92,0x52,0xa9,0x48,0xd1,0x99,0x67,0x2e,0x3f,0x43,0x7c,0xcb,0x97,0xd8,0x75,0xff,0xb7,0xc9,0x8c,0x5c,0xe4,0xec,0xb1,0x51,0xca,0xa5,0x1c,0x0,0x3e,0x9f,0x9f,0xab,0xb6,0x74,0x92,0x48,0x24,0xc8,0x64,0xa6,0x39,0xfc,0xfe,0xa1,0x6a,0x6c,0x2d,0x68,0x6d,0xed,0x60,0xe3,0xc6,0x76,0xa4,0x94,0xcc,0xcc,0x4c,0x25,0x57,0x5,0x30,0x9b,0x49,0x61,0x99,0xb5,0xd8,0x76,0x0,0x69,0xfb,0x10,0x96,0x20,0x1e,0x5f,0x3,0x18,0x4c,0x66,0x92,0x43,0x7b,0x9f,0x42,0xd6,0xaf,0xa5,0x36,0xba,0x16,0xaf,0xe5,0x5a,0x16,0x7,0x7,0x68,0x6b,0x2b,0x30,0x38,0x74,0xe,0xc7,0x71,0x0,0x90,0xd2,0xa6,0xbd,0xed,0x2a,0xea,0xeb,0x1b,0x98,0xcd,0x66,0x18,0x18,0x38,0x4d,0x34,0x1a,0x27,0x18,0xc,0x35,0xac,0xa,0xa0,0xa5,0x65,0x23,0xa0,0x11,0xd2,0x60,0x4b,0x8f,0xe1,0xe1,0x61,0xca,0x65,0x97,0x44,0xa2,0x9e,0x70,0x20,0x84,0x52,0x60,0xb9,0xa3,0x64,0xa7,0xc7,0xf1,0x7,0xfa,0x89,0xe6,0x7,0x39,0x71,0xb6,0x1f,0x80,0x70,0x38,0x4a,0x67,0x67,0x17,0x15,0xd7,0xe5,0xc2,0xe0,0x39,0x46,0xc7,0x86,0x8,0x87,0xa3,0x34,0xae,0x6d,0xa1,0xb9,0x79,0x3d,0xc5,0x52,0xf1,0xf0,0x6a,0x0,0x44,0xa1,0x50,0x24,0x10,0x8,0xe1,0x55,0x34,0x65,0x14,0xcd,0xcd,0x6d,0x48,0x29,0x28,0x14,0xe6,0x11,0xeb,0xb7,0xb0,0xeb,0xfb,0xf,0x30,0xb5,0xe0,0x31,0xfb,0xc1,0xc,0x73,0x73,0x13,0x94,0x2e,0xc,0xd1,0xd8,0xd8,0x4c,0x53,0x53,0xb,0xf3,0xf3,0x39,0x8e,0x1d,0x3b,0xbc,0x44,0xbf,0xc5,0xc6,0x8d,0x1d,0x34,0x35,0xb6,0xe0,0x38,0xe,0x93,0x53,0x69,0xc2,0xa1,0xc8,0xd7,0x80,0xde,0x2b,0x1,0xd0,0xc5,0x62,0x96,0x72,0x39,0x4f,0x30,0x18,0xc1,0x68,0xb,0x63,0x34,0x95,0x8a,0xc0,0xb2,0x6c,0xbc,0xf1,0xb,0x1c,0xfc,0xd5,0x13,0x68,0xe5,0xe1,0x8a,0x20,0xb5,0xeb,0xdb,0xa9,0xa9,0xa9,0xc1,0x71,0xa,0xf4,0xf6,0x1e,0x5d,0xce,0xea,0xd6,0xd6,0x4d,0xd4,0xd7,0x25,0xc9,0xe6,0x66,0x19,0x1e,0xb9,0x40,0x2c,0x9a,0xa0,0xa1,0xa1,0x11,0x9f,0xcf,0x3e,0xb8,0x1a,0x3,0xd2,0xb6,0xc3,0x24,0xe2,0x75,0x28,0x5d,0xc1,0x71,0x16,0x99,0x18,0xbf,0x88,0xb4,0x25,0x16,0x16,0xf1,0x96,0xcd,0x24,0x1a,0x1a,0xc8,0x8f,0x8e,0x52,0x23,0x1c,0x54,0x66,0x8,0x51,0x98,0x21,0x9f,0xcf,0x63,0x59,0x82,0xcd,0x9b,0xdb,0xf1,0xfb,0xfd,0x8c,0x8d,0x8d,0x30,0x35,0x99,0x26,0x12,0x8d,0xd3,0xd4,0xb4,0x8e,0x48,0x38,0x86,0x52,0x1e,0x42,0x88,0x5b,0x80,0x93,0x57,0x2,0xa0,0x8a,0xc5,0x79,0xa4,0x10,0x8,0xe9,0x43,0x4a,0x3f,0x6b,0xd6,0xac,0xc3,0xb2,0x2c,0x7c,0x3e,0x1f,0x4,0x42,0x6c,0xfe,0xea,0x2e,0x8a,0x75,0xad,0xa4,0x3f,0x2c,0x61,0x6a,0x34,0x3a,0xff,0x24,0x6d,0x9b,0xb3,0x60,0x59,0x8c,0x8d,0xd,0x57,0xaf,0x60,0x21,0x68,0xdd,0xd0,0x46,0x6d,0x6d,0x1d,0x45,0xa7,0x48,0xa1,0xb0,0x40,0x24,0x12,0xc3,0xf6,0xf9,0xd4,0xaa,0x22,0x8c,0x44,0x63,0xf8,0xfd,0x12,0x4b,0x68,0xfa,0x4f,0xf7,0x13,0x8,0x4,0xa9,0xab,0x4d,0x12,0x8,0x46,0xd0,0xc5,0x49,0x8e,0x3f,0xbf,0x1f,0xb4,0x46,0x6b,0xf,0x19,0x8,0xe3,0x65,0xd3,0xc,0xe,0x5d,0x58,0xde,0xdf,0xd4,0xd4,0x42,0x22,0x51,0xcb,0xdc,0xdc,0x1c,0x93,0x93,0x13,0x44,0xa3,0xb1,0xe5,0x3b,0xc5,0x96,0x36,0xab,0x8a,0xd0,0x29,0x96,0xd1,0xda,0x2,0x3,0x1d,0x5b,0xba,0x50,0x5e,0x85,0xc5,0xc5,0x79,0xe6,0x17,0x32,0x24,0xb6,0x6d,0x67,0xd7,0xdd,0xdf,0xa0,0xb7,0xa7,0x97,0xf4,0xf1,0x53,0xf8,0xdc,0xc,0x7e,0x5c,0x0,0x92,0xc9,0x6,0x62,0xb1,0x38,0x53,0x53,0x93,0x64,0xb3,0x59,0xa2,0x91,0x28,0xe1,0x70,0x14,0x69,0xdb,0x48,0xdb,0xc6,0x5e,0xfa,0xae,0xca,0x80,0x36,0xd5,0x67,0xd5,0x0,0x8e,0xe3,0x70,0xfe,0xc2,0x19,0x9c,0xe2,0x22,0xd7,0x74,0x5d,0x4f,0xf1,0xec,0x49,0x5e,0x1e,0x3b,0x4f,0xb2,0x7d,0x1b,0xf5,0x5b,0x77,0x53,0x5a,0xd3,0x4e,0xf1,0xc0,0x5f,0x68,0x6e,0x4e,0x91,0xcb,0xe5,0xc8,0x64,0x66,0x10,0x42,0x10,0xe,0x45,0x96,0xd,0x7f,0xe4,0xf9,0x67,0x65,0x0,0xad,0x34,0x5a,0x2b,0xb4,0x31,0x60,0xe0,0x3b,0xb7,0xda,0x68,0x13,0xa1,0x67,0xa0,0x82,0x6d,0x4b,0x28,0x16,0xc9,0x9c,0x3a,0xa,0xa6,0x87,0x80,0xdf,0x47,0x68,0x66,0x8a,0x33,0xe9,0xf4,0x25,0x8f,0xcf,0x4a,0xc3,0xb6,0x5c,0xc1,0x80,0x94,0xd6,0x65,0x8f,0x9f,0x1,0x10,0x97,0x0,0xd0,0x1a,0xa5,0x34,0x46,0x6b,0xdc,0x4a,0x99,0x2d,0xdb,0x1f,0xe3,0xce,0xef,0x3e,0x49,0x3a,0x35,0x8a,0xf6,0x14,0xda,0xa8,0xea,0x1a,0xa3,0xf1,0xb4,0x5,0x42,0x5c,0xea,0x4d,0xd5,0xd0,0xb2,0x61,0x69,0x7f,0xcc,0xc0,0x12,0x80,0x2b,0x33,0x50,0x2d,0x30,0x3c,0x34,0x90,0x4e,0x5f,0x64,0xcf,0x9e,0x3d,0x48,0x29,0x70,0x4a,0xb,0x28,0xad,0x30,0x1f,0x55,0x59,0xc6,0xa0,0xf5,0xa5,0xa2,0xb6,0x2c,0x81,0x6d,0xaf,0xf0,0x7a,0x25,0xfd,0xb6,0x4d,0xa5,0x52,0x9,0x3,0x21,0xa0,0x4,0xe8,0x65,0xd6,0x56,0x1e,0x62,0xb4,0x46,0x69,0x8d,0xe3,0xb8,0x1c,0x7e,0xc6,0xe5,0xbd,0x37,0xf7,0x57,0xaf,0xe8,0xa4,0xc2,0x75,0x4b,0x4b,0xc,0x29,0x94,0xae,0xce,0x95,0x8c,0x4a,0x29,0x90,0xd2,0x46,0xac,0xa0,0x5e,0x2e,0x31,0x12,0xc,0x45,0x48,0xd4,0xd7,0x45,0x81,0x75,0x40,0x1c,0xa8,0x59,0xda,0x6c,0x89,0xff,0x64,0x40,0x61,0x61,0xf8,0xe7,0xeb,0x1e,0xe1,0xdc,0x9f,0xf9,0xdd,0x6f,0x7e,0x4b,0xcf,0xb,0x16,0xdd,0x1d,0xe5,0x6a,0x29,0xa6,0x15,0x4a,0xe9,0x6a,0xa5,0xb4,0xf2,0x16,0x5b,0xe1,0xed,0x32,0xf5,0x42,0xb2,0xb5,0xab,0x9b,0x1b,0x77,0x5c,0xeb,0xbc,0xfd,0xe6,0x4b,0x47,0x81,0x24,0xd0,0xb8,0x4,0x42,0x0,0x66,0x39,0x4,0xc9,0xc6,0x66,0xa2,0x91,0x38,0xb,0x8b,0x79,0x84,0x25,0x78,0xe6,0xb5,0xf5,0xec,0x7d,0xa9,0x82,0x52,0xff,0x20,0x73,0x5a,0x70,0xf4,0xc,0xd8,0xbe,0xa5,0x12,0xec,0xb2,0x10,0x58,0x96,0x55,0xcd,0x77,0xbb,0x1a,0x7f,0xb,0x8b,0xe6,0xd,0x6d,0x6c,0xdf,0x71,0x3,0x5e,0x29,0x8f,0xf1,0xdc,0xe0,0x97,0xbf,0xb2,0xeb,0x9a,0xd1,0xd1,0xbf,0x4d,0x2,0x8b,0x40,0xe,0x50,0x97,0x68,0x20,0x33,0x99,0xd6,0x37,0xff,0xe8,0x67,0x0,0x5c,0x4c,0x8d,0x30,0x35,0x95,0x26,0x37,0x37,0x8b,0x5b,0xb6,0x78,0xeb,0x5c,0x2b,0x96,0x71,0x97,0x6a,0x40,0x30,0x18,0x94,0x56,0xcb,0x1,0x90,0x52,0x56,0xa7,0x25,0x88,0x26,0x92,0xdc,0x7a,0xdb,0xed,0xd4,0xc5,0x7c,0x68,0x77,0x81,0x1a,0x7f,0x70,0xe6,0xc5,0x97,0x5e,0xfc,0xf5,0xa1,0xb7,0xdf,0x3a,0x8,0x8c,0x0,0xc5,0x4f,0x15,0x61,0x47,0xe7,0x9a,0xbd,0xd3,0x53,0x33,0xf,0xd7,0x25,0xb7,0x32,0x3e,0x16,0x63,0x64,0x78,0x14,0xaf,0x54,0x60,0xe6,0x86,0x6f,0xe2,0x8,0x3f,0x35,0x83,0xef,0x13,0x9f,0x1d,0x41,0xb9,0x15,0xe6,0x17,0x16,0xb0,0xac,0x8f,0xd3,0xcf,0xf6,0xd5,0x70,0xf3,0xee,0x3b,0xd8,0xb6,0x75,0x13,0xaa,0x52,0xc2,0xf6,0x85,0x38,0x3f,0x70,0x7e,0xdf,0xbe,0x7d,0x4f,0xef,0x95,0x52,0xe,0x2,0x73,0xab,0x66,0x81,0x2d,0x7c,0xf,0xc7,0x63,0x31,0x2e,0x5e,0x1c,0xa7,0xb1,0x39,0x4e,0xe7,0xb6,0x5d,0xcc,0x66,0x73,0x4c,0x5c,0xec,0xe3,0xe4,0xd1,0x1e,0x46,0x88,0xd3,0xb1,0xa5,0x99,0x5b,0x77,0xef,0xe4,0x54,0xdf,0x0,0xef,0xbc,0xfd,0x16,0x0,0x5b,0xbb,0x6e,0xe4,0xb6,0xdb,0x6e,0x41,0xb9,0xb,0x8,0xcb,0xe0,0x54,0x54,0xcf,0x1f,0xff,0xf0,0xfb,0x5f,0x66,0x32,0x33,0xc7,0x81,0x8c,0x52,0xea,0x33,0x37,0x26,0xfe,0x67,0x9f,0xdb,0x7f,0xdf,0xa6,0xcd,0x9b,0xee,0x9,0xf8,0xfd,0x77,0xcd,0xe5,0xe6,0xa2,0x7d,0x7d,0x1f,0x92,0x4a,0xa7,0x88,0x44,0x22,0xa8,0x8a,0x22,0x9b,0x9b,0xe3,0xd4,0xa9,0x93,0x9c,0x39,0xd3,0xcf,0xd5,0x57,0x5f,0x4f,0xc7,0xb6,0x2e,0x82,0x76,0x85,0x40,0x20,0x40,0x38,0x1c,0xcd,0xf7,0xf4,0x1c,0xf9,0xc5,0x6b,0xaf,0xbd,0xf2,0xf7,0x3b,0xba,0x98,0x7e,0xb5,0xef,0xb,0x76,0x46,0xf,0x3e,0xb4,0x67,0xeb,0x1d,0xb7,0xdf,0x7e,0xaf,0xcf,0xe7,0xfb,0x96,0xe3,0x38,0x37,0xe5,0x72,0x59,0x86,0x86,0x87,0x28,0x97,0xca,0xb4,0x77,0xb4,0x23,0x2c,0x41,0x7f,0x7f,0x3f,0x6b,0xd7,0x36,0x91,0x4a,0xa5,0x9e,0x7d,0xee,0xb9,0xbf,0x3e,0x51,0x5b,0x5b,0x9b,0xca,0xe5,0x72,0xfa,0xbf,0xd1,0x1b,0x5a,0x8f,0x3f,0xfe,0xf3,0xaf,0xd7,0xd7,0x27,0xef,0xf1,0xfb,0xfd,0x77,0x5e,0x77,0x5d,0x77,0x6b,0x3e,0x9f,0xe7,0xe0,0xc1,0x37,0x8f,0x1f,0x38,0xf0,0xfa,0xc3,0x7d,0x7d,0x1f,0x9e,0x0,0xbc,0xff,0x49,0x47,0x2b,0xa5,0xc4,0xb2,0xec,0xc6,0x47,0x1e,0x79,0x74,0xf7,0xe5,0x3a,0xfa,0xbc,0xe3,0xdf,0x37,0xe9,0xbc,0x82,0x8a,0x4b,0xde,0x1,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82}; diff --git a/platform/server/logo.png b/platform/server/logo.png Binary files differnew file mode 100644 index 0000000000..6b7490097f --- /dev/null +++ b/platform/server/logo.png diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp new file mode 100644 index 0000000000..7bc8f61744 --- /dev/null +++ b/platform/server/os_server.cpp @@ -0,0 +1,236 @@ +/*************************************************************************/ +/* os_server.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 "servers/visual/visual_server_raster.h" +#include "servers/visual/rasterizer_dummy.h" +#include "os_server.h" +#include <stdio.h> +#include <stdlib.h> +#include "print_string.h" +#include "servers/physics/physics_server_sw.h" + +#include "main/main.h" + +#include <unistd.h> + +int OS_Server::get_video_driver_count() const { + + return 1; +} +const char * OS_Server::get_video_driver_name(int p_driver) const { + + return "Dummy"; +} +OS::VideoMode OS_Server::get_default_video_mode() const { + + return OS::VideoMode(800,600,false); +} + +void OS_Server::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + args=OS::get_singleton()->get_cmdline_args(); + current_videomode=p_desired; + main_loop=NULL; + + + rasterizer = memnew( RasterizerDummy ); + + visual_server = memnew( VisualServerRaster(rasterizer) ); + + 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->init(); + spatial_sound_server = memnew( SpatialSoundServerSW ); + spatial_sound_server->init(); + spatial_sound_2d_server = memnew( SpatialSound2DServerSW ); + spatial_sound_2d_server->init(); + + + ERR_FAIL_COND(!visual_server); + + visual_server->init(); + // + physics_server = memnew( PhysicsServerSW ); + physics_server->init(); + physics_2d_server = memnew( Physics2DServerSW ); + physics_2d_server->init(); + + input = memnew( InputDefault ); + + _ensure_data_dir(); + + +} +void OS_Server::finalize() { + + if(main_loop) + memdelete(main_loop); + main_loop=NULL; + + spatial_sound_server->finish(); + memdelete(spatial_sound_server); + spatial_sound_2d_server->finish(); + memdelete(spatial_sound_2d_server); + + //if (debugger_connection_console) { +// memdelete(debugger_connection_console); +//} + + audio_server->finish(); + memdelete(audio_server); + memdelete(sample_manager); + + visual_server->finish(); + memdelete(visual_server); + memdelete(rasterizer); + + physics_server->finish(); + memdelete(physics_server); + + physics_2d_server->finish(); + memdelete(physics_2d_server); + + memdelete(input); + + args.clear(); +} + +void OS_Server::set_mouse_show(bool p_show) { + + +} +void OS_Server::set_mouse_grab(bool p_grab) { + + grab=p_grab; +} +bool OS_Server::is_mouse_grab_enabled() const { + + return grab; +} + +int OS_Server::get_mouse_button_state() const { + + return 0; +} + +Point2 OS_Server::get_mouse_pos() const { + + return Point2(); +} + +void OS_Server::set_window_title(const String& p_title) { + + +} + +void OS_Server::set_video_mode(const VideoMode& p_video_mode,int p_screen) { + + +} +OS::VideoMode OS_Server::get_video_mode(int p_screen) const { + + return current_videomode; +} +void OS_Server::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const { + + +} + + +MainLoop *OS_Server::get_main_loop() const { + + return main_loop; +} + +void OS_Server::delete_main_loop() { + + if (main_loop) + memdelete(main_loop); + main_loop=NULL; +} + +void OS_Server::set_main_loop( MainLoop * p_main_loop ) { + + main_loop=p_main_loop; + input->set_main_loop(p_main_loop); +} + +bool OS_Server::can_draw() const { + + return false; //can never draw +}; + + +String OS_Server::get_name() { + + return "Server"; +} + + + +void OS_Server::move_window_to_foreground() { + +} + +void OS_Server::set_cursor_shape(CursorShape p_shape) { + + +} + +void OS_Server::run() { + + force_quit = false; + + if (!main_loop) + return; + + main_loop->init(); + + while (!force_quit) { + + if (Main::iteration()==true) + break; + }; + + main_loop->finish(); +} + +OS_Server::OS_Server() { + + AudioDriverManagerSW::add_driver(&driver_dummy); + //adriver here + grab=false; + +}; diff --git a/platform/server/os_server.h b/platform/server/os_server.h new file mode 100644 index 0000000000..fcf96253ad --- /dev/null +++ b/platform/server/os_server.h @@ -0,0 +1,119 @@ +/*************************************************************************/ +/* os_server.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 OS_SERVER_H +#define OS_SERVER_H + + +#include "os/input.h" +#include "drivers/unix/os_unix.h" +#include "servers/visual_server.h" +#include "servers/visual/rasterizer.h" +#include "servers/audio/audio_driver_dummy.h" +#include "servers/physics_server.h" +#include "servers/audio/audio_server_sw.h" +#include "servers/audio/sample_manager_sw.h" +#include "servers/spatial_sound/spatial_sound_server_sw.h" +#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" +#include "drivers/rtaudio/audio_driver_rtaudio.h" +#include "servers/physics_2d/physics_2d_server_sw.h" + +//bitch +#undef CursorShape +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class OS_Server : public OS_Unix { + + Rasterizer *rasterizer; + VisualServer *visual_server; + VideoMode current_videomode; + List<String> args; + MainLoop *main_loop; + + AudioDriverDummy driver_dummy; + bool grab; + + PhysicsServer *physics_server; + Physics2DServer *physics_2d_server; + + virtual void delete_main_loop(); + IP_Unix *ip_unix; + + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSoundServerSW *spatial_sound_server; + SpatialSound2DServerSW *spatial_sound_2d_server; + + bool force_quit; + + InputDefault *input; + + + +protected: + + 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 void initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver); + virtual void finalize(); + + virtual void set_main_loop( MainLoop * p_main_loop ); + +public: + + virtual String get_name(); + + virtual void set_cursor_shape(CursorShape p_shape); + + 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 MainLoop *get_main_loop() const; + + virtual bool can_draw() const; + + 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 void move_window_to_foreground(); + + void run(); + + OS_Server(); +}; + +#endif diff --git a/platform/server/platform_config.h b/platform/server/platform_config.h new file mode 100644 index 0000000000..1bb5afb002 --- /dev/null +++ b/platform/server/platform_config.h @@ -0,0 +1,30 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include <alloca.h> + diff --git a/platform/windows/SCsub b/platform/windows/SCsub new file mode 100644 index 0000000000..1d454e40c2 --- /dev/null +++ b/platform/windows/SCsub @@ -0,0 +1,13 @@ +Import('env') + + +common_win=[ + "context_gl_win.cpp", + "os_windows.cpp", + "ctxgl_procaddr.cpp", + "key_mapping_win.cpp", + "tcp_server_winsock.cpp", + "stream_peer_winsock.cpp", +] + +env.Program('#bin/godot.exe',['godot_win.cpp']+common_win) diff --git a/platform/windows/context_gl_win.cpp b/platform/windows/context_gl_win.cpp new file mode 100644 index 0000000000..a8f74fde2c --- /dev/null +++ b/platform/windows/context_gl_win.cpp @@ -0,0 +1,205 @@ +/*************************************************************************/ +/* context_gl_win.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. */ +/*************************************************************************/ +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) || defined(GLES2_ENABLED) + +// +// C++ Implementation: context_gl_x11 +// +// Description: +// +// +// Author: Juan Linietsky <reduzio@gmail.com>, (C) 2008 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#define WINVER 0x0500 +#include "context_gl_win.h" + +//#include "drivers/opengl/glwrapper.h" +//#include "ctxgl_procaddr.h" + +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 + +typedef HGLRC (APIENTRY* PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int*); + + +void ContextGL_Win::release_current() { + + + wglMakeCurrent(hDC,NULL); + +} + + +void ContextGL_Win::make_current() { + + wglMakeCurrent(hDC,hRC); +} + +int ContextGL_Win::get_window_width() { + + return OS::get_singleton()->get_video_mode().width; +} + +int ContextGL_Win::get_window_height() { + + return OS::get_singleton()->get_video_mode().height; +} + +void ContextGL_Win::swap_buffers() { + + SwapBuffers(hDC); +} + +/* +static GLWrapperFuncPtr wrapper_get_proc_address(const char* p_function) { + + print_line(String()+"getting proc of: "+p_function); + GLWrapperFuncPtr func=(GLWrapperFuncPtr)get_gl_proc_address(p_function); + if (!func) { + print_line("Couldn't find function: "+String(p_function)); + print_line("error: "+itos(GetLastError())); + } + return func; + +} +*/ + + +Error ContextGL_Win::initialize() { + + static PIXELFORMATDESCRIPTOR pfd= { + sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor + 1, + PFD_DRAW_TO_WINDOW | // Format Must Support Window + PFD_SUPPORT_OPENGL | // Format Must Support OpenGL + PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, + 24, + 0, 0, 0, 0, 0, 0, // Color Bits Ignored + 0,// No Alpha Buffer + 0,// Shift Bit Ignored + 0,// No Accumulation Buffer + 0, 0, 0, 0,// Accumulation Bits Ignored + 24,// 24Bit Z-Buffer (Depth Buffer) + 0,// No Stencil Buffer + 0,// No Auxiliary Buffer + PFD_MAIN_PLANE, // Main Drawing Layer + 0,// Reserved + 0, 0, 0 // Layer Masks Ignored + }; + + if (!(hDC=GetDC(hWnd))) { + MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return ERR_CANT_CREATE; // Return FALSE + } + + if (!(pixel_format=ChoosePixelFormat(hDC,&pfd))) // Did Windows Find A Matching Pixel Format? + { + MessageBox(NULL,"Can't Find A Suitable pixel_format.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return ERR_CANT_CREATE; // Return FALSE + } + + if(!SetPixelFormat(hDC,pixel_format,&pfd)) // Are We Able To Set The Pixel Format? + { + MessageBox(NULL,"Can't Set The pixel_format.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return ERR_CANT_CREATE; // Return FALSE + } + + if (!(hRC=wglCreateContext(hDC))) // Are We Able To Get A Rendering Context? + { + MessageBox(NULL,"Can't Create A Temporary GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return ERR_CANT_CREATE; // Return FALSE + } + + wglMakeCurrent(hDC,hRC); + + if (opengl_3_context) { + + int attribs[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, 3,//we want a 3.1 context + WGL_CONTEXT_MINOR_VERSION_ARB, 2, + //and it shall be forward compatible so that we can only use up to date functionality + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + 0}; //zero indicates the end of the array + + PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; //pointer to the method + wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB"); + + if(wglCreateContextAttribsARB == NULL) //OpenGL 3.0 is not supported + { + MessageBox(NULL,"Cannot get Proc Adress for CreateContextAttribs", "ERROR",MB_OK|MB_ICONEXCLAMATION); + wglDeleteContext(hRC); + return ERR_CANT_CREATE; + } + + HGLRC new_hRC; + if (!(new_hRC=wglCreateContextAttribsARB(hDC,0, attribs))) + { + wglDeleteContext(hRC); + MessageBox(NULL,"Can't Create An OpenGL 3.1 Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return ERR_CANT_CREATE; // Return false + } + wglMakeCurrent(hDC,NULL); + wglDeleteContext(hRC); + hRC=new_hRC; + + if (!wglMakeCurrent(hDC,hRC)) // Try To Activate The Rendering Context + { + MessageBox(NULL,"Can't Activate The GL 3.1 Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return ERR_CANT_CREATE; // Return FALSE + } + + printf("Activated GL 3.1 context"); + } + + +// glWrapperInit(wrapper_get_proc_address); + + return OK; +} + +ContextGL_Win::ContextGL_Win(HWND hwnd,bool p_opengl_3_context) { + + opengl_3_context=p_opengl_3_context; + hWnd=hwnd; +} + +ContextGL_Win::~ContextGL_Win() { + + +} + + +#endif diff --git a/platform/windows/context_gl_win.h b/platform/windows/context_gl_win.h new file mode 100644 index 0000000000..6e8d99a5f0 --- /dev/null +++ b/platform/windows/context_gl_win.h @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* context_gl_win.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. */ +/*************************************************************************/ +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) || defined(GLES2_ENABLED) +// +// C++ Interface: context_gl_x11 +// +// Description: +// +// +// Author: Juan Linietsky <reduzio@gmail.com>, (C) 2008 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#ifndef CONTEXT_GL_WIN_H +#define CONTEXT_GL_WIN_H + + +#include "os/os.h" +#include "drivers/gl_context/context_gl.h" +#include "error_list.h" + +#include <windows.h> + +class ContextGL_Win : public ContextGL { + + HDC hDC; + HGLRC hRC; + unsigned int pixel_format; + HWND hWnd; + bool opengl_3_context; +public: + + + virtual void release_current(); + + virtual void make_current(); + + virtual int get_window_width(); + virtual int get_window_height(); + virtual void swap_buffers(); + + virtual Error initialize(); + + + ContextGL_Win(HWND hwnd,bool p_opengl_3_context); + ~ContextGL_Win(); + +}; + +#endif +#endif diff --git a/platform/windows/ctxgl_procaddr.cpp b/platform/windows/ctxgl_procaddr.cpp new file mode 100644 index 0000000000..9715784c32 --- /dev/null +++ b/platform/windows/ctxgl_procaddr.cpp @@ -0,0 +1,186 @@ +/*************************************************************************/ +/* ctxgl_procaddr.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 OPENGL_ENABLED
+#include "ctxgl_procaddr.h"
+#include <GL/gl.h>
+#include <stdio.h>
+
+static PROC _gl_procs[]={
+ (PROC)glCullFace,
+ (PROC)glFrontFace,
+ (PROC)glHint,
+ (PROC)glLineWidth,
+ (PROC)glPointSize,
+ (PROC)glPolygonMode,
+ (PROC)glScissor,
+ (PROC)glTexParameterf,
+ (PROC)glTexParameterfv,
+ (PROC)glTexParameteri,
+ (PROC)glTexParameteriv,
+ (PROC)glTexImage1D,
+ (PROC)glTexImage2D,
+ (PROC)glDrawBuffer,
+ (PROC)glClear,
+ (PROC)glClearColor,
+ (PROC)glClearStencil,
+ (PROC)glClearDepth,
+ (PROC)glStencilMask,
+ (PROC)glColorMask,
+ (PROC)glDepthMask,
+ (PROC)glDisable,
+ (PROC)glEnable,
+ (PROC)glFinish,
+ (PROC)glFlush,
+ (PROC)glBlendFunc,
+ (PROC)glLogicOp,
+ (PROC)glStencilFunc,
+ (PROC)glStencilOp,
+ (PROC)glDepthFunc,
+ (PROC)glPixelStoref,
+ (PROC)glPixelStorei,
+ (PROC)glReadBuffer,
+ (PROC)glReadPixels,
+ (PROC)glGetBooleanv,
+ (PROC)glGetDoublev,
+ (PROC)glGetError,
+ (PROC)glGetFloatv,
+ (PROC)glGetIntegerv,
+ (PROC)glGetString,
+ (PROC)glGetTexImage,
+ (PROC)glGetTexParameterfv,
+ (PROC)glGetTexParameteriv,
+ (PROC)glGetTexLevelParameterfv,
+ (PROC)glGetTexLevelParameteriv,
+ (PROC)glIsEnabled,
+ (PROC)glDepthRange,
+ (PROC)glViewport,
+ /* not detected in ATI */
+ (PROC)glDrawArrays,
+ (PROC)glDrawElements,
+ (PROC)glGetPointerv,
+ (PROC)glPolygonOffset,
+ (PROC)glCopyTexImage1D,
+ (PROC)glCopyTexImage2D,
+ (PROC)glCopyTexSubImage1D,
+ (PROC)glCopyTexSubImage2D,
+ (PROC)glTexSubImage1D,
+ (PROC)glTexSubImage2D,
+ (PROC)glBindTexture,
+ (PROC)glDeleteTextures,
+ (PROC)glGenTextures,
+ (PROC)glIsTexture,
+
+ 0
+};
+
+static const char* _gl_proc_names[]={
+ "glCullFace",
+ "glFrontFace",
+ "glHint",
+ "glLineWidth",
+ "glPointSize",
+ "glPolygonMode",
+ "glScissor",
+ "glTexParameterf",
+ "glTexParameterfv",
+ "glTexParameteri",
+ "glTexParameteriv",
+ "glTexImage1D",
+ "glTexImage2D",
+ "glDrawBuffer",
+ "glClear",
+ "glClearColor",
+ "glClearStencil",
+ "glClearDepth",
+ "glStencilMask",
+ "glColorMask",
+ "glDepthMask",
+ "glDisable",
+ "glEnable",
+ "glFinish",
+ "glFlush",
+ "glBlendFunc",
+ "glLogicOp",
+ "glStencilFunc",
+ "glStencilOp",
+ "glDepthFunc",
+ "glPixelStoref",
+ "glPixelStorei",
+ "glReadBuffer",
+ "glReadPixels",
+ "glGetBooleanv",
+ "glGetDoublev",
+ "glGetError",
+ "glGetFloatv",
+ "glGetIntegerv",
+ "glGetString",
+ "glGetTexImage",
+ "glGetTexParameterfv",
+ "glGetTexParameteriv",
+ "glGetTexLevelParameterfv",
+ "glGetTexLevelParameteriv",
+ "glIsEnabled",
+ "glDepthRange",
+ "glViewport",
+ /* not detected in ati */
+ "glDrawArrays",
+ "glDrawElements",
+ "glGetPointerv",
+ "glPolygonOffset",
+ "glCopyTexImage1D",
+ "glCopyTexImage2D",
+ "glCopyTexSubImage1D",
+ "glCopyTexSubImage2D",
+ "glTexSubImage1D",
+ "glTexSubImage2D",
+ "glBindTexture",
+ "glDeleteTextures",
+ "glGenTextures",
+ "glIsTexture",
+
+ 0
+};
+
+PROC get_gl_proc_address(const char* p_address) {
+
+ PROC proc = wglGetProcAddress((const CHAR*)p_address);
+ if (!proc) {
+
+ int i=0;
+ while(_gl_procs[i]) {
+
+ if (strcmp(p_address,_gl_proc_names[i])==0) {
+ return _gl_procs[i];
+ }
+ i++;
+ }
+ }
+ return proc;
+}
+#endif
diff --git a/platform/windows/ctxgl_procaddr.h b/platform/windows/ctxgl_procaddr.h new file mode 100644 index 0000000000..d3ab20e82d --- /dev/null +++ b/platform/windows/ctxgl_procaddr.h @@ -0,0 +1,38 @@ +/*************************************************************************/ +/* ctxgl_procaddr.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 CTXGL_PROCADDR_H
+#define CTXGL_PROCADDR_H
+
+#ifdef OPENGL_ENABLED
+#include <windows.h>
+
+
+PROC get_gl_proc_address(const char* p_address);
+#endif
+#endif // CTXGL_PROCADDR_H
diff --git a/platform/windows/detect.py b/platform/windows/detect.py new file mode 100644 index 0000000000..1774ba17f8 --- /dev/null +++ b/platform/windows/detect.py @@ -0,0 +1,208 @@ +
+
+import os
+
+import sys
+
+
+def is_active():
+ return True
+
+def get_name():
+ return "Windows"
+
+def can_build():
+
+
+ if (os.name=="nt"):
+ #building natively on windows!
+ if (os.getenv("VSINSTALLDIR")):
+ return True
+ else:
+ print("MSVC Not detected, attempting mingw.")
+ return True
+
+
+
+ if (os.name=="posix"):
+
+ if os.system("i586-mingw32msvc-gcc --version") == 0:
+ return True
+
+
+ return False
+
+def get_opts():
+
+ mwp=""
+ mwp64=""
+ if (os.name!="nt"):
+ mwp="i586-mingw32msvc-"
+ mwp64="x86_64-w64-mingw32-"
+
+ return [
+ ('force_64_bits','Force 64 bits binary','no'),
+ ('force_32_bits','Force 32 bits binary','no'),
+ ('mingw_prefix','Mingw Prefix',mwp),
+ ('mingw_prefix_64','Mingw Prefix 64 bits',mwp64),
+ ]
+
+def get_flags():
+
+ return [
+ ('freetype','builtin'), #use builtin freetype
+ ]
+
+
+
+def configure(env):
+
+ if os.name == "posix":
+ env['OBJSUFFIX'] = ".win"+env['OBJSUFFIX']
+ env['LIBSUFFIX'] = ".win"+env['LIBSUFFIX']
+
+ env.Append(CPPPATH=['#platform/windows'])
+
+ if (env["tools"]=="no"):
+ #no tools suffix
+ env['OBJSUFFIX'] = ".nt"+env['OBJSUFFIX']
+ env['LIBSUFFIX'] = ".nt"+env['LIBSUFFIX']
+
+
+ if (os.name=="nt" and os.getenv("VSINSTALLDIR")!=None):
+ #build using visual studio
+ env['ENV']['TMP'] = os.environ['TMP']
+ env.Append(CPPPATH=['#platform/windows/include'])
+ env.Append(LIBPATH=['#platform/windows/lib'])
+
+ if (env["freetype"]!="no"):
+ env.Append(CCFLAGS=['/DFREETYPE_ENABLED'])
+ env.Append(CPPPATH=['#tools/freetype'])
+ env.Append(CPPPATH=['#tools/freetype/freetype/include'])
+
+ if (env["target"]=="release"):
+
+ env.Append(CCFLAGS=['/O2'])
+ env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS'])
+
+ elif (env["target"]=="test"):
+
+ env.Append(CCFLAGS=['/O2','/DDEBUG_ENABLED','/DD3D_DEBUG_INFO'])
+ env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE'])
+
+ elif (env["target"]=="debug"):
+
+ env.Append(CCFLAGS=['/Zi','/DDEBUG_ENABLED','/DD3D_DEBUG_INFO','/O1'])
+ env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE'])
+
+ elif (env["target"]=="profile"):
+
+ env.Append(CCFLAGS=['-g','-pg'])
+ env.Append(LINKFLAGS=['-pg'])
+
+ env.Append(CCFLAGS=['/MT','/Gd','/GR','/nologo'])
+ env.Append(CXXFLAGS=['/TP'])
+ env.Append(CPPFLAGS=['/DMSVC', '/GR', ])
+ env.Append(CCFLAGS=['/I'+os.getenv("WindowsSdkDir")+"/Include"])
+ env.Append(CCFLAGS=['/DWINDOWS_ENABLED'])
+ env.Append(CCFLAGS=['/DRTAUDIO_ENABLED'])
+ env.Append(CCFLAGS=['/DWIN32'])
+ env.Append(CCFLAGS=['/DTYPED_METHOD_BIND'])
+
+ #env.Append(CCFLAGS=['/DLEGACYGL_ENABLED'])
+ env.Append(CCFLAGS=['/DGLES_ENABLED'])
+ #env.Append(CCFLAGS=['/DGLES1_ENABLED'])
+ env.Append(CCFLAGS=['/DGLEW_ENABLED'])
+ env.Append(LIBS=['winmm','opengl32','dsound','kernel32','ole32','user32','gdi32','wsock32'])
+ env.Append(LINKFLAGS=['/DEBUG'])
+
+ env.Append(LIBPATH=[os.getenv("WindowsSdkDir")+"/Lib"])
+ if (os.getenv("DXSDK_DIR")):
+ DIRECTX_PATH=os.getenv("DXSDK_DIR")
+ else:
+ DIRECTX_PATH="C:/Program Files/Microsoft DirectX SDK (March 2009)"
+
+ if (os.getenv("VCINSTALLDIR")):
+ VC_PATH=os.getenv("VCINSTALLDIR")
+ else:
+ VC_PATH=""
+
+ env.Append(CCFLAGS=["/I"+VC_PATH+"/Include"])
+ env.Append(LIBPATH=[VC_PATH+"/Lib"])
+ env.Append(CCFLAGS=["/I"+DIRECTX_PATH+"/Include"])
+ env.Append(LIBPATH=[DIRECTX_PATH+"/Lib/x86"])
+ env['ENV'] = os.environ;
+ else:
+ #build using mingw
+ if (os.name=="nt"):
+ env['ENV']['TMP'] = os.environ['TMP'] #way to go scons, you can be so stupid sometimes
+
+ mingw_prefix=""
+
+ if (env["force_32_bits"]!="no"):
+ env['OBJSUFFIX'] = ".32"+env['OBJSUFFIX']
+ env['LIBSUFFIX'] = ".32"+env['LIBSUFFIX']
+ env.Append(CCFLAGS=['-m32'])
+ env.Append(LINKFLAGS=['-m32'])
+
+
+
+
+ if (env["force_64_bits"]!="no"):
+ mingw_prefix=env["mingw_prefix_64"];
+ env['OBJSUFFIX'] = ".64"+env['OBJSUFFIX']
+ env['LIBSUFFIX'] = ".64"+env['LIBSUFFIX']
+ env.Append(LINKFLAGS=['-static'])
+ else:
+ mingw_prefix=env["mingw_prefix"];
+
+ if (env["target"]=="release"):
+
+ env.Append(CCFLAGS=['-O3','-ffast-math','-fomit-frame-pointer','-msse2'])
+ env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX']
+ env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX']
+ elif (env["target"]=="release_debug"):
+
+ env.Append(CCFLAGS=['-O2','-DDEBUG_ENABLED'])
+ env['OBJSUFFIX'] = "_optd"+env['OBJSUFFIX']
+ env['LIBSUFFIX'] = "_optd"+env['LIBSUFFIX']
+
+ elif (env["target"]=="debug"):
+
+ env.Append(CCFLAGS=['-g', '-Wall','-DDEBUG_ENABLED'])
+ elif (env["target"]=="release_tools"):
+
+ env.Append(CCFLAGS=['-O2','-Wall','-DDEBUG_ENABLED'])
+
+
+ if (env["freetype"]!="no"):
+ env.Append(CCFLAGS=['-DFREETYPE_ENABLED'])
+ env.Append(CPPPATH=['#tools/freetype'])
+ env.Append(CPPPATH=['#tools/freetype/freetype/include'])
+
+ env["CC"]=mingw_prefix+"gcc"
+ env['AS']=mingw_prefix+"as"
+ env['CXX'] = mingw_prefix+"g++"
+ env['AR'] = mingw_prefix+"ar"
+ env['RANLIB'] = mingw_prefix+"ranlib"
+ env['LD'] = mingw_prefix+"g++"
+
+ #env['CC'] = "winegcc"
+ #env['CXX'] = "wineg++"
+
+ env.Append(CCFLAGS=['-DWINDOWS_ENABLED','-mwindows'])
+ env.Append(CPPFLAGS=['-DRTAUDIO_ENABLED'])
+ env.Append(CCFLAGS=['-DGLES2_ENABLED','-DGLES1_ENABLED','-DGLEW_ENABLED'])
+ env.Append(LIBS=['mingw32','opengl32', 'dsound', 'ole32', 'd3d9','winmm','gdi32','wsock32','kernel32'])
+ #'d3dx9d'
+ env.Append(CPPFLAGS=['-DMINGW_ENABLED'])
+ env.Append(LINKFLAGS=['-g'])
+
+ 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 = { 'HLSL9' : env.Builder(action = methods.build_hlsl_dx9_headers, suffix = 'hlsl.h',src_suffix = '.hlsl') } )
+ env.Append( BUILDERS = { 'GLSL120GLES' : env.Builder(action = methods.build_gles2_headers, suffix = 'glsl.h',src_suffix = '.glsl') } )
+
+
+
diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp new file mode 100644 index 0000000000..952f51fdd4 --- /dev/null +++ b/platform/windows/export/export.cpp @@ -0,0 +1,24 @@ +#include "export.h" +#include "platform/windows/logo.h" +#include "tools/editor/editor_import_export.h" + +void register_windows_exporter() { + + Image img(_windows_logo); + Ref<ImageTexture> logo = memnew( ImageTexture ); + logo->create_from_image(img); + + { + Ref<EditorExportPlatformPC> exporter = Ref<EditorExportPlatformPC>( memnew(EditorExportPlatformPC) ); + exporter->set_binary_extension("exe"); + exporter->set_release_binary32("windows_32_release.exe"); + exporter->set_debug_binary32("windows_32_debug.exe"); + exporter->set_release_binary64("windows_64_release.exe"); + exporter->set_debug_binary64("windows_64_debug.exe"); + exporter->set_name("Windows Desktop"); + exporter->set_logo(logo); + EditorImportExport::get_singleton()->add_export_platform(exporter); + } + + +} diff --git a/platform/windows/export/export.h b/platform/windows/export/export.h new file mode 100644 index 0000000000..de3dc3fa50 --- /dev/null +++ b/platform/windows/export/export.h @@ -0,0 +1,3 @@ + +void register_windows_exporter(); + diff --git a/platform/windows/godot_win.cpp b/platform/windows/godot_win.cpp new file mode 100644 index 0000000000..2999a9beae --- /dev/null +++ b/platform/windows/godot_win.cpp @@ -0,0 +1,207 @@ +/*************************************************************************/ +/* godot_win.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 "os_windows.h" +#include "main/main.h" +#include <stdio.h> + +PCHAR* + CommandLineToArgvA( + PCHAR CmdLine, + int* _argc + ) + { + PCHAR* argv; + PCHAR _argv; + ULONG len; + ULONG argc; + CHAR a; + ULONG i, j; + + BOOLEAN in_QM; + BOOLEAN in_TEXT; + BOOLEAN in_SPACE; + + len = strlen(CmdLine); + i = ((len+2)/2)*sizeof(PVOID) + sizeof(PVOID); + + argv = (PCHAR*)GlobalAlloc(GMEM_FIXED, + i + (len+2)*sizeof(CHAR)); + + _argv = (PCHAR)(((PUCHAR)argv)+i); + + argc = 0; + argv[argc] = _argv; + in_QM = FALSE; + in_TEXT = FALSE; + in_SPACE = TRUE; + i = 0; + j = 0; + + while( (a = CmdLine[i]) ) { + if(in_QM) { + if(a == '\"') { + in_QM = FALSE; + } else { + _argv[j] = a; + j++; + } + } else { + switch(a) { + case '\"': + in_QM = TRUE; + in_TEXT = TRUE; + if(in_SPACE) { + argv[argc] = _argv+j; + argc++; + } + in_SPACE = FALSE; + break; + case ' ': + case '\t': + case '\n': + case '\r': + if(in_TEXT) { + _argv[j] = '\0'; + j++; + } + in_TEXT = FALSE; + in_SPACE = TRUE; + break; + default: + in_TEXT = TRUE; + if(in_SPACE) { + argv[argc] = _argv+j; + argc++; + } + _argv[j] = a; + j++; + in_SPACE = FALSE; + break; + } + } + i++; + } + _argv[j] = '\0'; + argv[argc] = NULL; + + (*_argc) = argc; + return argv; + } + +int main(int argc, char** argv) { + + OS_Windows os(NULL); + + Main::setup(argv[0], argc - 1, &argv[1]); + if (Main::start()) + os.run(); + Main::cleanup(); + + return os.get_exit_code(); +}; + +HINSTANCE godot_hinstance = NULL; + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + + int argc; + char** argv; + + char* arg; + int index; + int result; + + // count the arguments + + argc = 1; + arg = lpCmdLine; + + while (arg[0] != 0) { + + while (arg[0] != 0 && arg[0] == ' ') { + arg++; + } + + if (arg[0] != 0) { + + argc++; + + while (arg[0] != 0 && arg[0] != ' ') { + arg++; + } + + } + + } + + // tokenize the arguments + + argv = (char**)malloc(argc * sizeof(char*)); + + arg = lpCmdLine; + index = 1; + + while (arg[0] != 0) { + + while (arg[0] != 0 && arg[0] == ' ') { + arg++; + } + + if (arg[0] != 0) { + + argv[index] = arg; + index++; + + while (arg[0] != 0 && arg[0] != ' ') { + arg++; + } + + if (arg[0] != 0) { + arg[0] = 0; + arg++; + } + + } + + } + + // put the program name into argv[0] + + char filename[_MAX_PATH]; + + GetModuleFileName(NULL, filename, _MAX_PATH); + argv[0] = filename; + + // call the user specified main function + + result = main(argc, argv); + + free(argv); + return result; +} diff --git a/platform/windows/key_mapping_win.cpp b/platform/windows/key_mapping_win.cpp new file mode 100644 index 0000000000..bdf3b2a92f --- /dev/null +++ b/platform/windows/key_mapping_win.cpp @@ -0,0 +1,256 @@ +/*************************************************************************/ +/* key_mapping_win.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. */ +/*************************************************************************/ +#define WINVER 0x0500 +#include "key_mapping_win.h" +#include <stdio.h> + +struct _WinTranslatePair { + + unsigned int keysym; + unsigned int keycode; +}; + + +static _WinTranslatePair _vk_to_keycode[]={ + +{ KEY_BACKSPACE, VK_BACK },// (0x08) // backspace +{ KEY_TAB, VK_TAB },//(0x09) + +//VK_CLEAR (0x0C) + +{ KEY_RETURN, VK_RETURN },//(0x0D) + +{ KEY_SHIFT, VK_SHIFT },//(0x10) + +{ KEY_CONTROL, VK_CONTROL },//(0x11) + +{ KEY_MENU, VK_MENU },//(0x12) + +{ KEY_PAUSE, VK_PAUSE },//(0x13) + +{ KEY_CAPSLOCK, VK_CAPITAL },//(0x14) + +{ KEY_ESCAPE, VK_ESCAPE },//(0x1B) + +{ KEY_SPACE, VK_SPACE },//(0x20) + +{ KEY_PAGEUP,VK_PRIOR },//(0x21) + +{ KEY_PAGEDOWN, VK_NEXT },//(0x22) + +{ KEY_END, VK_END },//(0x23) + +{ KEY_HOME, VK_HOME },//(0x24) + +{ KEY_LEFT, VK_LEFT },//(0x25) + +{ KEY_UP, VK_UP },//(0x26) + +{ KEY_RIGHT,VK_RIGHT },//(0x27) + +{ KEY_DOWN, VK_DOWN},// (0x28) + +//VK_SELECT (0x29) + +{ KEY_PRINT, VK_PRINT},// (0x2A) + +//VK_EXECUTE (0x2B) + +{ KEY_PRINT, VK_SNAPSHOT},// (0x2C) + +{ KEY_INSERT, VK_INSERT},// (0x2D) + +{ KEY_DELETE, VK_DELETE},// (0x2E) + +{ KEY_HELP, VK_HELP},// (0x2F) + + +{ KEY_0, (0x30) },////0 key +{ KEY_1, (0x31) },////1 key +{ KEY_2, (0x32) },////2 key +{ KEY_3, (0x33) },////3 key +{ KEY_4, (0x34) },////4 key +{ KEY_5, (0x35) },////5 key +{ KEY_6, (0x36) },////6 key +{ KEY_7, (0x37) },////7 key +{ KEY_8, (0x38) },////8 key +{ KEY_9, (0x39) },////9 key +{ KEY_A, (0x41) },////A key +{ KEY_B, (0x42) },////B key +{ KEY_C, (0x43) },////C key +{ KEY_D, (0x44) },////D key +{ KEY_E, (0x45) },////E key +{ KEY_F, (0x46) },////F key +{ KEY_G, (0x47) },////G key +{ KEY_H, (0x48) },////H key +{ KEY_I, (0x49) },////I key +{ KEY_J, (0x4A) },////J key +{ KEY_K, (0x4B) },////K key +{ KEY_L, (0x4C) },////L key +{ KEY_M, (0x4D) },////M key +{ KEY_N, (0x4E) },////N key +{ KEY_O, (0x4F) },////O key +{ KEY_P, (0x50) },////P key +{ KEY_Q, (0x51) },////Q key +{ KEY_R, (0x52) },////R key +{ KEY_S, (0x53) },////S key +{ KEY_T, (0x54) },////T key +{ KEY_U, (0x55) },////U key +{ KEY_V, (0x56) },////V key +{ KEY_W, (0x57) },////W key +{ KEY_X, (0x58) },////X key +{ KEY_Y, (0x59) },////Y key +{ KEY_Z, (0x5A) },////Z key + +{ KEY_MASK_META, VK_LWIN },//(0x5B) +{ KEY_MASK_META, VK_RWIN },//(0x5C) +//VK_APPS (0x5D) +{ KEY_STANDBY,VK_SLEEP },//(0x5F) +{ KEY_KP_0,VK_NUMPAD0 },//(0x60) +{ KEY_KP_1,VK_NUMPAD1 },//(0x61) +{ KEY_KP_2,VK_NUMPAD2 },//(0x62) +{ KEY_KP_3,VK_NUMPAD3 },//(0x63) +{ KEY_KP_4,VK_NUMPAD4 },//(0x64) +{ KEY_KP_5,VK_NUMPAD5 },//(0x65) +{ KEY_KP_6,VK_NUMPAD6 },//(0x66) +{ KEY_KP_7,VK_NUMPAD7 },//(0x67) +{ KEY_KP_8,VK_NUMPAD8 },//(0x68) +{ KEY_KP_9,VK_NUMPAD9 },//(0x69) +{ KEY_KP_MULTIPLY,VK_MULTIPLY},// (0x6A) +{ KEY_KP_ADD,VK_ADD},// (0x6B) +//VK_SEPARATOR (0x6C) +{ KEY_KP_SUBSTRACT,VK_SUBTRACT},// (0x6D) +{ KEY_KP_PERIOD,VK_DECIMAL},// (0x6E) +{ KEY_KP_DIVIDE,VK_DIVIDE},// (0x6F) +{ KEY_F1,VK_F1},// (0x70) +{ KEY_F2,VK_F2},// (0x71) +{ KEY_F3,VK_F3},// (0x72) +{ KEY_F4,VK_F4},// (0x73) +{ KEY_F5,VK_F5},// (0x74) +{ KEY_F6,VK_F6},// (0x75) +{ KEY_F7,VK_F7},// (0x76) +{ KEY_F8,VK_F8},// (0x77) +{ KEY_F9,VK_F9},// (0x78) +{ KEY_F10,VK_F10},// (0x79) +{ KEY_F11,VK_F11},// (0x7A) +{ KEY_F12,VK_F12},// (0x7B) +{ KEY_F13,VK_F13},// (0x7C) +{ KEY_F14,VK_F14},// (0x7D) +{ KEY_F15,VK_F15},// (0x7E) +{ KEY_F16,VK_F16},// (0x7F) +{ KEY_NUMLOCK,VK_NUMLOCK},// (0x90) +{ KEY_SCROLLLOCK,VK_SCROLL},// (0x91) +{ KEY_SHIFT,VK_LSHIFT},// (0xA0) +{ KEY_SHIFT,VK_RSHIFT},// (0xA1) +{ KEY_CONTROL,VK_LCONTROL},// (0xA2) +{ KEY_CONTROL,VK_RCONTROL},// (0xA3) +{ KEY_MENU,VK_LMENU},// (0xA4) +{ KEY_MENU,VK_RMENU},// (0xA5) + + +{ KEY_BACK,VK_BROWSER_BACK},// (0xA6) + +{ KEY_FORWARD,VK_BROWSER_FORWARD},// (0xA7) + +{ KEY_REFRESH,VK_BROWSER_REFRESH},// (0xA8) + +{ KEY_STOP,VK_BROWSER_STOP},// (0xA9) + +{ KEY_SEARCH,VK_BROWSER_SEARCH},// (0xAA) + +{ KEY_FAVORITES, VK_BROWSER_FAVORITES},// (0xAB) + +{ KEY_HOMEPAGE,VK_BROWSER_HOME},// (0xAC) + +{ KEY_VOLUMEMUTE,VK_VOLUME_MUTE},// (0xAD) + +{ KEY_VOLUMEDOWN,VK_VOLUME_DOWN},// (0xAE) + +{ KEY_VOLUMEUP,VK_VOLUME_UP},// (0xAF) + + +{ KEY_MEDIANEXT,VK_MEDIA_NEXT_TRACK},// (0xB0) + +{ KEY_MEDIAPREVIOUS,VK_MEDIA_PREV_TRACK},// (0xB1) + +{ KEY_MEDIASTOP,VK_MEDIA_STOP},// (0xB2) + +//VK_MEDIA_PLAY_PAUSE (0xB3) + +{ KEY_LAUNCHMAIL, VK_LAUNCH_MAIL},// (0xB4) + +{ KEY_LAUNCHMEDIA,VK_LAUNCH_MEDIA_SELECT},// (0xB5) + +{ KEY_LAUNCH0,VK_LAUNCH_APP1},// (0xB6) + +{ KEY_LAUNCH1,VK_LAUNCH_APP2},// (0xB7) + +{ KEY_SEMICOLON,VK_OEM_1},// (0xBA) + + +{ KEY_EQUAL, VK_OEM_PLUS},// (0xBB) // Windows 2000/XP: For any country/region, the '+' key +{ KEY_COLON,VK_OEM_COMMA},// (0xBC) // Windows 2000/XP: For any country/region, the ',' key +{ KEY_MINUS,VK_OEM_MINUS},// (0xBD) // Windows 2000/XP: For any country/region, the '-' key +{ KEY_PERIOD,VK_OEM_PERIOD},// (0xBE) // Windows 2000/XP: For any country/region, the '.' key +{ KEY_SLASH,VK_OEM_2},// (0xBF) //Windows 2000/XP: For the US standard keyboard, the '/?' key + +{KEY_QUOTELEFT, VK_OEM_3},// (0xC0) +{KEY_BRACELEFT,VK_OEM_4},// (0xDB) +{KEY_BACKSLASH,VK_OEM_5},// (0xDC) +{KEY_BRACERIGHT,VK_OEM_6},// (0xDD) +{KEY_APOSTROPHE, VK_OEM_7},// (0xDE) +/* +{VK_OEM_8 (0xDF) +{VK_OEM_102 (0xE2) // Windows 2000/XP: Either the angle bracket key or the backslash key on the RT 102-key keyboard +*/ +//{ KEY_PLAY, VK_PLAY},// (0xFA) + +{KEY_UNKNOWN, 0} }; + +/* +VK_ZOOM (0xFB) +VK_NONAME (0xFC) +VK_PA1 (0xFD) +VK_OEM_CLEAR (0xFE) +*/ + +unsigned int KeyMappingWindows::get_keysym(unsigned int p_code) { + + for(int i=0;_vk_to_keycode[i].keysym!=KEY_UNKNOWN;i++) { + + if (_vk_to_keycode[i].keycode==p_code) { + //printf("outcode: %x\n",_vk_to_keycode[i].keysym); + + return _vk_to_keycode[i].keysym; + } + } + + + return KEY_UNKNOWN; +} diff --git a/platform/windows/key_mapping_win.h b/platform/windows/key_mapping_win.h new file mode 100644 index 0000000000..3e351675b0 --- /dev/null +++ b/platform/windows/key_mapping_win.h @@ -0,0 +1,51 @@ +/*************************************************************************/ +/* key_mapping_win.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 KEY_MAPPING_WINDOWS_H
+#define KEY_MAPPING_WINDOWS_H
+
+
+#include "os/keyboard.h"
+
+#include <windows.h>
+
+#include <winuser.h>
+
+
+class KeyMappingWindows {
+
+ KeyMappingWindows() {};
+public:
+
+ static unsigned int get_keysym(unsigned int p_code);
+
+};
+
+
+
+#endif
diff --git a/platform/windows/lang_table.h b/platform/windows/lang_table.h new file mode 100644 index 0000000000..83f3fb52fd --- /dev/null +++ b/platform/windows/lang_table.h @@ -0,0 +1,190 @@ +/*************************************************************************/ +/* lang_table.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 LANG_TABLE_H +#define LANG_TABLE_H + +//#include <windows.h> + +struct _WinLocale { + const char *locale; + int main_lang; + int sublang; +}; + +static const _WinLocale _win_locales[]={ +{"ar", LANG_ARABIC,SUBLANG_NEUTRAL}, +{"ar_AE", LANG_ARABIC,SUBLANG_ARABIC_UAE}, +{"ar_BH", LANG_ARABIC,SUBLANG_ARABIC_BAHRAIN}, +{"ar_DZ", LANG_ARABIC,SUBLANG_ARABIC_ALGERIA}, +{"ar_EG", LANG_ARABIC,SUBLANG_ARABIC_EGYPT}, +{"ar_IQ", LANG_ARABIC,SUBLANG_ARABIC_IRAQ}, +{"ar_JO", LANG_ARABIC,SUBLANG_ARABIC_JORDAN}, +{"ar_KW", LANG_ARABIC,SUBLANG_ARABIC_KUWAIT}, +{"ar_LB", LANG_ARABIC,SUBLANG_ARABIC_LEBANON}, +{"ar_LY", LANG_ARABIC,SUBLANG_ARABIC_LIBYA}, +{"ar_MA", LANG_ARABIC,SUBLANG_ARABIC_MOROCCO}, +{"ar_OM", LANG_ARABIC,SUBLANG_ARABIC_OMAN}, +{"ar_QA", LANG_ARABIC,SUBLANG_ARABIC_QATAR}, +{"ar_SA", LANG_ARABIC,SUBLANG_ARABIC_SAUDI_ARABIA}, +//no sudan +{"ar_SY", LANG_ARABIC,SUBLANG_ARABIC_SYRIA}, +{"ar_TN", LANG_ARABIC,SUBLANG_ARABIC_TUNISIA}, +{"ar_YE", LANG_ARABIC,SUBLANG_ARABIC_YEMEN}, +{"be", LANG_BELARUSIAN,SUBLANG_NEUTRAL}, +{"be_BY", LANG_BELARUSIAN,SUBLANG_BELARUSIAN_BELARUS}, +{"bg", LANG_BULGARIAN,SUBLANG_NEUTRAL}, +{"bg_BG", LANG_BULGARIAN,SUBLANG_BULGARIAN_BULGARIA}, +{"ca", LANG_CATALAN,SUBLANG_NEUTRAL}, +{"ca_ES", LANG_CATALAN,SUBLANG_CATALAN_CATALAN}, +{"cs", LANG_CZECH,SUBLANG_NEUTRAL}, +{"cs_CZ", LANG_CZECH,SUBLANG_CZECH_CZECH_REPUBLIC}, +{"da", LANG_DANISH,SUBLANG_NEUTRAL}, +{"da_DK", LANG_DANISH,SUBLANG_DANISH_DENMARK}, +{"de", LANG_GERMAN,SUBLANG_NEUTRAL}, +{"de_AT", LANG_GERMAN,SUBLANG_GERMAN_AUSTRIAN}, +{"de_CH", LANG_GERMAN,SUBLANG_GERMAN_SWISS}, +{"de_DE", LANG_GERMAN,SUBLANG_GERMAN}, +{"de_LU", LANG_GERMAN,SUBLANG_GERMAN_LUXEMBOURG}, +{"el", LANG_GREEK,SUBLANG_NEUTRAL}, +{"el_GR", LANG_GREEK,SUBLANG_GREEK_GREECE}, +//{"en_029", LANG_ENGLISH,SUBLANG_ENGLISH_CARIBBEAN}, +{"en", LANG_ENGLISH,SUBLANG_NEUTRAL}, +{"en_AU", LANG_ENGLISH,SUBLANG_ENGLISH_AUS}, +{"en_CA", LANG_ENGLISH,SUBLANG_ENGLISH_CAN}, +{"en_GB", LANG_ENGLISH,SUBLANG_ENGLISH_UK}, +//{"en_IE", LANG_ENGLISH,SUBLANG_ENGLISH_IRELAND}, +{"en_IN", LANG_ENGLISH,SUBLANG_ENGLISH_INDIA}, +//MT +{"en_NZ", LANG_ENGLISH,SUBLANG_ENGLISH_NZ}, +{"en_PH", LANG_ENGLISH,SUBLANG_ENGLISH_PHILIPPINES}, +{"en_SG", LANG_ENGLISH,SUBLANG_ENGLISH_SINGAPORE}, +{"en_US", LANG_ENGLISH,SUBLANG_ENGLISH_US}, +{"en_ZA", LANG_ENGLISH,SUBLANG_ENGLISH_SOUTH_AFRICA}, +{"es", LANG_SPANISH,SUBLANG_NEUTRAL}, +{"es_AR", LANG_SPANISH,SUBLANG_SPANISH_ARGENTINA}, +{"es_BO", LANG_SPANISH,SUBLANG_SPANISH_BOLIVIA}, +{"es_CL", LANG_SPANISH,SUBLANG_SPANISH_CHILE}, +{"es_CO", LANG_SPANISH,SUBLANG_SPANISH_COLOMBIA}, +{"es_CR", LANG_SPANISH,SUBLANG_SPANISH_COSTA_RICA}, +{"es_DO", LANG_SPANISH,SUBLANG_SPANISH_DOMINICAN_REPUBLIC}, +{"es_EC", LANG_SPANISH,SUBLANG_SPANISH_ECUADOR}, +{"es_ES", LANG_SPANISH,SUBLANG_SPANISH}, +{"es_GT", LANG_SPANISH,SUBLANG_SPANISH_GUATEMALA}, +{"es_HN", LANG_SPANISH,SUBLANG_SPANISH_HONDURAS}, +{"es_MX", LANG_SPANISH,SUBLANG_SPANISH_MEXICAN}, +{"es_NI", LANG_SPANISH,SUBLANG_SPANISH_NICARAGUA}, +{"es_PA", LANG_SPANISH,SUBLANG_SPANISH_PANAMA}, +{"es_PE", LANG_SPANISH,SUBLANG_SPANISH_PERU}, +{"es_PR", LANG_SPANISH,SUBLANG_SPANISH_PUERTO_RICO}, +{"es_PY", LANG_SPANISH,SUBLANG_SPANISH_PARAGUAY}, +{"es_SV", LANG_SPANISH,SUBLANG_SPANISH_EL_SALVADOR}, +{"es_US", LANG_SPANISH,SUBLANG_SPANISH_US}, +{"es_UY", LANG_SPANISH,SUBLANG_SPANISH_URUGUAY}, +{"es_VE", LANG_SPANISH,SUBLANG_SPANISH_VENEZUELA}, +{"et", LANG_ESTONIAN,SUBLANG_NEUTRAL}, +{"et_EE", LANG_ESTONIAN,SUBLANG_ESTONIAN_ESTONIA}, +{"fi", LANG_FINNISH,SUBLANG_NEUTRAL}, +{"fi_FI", LANG_FINNISH,SUBLANG_FINNISH_FINLAND}, +{"fr", LANG_FRENCH,SUBLANG_NEUTRAL}, +{"fr_BE", LANG_FRENCH,SUBLANG_FRENCH_BELGIAN}, +{"fr_CA", LANG_FRENCH,SUBLANG_FRENCH_CANADIAN}, +{"fr_CH", LANG_FRENCH,SUBLANG_FRENCH_SWISS}, +{"fr_FR", LANG_FRENCH,SUBLANG_FRENCH}, +{"fr_LU", LANG_FRENCH,SUBLANG_FRENCH_LUXEMBOURG}, +{"ga", LANG_IRISH,SUBLANG_NEUTRAL}, +{"ga_IE", LANG_IRISH,SUBLANG_IRISH_IRELAND}, +{"hi", LANG_HINDI,SUBLANG_NEUTRAL}, +{"hi_IN", LANG_HINDI,SUBLANG_HINDI_INDIA}, +{"hr", LANG_CROATIAN,SUBLANG_NEUTRAL}, +{"hr_HR", LANG_CROATIAN,SUBLANG_CROATIAN_CROATIA}, +{"hu", LANG_HUNGARIAN,SUBLANG_NEUTRAL}, +{"hu_HU", LANG_HUNGARIAN,SUBLANG_HUNGARIAN_HUNGARY}, +{"in", LANG_ARMENIAN,SUBLANG_NEUTRAL}, +{"in_ID", LANG_INDONESIAN,SUBLANG_INDONESIAN_INDONESIA}, +{"is", LANG_ICELANDIC,SUBLANG_NEUTRAL}, +{"is_IS", LANG_ICELANDIC,SUBLANG_ICELANDIC_ICELAND}, +{"it", LANG_ITALIAN,SUBLANG_NEUTRAL}, +{"it_CH", LANG_ITALIAN,SUBLANG_ITALIAN_SWISS}, +{"it_IT", LANG_ITALIAN,SUBLANG_ITALIAN}, +{"iw", LANG_HEBREW,SUBLANG_NEUTRAL}, +{"iw_IL", LANG_HEBREW,SUBLANG_HEBREW_ISRAEL}, +{"ja", LANG_JAPANESE,SUBLANG_NEUTRAL}, +{"ja_JP", LANG_JAPANESE,SUBLANG_JAPANESE_JAPAN}, +{"ko", LANG_KOREAN,SUBLANG_NEUTRAL}, +{"ko_KR", LANG_KOREAN,SUBLANG_KOREAN}, +{"lt", LANG_LITHUANIAN,SUBLANG_NEUTRAL}, +//{"lt_LT", LANG_LITHUANIAN,SUBLANG_LITHUANIAN_LITHUANIA}, +{"lv", LANG_LATVIAN,SUBLANG_NEUTRAL}, +{"lv_LV", LANG_LATVIAN,SUBLANG_LATVIAN_LATVIA}, +{"mk", LANG_MACEDONIAN,SUBLANG_NEUTRAL}, +{"mk_MK", LANG_MACEDONIAN,SUBLANG_MACEDONIAN_MACEDONIA}, +{"ms", LANG_MALAY,SUBLANG_NEUTRAL}, +{"ms_MY", LANG_MALAY,SUBLANG_MALAY_MALAYSIA}, +{"mt", LANG_MALTESE,SUBLANG_NEUTRAL}, +{"mt_MT", LANG_MALTESE,SUBLANG_MALTESE_MALTA}, +{"nl", LANG_DUTCH,SUBLANG_NEUTRAL}, +{"nl_BE", LANG_DUTCH,SUBLANG_DUTCH_BELGIAN}, +{"nl_NL", LANG_DUTCH,SUBLANG_DUTCH}, +{"no", LANG_NORWEGIAN,SUBLANG_NEUTRAL}, +{"no_NO", LANG_NORWEGIAN,SUBLANG_NORWEGIAN_BOKMAL}, +{"no_NO_NY", LANG_NORWEGIAN,SUBLANG_NORWEGIAN_NYNORSK}, +{"pl", LANG_POLISH,SUBLANG_NEUTRAL}, +{"pl_PL", LANG_POLISH,SUBLANG_POLISH_POLAND}, +{"pt", LANG_PORTUGUESE,SUBLANG_NEUTRAL}, +{"pt_BR", LANG_PORTUGUESE,SUBLANG_PORTUGUESE_BRAZILIAN}, +{"pt_PT", LANG_PORTUGUESE,SUBLANG_PORTUGUESE}, +{"ro", LANG_ROMANIAN,SUBLANG_NEUTRAL}, +{"ro_RO", LANG_ROMANIAN,SUBLANG_ROMANIAN_ROMANIA}, +{"ru", LANG_RUSSIAN,SUBLANG_NEUTRAL}, +{"ru_RU", LANG_RUSSIAN,SUBLANG_RUSSIAN_RUSSIA}, +{"sk", LANG_SLOVAK,SUBLANG_NEUTRAL}, +{"sk_SK", LANG_SLOVAK,SUBLANG_SLOVAK_SLOVAKIA}, +{"sl", LANG_SLOVENIAN,SUBLANG_NEUTRAL}, +{"sl_SI", LANG_SLOVENIAN,SUBLANG_SLOVENIAN_SLOVENIA}, +{"sq", LANG_ALBANIAN,SUBLANG_NEUTRAL}, +{"sq_AL", LANG_ALBANIAN,SUBLANG_ALBANIAN_ALBANIA}, +{"sr", LANG_SERBIAN_NEUTRAL,SUBLANG_NEUTRAL}, +{"sv", LANG_SWEDISH,SUBLANG_NEUTRAL}, +{"sv_SE", LANG_SWEDISH,SUBLANG_SWEDISH}, +{"th", LANG_THAI,SUBLANG_NEUTRAL}, +{"th_TH", LANG_THAI,SUBLANG_THAI_THAILAND}, +{"tr", LANG_TURKISH,SUBLANG_NEUTRAL}, +{"tr_TR", LANG_TURKISH,SUBLANG_TURKISH_TURKEY}, +{"uk", LANG_UKRAINIAN,SUBLANG_NEUTRAL}, +{"uk_UA", LANG_UKRAINIAN,SUBLANG_UKRAINIAN_UKRAINE}, +{"vi", LANG_VIETNAMESE,SUBLANG_NEUTRAL}, +{"vi_VN", LANG_VIETNAMESE,SUBLANG_VIETNAMESE_VIETNAM}, +{"zh", LANG_CHINESE,SUBLANG_NEUTRAL}, +{"zh_CN", LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED}, +{"zh_HK", LANG_CHINESE,SUBLANG_CHINESE_HONGKONG}, +{"zh_SG", LANG_CHINESE,SUBLANG_CHINESE_SINGAPORE}, +{0, 0,0}, +}; + +#endif // LANG_TABLE_H diff --git a/platform/windows/logo.png b/platform/windows/logo.png Binary files differnew file mode 100644 index 0000000000..a27e14dde8 --- /dev/null +++ b/platform/windows/logo.png diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp new file mode 100644 index 0000000000..efeb813a2d --- /dev/null +++ b/platform/windows/os_windows.cpp @@ -0,0 +1,1732 @@ +/*************************************************************************/ +/* os_windows.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 "drivers/gles2/rasterizer_gles2.h" +#include "drivers/gles1/rasterizer_gles1.h" +#include "os_windows.h" +#include "drivers/nedmalloc/memory_pool_static_nedmalloc.h" +#include "drivers/unix/memory_pool_static_malloc.h" +#include "os/memory_pool_dynamic_static.h" +#include "drivers/windows/thread_windows.h" +#include "drivers/windows/semaphore_windows.h" +#include "drivers/windows/mutex_windows.h" +#include "main/main.h" +#include "drivers/windows/file_access_windows.h" +#include "drivers/windows/dir_access_windows.h" + + +#include "servers/visual/visual_server_raster.h" +#include "servers/audio/audio_server_sw.h" +#include "servers/visual/visual_server_wrap_mt.h" + +#include "tcp_server_winsock.h" +#include "stream_peer_winsock.h" +#include "os/pc_joystick_map.h" +#include "lang_table.h" +#include "os/memory_pool_dynamic_prealloc.h" +#include "globals.h" +#include "io/marshalls.h" +static const WORD MAX_CONSOLE_LINES = 1500; + +//#define STDOUT_FILE + +extern HINSTANCE godot_hinstance; + +void RedirectIOToConsole() { + + int hConHandle; + + intptr_t lStdHandle; + + CONSOLE_SCREEN_BUFFER_INFO coninfo; + + FILE *fp; + + // allocate a console for this app + + AllocConsole(); + + // set the screen buffer to be big enough to let us scroll text + + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), + + &coninfo); + + coninfo.dwSize.Y = MAX_CONSOLE_LINES; + + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), + + coninfo.dwSize); + + // redirect unbuffered STDOUT to the console + + lStdHandle = (intptr_t)GetStdHandle(STD_OUTPUT_HANDLE); + + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + + fp = _fdopen( hConHandle, "w" ); + + *stdout = *fp; + + setvbuf( stdout, NULL, _IONBF, 0 ); + + // redirect unbuffered STDIN to the console + + lStdHandle = (intptr_t)GetStdHandle(STD_INPUT_HANDLE); + + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + + fp = _fdopen( hConHandle, "r" ); + + *stdin = *fp; + + setvbuf( stdin, NULL, _IONBF, 0 ); + + // redirect unbuffered STDERR to the console + + lStdHandle = (intptr_t)GetStdHandle(STD_ERROR_HANDLE); + + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + + fp = _fdopen( hConHandle, "w" ); + + *stderr = *fp; + + setvbuf( stderr, NULL, _IONBF, 0 ); + + // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog + + // point to console as well +} + +int OS_Windows::get_video_driver_count() const { + + return 2; +} +const char * OS_Windows::get_video_driver_name(int p_driver) const { + + return p_driver==0?"GLES2":"GLES1"; +} + +OS::VideoMode OS_Windows::get_default_video_mode() const { + + return VideoMode(800,600,false); +} + +int OS_Windows::get_audio_driver_count() const { + + return AudioDriverManagerSW::get_driver_count(); +} +const char * OS_Windows::get_audio_driver_name(int p_driver) const { + + AudioDriverSW* driver = AudioDriverManagerSW::get_driver(p_driver); + ERR_FAIL_COND_V( !driver, "" ); + return AudioDriverManagerSW::get_driver(p_driver)->get_name(); +} + +static MemoryPoolStatic *mempool_static=NULL; +static MemoryPoolDynamic *mempool_dynamic=NULL; + +void OS_Windows::initialize_core() { + + + last_button_state=0; + + //RedirectIOToConsole(); + + ThreadWindows::make_default(); + SemaphoreWindows::make_default(); + MutexWindows::make_default(); + + FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES); + FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA); + FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM); + //FileAccessBufferedFA<FileAccessWindows>::make_default(); + DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_RESOURCES); + DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_USERDATA); + DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_FILESYSTEM); + + TCPServerWinsock::make_default(); + StreamPeerWinsock::make_default(); + + mempool_static = new MemoryPoolStaticMalloc; +#if 1 + mempool_dynamic = memnew( MemoryPoolDynamicStatic ); +#else +#define DYNPOOL_SIZE 4*1024*1024 + void * buffer = malloc( DYNPOOL_SIZE ); + mempool_dynamic = memnew( MemoryPoolDynamicPrealloc(buffer,DYNPOOL_SIZE) ); + +#endif + + // We need to know how often the clock is updated + if( !QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second) ) + ticks_per_second = 1000; + // If timeAtGameStart is 0 then we get the time since + // the start of the computer when we call GetGameTime() + ticks_start = 0; + ticks_start = get_ticks_usec(); + + process_map = memnew((Map<ProcessID, ProcessInfo>)); + + IP_Unix::make_default(); + + cursor_shape=CURSOR_ARROW; + + +} + +bool OS_Windows::can_draw() const { + + return !minimized; +}; + + +LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam) { + + + switch (uMsg) // Check For Windows Messages + { + case WM_ACTIVATE: // Watch For Window Activate Message + { + minimized = HIWORD(wParam) != 0; + if (!main_loop) { + return 0; + }; + if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) { + + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); + alt_mem=false; + control_mem=false; + shift_mem=false; + if (mouse_mode==MOUSE_MODE_CAPTURED) { + RECT clipRect; + GetClientRect(hWnd, &clipRect); + ClientToScreen(hWnd, (POINT*) &clipRect.left); + ClientToScreen(hWnd, (POINT*) &clipRect.right); + ClipCursor(&clipRect); + SetCapture(hWnd); + + } + } else { + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); + alt_mem=false; + + }; + + return 0; // Return To The Message Loop + } + + case WM_PAINT: + + Main::force_redraw(); + break; + + case WM_SYSCOMMAND: // Intercept System Commands + { + switch (wParam) // Check System Calls + { + case SC_SCREENSAVE: // Screensaver Trying To Start? + case SC_MONITORPOWER: // Monitor Trying To Enter Powersave? + return 0; // Prevent From Happening + case SC_KEYMENU: + if ((lParam>>16)<=0) + return 0; + } + break; // Exit + } + + case WM_CLOSE: // Did We Receive A Close Message? + { + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); + //force_quit=true; + return 0; // Jump Back + } + case WM_MOUSELEAVE: { + + old_invalid=true; + outside=true; + + } break; + case WM_MOUSEMOVE: { + + if (outside) { + + CursorShape c=cursor_shape; + cursor_shape=CURSOR_MAX; + set_cursor_shape(c); + outside=false; + + //Once-Off notification, must call again.... + TRACKMOUSEEVENT tme; + tme.cbSize=sizeof(TRACKMOUSEEVENT); + tme.dwFlags=TME_LEAVE; + tme.hwndTrack=hWnd; + tme.dwHoverTime=HOVER_DEFAULT; + TrackMouseEvent(&tme); + + } + InputEvent event; + event.type=InputEvent::MOUSE_MOTION; + event.ID=++last_id; + InputEventMouseMotion &mm=event.mouse_motion; + + mm.mod.control=(wParam&MK_CONTROL)!=0; + mm.mod.shift=(wParam&MK_SHIFT)!=0; + mm.mod.alt=alt_mem; + + mm.button_mask|=(wParam&MK_LBUTTON)?(1<<0):0; + mm.button_mask|=(wParam&MK_RBUTTON)?(1<<1):0; + mm.button_mask|=(wParam&MK_MBUTTON)?(1<<2):0; + last_button_state=mm.button_mask; + /*mm.button_mask|=(wParam&MK_XBUTTON1)?(1<<5):0; + mm.button_mask|=(wParam&MK_XBUTTON2)?(1<<6):0;*/ + mm.x=GET_X_LPARAM(lParam); + mm.y=GET_Y_LPARAM(lParam); + + if (mouse_mode==MOUSE_MODE_CAPTURED) { + + Point2i c(video_mode.width/2,video_mode.height/2); + if (Point2i(mm.x,mm.y)==c) { + center=c; + return 0; + } + + Point2i ncenter(mm.x,mm.y); + mm.x = old_x + (mm.x-center.x); + mm.y = old_y + (mm.y-center.y); + center=ncenter; + POINT pos = { (int) c.x, (int) c.y }; + ClientToScreen(hWnd, &pos); + SetCursorPos(pos.x, pos.y); + + } + + input->set_mouse_pos(Point2(mm.x,mm.y)); + mm.speed_x=input->get_mouse_speed().x; + mm.speed_y=input->get_mouse_speed().y; + + if (old_invalid) { + + old_x=mm.x; + old_y=mm.y; + old_invalid=false; + } + + mm.relative_x=mm.x-old_x; + mm.relative_y=mm.y-old_y; + old_x=mm.x; + old_y=mm.y; + if (main_loop) + input->parse_input_event(event); + + + + } break; + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MOUSEWHEEL: + case WM_LBUTTONDBLCLK: + /*case WM_XBUTTONDOWN: + case WM_XBUTTONUP: */{ + + InputEvent event; + event.type=InputEvent::MOUSE_BUTTON; + event.ID=++last_id; + InputEventMouseButton &mb=event.mouse_button; + + switch (uMsg) { + case WM_LBUTTONDOWN: { + mb.pressed=true; + mb.button_index=1; + } break; + case WM_LBUTTONUP: { + mb.pressed=false; + mb.button_index=1; + } break; + case WM_MBUTTONDOWN: { + mb.pressed=true; + mb.button_index=3; + + } break; + case WM_MBUTTONUP: { + mb.pressed=false; + mb.button_index=3; + } break; + case WM_RBUTTONDOWN: { + mb.pressed=true; + mb.button_index=2; + } break; + case WM_RBUTTONUP: { + mb.pressed=false; + mb.button_index=2; + } break; + case WM_LBUTTONDBLCLK: { + + mb.pressed=true; + mb.button_index=1; + mb.doubleclick = true; + } break; + case WM_MOUSEWHEEL: { + + mb.pressed=true; + int motion = (short)HIWORD(wParam); + if (!motion) + return 0; + + + if (motion>0) + mb.button_index=4; + else + mb.button_index=5; + + + } break; + /* + case WM_XBUTTONDOWN: { + mb.pressed=true; + mb.button_index=(HIWORD(wParam)==XBUTTON1)?6:7; + } break; + case WM_XBUTTONUP: + mb.pressed=true; + mb.button_index=(HIWORD(wParam)==XBUTTON1)?6:7; + } break;*/ + default: { return 0; } + } + + + mb.mod.control=(wParam&MK_CONTROL)!=0; + mb.mod.shift=(wParam&MK_SHIFT)!=0; + mb.mod.alt=alt_mem; + //mb.mod.alt=(wParam&MK_MENU)!=0; + mb.button_mask|=(wParam&MK_LBUTTON)?(1<<0):0; + mb.button_mask|=(wParam&MK_RBUTTON)?(1<<1):0; + mb.button_mask|=(wParam&MK_MBUTTON)?(1<<2):0; + + last_button_state=mb.button_mask; + /* + mb.button_mask|=(wParam&MK_XBUTTON1)?(1<<5):0; + mb.button_mask|=(wParam&MK_XBUTTON2)?(1<<6):0;*/ + mb.x=GET_X_LPARAM(lParam); + mb.y=GET_Y_LPARAM(lParam); + + if (mouse_mode==MOUSE_MODE_CAPTURED) { + + mb.x=old_x; + mb.y=old_y; + } + + mb.global_x=mb.x; + mb.global_y=mb.y; + + + if (uMsg != WM_MOUSEWHEEL) { + if (mb.pressed) { + + if (++pressrc>0) + SetCapture(hWnd); + } else { + + + if (--pressrc<=0) { + ReleaseCapture(); + pressrc=0; + } + + } + } else if (mouse_mode!=MOUSE_MODE_CAPTURED) { + // for reasons unknown to mankind, wheel comes in screen cordinates + RECT rect; + GetWindowRect(hWnd,&rect); + mb.x-=rect.left; + mb.y-=rect.top; + + } + + if (main_loop) { + input->parse_input_event(event); + if (mb.pressed && mb.button_index>3) { + //send release for mouse wheel + mb.pressed=false; + event.ID=++last_id; + input->parse_input_event(event); + + } + } + + + + } break; + + case WM_SIZE: { + video_mode.width=LOWORD(lParam); + video_mode.height=HIWORD(lParam); + //return 0; // Jump Back + } break; + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYUP: + case WM_KEYDOWN: { + + + if (wParam==VK_SHIFT) + shift_mem=uMsg==WM_KEYDOWN; + if (wParam==VK_CONTROL) + control_mem=uMsg==WM_KEYDOWN; + if (wParam==VK_MENU) { + alt_mem=(uMsg==WM_KEYDOWN || uMsg==WM_SYSKEYDOWN); + if (lParam&(1<<24)) + gr_mem=alt_mem; + } + + //if (wParam==VK_WIN) TODO wtf is this? + // meta_mem=uMsg==WM_KEYDOWN; + + + } //fallthrough + case WM_CHAR: { + + ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE); + + KeyEvent ke; + ke.mod_state.shift=shift_mem; + ke.mod_state.alt=alt_mem; + ke.mod_state.control=control_mem; + ke.mod_state.meta=meta_mem; + ke.uMsg=uMsg; + + if (ke.uMsg==WM_SYSKEYDOWN) + ke.uMsg=WM_KEYDOWN; + if (ke.uMsg==WM_SYSKEYUP) + ke.uMsg=WM_KEYUP; + + + /*if (ke.uMsg==WM_KEYDOWN && alt_mem && uMsg!=WM_SYSKEYDOWN) { + //altgr hack for intl keyboards, not sure how good it is + //windows is weeeeird + ke.mod_state.alt=false; + ke.mod_state.control=false; + print_line("") + }*/ + + + ke.wParam=wParam; + ke.lParam=lParam; + key_event_buffer[key_event_pos++]=ke; + + } break; + case WM_INPUTLANGCHANGEREQUEST: { + + print_line("input lang change"); + } break; + default: { + + if (user_proc) { + + return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam); + }; + }; + } + + return DefWindowProcW(hWnd,uMsg,wParam,lParam); + +} + +LRESULT CALLBACK WndProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam) { + + OS_Windows *os_win = static_cast<OS_Windows*>(OS::get_singleton()); + if (os_win) + return os_win->WndProc(hWnd,uMsg,wParam,lParam); + else + return DefWindowProcW(hWnd,uMsg,wParam,lParam); + +} + +void OS_Windows::probe_joysticks() { + + int device_count = joyGetNumDevs(); + + JOYINFOEX jinfo; + jinfo.dwSize = sizeof(JOYINFOEX); + jinfo.dwFlags = JOY_RETURNALL; + + for (int i=0; i<JOYSTICKS_MAX; i++) { + + joysticks[i].attached = (device_count > 0) && (joyGetPosEx(JOYSTICKID1 + i, &jinfo) == JOYERR_NOERROR); + + if (!joysticks[i].attached) { + + continue; + }; + + joysticks[i].last_buttons = jinfo.dwButtons; + + joysticks[i].last_axis[0] = jinfo.dwXpos; + joysticks[i].last_axis[1] = jinfo.dwYpos; + joysticks[i].last_axis[2] = jinfo.dwZpos; + joysticks[i].last_axis[3] = jinfo.dwRpos; + joysticks[i].last_axis[4] = jinfo.dwUpos; + joysticks[i].last_axis[5] = jinfo.dwVpos; + }; +}; + +void OS_Windows::process_key_events() { + + for(int i=0;i<key_event_pos;i++) { + + KeyEvent &ke = key_event_buffer[i]; + switch(ke.uMsg) { + + case WM_CHAR: { + + //do nothing + } break; + case WM_KEYUP: + case WM_KEYDOWN: { + + + InputEvent event; + event.type=InputEvent::KEY; + event.ID=++last_id; + InputEventKey &k=event.key; + + + k.mod=ke.mod_state; + k.pressed=(ke.uMsg==WM_KEYDOWN); + + k.scancode=KeyMappingWindows::get_keysym(ke.wParam); + if (i+1 < key_event_pos && key_event_buffer[i+1].uMsg==WM_CHAR) + k.unicode=key_event_buffer[i+1].wParam; + if (k.unicode && gr_mem) { + k.mod.alt=false; + k.mod.control=false; + } + + if (k.unicode<32) + k.unicode=0; + + + + k.echo=(ke.uMsg==WM_KEYDOWN && (ke.lParam&(1<<30))); + + input->parse_input_event(event); + + + } break; + } + } + + key_event_pos=0; +} + +void OS_Windows::_post_dpad(DWORD p_dpad, int p_device, bool p_pressed) { + + InputEvent ievent; + ievent.device = p_device; + ievent.type = InputEvent::JOYSTICK_BUTTON; + ievent.joy_button.pressed = p_pressed; + ievent.joy_button.pressure = p_pressed ? 1.0 : 0.0; + + if (p_dpad == 0) { + + ievent.joy_button.button_index = JOY_DPAD_UP; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 4500) { + + ievent.joy_button.button_index = JOY_DPAD_UP; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + ievent.joy_button.button_index = JOY_DPAD_RIGHT; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 9000) { + + ievent.joy_button.button_index = JOY_DPAD_RIGHT; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 13500) { + + ievent.joy_button.button_index = JOY_DPAD_RIGHT; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + ievent.joy_button.button_index = JOY_DPAD_DOWN; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 18000) { + + ievent.joy_button.button_index = JOY_DPAD_DOWN; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 22500) { + + ievent.joy_button.button_index = JOY_DPAD_DOWN; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + ievent.joy_button.button_index = JOY_DPAD_LEFT; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 27000) { + + ievent.joy_button.button_index = JOY_DPAD_LEFT; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + } else if (p_dpad == 31500) { + + ievent.joy_button.button_index = JOY_DPAD_LEFT; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + + ievent.joy_button.button_index = JOY_DPAD_UP; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + }; +}; + +void OS_Windows::process_joysticks() { + + if (!main_loop) { + return; + }; + + InputEvent ievent; + + JOYINFOEX jinfo; + jinfo.dwSize = sizeof(JOYINFOEX); + jinfo.dwFlags = JOY_RETURNALL; + + for (int i=0; i<JOYSTICKS_MAX; i++) { + + if (!joysticks[i].attached) { + continue; + }; + + if (joyGetPosEx(JOYSTICKID1 + i, &jinfo) != JOYERR_NOERROR) { + + continue; + }; + + ievent.device = i; + + #define CHECK_AXIS(n, var) \ + if (joysticks[i].last_axis[n] != var) {\ + ievent.type = InputEvent::JOYSTICK_MOTION;\ + ievent.ID = ++last_id;\ + ievent.joy_motion.axis = n;\ + ievent.joy_motion.axis_value = (float)((int)var - MAX_JOY_AXIS) / (float)MAX_JOY_AXIS;\ + joysticks[i].last_axis[n] = var;\ + input->parse_input_event(ievent);\ + }; + + CHECK_AXIS(0, jinfo.dwXpos); + CHECK_AXIS(1, jinfo.dwYpos); + CHECK_AXIS(2, jinfo.dwZpos); + CHECK_AXIS(3, jinfo.dwRpos); + CHECK_AXIS(4, jinfo.dwUpos); + CHECK_AXIS(5, jinfo.dwVpos); + + if (joysticks[i].last_pov != jinfo.dwPOV) { + + if (joysticks[i].last_pov != JOY_POVCENTERED) + _post_dpad(joysticks[i].last_pov, i, false); + + if (jinfo.dwPOV != JOY_POVCENTERED) + _post_dpad(jinfo.dwPOV, i, true); + + joysticks[i].last_pov = jinfo.dwPOV; + }; + + if (joysticks[i].last_buttons == jinfo.dwButtons) { + continue; + }; + + ievent.type = InputEvent::JOYSTICK_BUTTON; + for (int j=0; j<32; j++) { + + if ( (joysticks[i].last_buttons & (1<<j)) != (jinfo.dwButtons & (1<<j)) ) { + + ievent.joy_button.button_index = _pc_joystick_get_native_button(j); + ievent.joy_button.pressed = jinfo.dwButtons & 1<<j; + ievent.ID = ++last_id; + input->parse_input_event(ievent); + }; + }; + + joysticks[i].last_buttons = jinfo.dwButtons; + }; +}; + +void OS_Windows::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + + + main_loop=NULL; + outside=true; + + WNDCLASSEXW wc; + + video_mode=p_desired; + //printf("**************** desired %s, mode %s\n", p_desired.fullscreen?"true":"false", video_mode.fullscreen?"true":"false"); + RECT WindowRect; + + WindowRect.left=0; + WindowRect.right=video_mode.width; + WindowRect.top=0; + WindowRect.bottom=video_mode.height; + + memset(&wc,0,sizeof(WNDCLASSEXW)); + wc.cbSize=sizeof(WNDCLASSEXW); + wc.style= CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; + wc.lpfnWndProc = (WNDPROC)::WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra= 0; + //wc.hInstance = hInstance; + wc.hInstance = godot_hinstance ? godot_hinstance : GetModuleHandle(NULL); + wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wc.hCursor = NULL;//LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = L"Engine"; + + if (!RegisterClassExW(&wc)) { + MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return; // Return + } + + + if (video_mode.fullscreen) { + + DEVMODE current; + memset(¤t,0,sizeof(current)); + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, ¤t); + + DEVMODE dmScreenSettings; + memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); + dmScreenSettings.dmSize=sizeof(dmScreenSettings); + dmScreenSettings.dmPelsWidth = video_mode.width; + dmScreenSettings.dmPelsHeight = video_mode.height; + dmScreenSettings.dmBitsPerPel = current.dmBitsPerPel; + dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; + + LONG err = ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN); + if (err!=DISP_CHANGE_SUCCESSFUL) { + + video_mode.fullscreen=false; + } + } + + DWORD dwExStyle; + DWORD dwStyle; + + if (video_mode.fullscreen) { + + dwExStyle=WS_EX_APPWINDOW; + dwStyle=WS_POPUP; + + } else { + dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + dwStyle=WS_OVERLAPPEDWINDOW; + } + + AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); + + + char* windowid = getenv("GODOT_WINDOWID"); + if (windowid) { + + // strtoull on mingw + #ifdef MINGW_ENABLED + hWnd = (HWND)strtoull(windowid, NULL, 0); + #else + hWnd = (HWND)_strtoui64(windowid, NULL, 0); + #endif + SetLastError(0); + user_proc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC); + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)(WNDPROC)::WndProc); + DWORD le = GetLastError(); + if (user_proc == 0 && le != 0) { + + printf("Error setting WNDPROC: %li\n", le); + }; + LONG_PTR proc = GetWindowLongPtr(hWnd, GWLP_WNDPROC); + + RECT rect; + if (!GetClientRect(hWnd, &rect)) { + MessageBoxW(NULL,L"Window Creation Error.",L"ERROR",MB_OK|MB_ICONEXCLAMATION); + return; // Return FALSE + }; + video_mode.width = rect.right; + video_mode.height = rect.bottom; + video_mode.fullscreen = false; + } else { + + if (!(hWnd=CreateWindowExW(dwExStyle,L"Engine",L"", dwStyle|WS_CLIPSIBLINGS|WS_CLIPCHILDREN, 0, 0,WindowRect.right-WindowRect.left,WindowRect.bottom-WindowRect.top, NULL,NULL, hInstance,NULL))) { + MessageBoxW(NULL,L"Window Creation Error.",L"ERROR",MB_OK|MB_ICONEXCLAMATION); + return; // Return FALSE + } + + + }; + +#if defined(OPENGL_ENABLED) || defined(GLES2_ENABLED) || defined(LEGACYGL_ENABLED) + gl_context = memnew( ContextGL_Win(hWnd,false) ); + gl_context->initialize(); + rasterizer = memnew( RasterizerGLES2 ); +#else + #ifdef DX9_ENABLED + rasterizer = memnew( RasterizerDX9(hWnd) ); + #endif +#endif + + visual_server = memnew( VisualServerRaster(rasterizer) ); + if (get_render_thread_mode()!=RENDER_THREAD_UNSAFE) { + + visual_server =memnew(VisualServerWrapMT(visual_server,get_render_thread_mode()==RENDER_SEPARATE_THREAD)); + } + + // + physics_server = memnew( PhysicsServerSW ); + physics_server->init(); + + physics_2d_server = memnew( Physics2DServerSW ); + physics_2d_server->init(); + + if (!is_no_window_mode_enabled()) { + ShowWindow(hWnd,SW_SHOW); // Show The Window + SetForegroundWindow(hWnd); // Slightly Higher Priority + SetFocus(hWnd); // Sets Keyboard Focus To + } + +/* + DEVMODE dmScreenSettings; // Device Mode + memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared + dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Size Of The Devmode Structure + dmScreenSettings.dmPelsWidth = width; // Selected Screen Width + dmScreenSettings.dmPelsHeight = height; // Selected Screen Height + dmScreenSettings.dmBitsPerPel = bits; // Selected Bits Per Pixel + dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; + if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) + + + + + */ + visual_server->init(); + + input = memnew( InputDefault ); + + 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->init(); + + spatial_sound_server = memnew( SpatialSoundServerSW ); + spatial_sound_server->init(); + spatial_sound_2d_server = memnew( SpatialSound2DServerSW ); + spatial_sound_2d_server->init(); + + probe_joysticks(); + + TRACKMOUSEEVENT tme; + tme.cbSize=sizeof(TRACKMOUSEEVENT); + tme.dwFlags=TME_LEAVE; + tme.hwndTrack=hWnd; + tme.dwHoverTime=HOVER_DEFAULT; + TrackMouseEvent(&tme); + + + _ensure_data_dir(); + + +} + +void OS_Windows::set_clipboard(const String& p_text) { + + if (!OpenClipboard(hWnd)) { + ERR_EXPLAIN("Unable to open clipboard."); + ERR_FAIL(); + }; + EmptyClipboard(); + + HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (p_text.length() + 1) * sizeof(CharType)); + if (mem == NULL) { + ERR_EXPLAIN("Unable to allocate memory for clipboard contents."); + ERR_FAIL(); + }; + LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem); + memcpy(lptstrCopy, p_text.c_str(), (p_text.length() + 1) * sizeof(CharType)); + //memset((lptstrCopy + p_text.length()), 0, sizeof(CharType)); + GlobalUnlock(mem); + + SetClipboardData(CF_UNICODETEXT, mem); + + // set the CF_TEXT version (not needed?) + CharString utf8 = p_text.utf8(); + mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1); + if (mem == NULL) { + ERR_EXPLAIN("Unable to allocate memory for clipboard contents."); + ERR_FAIL(); + }; + LPTSTR ptr = (LPTSTR)GlobalLock(mem); + memcpy(ptr, utf8.get_data(), utf8.length()); + ptr[utf8.length()] = 0; + GlobalUnlock(mem); + + SetClipboardData(CF_TEXT, mem); + + CloseClipboard(); +}; + +String OS_Windows::get_clipboard() const { + + String ret; + if (!OpenClipboard(hWnd)) { + ERR_EXPLAIN("Unable to open clipboard."); + ERR_FAIL_V(""); + }; + + if (IsClipboardFormatAvailable(CF_UNICODETEXT)) { + + HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); + if (mem != NULL) { + + LPWSTR ptr = (LPWSTR)GlobalLock(mem); + if (ptr != NULL) { + + ret = String((CharType*)ptr); + GlobalUnlock(mem); + }; + }; + + } else if (IsClipboardFormatAvailable(CF_TEXT)) { + + HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); + if (mem != NULL) { + + LPTSTR ptr = (LPTSTR)GlobalLock(mem); + if (ptr != NULL) { + + ret.parse_utf8((const char*)ptr); + GlobalUnlock(mem); + }; + }; + }; + + CloseClipboard(); + + return ret; +}; + + +void OS_Windows::delete_main_loop() { + + if (main_loop) + memdelete(main_loop); + main_loop=NULL; +} + +void OS_Windows::set_main_loop( MainLoop * p_main_loop ) { + + input->set_main_loop(p_main_loop); + main_loop=p_main_loop; +} + +void OS_Windows::finalize() { + + if(main_loop) + memdelete(main_loop); + + main_loop=NULL; + + visual_server->finish(); + memdelete(visual_server); +#ifdef OPENGL_ENABLED + if (gl_context) + memdelete(gl_context); +#endif + if (rasterizer) + memdelete(rasterizer); + + if (user_proc) { + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc); + }; + + spatial_sound_server->finish(); + memdelete(spatial_sound_server); + spatial_sound_2d_server->finish(); + memdelete(spatial_sound_2d_server); + + //if (debugger_connection_console) { +// memdelete(debugger_connection_console); +//} + + audio_server->finish(); + memdelete(audio_server); + memdelete(sample_manager); + + memdelete(input); + + physics_server->finish(); + memdelete(physics_server); + + physics_2d_server->finish(); + memdelete(physics_2d_server); + +} +void OS_Windows::finalize_core() { + + memdelete(process_map); + + if (mempool_dynamic) + memdelete( mempool_dynamic ); + if (mempool_static) + delete mempool_static; + + + TCPServerWinsock::cleanup(); + StreamPeerWinsock::cleanup(); +} + +void OS_Windows::vprint(const char* p_format, va_list p_list, bool p_stderr) { + + char buf[16384+1]; + int len = vsnprintf(buf,16384,p_format,p_list); + if (len<=0) + return; + buf[len]=0; + + + int wlen = MultiByteToWideChar(CP_UTF8,0,buf,len,NULL,0); + if (wlen<0) + return; + + wchar_t *wbuf = (wchar_t*)malloc((len+1)*sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8,0,buf,len,wbuf,wlen); + wbuf[wlen]=0; + + if (p_stderr) + fwprintf(stderr,L"%s",wbuf); + else + wprintf(L"%s",wbuf); + +#ifdef STDOUT_FILE + //vwfprintf(stdo,p_format,p_list); +#endif + free(wbuf); + + fflush(stdout); +}; + +void OS_Windows::alert(const String& p_alert,const String& p_title) { + + if (!is_no_window_mode_enabled()) + MessageBoxW(NULL,p_alert.c_str(),p_title.c_str(),MB_OK|MB_ICONEXCLAMATION); + else + print_line("ALERT: "+p_alert); +} + +void OS_Windows::set_mouse_mode(MouseMode p_mode) { + + if (mouse_mode==p_mode) + return; + ShowCursor(p_mode==MOUSE_MODE_VISIBLE); + mouse_mode=p_mode; + if (p_mode==MOUSE_MODE_CAPTURED) { + RECT clipRect; + GetClientRect(hWnd, &clipRect); + ClientToScreen(hWnd, (POINT*) &clipRect.left); + ClientToScreen(hWnd, (POINT*) &clipRect.right); + ClipCursor(&clipRect); + SetCapture(hWnd); + center=Point2i(video_mode.width/2,video_mode.height/2); + POINT pos = { (int) center.x, (int) center.y }; + ClientToScreen(hWnd, &pos); + SetCursorPos(pos.x, pos.y); + } else { + ReleaseCapture(); + ClipCursor(NULL); + } + +} + +OS_Windows::MouseMode OS_Windows::get_mouse_mode() const{ + + return mouse_mode; +} + + + +Point2 OS_Windows::get_mouse_pos() const { + + return Point2(old_x, old_y); +} + +int OS_Windows::get_mouse_button_state() const { + + return last_button_state; +} + +void OS_Windows::set_window_title(const String& p_title) { + + SetWindowTextW(hWnd,p_title.c_str()); +} + +void OS_Windows::set_video_mode(const VideoMode& p_video_mode,int p_screen) { + + +} +OS::VideoMode OS_Windows::get_video_mode(int p_screen) const { + + return video_mode; +} +void OS_Windows::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const { + + +} + +void OS_Windows::print_error(const char* p_function,const char* p_file,int p_line,const char *p_code,const char*p_rationale,ErrorType p_type) { + + HANDLE hCon=GetStdHandle(STD_OUTPUT_HANDLE); + if (!hCon || hCon==INVALID_HANDLE_VALUE) { + if (p_rationale && p_rationale[0]) { + + print("\E[1;31;40mERROR: %s: \E[1;37;40m%s\n",p_function,p_rationale); + print("\E[0;31;40m At: %s:%i.\E[0;0;37m\n",p_file,p_line); + + } else { + print("\E[1;31;40mERROR: %s: \E[1;37;40m%s\n",p_function,p_code); + print("\E[0;31;40m At: %s:%i.\E[0;0;37m\n",p_file,p_line); + + } + } else { + + CONSOLE_SCREEN_BUFFER_INFO sbi; //original + GetConsoleScreenBufferInfo(hCon,&sbi); + + SetConsoleTextAttribute(hCon,sbi.wAttributes); + + + + uint32_t basecol=0; + switch(p_type) { + case ERR_ERROR: basecol = FOREGROUND_RED; break; + case ERR_WARNING: basecol = FOREGROUND_RED|FOREGROUND_GREEN; break; + case ERR_SCRIPT: basecol = FOREGROUND_GREEN; break; + } + + if (p_rationale && p_rationale[0]) { + + SetConsoleTextAttribute(hCon,basecol|FOREGROUND_INTENSITY); + + + switch(p_type) { + case ERR_ERROR: print("ERROR: "); break; + case ERR_WARNING: print("WARNING: "); break; + case ERR_SCRIPT: print("SCRIPT ERROR: "); break; + } + + SetConsoleTextAttribute(hCon,FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY); + print(" %s\n",p_rationale); + SetConsoleTextAttribute(hCon,basecol); + print("At: "); + SetConsoleTextAttribute(hCon,FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN); + print(" %s:%i\n",p_file,p_line); + + + } else { + SetConsoleTextAttribute(hCon,basecol|FOREGROUND_INTENSITY); + switch(p_type) { + case ERR_ERROR: print("ERROR: %s: ",p_function); break; + case ERR_WARNING: print("WARNING: %s: ",p_function); break; + case ERR_SCRIPT: print("SCRIPT ERROR: %s: ",p_function); break; + } + SetConsoleTextAttribute(hCon,FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY); + print(" %s\n",p_code); + SetConsoleTextAttribute(hCon,basecol); + print("At: "); + SetConsoleTextAttribute(hCon,FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN); + print(" %s:%i\n",p_file,p_line); + } + + SetConsoleTextAttribute(hCon,sbi.wAttributes); + } + +} + + +String OS_Windows::get_name() { + + return "Windows"; +} + +OS::Date OS_Windows::get_date() const { + + SYSTEMTIME systemtime; + GetSystemTime(&systemtime); + Date date; + date.day=systemtime.wDay; + date.month=Month(systemtime.wMonth); + date.weekday=Weekday(systemtime.wDayOfWeek); + date.year=systemtime.wYear; + date.dst=false; + return date; +} +OS::Time OS_Windows::get_time() const { + + SYSTEMTIME systemtime; + GetSystemTime(&systemtime); + + Time time; + time.hour=systemtime.wHour; + time.min=systemtime.wMinute; + time.sec=systemtime.wSecond; + return time; +} + +uint64_t OS_Windows::get_unix_time() const { + + FILETIME ft; + SYSTEMTIME st; + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); + + SYSTEMTIME ep; + ep.wYear = 1970; + ep.wMonth = 1; + ep.wDayOfWeek = 4; + ep.wDay = 1; + ep.wHour = 0; + ep.wMinute = 0; + ep.wSecond = 0; + ep.wMilliseconds = 0; + FILETIME fep; + SystemTimeToFileTime(&ep, &fep); + + return (*(uint64_t*)&ft - *(uint64_t*)&fep) / 10000000; +}; + +void OS_Windows::delay_usec(uint32_t p_usec) const { + + if (p_usec < 1000) + Sleep(1); + else + Sleep(p_usec / 1000); + +} +uint64_t OS_Windows::get_ticks_usec() const { + + uint64_t ticks; + uint64_t time; + // This is the number of clock ticks since start + if( !QueryPerformanceCounter((LARGE_INTEGER *)&ticks) ) + ticks = (UINT64)timeGetTime(); + // Divide by frequency to get the time in seconds + time = ticks * 1000000L / ticks_per_second; + // Subtract the time at game start to get + // the time since the game started + time -= ticks_start; + return time; +} + + +void OS_Windows::process_events() { + + MSG msg; + + process_joysticks(); + + while(PeekMessageW(&msg,NULL,0,0,PM_REMOVE)) { + + + TranslateMessage(&msg); + DispatchMessageW(&msg); + + } + + process_key_events(); + +} + +void OS_Windows::set_cursor_shape(CursorShape p_shape) { + + ERR_FAIL_INDEX(p_shape,CURSOR_MAX); + + if (cursor_shape==p_shape) + return; + + static const LPCTSTR win_cursors[CURSOR_MAX]={ + IDC_ARROW, + IDC_IBEAM, + IDC_HAND,//finger + IDC_CROSS, + IDC_WAIT, + IDC_APPSTARTING, + IDC_ARROW, + IDC_ARROW, + IDC_NO, + IDC_SIZENS, + IDC_SIZEWE, + IDC_SIZENESW, + IDC_SIZENWSE, + IDC_SIZEALL, + IDC_SIZENS, + IDC_SIZEWE, + IDC_HELP + }; + + SetCursor(LoadCursor(hInstance,win_cursors[p_shape])); + cursor_shape=p_shape; +} + +Error OS_Windows::execute(const String& p_path, const List<String>& p_arguments,bool p_blocking,ProcessID *r_child_id,String* r_pipe,int *r_exitcode) { + + if (p_blocking && r_pipe) { + + + String argss; + argss="\"\""+p_path+"\""; + + for(int i=0;i<p_arguments.size();i++) { + + argss+=String(" \"")+p_arguments[i]+"\""; + } + +// print_line("ARGS: "+argss); + //argss+"\""; + //argss+=" 2>nul"; + + FILE* f=_wpopen(argss.c_str(),L"r"); + + ERR_FAIL_COND_V(!f,ERR_CANT_OPEN); + + char buf[65535]; + while(fgets(buf,65535,f)) { + + (*r_pipe)+=buf; + } + + int rv = _pclose(f); + if (r_exitcode) + *r_exitcode=rv; + + return OK; + } + + String cmdline = "\""+p_path+"\""; + const List<String>::Element* I = p_arguments.front(); + while (I) { + + + cmdline += " \""+I->get() + "\""; + + I = I->next(); + }; + + //cmdline+="\""; + + ProcessInfo pi; + ZeroMemory( &pi.si, sizeof(pi.si) ); + pi.si.cb = sizeof(pi.si); + ZeroMemory( &pi.pi, sizeof(pi.pi) ); + + print_line("running cmdline: "+cmdline); + + int ret = CreateProcess(NULL, (LPSTR)cmdline.utf8().get_data(), NULL, NULL, 0, NORMAL_PRIORITY_CLASS, NULL, NULL, &pi.si, &pi.pi); + ERR_FAIL_COND_V(ret == 0, ERR_CANT_FORK); + + if (p_blocking) { + + DWORD ret = WaitForSingleObject(pi.pi.hProcess, INFINITE); + if (r_exitcode) + *r_exitcode=ret; + + } else { + + ProcessID pid = pi.pi.dwProcessId; + if (r_child_id) { + *r_child_id = pid; + }; + process_map->insert(pid, pi); + }; + return OK; +}; + +Error OS_Windows::kill(const ProcessID& p_pid) { + + HANDLE h; + + if (process_map->has(p_pid)) { + h = (*process_map)[p_pid].pi.hProcess; + process_map->erase(p_pid); + } else { + + ERR_FAIL_COND_V(!process_map->has(p_pid), FAILED); + }; + + int ret = TerminateProcess(h, 0); + + return ret != 0?OK:FAILED; +}; + +Error OS_Windows::set_cwd(const String& p_cwd) { + + if (_wchdir(p_cwd.c_str())!=0) + return ERR_CANT_OPEN; + + return OK; +} + +void OS_Windows::set_icon(const Image& p_icon) { + + + Image icon=p_icon; + if (icon.get_format()!=Image::FORMAT_RGBA) + icon.convert(Image::FORMAT_RGBA); + int w = icon.get_width(); + int h = icon.get_height(); + + /* Create temporary bitmap buffer */ + int icon_len = 40 + h * w * 4; + BYTE *icon_bmp = (BYTE*)alloca(icon_len); + + encode_uint32(40,&icon_bmp[0]); + encode_uint32(w,&icon_bmp[4]); + encode_uint32(h*2,&icon_bmp[8]); + encode_uint16(1,&icon_bmp[12]); + encode_uint16(32,&icon_bmp[14]); + encode_uint32(BI_RGB,&icon_bmp[16]); + encode_uint32(w*h*4,&icon_bmp[20]); + encode_uint32(0,&icon_bmp[24]); + encode_uint32(0,&icon_bmp[28]); + encode_uint32(0,&icon_bmp[32]); + encode_uint32(0,&icon_bmp[36]); + + uint8_t *wr=&icon_bmp[40]; + DVector<uint8_t>::Read r= icon.get_data().read(); + + for(int i=0;i<h;i++) { + + for(int j=0;j<w;j++) { + + const uint8_t *rpx = &r[((h-i-1)*w+j)*4]; + uint8_t *wpx = &wr[(i*w+j)*4]; + wpx[0]=rpx[2]; + wpx[1]=rpx[1]; + wpx[2]=rpx[0]; + wpx[3]=rpx[3]; + } + } + + + HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000); + + /* Set the icon for the window */ + SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hicon); + + /* Set the icon in the task manager (should we do this?) */ + SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hicon); +} + + +bool OS_Windows::has_environment(const String& p_var) const { + + return getenv(p_var.utf8().get_data()) != NULL; +}; + +String OS_Windows::get_environment(const String& p_var) const { + + return getenv(p_var.utf8().get_data()); +}; + +String OS_Windows::get_stdin_string(bool p_block) { + + if (p_block) { + char buff[1024]; + return fgets(buff,1024,stdin); + }; + + return String(); +} + + +void OS_Windows::move_window_to_foreground() { + + SetForegroundWindow(hWnd); + +} + +String OS_Windows::get_locale() const { + + const _WinLocale *wl = &_win_locales[0]; + + LANGID langid = GetUserDefaultUILanguage(); + String neutral; + int lang = langid&((1<<9)-1); + int sublang = langid&~((1<<9)-1); + + while(wl->locale) { + + if (wl->main_lang==lang && wl->sublang==SUBLANG_NEUTRAL) + neutral=wl->locale; + + if (lang==wl->main_lang && sublang==wl->sublang) + return wl->locale; + + + wl++; + } + + if (neutral!="") + return neutral; + + return "en"; +} + +void OS_Windows::release_rendering_thread() { + + gl_context->release_current(); + +} + +void OS_Windows::make_rendering_thread() { + + gl_context->make_current(); +} + +void OS_Windows::swap_buffers() { + + gl_context->swap_buffers(); +} + + +void OS_Windows::run() { + + if (!main_loop) + return; + + main_loop->init(); + + uint64_t last_ticks=get_ticks_usec(); + + int frames=0; + uint64_t frame=0; + + while (!force_quit) { + + process_events(); // get rid of pending events + if (Main::iteration()==true) + break; + }; + + main_loop->finish(); + +} + + + +MainLoop *OS_Windows::get_main_loop() const { + + return main_loop; +} + + +String OS_Windows::get_data_dir() const { + + String an = Globals::get_singleton()->get("application/name"); + if (an!="") { + + if (has_environment("APPDATA")) { + + return OS::get_singleton()->get_environment("APPDATA")+"\\"+an; + } + } + + return Globals::get_singleton()->get_resource_path(); + + +} + + +OS_Windows::OS_Windows(HINSTANCE _hInstance) { + + key_event_pos=0; + force_quit=false; + alt_mem=false; + gr_mem=false; + shift_mem=false; + control_mem=false; + meta_mem=false; + minimized = false; + + hInstance=_hInstance; + pressrc=0; + old_invalid=true; + last_id=0; + mouse_mode=MOUSE_MODE_VISIBLE; +#ifdef STDOUT_FILE + stdo=fopen("stdout.txt","wb"); +#endif + user_proc = NULL; + +#ifdef RTAUDIO_ENABLED + AudioDriverManagerSW::add_driver(&driver_rtaudio); +#endif + +} + + +OS_Windows::~OS_Windows() +{ +#ifdef STDOUT_FILE + fclose(stdo); +#endif +} + + diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h new file mode 100644 index 0000000000..5587d89763 --- /dev/null +++ b/platform/windows/os_windows.h @@ -0,0 +1,257 @@ +/*************************************************************************/ +/* os_windows.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 OS_WINDOWS_H +#define OS_WINDOWS_H + +#define WINVER 0x0500 + +#include "os/input.h" +#include "os/os.h" +#include "context_gl_win.h" +#include "servers/visual_server.h" +#include "servers/visual/rasterizer.h" +#include "servers/physics/physics_server_sw.h" + +#include "servers/audio/audio_server_sw.h" +#include "servers/audio/sample_manager_sw.h" +#include "drivers/rtaudio/audio_driver_rtaudio.h" +#include "servers/spatial_sound/spatial_sound_server_sw.h" +#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" +#include "drivers/unix/ip_unix.h" +#include "servers/physics_2d/physics_2d_server_sw.h" + + +#include <windows.h> + +#include "key_mapping_win.h" +#include <windowsx.h> +#include <io.h> + +#include <fcntl.h> +#include <stdio.h> +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +class OS_Windows : public OS { + + enum { + JOYSTICKS_MAX = 8, + JOY_AXIS_COUNT = 6, + MAX_JOY_AXIS = 32768, // I've no idea + KEY_EVENT_BUFFER_SIZE=512 + }; + + FILE *stdo; + + + struct KeyEvent { + + InputModifierState mod_state; + UINT uMsg; + WPARAM wParam; + LPARAM lParam; + + }; + + KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE]; + int key_event_pos; + + + uint64_t ticks_start; + uint64_t ticks_per_second; + + bool minimized; + bool old_invalid; + bool outside; + int old_x,old_y; + Point2i center; + unsigned int last_id; +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) || defined(GLES2_ENABLED) + ContextGL_Win *gl_context; +#endif + VisualServer *visual_server; + Rasterizer *rasterizer; + PhysicsServer *physics_server; + Physics2DServer *physics_2d_server; + int pressrc; + HDC hDC; // Private GDI Device Context + HINSTANCE hInstance; // Holds The Instance Of The Application + HWND hWnd; + + struct Joystick { + + bool attached; + + DWORD last_axis[JOY_AXIS_COUNT]; + DWORD last_buttons; + DWORD last_pov; + + Joystick() { + attached = false; + for (int i=0; i<JOY_AXIS_COUNT; i++) { + + last_axis[i] = 0; + }; + last_buttons = 0; + last_pov = 0; + }; + }; + + int joystick_count; + Joystick joysticks[JOYSTICKS_MAX]; + + VideoMode video_mode; + + MainLoop *main_loop; + + WNDPROC user_proc; + + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSoundServerSW *spatial_sound_server; + SpatialSound2DServerSW *spatial_sound_2d_server; + + MouseMode mouse_mode; + bool alt_mem; + bool gr_mem; + bool shift_mem; + bool control_mem; + bool meta_mem; + bool force_quit; + uint32_t last_button_state; + + CursorShape cursor_shape; + + InputDefault *input; + +#ifdef RTAUDIO_ENABLED + AudioDriverRtAudio driver_rtaudio; +#endif + + void _post_dpad(DWORD p_dpad, int p_device, bool p_pressed); + + // functions used by main to initialize/deintialize the OS +protected: + 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_core(); + 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(); + virtual void finalize_core(); + + void process_events(); + + void probe_joysticks(); + void process_joysticks(); + void process_key_events(); + + struct ProcessInfo { + + STARTUPINFO si; + PROCESS_INFORMATION pi; + }; + Map<ProcessID, ProcessInfo>* process_map; + +public: + LRESULT WndProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam); + + + void print_error(const char* p_function,const char* p_file,int p_line,const char *p_code,const char*p_rationale,ErrorType p_type); + + virtual void vprint(const char *p_format, va_list p_list, bool p_stderr=false); + virtual void alert(const String& p_alert,const String& p_title="ALERT!"); + String get_stdin_string(bool p_block); + + void set_mouse_mode(MouseMode p_mode); + MouseMode get_mouse_mode() 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_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 MainLoop *get_main_loop() const; + + virtual String get_name(); + + virtual Date get_date() const; + virtual Time get_time() const; + virtual uint64_t get_unix_time() const; + + virtual bool can_draw() const; + virtual Error set_cwd(const String& p_cwd); + + virtual void delay_usec(uint32_t p_usec) const; + virtual uint64_t get_ticks_usec() const; + + virtual Error execute(const String& p_path, const List<String>& p_arguments,bool p_blocking,ProcessID *r_child_id=NULL,String* r_pipe=NULL,int *r_exitcode=NULL); + virtual Error kill(const ProcessID& p_pid); + + virtual bool has_environment(const String& p_var) const; + virtual String get_environment(const String& p_var) const; + + virtual void set_clipboard(const String& p_text); + virtual String get_clipboard() const; + + void set_cursor_shape(CursorShape p_shape); + void set_icon(const Image& p_icon); + + + virtual String get_locale() const; + + virtual void move_window_to_foreground(); + virtual String get_data_dir() const; + + virtual void release_rendering_thread(); + virtual void make_rendering_thread(); + virtual void swap_buffers(); + + void run(); + + virtual bool get_swap_ok_cancel() { return true; } + + OS_Windows(HINSTANCE _hInstance); + ~OS_Windows(); + +}; + +#endif diff --git a/platform/windows/platform_config.h b/platform/windows/platform_config.h new file mode 100644 index 0000000000..7bc3e42833 --- /dev/null +++ b/platform/windows/platform_config.h @@ -0,0 +1,35 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include <malloc.h> +//#else +//#include <alloca.h> +//#endif +#define GLES2_INCLUDE_H "gl_context/glew.h" +#define GLES1_INCLUDE_H "gl_context/glew.h" + diff --git a/platform/windows/stream_peer_winsock.cpp b/platform/windows/stream_peer_winsock.cpp new file mode 100644 index 0000000000..2c3a8db7b1 --- /dev/null +++ b/platform/windows/stream_peer_winsock.cpp @@ -0,0 +1,368 @@ +/*************************************************************************/ +/* stream_peer_winsock.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 WINDOWS_ENABLED + +#include "stream_peer_winsock.h" + +#include <winsock2.h> + +int winsock_refcount = 0; + +static void set_addr_in(struct sockaddr_in& their_addr, const IP_Address& p_host, uint16_t p_port) { + + their_addr.sin_family = AF_INET; // host byte order + their_addr.sin_port = htons(p_port); // short, network byte order + their_addr.sin_addr = *((struct in_addr*)&p_host.host); + memset(&(their_addr.sin_zero), '\0', 8); +}; + +StreamPeerTCP* StreamPeerWinsock::_create() { + + return memnew(StreamPeerWinsock); +}; + +void StreamPeerWinsock::make_default() { + + StreamPeerTCP::_create = StreamPeerWinsock::_create; + + if (winsock_refcount == 0) { + WSADATA data; + WSAStartup(MAKEWORD(2,2), &data); + }; + ++winsock_refcount; +}; + +void StreamPeerWinsock::cleanup() { + + --winsock_refcount; + if (winsock_refcount == 0) { + + WSACleanup(); + }; +}; + + +Error StreamPeerWinsock::_block(int p_sockfd, bool p_read, bool p_write) const { + + fd_set read, write; + FD_ZERO(&read); + FD_ZERO(&write); + + if (p_read) + FD_SET(p_sockfd, &read); + if (p_write) + FD_SET(p_sockfd, &write); + + int ret = select(p_sockfd + 1, &read, &write, NULL, NULL); // block forever + return ret < 0 ? FAILED : OK; +}; + +Error StreamPeerWinsock::_poll_connection(bool p_block) const { + + ERR_FAIL_COND_V(status != STATUS_CONNECTING || sockfd == INVALID_SOCKET, FAILED); + + if (p_block) { + + _block(sockfd, false, true); + }; + + struct sockaddr_in their_addr; + set_addr_in(their_addr, peer_host, peer_port); + + if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == SOCKET_ERROR) { + + int err = WSAGetLastError(); + if (err == WSAEISCONN) { + status = STATUS_CONNECTED; + return OK; + }; + + return OK; + } else { + + status = STATUS_CONNECTED; + return OK; + }; + + return OK; +}; + +Error StreamPeerWinsock::write(const uint8_t* p_data,int p_bytes, int &r_sent, bool p_block) { + + if (status == STATUS_NONE || status == STATUS_ERROR) { + + return FAILED; + }; + + if (status != STATUS_CONNECTED) { + + if (_poll_connection(p_block) != OK) { + + return FAILED; + }; + + if (status != STATUS_CONNECTED) { + r_sent = 0; + return OK; + }; + }; + + int data_to_send = p_bytes; + const uint8_t *offset = p_data; + if (sockfd == -1) return FAILED; + errno = 0; + int total_sent = 0; + + while (data_to_send) { + + int sent_amount = send(sockfd, (const char*)offset, data_to_send, 0); + //printf("Sent TCP data of %d bytes, errno %d\n", sent_amount, errno); + + if (sent_amount == -1) { + + if (WSAGetLastError() != WSAEWOULDBLOCK) { + + perror("shit?"); + disconnect(); + ERR_PRINT("Server disconnected!\n"); + return FAILED; + }; + + if (!p_block) { + r_sent = total_sent; + return OK; + }; + + _block(sockfd, false, true); + } else { + + data_to_send -= sent_amount; + offset += sent_amount; + total_sent += sent_amount; + }; + } + + r_sent = total_sent; + + return OK; +}; + +Error StreamPeerWinsock::read(uint8_t* p_buffer, int p_bytes,int &r_received, bool p_block) { + + if (!is_connected()) { + + return FAILED; + }; + + if (status != STATUS_CONNECTED) { + + if (_poll_connection(p_block) != OK) { + + return FAILED; + }; + + if (status != STATUS_CONNECTED) { + r_received = 0; + return OK; + }; + }; + + int to_read = p_bytes; + int total_read = 0; + errno = 0; + + while (to_read) { + + int read = recv(sockfd, (char*)p_buffer + total_read, to_read, 0); + + if (read == -1) { + + if (WSAGetLastError() != WSAEWOULDBLOCK) { + + perror("shit?"); + disconnect(); + ERR_PRINT("Server disconnected!\n"); + return FAILED; + }; + + if (!p_block) { + + r_received = total_read; + return OK; + }; + _block(sockfd, true, false); + } else if (read == 0) { + disconnect(); + return ERR_FILE_EOF; + } else { + + to_read -= read; + total_read += read; + }; + }; + + r_received = total_read; + + return OK; +}; + +Error StreamPeerWinsock::put_data(const uint8_t* p_data,int p_bytes) { + + int total; + return write(p_data, p_bytes, total, true); +}; + +Error StreamPeerWinsock::put_partial_data(const uint8_t* p_data,int p_bytes, int &r_sent) { + + return write(p_data, p_bytes, r_sent, false); +}; + +Error StreamPeerWinsock::get_data(uint8_t* p_buffer, int p_bytes) { + + int total; + return read(p_buffer, p_bytes, total, true); +}; + +Error StreamPeerWinsock::get_partial_data(uint8_t* p_buffer, int p_bytes,int &r_received) { + + return read(p_buffer, p_bytes, r_received, false); +}; + +StreamPeerTCP::Status StreamPeerWinsock::get_status() const { + + if (status == STATUS_CONNECTING) { + _poll_connection(false); + }; + + return status; +}; + + +bool StreamPeerWinsock::is_connected() const { + + if (status == STATUS_NONE || status == STATUS_ERROR) { + + return false; + }; + if (status != STATUS_CONNECTED) { + return true; + }; + + return (sockfd!=INVALID_SOCKET); +}; + +void StreamPeerWinsock::disconnect() { + + if (sockfd != INVALID_SOCKET) + closesocket(sockfd); + sockfd=INVALID_SOCKET; + + status = STATUS_NONE; + + peer_host = IP_Address(); + peer_port = 0; +}; + +void StreamPeerWinsock::set_socket(int p_sockfd, IP_Address p_host, int p_port) { + + sockfd = p_sockfd; + status = STATUS_CONNECTING; + peer_host = p_host; + peer_port = p_port; +}; + +Error StreamPeerWinsock::connect(const IP_Address& p_host, uint16_t p_port) { + + ERR_FAIL_COND_V( p_host.host == 0, ERR_INVALID_PARAMETER); + + if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { + ERR_PRINT("Socket creation failed!"); + disconnect(); + //perror("socket"); + return FAILED; + }; + + unsigned long par = 1; + if (ioctlsocket(sockfd, FIONBIO, &par)) { + perror("setting non-block mode"); + disconnect(); + return FAILED; + }; + + struct sockaddr_in their_addr; + set_addr_in(their_addr, p_host, p_port); + + if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == SOCKET_ERROR) { + + if (WSAGetLastError() != WSAEWOULDBLOCK) { + ERR_PRINT("Connection to remote host failed!"); + disconnect(); + return FAILED; + }; + status = STATUS_CONNECTING; + } else { + status = STATUS_CONNECTED; + }; + + peer_host = p_host; + peer_port = p_port; + + return OK; +}; + +void StreamPeerWinsock::set_nodelay(bool p_enabled) { + + +} + + +IP_Address StreamPeerWinsock::get_connected_host() const { + + return peer_host; +}; + +uint16_t StreamPeerWinsock::get_connected_port() const { + + return peer_port; +}; + +StreamPeerWinsock::StreamPeerWinsock() { + + sockfd = INVALID_SOCKET; + status = STATUS_NONE; + peer_port = 0; +}; + +StreamPeerWinsock::~StreamPeerWinsock() { + + disconnect(); +}; + + +#endif diff --git a/platform/windows/stream_peer_winsock.h b/platform/windows/stream_peer_winsock.h new file mode 100644 index 0000000000..14dd5f0bb1 --- /dev/null +++ b/platform/windows/stream_peer_winsock.h @@ -0,0 +1,90 @@ +/*************************************************************************/ +/* stream_peer_winsock.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. */ +/*************************************************************************/ +#ifdef WINDOWS_ENABLED + +#ifndef STREAM_PEER_WINSOCK_H +#define STREAM_PEER_WINSOCK_H + +#include "error_list.h" + +#include "core/io/ip_address.h" +#include "core/io/stream_peer_tcp.h" + +class StreamPeerWinsock : public StreamPeerTCP { + +protected: + + mutable Status status; + + int sockfd; + + Error _block(int p_sockfd, bool p_read, bool p_write) const; + + Error _poll_connection(bool p_block) const; + + IP_Address peer_host; + int peer_port; + + Error write(const uint8_t* p_data,int p_bytes, int &r_sent, bool p_block); + Error read(uint8_t* p_buffer, int p_bytes,int &r_received, bool p_block); + + static StreamPeerTCP* _create(); + +public: + + virtual Error connect(const IP_Address& p_host, uint16_t p_port); + + virtual Error put_data(const uint8_t* p_data,int p_bytes); + virtual Error put_partial_data(const uint8_t* p_data,int p_bytes, int &r_sent); + + virtual Error get_data(uint8_t* p_buffer, int p_bytes); + virtual Error get_partial_data(uint8_t* p_buffer, int p_bytes,int &r_received); + + void set_socket(int p_sockfd, IP_Address p_host, int p_port); + + virtual IP_Address get_connected_host() const; + virtual uint16_t get_connected_port() const; + + virtual bool is_connected() const; + virtual Status get_status() const; + virtual void disconnect(); + + static void make_default(); + static void cleanup(); + + virtual void set_nodelay(bool p_enabled); + + + StreamPeerWinsock(); + ~StreamPeerWinsock(); +}; + +#endif // TCP_SOCKET_POSIX_H + +#endif diff --git a/platform/windows/tcp_server_winsock.cpp b/platform/windows/tcp_server_winsock.cpp new file mode 100644 index 0000000000..7b35bcc7ad --- /dev/null +++ b/platform/windows/tcp_server_winsock.cpp @@ -0,0 +1,166 @@ +/*************************************************************************/ +/* tcp_server_winsock.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 "tcp_server_winsock.h"
+
+#include "stream_peer_winsock.h"
+
+#include <winsock2.h>
+
+extern int winsock_refcount;
+
+TCP_Server* TCPServerWinsock::_create() {
+
+ return memnew(TCPServerWinsock);
+};
+
+void TCPServerWinsock::make_default() {
+
+ TCP_Server::_create = TCPServerWinsock::_create;
+
+ if (winsock_refcount == 0) {
+ WSADATA data;
+ WSAStartup(MAKEWORD(2,2), &data);
+ };
+ ++winsock_refcount;
+};
+
+void TCPServerWinsock::cleanup() {
+
+ --winsock_refcount;
+ if (winsock_refcount == 0) {
+
+ WSACleanup();
+ };
+};
+
+
+Error TCPServerWinsock::listen(uint16_t p_port,const List<String> *p_accepted_hosts) {
+
+ int sockfd;
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ ERR_FAIL_COND_V(sockfd == INVALID_SOCKET, FAILED);
+
+ unsigned long par = 1;
+ if (ioctlsocket(sockfd, FIONBIO, &par)) {
+ perror("setting non-block mode");
+ stop();
+ return FAILED;
+ };
+
+ struct sockaddr_in my_addr;
+ my_addr.sin_family = AF_INET; // host byte order
+ my_addr.sin_port = htons(p_port); // short, network byte order
+ my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP TODO: use p_accepted_hosts
+ memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
+
+ if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) != SOCKET_ERROR) {
+
+ if (::listen(sockfd, SOMAXCONN) == SOCKET_ERROR) {
+
+ closesocket(sockfd);
+ ERR_FAIL_V(FAILED);
+ };
+ };
+
+ if (listen_sockfd != INVALID_SOCKET) {
+
+ stop();
+ };
+
+ listen_sockfd = sockfd;
+
+ return OK;
+};
+
+bool TCPServerWinsock::is_connection_available() const {
+
+ if (listen_sockfd == -1) {
+ return false;
+ };
+
+ timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ fd_set pfd;
+ FD_ZERO(&pfd);
+ FD_SET(listen_sockfd, &pfd);
+
+ int ret = select(listen_sockfd + 1, &pfd, NULL, NULL, &timeout);
+ ERR_FAIL_COND_V(ret < 0, 0);
+
+ if (ret && (FD_ISSET(listen_sockfd, &pfd))) {
+
+ return true;
+ };
+
+ return false;
+};
+
+
+Ref<StreamPeerTCP> TCPServerWinsock::take_connection() {
+
+ if (!is_connection_available()) {
+ return NULL;
+ };
+
+ struct sockaddr_in their_addr;
+ int sin_size = sizeof(their_addr);
+ int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &sin_size);
+ ERR_FAIL_COND_V(fd == INVALID_SOCKET, NULL);
+
+ Ref<StreamPeerWinsock> conn = memnew(StreamPeerWinsock);
+ IP_Address ip;
+ ip.host = (uint32_t)their_addr.sin_addr.s_addr;
+
+ conn->set_socket(fd, ip, ntohs(their_addr.sin_port));
+
+ return conn;
+};
+
+void TCPServerWinsock::stop() {
+
+ if (listen_sockfd != INVALID_SOCKET) {
+ closesocket(listen_sockfd);
+ };
+
+ listen_sockfd = -1;
+};
+
+
+TCPServerWinsock::TCPServerWinsock() {
+
+ listen_sockfd = INVALID_SOCKET;
+};
+
+TCPServerWinsock::~TCPServerWinsock() {
+
+ stop();
+};
+
diff --git a/platform/windows/tcp_server_winsock.h b/platform/windows/tcp_server_winsock.h new file mode 100644 index 0000000000..2d54b6ce40 --- /dev/null +++ b/platform/windows/tcp_server_winsock.h @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* tcp_server_winsock.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 TCP_SERVER_WINSOCK_H
+#define TCP_SERVER_WINSOCK_H
+
+#include "core/io/tcp_server.h"
+
+class TCPServerWinsock : public TCP_Server {
+
+ int listen_sockfd;
+
+ static TCP_Server* _create();
+
+public:
+
+ virtual Error listen(uint16_t p_port,const List<String> *p_accepted_hosts=NULL);
+ virtual bool is_connection_available() const;
+ virtual Ref<StreamPeerTCP> take_connection();
+
+ virtual void stop(); //stop listening
+
+ static void make_default();
+ static void cleanup();
+
+ TCPServerWinsock();
+ ~TCPServerWinsock();
+};
+
+#endif
diff --git a/platform/x11/SCsub b/platform/x11/SCsub new file mode 100644 index 0000000000..0644ba52ec --- /dev/null +++ b/platform/x11/SCsub @@ -0,0 +1,13 @@ +Import('env') + + +common_x11=[\ + "context_gl_x11.cpp",\ + "os_x11.cpp",\ + "key_mapping_x11.cpp",\ +] + +if env["target"]=="release": + env.Program('#bin/godot_rel',['godot_x11.cpp']+common_x11) +else: + env.Program('#bin/godot',['godot_x11.cpp']+common_x11) diff --git a/platform/x11/context_gl_x11.cpp b/platform/x11/context_gl_x11.cpp new file mode 100644 index 0000000000..12708f52e2 --- /dev/null +++ b/platform/x11/context_gl_x11.cpp @@ -0,0 +1,197 @@ +/*************************************************************************/ +/* context_gl_x11.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 "context_gl_x11.h" + +#ifdef X11_ENABLED +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +#include <GL/glx.h> + +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 + +typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display*, GLXFBConfig, GLXContext, Bool, const int*); + +struct ContextGL_X11_Private { + + ::GLXContext glx_context; +}; + + +void ContextGL_X11::release_current() { + + glXMakeCurrent(x11_display, None, NULL); +} + +void ContextGL_X11::make_current() { + + glXMakeCurrent(x11_display, x11_window, p->glx_context); +} +void ContextGL_X11::swap_buffers() { + + glXSwapBuffers(x11_display,x11_window); +} +/* +static GLWrapperFuncPtr wrapper_get_proc_address(const char* p_function) { + + //print_line(String()+"getting proc of: "+p_function); + GLWrapperFuncPtr func=(GLWrapperFuncPtr)glXGetProcAddress( (const GLubyte*) p_function); + if (!func) { + print_line("Couldn't find function: "+String(p_function)); + } + + return func; + +}*/ + +Error ContextGL_X11::initialize() { + + + GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = NULL; + +// const char *extensions = glXQueryExtensionsString(x11_display, DefaultScreen(x11_display)); + + glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); + + ERR_FAIL_COND_V( !glXCreateContextAttribsARB, ERR_UNCONFIGURED ); + + + static int visual_attribs[] = { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_DOUBLEBUFFER, true, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DEPTH_SIZE,0, + None + }; + + int fbcount; + GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount); + ERR_FAIL_COND_V(!fbc,ERR_UNCONFIGURED); + + XVisualInfo *vi = glXGetVisualFromFBConfig(x11_display, fbc[0]); + + XSetWindowAttributes swa; + + swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone); + swa.border_pixel = 0; + swa.event_mask = StructureNotifyMask; + + /* + char* windowid = getenv("GODOT_WINDOWID"); + if (windowid) { + + //freopen("/home/punto/stdout", "w", stdout); + //reopen("/home/punto/stderr", "w", stderr); + x11_window = atol(windowid); + } else { + */ + x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel|CWColormap|CWEventMask, &swa); + ERR_FAIL_COND_V(!x11_window,ERR_UNCONFIGURED); + XMapWindow(x11_display, x11_window); + while(true) { + // wait for mapnotify (window created) + XEvent e; + XNextEvent(x11_display, &e); + if (e.type == MapNotify) + break; + } + //}; + + if (!opengl_3_context) { + //oldstyle context: + p->glx_context = glXCreateContext(x11_display, vi, 0, GL_TRUE); + } else { + static int context_attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + None + }; + + p->glx_context = glXCreateContextAttribsARB(x11_display, fbc[0], NULL, true, context_attribs); + ERR_FAIL_COND_V(!p->glx_context,ERR_UNCONFIGURED); + } + + glXMakeCurrent(x11_display, x11_window, p->glx_context); + + /* + glWrapperInit(wrapper_get_proc_address); + glFlush(); + + glXSwapBuffers(x11_display,x11_window); +*/ + //glXMakeCurrent(x11_display, None, NULL); + + return OK; +} + +int ContextGL_X11::get_window_width() { + + XWindowAttributes xwa; + XGetWindowAttributes(x11_display,x11_window,&xwa); + + return xwa.width; +} +int ContextGL_X11::get_window_height() { + XWindowAttributes xwa; + XGetWindowAttributes(x11_display,x11_window,&xwa); + + return xwa.height; + +} + + +ContextGL_X11::ContextGL_X11(::Display *p_x11_display,::Window &p_x11_window,const OS::VideoMode& p_default_video_mode,bool p_opengl_3_context) : x11_window(p_x11_window) { + + default_video_mode=p_default_video_mode; + x11_display=p_x11_display; + + opengl_3_context=p_opengl_3_context; + + double_buffer=false; + direct_render=false; + glx_minor=glx_major=0; + p = memnew( ContextGL_X11_Private ); + p->glx_context=0; +} + + +ContextGL_X11::~ContextGL_X11() { + + memdelete( p ); +} + + +#endif +#endif diff --git a/platform/x11/context_gl_x11.h b/platform/x11/context_gl_x11.h new file mode 100644 index 0000000000..20a858bcdd --- /dev/null +++ b/platform/x11/context_gl_x11.h @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* context_gl_x11.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 CONTEXT_GL_X11_H +#define CONTEXT_GL_X11_H + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +#ifdef X11_ENABLED + +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) + + + +#include "os/os.h" +#include "drivers/gl_context/context_gl.h" +#include <X11/Xlib.h> + +struct ContextGL_X11_Private; + +class ContextGL_X11 : public ContextGL { + + ContextGL_X11_Private *p; + OS::VideoMode default_video_mode; +// ::Colormap x11_colormap; + ::Display *x11_display; + ::Window& x11_window; + bool double_buffer; + bool direct_render; + int glx_minor,glx_major; + bool opengl_3_context; +public: + + virtual void release_current(); + virtual void make_current(); + virtual void swap_buffers(); + virtual int get_window_width(); + virtual int get_window_height(); + + virtual Error initialize(); + + ContextGL_X11(::Display *p_x11_display,::Window &p_x11_window,const OS::VideoMode& p_default_video_mode,bool p_opengl_3_context); + ~ContextGL_X11(); + +}; + +#endif + +#endif +#endif diff --git a/platform/x11/detect.py b/platform/x11/detect.py new file mode 100644 index 0000000000..38f697ef10 --- /dev/null +++ b/platform/x11/detect.py @@ -0,0 +1,146 @@ + +import os +import sys + + +def is_active(): + return True + +def get_name(): + return "X11" + + +def can_build(): + + if (os.name!="posix"): + return False + + if sys.platform == "darwin": + return False # no x11 on mac for now + + errorval=os.system("pkg-config --version > /dev/null") + + if (errorval): + print("pkg-config not found.. x11 disabled.") + return False + + x11_error=os.system("pkg-config x11 --modversion > /dev/null ") + if (x11_error): + print("X11 not found.. x11 disabled.") + return False + + x11_error=os.system("pkg-config xcursor --modversion > /dev/null ") + if (x11_error): + print("xcursor not found.. x11 disabled.") + return False + + return True # X11 enabled + +def get_opts(): + + return [ + ('use_llvm','Use llvm compiler','no'), + ('use_sanitizer','Use llvm compiler sanitize address','no'), + ('force_32_bits','Force 32 bits binary','no') + ] + +def get_flags(): + + return [ + ('opengl', 'no'), + ('legacygl', 'yes'), + ('builtin_zlib', 'no'), + ] + + + +def configure(env): + + env.Append(CPPPATH=['#platform/x11']) + if (env["use_llvm"]=="yes"): + env["CC"]="clang" + env["CXX"]="clang++" + env["LD"]="clang++" + if (env["use_sanitizer"]=="yes"): + env.Append(CXXFLAGS=['-fsanitize=address','-fno-omit-frame-pointer']) + env.Append(LINKFLAGS=['-fsanitize=address']) + + + + if (env["tools"]=="no"): + #no tools suffix + env['OBJSUFFIX'] = ".nt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = ".nt"+env['LIBSUFFIX'] + + + 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'] + + +# env.Append(CCFLAGS=['-Os','-ffast-math','-fomit-frame-pointer']) +#does not seem to have much effect +# env.Append(CCFLAGS=['-fno-default-inline']) +#recommended by wxwidgets +# env.Append(CCFLAGS=['-ffunction-sections','-fdata-sections']) +# env.Append(LINKFLAGS=['-Wl','--gc-sections']) + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-g2', '-Wall','-DDEBUG_ENABLED','-DDEBUG_MEMORY_ENABLED']) +#does not seem to have much effect +# env.Append(CCFLAGS=['-fno-default-inline']) +#recommended by wxwidgets +# env.Append(CCFLAGS=['-ffunction-sections','-fdata-sections']) +# env.Append(LINKFLAGS=['-Wl','--gc-sections']) + + elif (env["target"]=="debug_light"): + + env.Append(CCFLAGS=['-g1', '-Wall','-DDEBUG_ENABLED','-DDEBUG_MEMORY_ENABLED']) + + + elif (env["target"]=="profile"): + + env.Append(CCFLAGS=['-g','-pg']) + env.Append(LINKFLAGS=['-pg']) + + env.ParseConfig('pkg-config x11 --cflags --libs') + env.ParseConfig('pkg-config xcursor --cflags --libs') + + + env.ParseConfig('pkg-config freetype2 --cflags --libs') + env.Append(CCFLAGS=['-DFREETYPE_ENABLED']) + + + if env['opengl'] == 'yes': + env.Append(CPPFLAGS=['-DOPENGL_ENABLED','-DGLEW_ENABLED']) + #env.Append(CPPFLAGS=["-DRTAUDIO_ENABLED"]) + env.Append(CPPFLAGS=["-DALSA_ENABLED"]) + env.Append(CPPFLAGS=['-DX11_ENABLED','-DUNIX_ENABLED','-DGLES2_ENABLED','-DGLES1_ENABLED','-DGLES_OVER_GL']) +# env.Append(CPPFLAGS=['-DX11_ENABLED','-DUNIX_ENABLED','-DGLES2_ENABLED','-DGLES_OVER_GL']) + env.Append(LIBS=['GL', 'GLU', 'pthread','asound','z']) #TODO detect linux/BSD! + #env.Append(CPPFLAGS=['-DMPC_FIXED_POINT']) + if (env["force_32_bits"]=="yes"): + env.Append(CPPFLAGS=['-m32']) + env.Append(LINKFLAGS=['-m32','-L/usr/lib/i386-linux-gnu']) + + if (env["CXX"]=="clang++"): + env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) + env["CC"]="clang" + env["LD"]="clang++" + + 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') } ) + #env.Append( BUILDERS = { 'HLSL9' : env.Builder(action = methods.build_hlsl_dx9_headers, suffix = 'hlsl.h',src_suffix = '.hlsl') } ) + diff --git a/platform/x11/export/export.cpp b/platform/x11/export/export.cpp new file mode 100644 index 0000000000..b17b92bccf --- /dev/null +++ b/platform/x11/export/export.cpp @@ -0,0 +1,24 @@ +#include "export.h" +#include "platform/x11/logo.h" +#include "tools/editor/editor_import_export.h" +#include "scene/resources/texture.h" + +void register_x11_exporter() { + + Image img(_x11_logo); + Ref<ImageTexture> logo = memnew( ImageTexture ); + logo->create_from_image(img); + + { + Ref<EditorExportPlatformPC> exporter = Ref<EditorExportPlatformPC>( memnew(EditorExportPlatformPC) ); + exporter->set_binary_extension("bin"); + exporter->set_release_binary32("linux_x11_32_release"); + exporter->set_debug_binary32("linux_x11_32_debug"); + exporter->set_release_binary64("linux_x11_64_release"); + exporter->set_debug_binary64("linux_x11_64_debug"); + exporter->set_name("Linux X11"); + exporter->set_logo(logo); + EditorImportExport::get_singleton()->add_export_platform(exporter); + } + +} diff --git a/platform/x11/export/export.h b/platform/x11/export/export.h new file mode 100644 index 0000000000..1077709ea1 --- /dev/null +++ b/platform/x11/export/export.h @@ -0,0 +1,4 @@ + + +void register_x11_exporter(); + diff --git a/platform/x11/godot_x11.cpp b/platform/x11/godot_x11.cpp new file mode 100644 index 0000000000..3b50e8e515 --- /dev/null +++ b/platform/x11/godot_x11.cpp @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* godot_x11.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 "main/main.h" +#include "os_x11.h" + +int main(int argc, char* argv[]) { + + OS_X11 os; + + Error err = Main::setup(argv[0],argc-1,&argv[1]); + if (err!=OK) + return 255; + + if (Main::start()) + os.run(); // it is actually the OS that decides how to run + Main::cleanup(); + + return os.get_exit_code(); +} diff --git a/platform/x11/key_mapping_x11.cpp b/platform/x11/key_mapping_x11.cpp new file mode 100644 index 0000000000..2f109355b8 --- /dev/null +++ b/platform/x11/key_mapping_x11.cpp @@ -0,0 +1,1812 @@ +/*************************************************************************/ +/* key_mapping_x11.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 "key_mapping_x11.h" + + +/***** SCAN CODE CONVERSION ******/ + +struct _XTranslatePair { + + KeySym keysym; + unsigned int keycode; +}; + +static _XTranslatePair _xkeysym_to_keycode[]={ + // misc keys + + { XK_Escape, KEY_ESCAPE }, + { XK_Tab, KEY_TAB }, + { XK_ISO_Left_Tab, KEY_BACKTAB }, + { XK_BackSpace, KEY_BACKSPACE }, + { XK_Return, KEY_RETURN }, + { XK_Insert, KEY_INSERT }, + { XK_Delete, KEY_DELETE }, + { XK_Clear, KEY_DELETE }, + { XK_Pause, KEY_PAUSE }, + { XK_Print, KEY_PRINT }, + { XK_Home, KEY_HOME }, + { XK_End, KEY_END }, + { XK_Left, KEY_LEFT }, + { XK_Up, KEY_UP }, + { XK_Right, KEY_RIGHT }, + { XK_Down, KEY_DOWN }, + { XK_Prior, KEY_PAGEUP }, + { XK_Next, KEY_PAGEDOWN }, + { XK_Shift_L, KEY_SHIFT }, + { XK_Shift_R, KEY_SHIFT }, + { XK_Shift_Lock, KEY_SHIFT }, + { XK_Control_L, KEY_CONTROL }, + { XK_Control_R, KEY_CONTROL }, + { XK_Meta_L, KEY_META }, + { XK_Meta_R, KEY_META }, + { XK_Alt_L, KEY_ALT }, + { XK_Alt_R, KEY_ALT }, + { XK_Caps_Lock, KEY_CAPSLOCK }, + { XK_Num_Lock, KEY_NUMLOCK }, + { XK_Scroll_Lock, KEY_SCROLLLOCK }, + { XK_Super_L, KEY_SUPER_L }, + { XK_Super_R, KEY_SUPER_R }, + { XK_Menu, KEY_MENU }, + { XK_Hyper_L, KEY_HYPER_L }, + { XK_Hyper_R, KEY_HYPER_R }, + { XK_Help, KEY_HELP }, + { XK_KP_Space, KEY_SPACE }, + { XK_KP_Tab, KEY_TAB }, + { XK_KP_Enter, KEY_ENTER }, + { XK_Home, KEY_HOME }, + { XK_Left, KEY_LEFT }, + { XK_Up, KEY_UP }, + { XK_Right, KEY_RIGHT }, + { XK_Down, KEY_DOWN }, + { XK_Prior, KEY_PAGEUP }, + { XK_Next, KEY_PAGEDOWN }, + { XK_End, KEY_END }, + { XK_Begin, KEY_CLEAR }, + { XK_Insert, KEY_INSERT }, + { XK_Delete, KEY_DELETE }, +// { XK_KP_Equal, KEY_EQUAL }, +// { XK_KP_Separator, KEY_COMMA }, + { XK_KP_Decimal, KEY_KP_PERIOD }, + { XK_KP_Delete, KEY_KP_PERIOD }, + { XK_KP_Enter, KEY_KP_ENTER }, + { XK_KP_Multiply, KEY_KP_MULTIPLY}, + { XK_KP_Divide, KEY_KP_DIVIDE}, + { XK_KP_Subtract, KEY_KP_SUBSTRACT}, + { XK_KP_Add, KEY_KP_ADD}, + { XK_KP_0, KEY_KP_0}, + { XK_KP_1, KEY_KP_1}, + { XK_KP_2, KEY_KP_2}, + { XK_KP_3, KEY_KP_3}, + { XK_KP_4, KEY_KP_4}, + { XK_KP_5, KEY_KP_5}, + { XK_KP_6, KEY_KP_6}, + { XK_KP_7, KEY_KP_7}, + { XK_KP_8, KEY_KP_8}, + { XK_KP_9, KEY_KP_9}, + // same but with numlock + { XK_KP_Insert, KEY_KP_0}, + { XK_KP_End, KEY_KP_1}, + { XK_KP_Down, KEY_KP_2}, + { XK_KP_Page_Down, KEY_KP_3}, + { XK_KP_Left, KEY_KP_4}, + { XK_KP_Begin, KEY_KP_5}, + { XK_KP_Right, KEY_KP_6}, + { XK_KP_Home, KEY_KP_7}, + { XK_KP_Up, KEY_KP_8}, + { XK_KP_Page_Up, KEY_KP_9}, + { XK_F1, KEY_F1}, + { XK_F2, KEY_F2}, + { XK_F3, KEY_F3}, + { XK_F4, KEY_F4}, + { XK_F5, KEY_F5}, + { XK_F6, KEY_F6}, + { XK_F7, KEY_F7}, + { XK_F8, KEY_F8}, + { XK_F9, KEY_F9}, + { XK_F10, KEY_F10}, + { XK_F11, KEY_F11}, + { XK_F12, KEY_F12}, + { XK_F13, KEY_F13}, + { XK_F14, KEY_F14}, + { XK_F15, KEY_F15}, + { XK_F16, KEY_F16}, + + // media keys + { XF86XK_Back, KEY_BACK }, + { XF86XK_Forward, KEY_FORWARD }, + { XF86XK_Stop, KEY_STOP }, + { XF86XK_Refresh, KEY_REFRESH }, + { XF86XK_Favorites, KEY_FAVORITES }, + { XF86XK_AudioMedia, KEY_LAUNCHMEDIA }, + { XF86XK_OpenURL, KEY_OPENURL }, + { XF86XK_HomePage, KEY_HOMEPAGE }, + { XF86XK_Search, KEY_SEARCH }, + { XF86XK_AudioLowerVolume, KEY_VOLUMEDOWN }, + { XF86XK_AudioMute, KEY_VOLUMEMUTE }, + { XF86XK_AudioRaiseVolume, KEY_VOLUMEUP }, + { XF86XK_AudioPlay, KEY_MEDIAPLAY }, + { XF86XK_AudioStop, KEY_MEDIASTOP }, + { XF86XK_AudioPrev, KEY_MEDIAPREVIOUS }, + { XF86XK_AudioNext, KEY_MEDIANEXT }, + { XF86XK_AudioRecord, KEY_MEDIARECORD }, + + // launch keys + { XF86XK_Mail, KEY_LAUNCHMAIL }, + { XF86XK_MyComputer, KEY_LAUNCH0 }, + { XF86XK_Calculator, KEY_LAUNCH1 }, + { XF86XK_Standby, KEY_STANDBY }, + + { XF86XK_Launch0, KEY_LAUNCH2 }, + { XF86XK_Launch1, KEY_LAUNCH3 }, + { XF86XK_Launch2, KEY_LAUNCH4 }, + { XF86XK_Launch3, KEY_LAUNCH5 }, + { XF86XK_Launch4, KEY_LAUNCH6 }, + { XF86XK_Launch5, KEY_LAUNCH7 }, + { XF86XK_Launch6, KEY_LAUNCH8 }, + { XF86XK_Launch7, KEY_LAUNCH9 }, + { XF86XK_Launch8, KEY_LAUNCHA }, + { XF86XK_Launch9, KEY_LAUNCHB }, + { XF86XK_LaunchA, KEY_LAUNCHC }, + { XF86XK_LaunchB, KEY_LAUNCHD }, + { XF86XK_LaunchC, KEY_LAUNCHE }, + { XF86XK_LaunchD, KEY_LAUNCHF }, + + {0, 0 } +}; + + +unsigned int KeyMappingX11::get_keycode(KeySym p_keysym) { + + // kinda bruteforce.. could optimize. + + if (p_keysym<0x100) // Latin 1, maps 1-1 + return p_keysym; + + // look for special key + for(int idx=0;_xkeysym_to_keycode[idx].keysym!=0;idx++) { + + if (_xkeysym_to_keycode[idx].keysym==p_keysym) + return _xkeysym_to_keycode[idx].keycode; + } + + return 0; +} +KeySym KeyMappingX11::get_keysym(unsigned int p_code) { + + // kinda bruteforce.. could optimize. + + if (p_code<0x100) // Latin 1, maps 1-1 + return p_code; + + // look for special key + for(int idx=0;_xkeysym_to_keycode[idx].keysym!=0;idx++) { + + if (_xkeysym_to_keycode[idx].keycode==p_code) + return _xkeysym_to_keycode[idx].keysym; + } + + return 0; +} + + +/***** UNICODE CONVERSION ******/ + +// Tables taken from FOX toolkit + +struct _XTranslateUnicodePair { + + KeySym keysym; + unsigned int unicode; +}; + +enum { + + _KEYSYM_MAX=759 +}; + +static _XTranslateUnicodePair _xkeysym_to_unicode[_KEYSYM_MAX] = { + { 0x01A1, 0x0104 }, + { 0x01A2, 0x02D8 }, + { 0x01A3, 0x0141 }, + { 0x01A5, 0x013D }, + { 0x01A6, 0x015A }, + { 0x01A9, 0x0160 }, + { 0x01AA, 0x015E }, + { 0x01AB, 0x0164 }, + { 0x01AC, 0x0179 }, + { 0x01AE, 0x017D }, + { 0x01AF, 0x017B }, + { 0x01B1, 0x0105 }, + { 0x01B2, 0x02DB }, + { 0x01B3, 0x0142 }, + { 0x01B5, 0x013E }, + { 0x01B6, 0x015B }, + { 0x01B7, 0x02C7 }, + { 0x01B9, 0x0161 }, + { 0x01BA, 0x015F }, + { 0x01BB, 0x0165 }, + { 0x01BC, 0x017A }, + { 0x01BD, 0x02DD }, + { 0x01BE, 0x017E }, + { 0x01BF, 0x017C }, + { 0x01C0, 0x0154 }, + { 0x01C3, 0x0102 }, + { 0x01C5, 0x0139 }, + { 0x01C6, 0x0106 }, + { 0x01C8, 0x010C }, + { 0x01CA, 0x0118 }, + { 0x01CC, 0x011A }, + { 0x01CF, 0x010E }, + { 0x01D0, 0x0110 }, + { 0x01D1, 0x0143 }, + { 0x01D2, 0x0147 }, + { 0x01D5, 0x0150 }, + { 0x01D8, 0x0158 }, + { 0x01D9, 0x016E }, + { 0x01DB, 0x0170 }, + { 0x01DE, 0x0162 }, + { 0x01E0, 0x0155 }, + { 0x01E3, 0x0103 }, + { 0x01E5, 0x013A }, + { 0x01E6, 0x0107 }, + { 0x01E8, 0x010D }, + { 0x01EA, 0x0119 }, + { 0x01EC, 0x011B }, + { 0x01EF, 0x010F }, + { 0x01F0, 0x0111 }, + { 0x01F1, 0x0144 }, + { 0x01F2, 0x0148 }, + { 0x01F5, 0x0151 }, + { 0x01F8, 0x0159 }, + { 0x01F9, 0x016F }, + { 0x01FB, 0x0171 }, + { 0x01FE, 0x0163 }, + { 0x01FF, 0x02D9 }, + { 0x02A1, 0x0126 }, + { 0x02A6, 0x0124 }, + { 0x02A9, 0x0130 }, + { 0x02AB, 0x011E }, + { 0x02AC, 0x0134 }, + { 0x02B1, 0x0127 }, + { 0x02B6, 0x0125 }, + { 0x02B9, 0x0131 }, + { 0x02BB, 0x011F }, + { 0x02BC, 0x0135 }, + { 0x02C5, 0x010A }, + { 0x02C6, 0x0108 }, + { 0x02D5, 0x0120 }, + { 0x02D8, 0x011C }, + { 0x02DD, 0x016C }, + { 0x02DE, 0x015C }, + { 0x02E5, 0x010B }, + { 0x02E6, 0x0109 }, + { 0x02F5, 0x0121 }, + { 0x02F8, 0x011D }, + { 0x02FD, 0x016D }, + { 0x02FE, 0x015D }, + { 0x03A2, 0x0138 }, + { 0x03A3, 0x0156 }, + { 0x03A5, 0x0128 }, + { 0x03A6, 0x013B }, + { 0x03AA, 0x0112 }, + { 0x03AB, 0x0122 }, + { 0x03AC, 0x0166 }, + { 0x03B3, 0x0157 }, + { 0x03B5, 0x0129 }, + { 0x03B6, 0x013C }, + { 0x03BA, 0x0113 }, + { 0x03BB, 0x0123 }, + { 0x03BC, 0x0167 }, + { 0x03BD, 0x014A }, + { 0x03BF, 0x014B }, + { 0x03C0, 0x0100 }, + { 0x03C7, 0x012E }, + { 0x03CC, 0x0116 }, + { 0x03CF, 0x012A }, + { 0x03D1, 0x0145 }, + { 0x03D2, 0x014C }, + { 0x03D3, 0x0136 }, + { 0x03D9, 0x0172 }, + { 0x03DD, 0x0168 }, + { 0x03DE, 0x016A }, + { 0x03E0, 0x0101 }, + { 0x03E7, 0x012F }, + { 0x03EC, 0x0117 }, + { 0x03EF, 0x012B }, + { 0x03F1, 0x0146 }, + { 0x03F2, 0x014D }, + { 0x03F3, 0x0137 }, + { 0x03F9, 0x0173 }, + { 0x03FD, 0x0169 }, + { 0x03FE, 0x016B }, + { 0x047E, 0x203E }, + { 0x04A1, 0x3002 }, + { 0x04A2, 0x300C }, + { 0x04A3, 0x300D }, + { 0x04A4, 0x3001 }, + { 0x04A5, 0x30FB }, + { 0x04A6, 0x30F2 }, + { 0x04A7, 0x30A1 }, + { 0x04A8, 0x30A3 }, + { 0x04A9, 0x30A5 }, + { 0x04AA, 0x30A7 }, + { 0x04AB, 0x30A9 }, + { 0x04AC, 0x30E3 }, + { 0x04AD, 0x30E5 }, + { 0x04AE, 0x30E7 }, + { 0x04AF, 0x30C3 }, + { 0x04B0, 0x30FC }, + { 0x04B1, 0x30A2 }, + { 0x04B2, 0x30A4 }, + { 0x04B3, 0x30A6 }, + { 0x04B4, 0x30A8 }, + { 0x04B5, 0x30AA }, + { 0x04B6, 0x30AB }, + { 0x04B7, 0x30AD }, + { 0x04B8, 0x30AF }, + { 0x04B9, 0x30B1 }, + { 0x04BA, 0x30B3 }, + { 0x04BB, 0x30B5 }, + { 0x04BC, 0x30B7 }, + { 0x04BD, 0x30B9 }, + { 0x04BE, 0x30BB }, + { 0x04BF, 0x30BD }, + { 0x04C0, 0x30BF }, + { 0x04C1, 0x30C1 }, + { 0x04C2, 0x30C4 }, + { 0x04C3, 0x30C6 }, + { 0x04C4, 0x30C8 }, + { 0x04C5, 0x30CA }, + { 0x04C6, 0x30CB }, + { 0x04C7, 0x30CC }, + { 0x04C8, 0x30CD }, + { 0x04C9, 0x30CE }, + { 0x04CA, 0x30CF }, + { 0x04CB, 0x30D2 }, + { 0x04CC, 0x30D5 }, + { 0x04CD, 0x30D8 }, + { 0x04CE, 0x30DB }, + { 0x04CF, 0x30DE }, + { 0x04D0, 0x30DF }, + { 0x04D1, 0x30E0 }, + { 0x04D2, 0x30E1 }, + { 0x04D3, 0x30E2 }, + { 0x04D4, 0x30E4 }, + { 0x04D5, 0x30E6 }, + { 0x04D6, 0x30E8 }, + { 0x04D7, 0x30E9 }, + { 0x04D8, 0x30EA }, + { 0x04D9, 0x30EB }, + { 0x04DA, 0x30EC }, + { 0x04DB, 0x30ED }, + { 0x04DC, 0x30EF }, + { 0x04DD, 0x30F3 }, + { 0x04DE, 0x309B }, + { 0x04DF, 0x309C }, + { 0x05AC, 0x060C }, + { 0x05BB, 0x061B }, + { 0x05BF, 0x061F }, + { 0x05C1, 0x0621 }, + { 0x05C2, 0x0622 }, + { 0x05C3, 0x0623 }, + { 0x05C4, 0x0624 }, + { 0x05C5, 0x0625 }, + { 0x05C6, 0x0626 }, + { 0x05C7, 0x0627 }, + { 0x05C8, 0x0628 }, + { 0x05C9, 0x0629 }, + { 0x05CA, 0x062A }, + { 0x05CB, 0x062B }, + { 0x05CC, 0x062C }, + { 0x05CD, 0x062D }, + { 0x05CE, 0x062E }, + { 0x05CF, 0x062F }, + { 0x05D0, 0x0630 }, + { 0x05D1, 0x0631 }, + { 0x05D2, 0x0632 }, + { 0x05D3, 0x0633 }, + { 0x05D4, 0x0634 }, + { 0x05D5, 0x0635 }, + { 0x05D6, 0x0636 }, + { 0x05D7, 0x0637 }, + { 0x05D8, 0x0638 }, + { 0x05D9, 0x0639 }, + { 0x05DA, 0x063A }, + { 0x05E0, 0x0640 }, + { 0x05E1, 0x0641 }, + { 0x05E2, 0x0642 }, + { 0x05E3, 0x0643 }, + { 0x05E4, 0x0644 }, + { 0x05E5, 0x0645 }, + { 0x05E6, 0x0646 }, + { 0x05E7, 0x0647 }, + { 0x05E8, 0x0648 }, + { 0x05E9, 0x0649 }, + { 0x05EA, 0x064A }, + { 0x05EB, 0x064B }, + { 0x05EC, 0x064C }, + { 0x05ED, 0x064D }, + { 0x05EE, 0x064E }, + { 0x05EF, 0x064F }, + { 0x05F0, 0x0650 }, + { 0x05F1, 0x0651 }, + { 0x05F2, 0x0652 }, + { 0x06A1, 0x0452 }, + { 0x06A2, 0x0453 }, + { 0x06A3, 0x0451 }, + { 0x06A4, 0x0454 }, + { 0x06A5, 0x0455 }, + { 0x06A6, 0x0456 }, + { 0x06A7, 0x0457 }, + { 0x06A8, 0x0458 }, + { 0x06A9, 0x0459 }, + { 0x06AA, 0x045A }, + { 0x06AB, 0x045B }, + { 0x06AC, 0x045C }, + { 0x06AE, 0x045E }, + { 0x06AF, 0x045F }, + { 0x06B0, 0x2116 }, + { 0x06B1, 0x0402 }, + { 0x06B2, 0x0403 }, + { 0x06B3, 0x0401 }, + { 0x06B4, 0x0404 }, + { 0x06B5, 0x0405 }, + { 0x06B6, 0x0406 }, + { 0x06B7, 0x0407 }, + { 0x06B8, 0x0408 }, + { 0x06B9, 0x0409 }, + { 0x06BA, 0x040A }, + { 0x06BB, 0x040B }, + { 0x06BC, 0x040C }, + { 0x06BE, 0x040E }, + { 0x06BF, 0x040F }, + { 0x06C0, 0x044E }, + { 0x06C1, 0x0430 }, + { 0x06C2, 0x0431 }, + { 0x06C3, 0x0446 }, + { 0x06C4, 0x0434 }, + { 0x06C5, 0x0435 }, + { 0x06C6, 0x0444 }, + { 0x06C7, 0x0433 }, + { 0x06C8, 0x0445 }, + { 0x06C9, 0x0438 }, + { 0x06CA, 0x0439 }, + { 0x06CB, 0x043A }, + { 0x06CC, 0x043B }, + { 0x06CD, 0x043C }, + { 0x06CE, 0x043D }, + { 0x06CF, 0x043E }, + { 0x06D0, 0x043F }, + { 0x06D1, 0x044F }, + { 0x06D2, 0x0440 }, + { 0x06D3, 0x0441 }, + { 0x06D4, 0x0442 }, + { 0x06D5, 0x0443 }, + { 0x06D6, 0x0436 }, + { 0x06D7, 0x0432 }, + { 0x06D8, 0x044C }, + { 0x06D9, 0x044B }, + { 0x06DA, 0x0437 }, + { 0x06DB, 0x0448 }, + { 0x06DC, 0x044D }, + { 0x06DD, 0x0449 }, + { 0x06DE, 0x0447 }, + { 0x06DF, 0x044A }, + { 0x06E0, 0x042E }, + { 0x06E1, 0x0410 }, + { 0x06E2, 0x0411 }, + { 0x06E3, 0x0426 }, + { 0x06E4, 0x0414 }, + { 0x06E5, 0x0415 }, + { 0x06E6, 0x0424 }, + { 0x06E7, 0x0413 }, + { 0x06E8, 0x0425 }, + { 0x06E9, 0x0418 }, + { 0x06EA, 0x0419 }, + { 0x06EB, 0x041A }, + { 0x06EC, 0x041B }, + { 0x06ED, 0x041C }, + { 0x06EE, 0x041D }, + { 0x06EF, 0x041E }, + { 0x06F0, 0x041F }, + { 0x06F1, 0x042F }, + { 0x06F2, 0x0420 }, + { 0x06F3, 0x0421 }, + { 0x06F4, 0x0422 }, + { 0x06F5, 0x0423 }, + { 0x06F6, 0x0416 }, + { 0x06F7, 0x0412 }, + { 0x06F8, 0x042C }, + { 0x06F9, 0x042B }, + { 0x06FA, 0x0417 }, + { 0x06FB, 0x0428 }, + { 0x06FC, 0x042D }, + { 0x06FD, 0x0429 }, + { 0x06FE, 0x0427 }, + { 0x06FF, 0x042A }, + { 0x07A1, 0x0386 }, + { 0x07A2, 0x0388 }, + { 0x07A3, 0x0389 }, + { 0x07A4, 0x038A }, + { 0x07A5, 0x03AA }, + { 0x07A7, 0x038C }, + { 0x07A8, 0x038E }, + { 0x07A9, 0x03AB }, + { 0x07AB, 0x038F }, + { 0x07AE, 0x0385 }, + { 0x07AF, 0x2015 }, + { 0x07B1, 0x03AC }, + { 0x07B2, 0x03AD }, + { 0x07B3, 0x03AE }, + { 0x07B4, 0x03AF }, + { 0x07B5, 0x03CA }, + { 0x07B6, 0x0390 }, + { 0x07B7, 0x03CC }, + { 0x07B8, 0x03CD }, + { 0x07B9, 0x03CB }, + { 0x07BA, 0x03B0 }, + { 0x07BB, 0x03CE }, + { 0x07C1, 0x0391 }, + { 0x07C2, 0x0392 }, + { 0x07C3, 0x0393 }, + { 0x07C4, 0x0394 }, + { 0x07C5, 0x0395 }, + { 0x07C6, 0x0396 }, + { 0x07C7, 0x0397 }, + { 0x07C8, 0x0398 }, + { 0x07C9, 0x0399 }, + { 0x07CA, 0x039A }, + { 0x07CB, 0x039B }, + { 0x07CC, 0x039C }, + { 0x07CD, 0x039D }, + { 0x07CE, 0x039E }, + { 0x07CF, 0x039F }, + { 0x07D0, 0x03A0 }, + { 0x07D1, 0x03A1 }, + { 0x07D2, 0x03A3 }, + { 0x07D4, 0x03A4 }, + { 0x07D5, 0x03A5 }, + { 0x07D6, 0x03A6 }, + { 0x07D7, 0x03A7 }, + { 0x07D8, 0x03A8 }, + { 0x07D9, 0x03A9 }, + { 0x07E1, 0x03B1 }, + { 0x07E2, 0x03B2 }, + { 0x07E3, 0x03B3 }, + { 0x07E4, 0x03B4 }, + { 0x07E5, 0x03B5 }, + { 0x07E6, 0x03B6 }, + { 0x07E7, 0x03B7 }, + { 0x07E8, 0x03B8 }, + { 0x07E9, 0x03B9 }, + { 0x07EA, 0x03BA }, + { 0x07EB, 0x03BB }, + { 0x07EC, 0x03BC }, + { 0x07ED, 0x03BD }, + { 0x07EE, 0x03BE }, + { 0x07EF, 0x03BF }, + { 0x07F0, 0x03C0 }, + { 0x07F1, 0x03C1 }, + { 0x07F2, 0x03C3 }, + { 0x07F3, 0x03C2 }, + { 0x07F4, 0x03C4 }, + { 0x07F5, 0x03C5 }, + { 0x07F6, 0x03C6 }, + { 0x07F7, 0x03C7 }, + { 0x07F8, 0x03C8 }, + { 0x07F9, 0x03C9 }, + { 0x08A1, 0x23B7 }, + { 0x08A2, 0x250C }, + { 0x08A3, 0x2500 }, + { 0x08A4, 0x2320 }, + { 0x08A5, 0x2321 }, + { 0x08A6, 0x2502 }, + { 0x08A7, 0x23A1 }, + { 0x08A8, 0x23A3 }, + { 0x08A9, 0x23A4 }, + { 0x08AA, 0x23A6 }, + { 0x08AB, 0x239B }, + { 0x08AC, 0x239D }, + { 0x08AD, 0x239E }, + { 0x08AE, 0x23A0 }, + { 0x08AF, 0x23A8 }, + { 0x08B0, 0x23AC }, + { 0x08BC, 0x2264 }, + { 0x08BD, 0x2260 }, + { 0x08BE, 0x2265 }, + { 0x08BF, 0x222B }, + { 0x08C0, 0x2234 }, + { 0x08C1, 0x221D }, + { 0x08C2, 0x221E }, + { 0x08C5, 0x2207 }, + { 0x08C8, 0x223C }, + { 0x08C9, 0x2243 }, + { 0x08CD, 0x21D4 }, + { 0x08CE, 0x21D2 }, + { 0x08CF, 0x2261 }, + { 0x08D6, 0x221A }, + { 0x08DA, 0x2282 }, + { 0x08DB, 0x2283 }, + { 0x08DC, 0x2229 }, + { 0x08DD, 0x222A }, + { 0x08DE, 0x2227 }, + { 0x08DF, 0x2228 }, + { 0x08EF, 0x2202 }, + { 0x08F6, 0x0192 }, + { 0x08FB, 0x2190 }, + { 0x08FC, 0x2191 }, + { 0x08FD, 0x2192 }, + { 0x08FE, 0x2193 }, + { 0x09E0, 0x25C6 }, + { 0x09E1, 0x2592 }, + { 0x09E2, 0x2409 }, + { 0x09E3, 0x240C }, + { 0x09E4, 0x240D }, + { 0x09E5, 0x240A }, + { 0x09E8, 0x2424 }, + { 0x09E9, 0x240B }, + { 0x09EA, 0x2518 }, + { 0x09EB, 0x2510 }, + { 0x09EC, 0x250C }, + { 0x09ED, 0x2514 }, + { 0x09EE, 0x253C }, + { 0x09EF, 0x23BA }, + { 0x09F0, 0x23BB }, + { 0x09F1, 0x2500 }, + { 0x09F2, 0x23BC }, + { 0x09F3, 0x23BD }, + { 0x09F4, 0x251C }, + { 0x09F5, 0x2524 }, + { 0x09F6, 0x2534 }, + { 0x09F7, 0x252C }, + { 0x09F8, 0x2502 }, + { 0x0AA1, 0x2003 }, + { 0x0AA2, 0x2002 }, + { 0x0AA3, 0x2004 }, + { 0x0AA4, 0x2005 }, + { 0x0AA5, 0x2007 }, + { 0x0AA6, 0x2008 }, + { 0x0AA7, 0x2009 }, + { 0x0AA8, 0x200A }, + { 0x0AA9, 0x2014 }, + { 0x0AAA, 0x2013 }, + { 0x0AAE, 0x2026 }, + { 0x0AAF, 0x2025 }, + { 0x0AB0, 0x2153 }, + { 0x0AB1, 0x2154 }, + { 0x0AB2, 0x2155 }, + { 0x0AB3, 0x2156 }, + { 0x0AB4, 0x2157 }, + { 0x0AB5, 0x2158 }, + { 0x0AB6, 0x2159 }, + { 0x0AB7, 0x215A }, + { 0x0AB8, 0x2105 }, + { 0x0ABB, 0x2012 }, + { 0x0ABC, 0x2329 }, + { 0x0ABE, 0x232A }, + { 0x0AC3, 0x215B }, + { 0x0AC4, 0x215C }, + { 0x0AC5, 0x215D }, + { 0x0AC6, 0x215E }, + { 0x0AC9, 0x2122 }, + { 0x0ACA, 0x2613 }, + { 0x0ACC, 0x25C1 }, + { 0x0ACD, 0x25B7 }, + { 0x0ACE, 0x25CB }, + { 0x0ACF, 0x25AF }, + { 0x0AD0, 0x2018 }, + { 0x0AD1, 0x2019 }, + { 0x0AD2, 0x201C }, + { 0x0AD3, 0x201D }, + { 0x0AD4, 0x211E }, + { 0x0AD6, 0x2032 }, + { 0x0AD7, 0x2033 }, + { 0x0AD9, 0x271D }, + { 0x0ADB, 0x25AC }, + { 0x0ADC, 0x25C0 }, + { 0x0ADD, 0x25B6 }, + { 0x0ADE, 0x25CF }, + { 0x0ADF, 0x25AE }, + { 0x0AE0, 0x25E6 }, + { 0x0AE1, 0x25AB }, + { 0x0AE2, 0x25AD }, + { 0x0AE3, 0x25B3 }, + { 0x0AE4, 0x25BD }, + { 0x0AE5, 0x2606 }, + { 0x0AE6, 0x2022 }, + { 0x0AE7, 0x25AA }, + { 0x0AE8, 0x25B2 }, + { 0x0AE9, 0x25BC }, + { 0x0AEA, 0x261C }, + { 0x0AEB, 0x261E }, + { 0x0AEC, 0x2663 }, + { 0x0AED, 0x2666 }, + { 0x0AEE, 0x2665 }, + { 0x0AF0, 0x2720 }, + { 0x0AF1, 0x2020 }, + { 0x0AF2, 0x2021 }, + { 0x0AF3, 0x2713 }, + { 0x0AF4, 0x2717 }, + { 0x0AF5, 0x266F }, + { 0x0AF6, 0x266D }, + { 0x0AF7, 0x2642 }, + { 0x0AF8, 0x2640 }, + { 0x0AF9, 0x260E }, + { 0x0AFA, 0x2315 }, + { 0x0AFB, 0x2117 }, + { 0x0AFC, 0x2038 }, + { 0x0AFD, 0x201A }, + { 0x0AFE, 0x201E }, + { 0x0BA3, 0x003C }, + { 0x0BA6, 0x003E }, + { 0x0BA8, 0x2228 }, + { 0x0BA9, 0x2227 }, + { 0x0BC0, 0x00AF }, + { 0x0BC2, 0x22A5 }, + { 0x0BC3, 0x2229 }, + { 0x0BC4, 0x230A }, + { 0x0BC6, 0x005F }, + { 0x0BCA, 0x2218 }, + { 0x0BCC, 0x2395 }, + { 0x0BCE, 0x22A4 }, + { 0x0BCF, 0x25CB }, + { 0x0BD3, 0x2308 }, + { 0x0BD6, 0x222A }, + { 0x0BD8, 0x2283 }, + { 0x0BDA, 0x2282 }, + { 0x0BDC, 0x22A2 }, + { 0x0BFC, 0x22A3 }, + { 0x0CDF, 0x2017 }, + { 0x0CE0, 0x05D0 }, + { 0x0CE1, 0x05D1 }, + { 0x0CE2, 0x05D2 }, + { 0x0CE3, 0x05D3 }, + { 0x0CE4, 0x05D4 }, + { 0x0CE5, 0x05D5 }, + { 0x0CE6, 0x05D6 }, + { 0x0CE7, 0x05D7 }, + { 0x0CE8, 0x05D8 }, + { 0x0CE9, 0x05D9 }, + { 0x0CEA, 0x05DA }, + { 0x0CEB, 0x05DB }, + { 0x0CEC, 0x05DC }, + { 0x0CED, 0x05DD }, + { 0x0CEE, 0x05DE }, + { 0x0CEF, 0x05DF }, + { 0x0CF0, 0x05E0 }, + { 0x0CF1, 0x05E1 }, + { 0x0CF2, 0x05E2 }, + { 0x0CF3, 0x05E3 }, + { 0x0CF4, 0x05E4 }, + { 0x0CF5, 0x05E5 }, + { 0x0CF6, 0x05E6 }, + { 0x0CF7, 0x05E7 }, + { 0x0CF8, 0x05E8 }, + { 0x0CF9, 0x05E9 }, + { 0x0CFA, 0x05EA }, + { 0x0DA1, 0x0E01 }, + { 0x0DA2, 0x0E02 }, + { 0x0DA3, 0x0E03 }, + { 0x0DA4, 0x0E04 }, + { 0x0DA5, 0x0E05 }, + { 0x0DA6, 0x0E06 }, + { 0x0DA7, 0x0E07 }, + { 0x0DA8, 0x0E08 }, + { 0x0DA9, 0x0E09 }, + { 0x0DAA, 0x0E0A }, + { 0x0DAB, 0x0E0B }, + { 0x0DAC, 0x0E0C }, + { 0x0DAD, 0x0E0D }, + { 0x0DAE, 0x0E0E }, + { 0x0DAF, 0x0E0F }, + { 0x0DB0, 0x0E10 }, + { 0x0DB1, 0x0E11 }, + { 0x0DB2, 0x0E12 }, + { 0x0DB3, 0x0E13 }, + { 0x0DB4, 0x0E14 }, + { 0x0DB5, 0x0E15 }, + { 0x0DB6, 0x0E16 }, + { 0x0DB7, 0x0E17 }, + { 0x0DB8, 0x0E18 }, + { 0x0DB9, 0x0E19 }, + { 0x0DBA, 0x0E1A }, + { 0x0DBB, 0x0E1B }, + { 0x0DBC, 0x0E1C }, + { 0x0DBD, 0x0E1D }, + { 0x0DBE, 0x0E1E }, + { 0x0DBF, 0x0E1F }, + { 0x0DC0, 0x0E20 }, + { 0x0DC1, 0x0E21 }, + { 0x0DC2, 0x0E22 }, + { 0x0DC3, 0x0E23 }, + { 0x0DC4, 0x0E24 }, + { 0x0DC5, 0x0E25 }, + { 0x0DC6, 0x0E26 }, + { 0x0DC7, 0x0E27 }, + { 0x0DC8, 0x0E28 }, + { 0x0DC9, 0x0E29 }, + { 0x0DCA, 0x0E2A }, + { 0x0DCB, 0x0E2B }, + { 0x0DCC, 0x0E2C }, + { 0x0DCD, 0x0E2D }, + { 0x0DCE, 0x0E2E }, + { 0x0DCF, 0x0E2F }, + { 0x0DD0, 0x0E30 }, + { 0x0DD1, 0x0E31 }, + { 0x0DD2, 0x0E32 }, + { 0x0DD3, 0x0E33 }, + { 0x0DD4, 0x0E34 }, + { 0x0DD5, 0x0E35 }, + { 0x0DD6, 0x0E36 }, + { 0x0DD7, 0x0E37 }, + { 0x0DD8, 0x0E38 }, + { 0x0DD9, 0x0E39 }, + { 0x0DDA, 0x0E3A }, + { 0x0DDF, 0x0E3F }, + { 0x0DE0, 0x0E40 }, + { 0x0DE1, 0x0E41 }, + { 0x0DE2, 0x0E42 }, + { 0x0DE3, 0x0E43 }, + { 0x0DE4, 0x0E44 }, + { 0x0DE5, 0x0E45 }, + { 0x0DE6, 0x0E46 }, + { 0x0DE7, 0x0E47 }, + { 0x0DE8, 0x0E48 }, + { 0x0DE9, 0x0E49 }, + { 0x0DEA, 0x0E4A }, + { 0x0DEB, 0x0E4B }, + { 0x0DEC, 0x0E4C }, + { 0x0DED, 0x0E4D }, + { 0x0DF0, 0x0E50 }, + { 0x0DF1, 0x0E51 }, + { 0x0DF2, 0x0E52 }, + { 0x0DF3, 0x0E53 }, + { 0x0DF4, 0x0E54 }, + { 0x0DF5, 0x0E55 }, + { 0x0DF6, 0x0E56 }, + { 0x0DF7, 0x0E57 }, + { 0x0DF8, 0x0E58 }, + { 0x0DF9, 0x0E59 }, + { 0x0EA1, 0x3131 }, + { 0x0EA2, 0x3132 }, + { 0x0EA3, 0x3133 }, + { 0x0EA4, 0x3134 }, + { 0x0EA5, 0x3135 }, + { 0x0EA6, 0x3136 }, + { 0x0EA7, 0x3137 }, + { 0x0EA8, 0x3138 }, + { 0x0EA9, 0x3139 }, + { 0x0EAA, 0x313A }, + { 0x0EAB, 0x313B }, + { 0x0EAC, 0x313C }, + { 0x0EAD, 0x313D }, + { 0x0EAE, 0x313E }, + { 0x0EAF, 0x313F }, + { 0x0EB0, 0x3140 }, + { 0x0EB1, 0x3141 }, + { 0x0EB2, 0x3142 }, + { 0x0EB3, 0x3143 }, + { 0x0EB4, 0x3144 }, + { 0x0EB5, 0x3145 }, + { 0x0EB6, 0x3146 }, + { 0x0EB7, 0x3147 }, + { 0x0EB8, 0x3148 }, + { 0x0EB9, 0x3149 }, + { 0x0EBA, 0x314A }, + { 0x0EBB, 0x314B }, + { 0x0EBC, 0x314C }, + { 0x0EBD, 0x314D }, + { 0x0EBE, 0x314E }, + { 0x0EBF, 0x314F }, + { 0x0EC0, 0x3150 }, + { 0x0EC1, 0x3151 }, + { 0x0EC2, 0x3152 }, + { 0x0EC3, 0x3153 }, + { 0x0EC4, 0x3154 }, + { 0x0EC5, 0x3155 }, + { 0x0EC6, 0x3156 }, + { 0x0EC7, 0x3157 }, + { 0x0EC8, 0x3158 }, + { 0x0EC9, 0x3159 }, + { 0x0ECA, 0x315A }, + { 0x0ECB, 0x315B }, + { 0x0ECC, 0x315C }, + { 0x0ECD, 0x315D }, + { 0x0ECE, 0x315E }, + { 0x0ECF, 0x315F }, + { 0x0ED0, 0x3160 }, + { 0x0ED1, 0x3161 }, + { 0x0ED2, 0x3162 }, + { 0x0ED3, 0x3163 }, + { 0x0ED4, 0x11A8 }, + { 0x0ED5, 0x11A9 }, + { 0x0ED6, 0x11AA }, + { 0x0ED7, 0x11AB }, + { 0x0ED8, 0x11AC }, + { 0x0ED9, 0x11AD }, + { 0x0EDA, 0x11AE }, + { 0x0EDB, 0x11AF }, + { 0x0EDC, 0x11B0 }, + { 0x0EDD, 0x11B1 }, + { 0x0EDE, 0x11B2 }, + { 0x0EDF, 0x11B3 }, + { 0x0EE0, 0x11B4 }, + { 0x0EE1, 0x11B5 }, + { 0x0EE2, 0x11B6 }, + { 0x0EE3, 0x11B7 }, + { 0x0EE4, 0x11B8 }, + { 0x0EE5, 0x11B9 }, + { 0x0EE6, 0x11BA }, + { 0x0EE7, 0x11BB }, + { 0x0EE8, 0x11BC }, + { 0x0EE9, 0x11BD }, + { 0x0EEA, 0x11BE }, + { 0x0EEB, 0x11BF }, + { 0x0EEC, 0x11C0 }, + { 0x0EED, 0x11C1 }, + { 0x0EEE, 0x11C2 }, + { 0x0EEF, 0x316D }, + { 0x0EF0, 0x3171 }, + { 0x0EF1, 0x3178 }, + { 0x0EF2, 0x317F }, + { 0x0EF3, 0x3181 }, + { 0x0EF4, 0x3184 }, + { 0x0EF5, 0x3186 }, + { 0x0EF6, 0x318D }, + { 0x0EF7, 0x318E }, + { 0x0EF8, 0x11EB }, + { 0x0EF9, 0x11F0 }, + { 0x0EFA, 0x11F9 }, + { 0x0EFF, 0x20A9 }, + { 0x13A4, 0x20AC }, + { 0x13BC, 0x0152 }, + { 0x13BD, 0x0153 }, + { 0x13BE, 0x0178 }, + { 0x20AC, 0x20AC }, +}; + +unsigned int KeyMappingX11::get_unicode_from_keysym(KeySym p_keysym) { + + /* Latin-1 */ + if (p_keysym>=0x20 && p_keysym<=0x7e) + return p_keysym; + if (p_keysym>=0xa0 && p_keysym<=0xff) + return p_keysym; + // keypad to latin1 is easy + if (p_keysym>=0xffaa && p_keysym<=0xffb9) + return p_keysym-0xff80; + + /* Unicode (may be present)*/ + + if((p_keysym&0xff000000)==0x01000000) + return p_keysym&0x00ffffff; + + int middle,low=0,high=_KEYSYM_MAX-1; + do { + middle=(high+low)/2; + if ( _xkeysym_to_unicode[middle].keysym==p_keysym) + return _xkeysym_to_unicode[middle].unicode; + if ( _xkeysym_to_unicode[middle].keysym<=p_keysym ) + low=middle+1; + else + high=middle-1; + } while (high>=low); + + return 0; + +} + +struct _XTranslateUnicodePairReverse { + + unsigned int unicode; + KeySym keysym; +}; + +enum { + + _UNICODE_MAX=750 +}; + +static _XTranslateUnicodePairReverse _unicode_to_xkeysym[_UNICODE_MAX] = { + { 0x0ABD, 0x002E }, + { 0x0BA3, 0x003C }, + { 0x0BA6, 0x003E }, + { 0x0BC6, 0x005F }, + { 0x0BC0, 0x00AF }, + { 0x03C0, 0x0100 }, + { 0x03E0, 0x0101 }, + { 0x01C3, 0x0102 }, + { 0x01E3, 0x0103 }, + { 0x01A1, 0x0104 }, + { 0x01B1, 0x0105 }, + { 0x01C6, 0x0106 }, + { 0x01E6, 0x0107 }, + { 0x02C6, 0x0108 }, + { 0x02E6, 0x0109 }, + { 0x02C5, 0x010A }, + { 0x02E5, 0x010B }, + { 0x01C8, 0x010C }, + { 0x01E8, 0x010D }, + { 0x01CF, 0x010E }, + { 0x01EF, 0x010F }, + { 0x01D0, 0x0110 }, + { 0x01F0, 0x0111 }, + { 0x03AA, 0x0112 }, + { 0x03BA, 0x0113 }, + { 0x03CC, 0x0116 }, + { 0x03EC, 0x0117 }, + { 0x01CA, 0x0118 }, + { 0x01EA, 0x0119 }, + { 0x01CC, 0x011A }, + { 0x01EC, 0x011B }, + { 0x02D8, 0x011C }, + { 0x02F8, 0x011D }, + { 0x02AB, 0x011E }, + { 0x02BB, 0x011F }, + { 0x02D5, 0x0120 }, + { 0x02F5, 0x0121 }, + { 0x03AB, 0x0122 }, + { 0x03BB, 0x0123 }, + { 0x02A6, 0x0124 }, + { 0x02B6, 0x0125 }, + { 0x02A1, 0x0126 }, + { 0x02B1, 0x0127 }, + { 0x03A5, 0x0128 }, + { 0x03B5, 0x0129 }, + { 0x03CF, 0x012A }, + { 0x03EF, 0x012B }, + { 0x03C7, 0x012E }, + { 0x03E7, 0x012F }, + { 0x02A9, 0x0130 }, + { 0x02B9, 0x0131 }, + { 0x02AC, 0x0134 }, + { 0x02BC, 0x0135 }, + { 0x03D3, 0x0136 }, + { 0x03F3, 0x0137 }, + { 0x03A2, 0x0138 }, + { 0x01C5, 0x0139 }, + { 0x01E5, 0x013A }, + { 0x03A6, 0x013B }, + { 0x03B6, 0x013C }, + { 0x01A5, 0x013D }, + { 0x01B5, 0x013E }, + { 0x01A3, 0x0141 }, + { 0x01B3, 0x0142 }, + { 0x01D1, 0x0143 }, + { 0x01F1, 0x0144 }, + { 0x03D1, 0x0145 }, + { 0x03F1, 0x0146 }, + { 0x01D2, 0x0147 }, + { 0x01F2, 0x0148 }, + { 0x03BD, 0x014A }, + { 0x03BF, 0x014B }, + { 0x03D2, 0x014C }, + { 0x03F2, 0x014D }, + { 0x01D5, 0x0150 }, + { 0x01F5, 0x0151 }, + { 0x13BC, 0x0152 }, + { 0x13BD, 0x0153 }, + { 0x01C0, 0x0154 }, + { 0x01E0, 0x0155 }, + { 0x03A3, 0x0156 }, + { 0x03B3, 0x0157 }, + { 0x01D8, 0x0158 }, + { 0x01F8, 0x0159 }, + { 0x01A6, 0x015A }, + { 0x01B6, 0x015B }, + { 0x02DE, 0x015C }, + { 0x02FE, 0x015D }, + { 0x01AA, 0x015E }, + { 0x01BA, 0x015F }, + { 0x01A9, 0x0160 }, + { 0x01B9, 0x0161 }, + { 0x01DE, 0x0162 }, + { 0x01FE, 0x0163 }, + { 0x01AB, 0x0164 }, + { 0x01BB, 0x0165 }, + { 0x03AC, 0x0166 }, + { 0x03BC, 0x0167 }, + { 0x03DD, 0x0168 }, + { 0x03FD, 0x0169 }, + { 0x03DE, 0x016A }, + { 0x03FE, 0x016B }, + { 0x02DD, 0x016C }, + { 0x02FD, 0x016D }, + { 0x01D9, 0x016E }, + { 0x01F9, 0x016F }, + { 0x01DB, 0x0170 }, + { 0x01FB, 0x0171 }, + { 0x03D9, 0x0172 }, + { 0x03F9, 0x0173 }, + { 0x13BE, 0x0178 }, + { 0x01AC, 0x0179 }, + { 0x01BC, 0x017A }, + { 0x01AF, 0x017B }, + { 0x01BF, 0x017C }, + { 0x01AE, 0x017D }, + { 0x01BE, 0x017E }, + { 0x08F6, 0x0192 }, + { 0x01B7, 0x02C7 }, + { 0x01A2, 0x02D8 }, + { 0x01FF, 0x02D9 }, + { 0x01B2, 0x02DB }, + { 0x01BD, 0x02DD }, + { 0x07AE, 0x0385 }, + { 0x07A1, 0x0386 }, + { 0x07A2, 0x0388 }, + { 0x07A3, 0x0389 }, + { 0x07A4, 0x038A }, + { 0x07A7, 0x038C }, + { 0x07A8, 0x038E }, + { 0x07AB, 0x038F }, + { 0x07B6, 0x0390 }, + { 0x07C1, 0x0391 }, + { 0x07C2, 0x0392 }, + { 0x07C3, 0x0393 }, + { 0x07C4, 0x0394 }, + { 0x07C5, 0x0395 }, + { 0x07C6, 0x0396 }, + { 0x07C7, 0x0397 }, + { 0x07C8, 0x0398 }, + { 0x07C9, 0x0399 }, + { 0x07CA, 0x039A }, + { 0x07CB, 0x039B }, + { 0x07CC, 0x039C }, + { 0x07CD, 0x039D }, + { 0x07CE, 0x039E }, + { 0x07CF, 0x039F }, + { 0x07D0, 0x03A0 }, + { 0x07D1, 0x03A1 }, + { 0x07D2, 0x03A3 }, + { 0x07D4, 0x03A4 }, + { 0x07D5, 0x03A5 }, + { 0x07D6, 0x03A6 }, + { 0x07D7, 0x03A7 }, + { 0x07D8, 0x03A8 }, + { 0x07D9, 0x03A9 }, + { 0x07A5, 0x03AA }, + { 0x07A9, 0x03AB }, + { 0x07B1, 0x03AC }, + { 0x07B2, 0x03AD }, + { 0x07B3, 0x03AE }, + { 0x07B4, 0x03AF }, + { 0x07BA, 0x03B0 }, + { 0x07E1, 0x03B1 }, + { 0x07E2, 0x03B2 }, + { 0x07E3, 0x03B3 }, + { 0x07E4, 0x03B4 }, + { 0x07E5, 0x03B5 }, + { 0x07E6, 0x03B6 }, + { 0x07E7, 0x03B7 }, + { 0x07E8, 0x03B8 }, + { 0x07E9, 0x03B9 }, + { 0x07EA, 0x03BA }, + { 0x07EB, 0x03BB }, + { 0x07EC, 0x03BC }, + { 0x07ED, 0x03BD }, + { 0x07EE, 0x03BE }, + { 0x07EF, 0x03BF }, + { 0x07F0, 0x03C0 }, + { 0x07F1, 0x03C1 }, + { 0x07F3, 0x03C2 }, + { 0x07F2, 0x03C3 }, + { 0x07F4, 0x03C4 }, + { 0x07F5, 0x03C5 }, + { 0x07F6, 0x03C6 }, + { 0x07F7, 0x03C7 }, + { 0x07F8, 0x03C8 }, + { 0x07F9, 0x03C9 }, + { 0x07B5, 0x03CA }, + { 0x07B9, 0x03CB }, + { 0x07B7, 0x03CC }, + { 0x07B8, 0x03CD }, + { 0x07BB, 0x03CE }, + { 0x06B3, 0x0401 }, + { 0x06B1, 0x0402 }, + { 0x06B2, 0x0403 }, + { 0x06B4, 0x0404 }, + { 0x06B5, 0x0405 }, + { 0x06B6, 0x0406 }, + { 0x06B7, 0x0407 }, + { 0x06B8, 0x0408 }, + { 0x06B9, 0x0409 }, + { 0x06BA, 0x040A }, + { 0x06BB, 0x040B }, + { 0x06BC, 0x040C }, + { 0x06BE, 0x040E }, + { 0x06BF, 0x040F }, + { 0x06E1, 0x0410 }, + { 0x06E2, 0x0411 }, + { 0x06F7, 0x0412 }, + { 0x06E7, 0x0413 }, + { 0x06E4, 0x0414 }, + { 0x06E5, 0x0415 }, + { 0x06F6, 0x0416 }, + { 0x06FA, 0x0417 }, + { 0x06E9, 0x0418 }, + { 0x06EA, 0x0419 }, + { 0x06EB, 0x041A }, + { 0x06EC, 0x041B }, + { 0x06ED, 0x041C }, + { 0x06EE, 0x041D }, + { 0x06EF, 0x041E }, + { 0x06F0, 0x041F }, + { 0x06F2, 0x0420 }, + { 0x06F3, 0x0421 }, + { 0x06F4, 0x0422 }, + { 0x06F5, 0x0423 }, + { 0x06E6, 0x0424 }, + { 0x06E8, 0x0425 }, + { 0x06E3, 0x0426 }, + { 0x06FE, 0x0427 }, + { 0x06FB, 0x0428 }, + { 0x06FD, 0x0429 }, + { 0x06FF, 0x042A }, + { 0x06F9, 0x042B }, + { 0x06F8, 0x042C }, + { 0x06FC, 0x042D }, + { 0x06E0, 0x042E }, + { 0x06F1, 0x042F }, + { 0x06C1, 0x0430 }, + { 0x06C2, 0x0431 }, + { 0x06D7, 0x0432 }, + { 0x06C7, 0x0433 }, + { 0x06C4, 0x0434 }, + { 0x06C5, 0x0435 }, + { 0x06D6, 0x0436 }, + { 0x06DA, 0x0437 }, + { 0x06C9, 0x0438 }, + { 0x06CA, 0x0439 }, + { 0x06CB, 0x043A }, + { 0x06CC, 0x043B }, + { 0x06CD, 0x043C }, + { 0x06CE, 0x043D }, + { 0x06CF, 0x043E }, + { 0x06D0, 0x043F }, + { 0x06D2, 0x0440 }, + { 0x06D3, 0x0441 }, + { 0x06D4, 0x0442 }, + { 0x06D5, 0x0443 }, + { 0x06C6, 0x0444 }, + { 0x06C8, 0x0445 }, + { 0x06C3, 0x0446 }, + { 0x06DE, 0x0447 }, + { 0x06DB, 0x0448 }, + { 0x06DD, 0x0449 }, + { 0x06DF, 0x044A }, + { 0x06D9, 0x044B }, + { 0x06D8, 0x044C }, + { 0x06DC, 0x044D }, + { 0x06C0, 0x044E }, + { 0x06D1, 0x044F }, + { 0x06A3, 0x0451 }, + { 0x06A1, 0x0452 }, + { 0x06A2, 0x0453 }, + { 0x06A4, 0x0454 }, + { 0x06A5, 0x0455 }, + { 0x06A6, 0x0456 }, + { 0x06A7, 0x0457 }, + { 0x06A8, 0x0458 }, + { 0x06A9, 0x0459 }, + { 0x06AA, 0x045A }, + { 0x06AB, 0x045B }, + { 0x06AC, 0x045C }, + { 0x06AE, 0x045E }, + { 0x06AF, 0x045F }, + { 0x0CE0, 0x05D0 }, + { 0x0CE1, 0x05D1 }, + { 0x0CE2, 0x05D2 }, + { 0x0CE3, 0x05D3 }, + { 0x0CE4, 0x05D4 }, + { 0x0CE5, 0x05D5 }, + { 0x0CE6, 0x05D6 }, + { 0x0CE7, 0x05D7 }, + { 0x0CE8, 0x05D8 }, + { 0x0CE9, 0x05D9 }, + { 0x0CEA, 0x05DA }, + { 0x0CEB, 0x05DB }, + { 0x0CEC, 0x05DC }, + { 0x0CED, 0x05DD }, + { 0x0CEE, 0x05DE }, + { 0x0CEF, 0x05DF }, + { 0x0CF0, 0x05E0 }, + { 0x0CF1, 0x05E1 }, + { 0x0CF2, 0x05E2 }, + { 0x0CF3, 0x05E3 }, + { 0x0CF4, 0x05E4 }, + { 0x0CF5, 0x05E5 }, + { 0x0CF6, 0x05E6 }, + { 0x0CF7, 0x05E7 }, + { 0x0CF8, 0x05E8 }, + { 0x0CF9, 0x05E9 }, + { 0x0CFA, 0x05EA }, + { 0x05AC, 0x060C }, + { 0x05BB, 0x061B }, + { 0x05BF, 0x061F }, + { 0x05C1, 0x0621 }, + { 0x05C2, 0x0622 }, + { 0x05C3, 0x0623 }, + { 0x05C4, 0x0624 }, + { 0x05C5, 0x0625 }, + { 0x05C6, 0x0626 }, + { 0x05C7, 0x0627 }, + { 0x05C8, 0x0628 }, + { 0x05C9, 0x0629 }, + { 0x05CA, 0x062A }, + { 0x05CB, 0x062B }, + { 0x05CC, 0x062C }, + { 0x05CD, 0x062D }, + { 0x05CE, 0x062E }, + { 0x05CF, 0x062F }, + { 0x05D0, 0x0630 }, + { 0x05D1, 0x0631 }, + { 0x05D2, 0x0632 }, + { 0x05D3, 0x0633 }, + { 0x05D4, 0x0634 }, + { 0x05D5, 0x0635 }, + { 0x05D6, 0x0636 }, + { 0x05D7, 0x0637 }, + { 0x05D8, 0x0638 }, + { 0x05D9, 0x0639 }, + { 0x05DA, 0x063A }, + { 0x05E0, 0x0640 }, + { 0x05E1, 0x0641 }, + { 0x05E2, 0x0642 }, + { 0x05E3, 0x0643 }, + { 0x05E4, 0x0644 }, + { 0x05E5, 0x0645 }, + { 0x05E6, 0x0646 }, + { 0x05E7, 0x0647 }, + { 0x05E8, 0x0648 }, + { 0x05E9, 0x0649 }, + { 0x05EA, 0x064A }, + { 0x05EB, 0x064B }, + { 0x05EC, 0x064C }, + { 0x05ED, 0x064D }, + { 0x05EE, 0x064E }, + { 0x05EF, 0x064F }, + { 0x05F0, 0x0650 }, + { 0x05F1, 0x0651 }, + { 0x05F2, 0x0652 }, + { 0x0DA1, 0x0E01 }, + { 0x0DA2, 0x0E02 }, + { 0x0DA3, 0x0E03 }, + { 0x0DA4, 0x0E04 }, + { 0x0DA5, 0x0E05 }, + { 0x0DA6, 0x0E06 }, + { 0x0DA7, 0x0E07 }, + { 0x0DA8, 0x0E08 }, + { 0x0DA9, 0x0E09 }, + { 0x0DAA, 0x0E0A }, + { 0x0DAB, 0x0E0B }, + { 0x0DAC, 0x0E0C }, + { 0x0DAD, 0x0E0D }, + { 0x0DAE, 0x0E0E }, + { 0x0DAF, 0x0E0F }, + { 0x0DB0, 0x0E10 }, + { 0x0DB1, 0x0E11 }, + { 0x0DB2, 0x0E12 }, + { 0x0DB3, 0x0E13 }, + { 0x0DB4, 0x0E14 }, + { 0x0DB5, 0x0E15 }, + { 0x0DB6, 0x0E16 }, + { 0x0DB7, 0x0E17 }, + { 0x0DB8, 0x0E18 }, + { 0x0DB9, 0x0E19 }, + { 0x0DBA, 0x0E1A }, + { 0x0DBB, 0x0E1B }, + { 0x0DBC, 0x0E1C }, + { 0x0DBD, 0x0E1D }, + { 0x0DBE, 0x0E1E }, + { 0x0DBF, 0x0E1F }, + { 0x0DC0, 0x0E20 }, + { 0x0DC1, 0x0E21 }, + { 0x0DC2, 0x0E22 }, + { 0x0DC3, 0x0E23 }, + { 0x0DC4, 0x0E24 }, + { 0x0DC5, 0x0E25 }, + { 0x0DC6, 0x0E26 }, + { 0x0DC7, 0x0E27 }, + { 0x0DC8, 0x0E28 }, + { 0x0DC9, 0x0E29 }, + { 0x0DCA, 0x0E2A }, + { 0x0DCB, 0x0E2B }, + { 0x0DCC, 0x0E2C }, + { 0x0DCD, 0x0E2D }, + { 0x0DCE, 0x0E2E }, + { 0x0DCF, 0x0E2F }, + { 0x0DD0, 0x0E30 }, + { 0x0DD1, 0x0E31 }, + { 0x0DD2, 0x0E32 }, + { 0x0DD3, 0x0E33 }, + { 0x0DD4, 0x0E34 }, + { 0x0DD5, 0x0E35 }, + { 0x0DD6, 0x0E36 }, + { 0x0DD7, 0x0E37 }, + { 0x0DD8, 0x0E38 }, + { 0x0DD9, 0x0E39 }, + { 0x0DDA, 0x0E3A }, + { 0x0DDF, 0x0E3F }, + { 0x0DE0, 0x0E40 }, + { 0x0DE1, 0x0E41 }, + { 0x0DE2, 0x0E42 }, + { 0x0DE3, 0x0E43 }, + { 0x0DE4, 0x0E44 }, + { 0x0DE5, 0x0E45 }, + { 0x0DE6, 0x0E46 }, + { 0x0DE7, 0x0E47 }, + { 0x0DE8, 0x0E48 }, + { 0x0DE9, 0x0E49 }, + { 0x0DEA, 0x0E4A }, + { 0x0DEB, 0x0E4B }, + { 0x0DEC, 0x0E4C }, + { 0x0DED, 0x0E4D }, + { 0x0DF0, 0x0E50 }, + { 0x0DF1, 0x0E51 }, + { 0x0DF2, 0x0E52 }, + { 0x0DF3, 0x0E53 }, + { 0x0DF4, 0x0E54 }, + { 0x0DF5, 0x0E55 }, + { 0x0DF6, 0x0E56 }, + { 0x0DF7, 0x0E57 }, + { 0x0DF8, 0x0E58 }, + { 0x0DF9, 0x0E59 }, + { 0x0ED4, 0x11A8 }, + { 0x0ED5, 0x11A9 }, + { 0x0ED6, 0x11AA }, + { 0x0ED7, 0x11AB }, + { 0x0ED8, 0x11AC }, + { 0x0ED9, 0x11AD }, + { 0x0EDA, 0x11AE }, + { 0x0EDB, 0x11AF }, + { 0x0EDC, 0x11B0 }, + { 0x0EDD, 0x11B1 }, + { 0x0EDE, 0x11B2 }, + { 0x0EDF, 0x11B3 }, + { 0x0EE0, 0x11B4 }, + { 0x0EE1, 0x11B5 }, + { 0x0EE2, 0x11B6 }, + { 0x0EE3, 0x11B7 }, + { 0x0EE4, 0x11B8 }, + { 0x0EE5, 0x11B9 }, + { 0x0EE6, 0x11BA }, + { 0x0EE7, 0x11BB }, + { 0x0EE8, 0x11BC }, + { 0x0EE9, 0x11BD }, + { 0x0EEA, 0x11BE }, + { 0x0EEB, 0x11BF }, + { 0x0EEC, 0x11C0 }, + { 0x0EED, 0x11C1 }, + { 0x0EEE, 0x11C2 }, + { 0x0EF8, 0x11EB }, + { 0x0EFA, 0x11F9 }, + { 0x0AA2, 0x2002 }, + { 0x0AA1, 0x2003 }, + { 0x0AA3, 0x2004 }, + { 0x0AA4, 0x2005 }, + { 0x0AA5, 0x2007 }, + { 0x0AA6, 0x2008 }, + { 0x0AA7, 0x2009 }, + { 0x0AA8, 0x200A }, + { 0x0ABB, 0x2012 }, + { 0x0AAA, 0x2013 }, + { 0x0AA9, 0x2014 }, + { 0x07AF, 0x2015 }, + { 0x0CDF, 0x2017 }, + { 0x0AD0, 0x2018 }, + { 0x0AD1, 0x2019 }, + { 0x0AFD, 0x201A }, + { 0x0AD2, 0x201C }, + { 0x0AD3, 0x201D }, + { 0x0AFE, 0x201E }, + { 0x0AF1, 0x2020 }, + { 0x0AF2, 0x2021 }, + { 0x0AE6, 0x2022 }, + { 0x0AAE, 0x2026 }, + { 0x0AD6, 0x2032 }, + { 0x0AD7, 0x2033 }, + { 0x0AFC, 0x2038 }, + { 0x047E, 0x203E }, + { 0x20A0, 0x20A0 }, + { 0x20A1, 0x20A1 }, + { 0x20A2, 0x20A2 }, + { 0x20A3, 0x20A3 }, + { 0x20A4, 0x20A4 }, + { 0x20A5, 0x20A5 }, + { 0x20A6, 0x20A6 }, + { 0x20A7, 0x20A7 }, + { 0x20A8, 0x20A8 }, + { 0x0EFF, 0x20A9 }, + { 0x20A9, 0x20A9 }, + { 0x20AA, 0x20AA }, + { 0x20AB, 0x20AB }, + { 0x20AC, 0x20AC }, + { 0x0AB8, 0x2105 }, + { 0x06B0, 0x2116 }, + { 0x0AFB, 0x2117 }, + { 0x0AD4, 0x211E }, + { 0x0AC9, 0x2122 }, + { 0x0AB0, 0x2153 }, + { 0x0AB1, 0x2154 }, + { 0x0AB2, 0x2155 }, + { 0x0AB3, 0x2156 }, + { 0x0AB4, 0x2157 }, + { 0x0AB5, 0x2158 }, + { 0x0AB6, 0x2159 }, + { 0x0AB7, 0x215A }, + { 0x0AC3, 0x215B }, + { 0x0AC4, 0x215C }, + { 0x0AC5, 0x215D }, + { 0x0AC6, 0x215E }, + { 0x08FB, 0x2190 }, + { 0x08FC, 0x2191 }, + { 0x08FD, 0x2192 }, + { 0x08FE, 0x2193 }, + { 0x08CE, 0x21D2 }, + { 0x08CD, 0x21D4 }, + { 0x08EF, 0x2202 }, + { 0x08C5, 0x2207 }, + { 0x0BCA, 0x2218 }, + { 0x08D6, 0x221A }, + { 0x08C1, 0x221D }, + { 0x08C2, 0x221E }, + { 0x08DE, 0x2227 }, + { 0x0BA9, 0x2227 }, + { 0x08DF, 0x2228 }, + { 0x0BA8, 0x2228 }, + { 0x08DC, 0x2229 }, + { 0x0BC3, 0x2229 }, + { 0x08DD, 0x222A }, + { 0x0BD6, 0x222A }, + { 0x08BF, 0x222B }, + { 0x08C0, 0x2234 }, + { 0x08C8, 0x2245 }, + { 0x08BD, 0x2260 }, + { 0x08CF, 0x2261 }, + { 0x08BC, 0x2264 }, + { 0x08BE, 0x2265 }, + { 0x08DA, 0x2282 }, + { 0x0BDA, 0x2282 }, + { 0x08DB, 0x2283 }, + { 0x0BD8, 0x2283 }, + { 0x0BFC, 0x22A2 }, + { 0x0BDC, 0x22A3 }, + { 0x0BC2, 0x22A4 }, + { 0x0BCE, 0x22A5 }, + { 0x0BD3, 0x2308 }, + { 0x0BC4, 0x230A }, + { 0x0AFA, 0x2315 }, + { 0x08A4, 0x2320 }, + { 0x08A5, 0x2321 }, + { 0x0ABC, 0x2329 }, + { 0x0ABE, 0x232A }, + { 0x0BCC, 0x2395 }, + { 0x09E2, 0x2409 }, + { 0x09E5, 0x240A }, + { 0x09E9, 0x240B }, + { 0x09E3, 0x240C }, + { 0x09E4, 0x240D }, + { 0x09DF, 0x2422 }, + { 0x09E8, 0x2424 }, + { 0x09F1, 0x2500 }, + { 0x08A6, 0x2502 }, + { 0x09F8, 0x2502 }, + { 0x09EC, 0x250C }, + { 0x09EB, 0x2510 }, + { 0x09ED, 0x2514 }, + { 0x09EA, 0x2518 }, + { 0x09F4, 0x251C }, + { 0x09F5, 0x2524 }, + { 0x09F7, 0x252C }, + { 0x09F6, 0x2534 }, + { 0x09EE, 0x253C }, + { 0x09E1, 0x2592 }, + { 0x0ADF, 0x25A0 }, + { 0x0ACF, 0x25A1 }, + { 0x0AE7, 0x25AA }, + { 0x0AE1, 0x25AB }, + { 0x0ADB, 0x25AC }, + { 0x0AE2, 0x25AD }, + { 0x0AE8, 0x25B2 }, + { 0x0AE3, 0x25B3 }, + { 0x0ADD, 0x25B6 }, + { 0x0ACD, 0x25B7 }, + { 0x0AE9, 0x25BC }, + { 0x0AE4, 0x25BD }, + { 0x0ADC, 0x25C0 }, + { 0x0ACC, 0x25C1 }, + { 0x09E0, 0x25C6 }, + { 0x0ACE, 0x25CB }, + { 0x0BCF, 0x25CB }, + { 0x0ADE, 0x25CF }, + { 0x0AE0, 0x25E6 }, + { 0x0AE5, 0x2606 }, + { 0x0AF9, 0x260E }, + { 0x0ACA, 0x2613 }, + { 0x0AEA, 0x261C }, + { 0x0AEB, 0x261E }, + { 0x0AF8, 0x2640 }, + { 0x0AF7, 0x2642 }, + { 0x0AEC, 0x2663 }, + { 0x0AEE, 0x2665 }, + { 0x0AED, 0x2666 }, + { 0x0AF6, 0x266D }, + { 0x0AF5, 0x266F }, + { 0x0AF3, 0x2713 }, + { 0x0AF4, 0x2717 }, + { 0x0AD9, 0x271D }, + { 0x0AF0, 0x2720 }, + { 0x04A4, 0x3001 }, + { 0x04A1, 0x3002 }, + { 0x04A2, 0x300C }, + { 0x04A3, 0x300D }, + { 0x04DE, 0x309B }, + { 0x04DF, 0x309C }, + { 0x04A7, 0x30A1 }, + { 0x04B1, 0x30A2 }, + { 0x04A8, 0x30A3 }, + { 0x04B2, 0x30A4 }, + { 0x04A9, 0x30A5 }, + { 0x04B3, 0x30A6 }, + { 0x04AA, 0x30A7 }, + { 0x04B4, 0x30A8 }, + { 0x04AB, 0x30A9 }, + { 0x04B5, 0x30AA }, + { 0x04B6, 0x30AB }, + { 0x04B7, 0x30AD }, + { 0x04B8, 0x30AF }, + { 0x04B9, 0x30B1 }, + { 0x04BA, 0x30B3 }, + { 0x04BB, 0x30B5 }, + { 0x04BC, 0x30B7 }, + { 0x04BD, 0x30B9 }, + { 0x04BE, 0x30BB }, + { 0x04BF, 0x30BD }, + { 0x04C0, 0x30BF }, + { 0x04C1, 0x30C1 }, + { 0x04AF, 0x30C3 }, + { 0x04C2, 0x30C4 }, + { 0x04C3, 0x30C6 }, + { 0x04C4, 0x30C8 }, + { 0x04C5, 0x30CA }, + { 0x04C6, 0x30CB }, + { 0x04C7, 0x30CC }, + { 0x04C8, 0x30CD }, + { 0x04C9, 0x30CE }, + { 0x04CA, 0x30CF }, + { 0x04CB, 0x30D2 }, + { 0x04CC, 0x30D5 }, + { 0x04CD, 0x30D8 }, + { 0x04CE, 0x30DB }, + { 0x04CF, 0x30DE }, + { 0x04D0, 0x30DF }, + { 0x04D1, 0x30E0 }, + { 0x04D2, 0x30E1 }, + { 0x04D3, 0x30E2 }, + { 0x04AC, 0x30E3 }, + { 0x04D4, 0x30E4 }, + { 0x04AD, 0x30E5 }, + { 0x04D5, 0x30E6 }, + { 0x04AE, 0x30E7 }, + { 0x04D6, 0x30E8 }, + { 0x04D7, 0x30E9 }, + { 0x04D8, 0x30EA }, + { 0x04D9, 0x30EB }, + { 0x04DA, 0x30EC }, + { 0x04DB, 0x30ED }, + { 0x04DC, 0x30EF }, + { 0x04A6, 0x30F2 }, + { 0x04DD, 0x30F3 }, + { 0x04A5, 0x30FB }, + { 0x04B0, 0x30FC }, + { 0x0EA1, 0x3131 }, + { 0x0EA2, 0x3132 }, + { 0x0EA3, 0x3133 }, + { 0x0EA4, 0x3134 }, + { 0x0EA5, 0x3135 }, + { 0x0EA6, 0x3136 }, + { 0x0EA7, 0x3137 }, + { 0x0EA8, 0x3138 }, + { 0x0EA9, 0x3139 }, + { 0x0EAA, 0x313A }, + { 0x0EAB, 0x313B }, + { 0x0EAC, 0x313C }, + { 0x0EAD, 0x313D }, + { 0x0EAE, 0x313E }, + { 0x0EAF, 0x313F }, + { 0x0EB0, 0x3140 }, + { 0x0EB1, 0x3141 }, + { 0x0EB2, 0x3142 }, + { 0x0EB3, 0x3143 }, + { 0x0EB4, 0x3144 }, + { 0x0EB5, 0x3145 }, + { 0x0EB6, 0x3146 }, + { 0x0EB7, 0x3147 }, + { 0x0EB8, 0x3148 }, + { 0x0EB9, 0x3149 }, + { 0x0EBA, 0x314A }, + { 0x0EBB, 0x314B }, + { 0x0EBC, 0x314C }, + { 0x0EBD, 0x314D }, + { 0x0EBE, 0x314E }, + { 0x0EBF, 0x314F }, + { 0x0EC0, 0x3150 }, + { 0x0EC1, 0x3151 }, + { 0x0EC2, 0x3152 }, + { 0x0EC3, 0x3153 }, + { 0x0EC4, 0x3154 }, + { 0x0EC5, 0x3155 }, + { 0x0EC6, 0x3156 }, + { 0x0EC7, 0x3157 }, + { 0x0EC8, 0x3158 }, + { 0x0EC9, 0x3159 }, + { 0x0ECA, 0x315A }, + { 0x0ECB, 0x315B }, + { 0x0ECC, 0x315C }, + { 0x0ECD, 0x315D }, + { 0x0ECE, 0x315E }, + { 0x0ECF, 0x315F }, + { 0x0ED0, 0x3160 }, + { 0x0ED1, 0x3161 }, + { 0x0ED2, 0x3162 }, + { 0x0ED3, 0x3163 }, + { 0x0EEF, 0x316D }, + { 0x0EF0, 0x3171 }, + { 0x0EF1, 0x3178 }, + { 0x0EF2, 0x317F }, + { 0x0EF4, 0x3184 }, + { 0x0EF5, 0x3186 }, + { 0x0EF6, 0x318D }, + { 0x0EF7, 0x318E } +}; + +KeySym KeyMappingX11::get_keysym_from_unicode(unsigned int p_unicode) { + + /* Latin 1 */ + + if (p_unicode>=0x20 && p_unicode<=0x7e) + return p_unicode; + + if (p_unicode>=0xa0 && p_unicode<=0xff) + return p_unicode; + + int middle,low=0,high=_UNICODE_MAX-1; + do { + middle=(high+low)/2; + if ( _unicode_to_xkeysym[middle].keysym==p_unicode) + return _unicode_to_xkeysym[middle].keysym; + if ( _unicode_to_xkeysym[middle].keysym<=p_unicode ) + low=middle+1; + else + high=middle-1; + } while (high>=low); + + // if not found, let's hope X understands it as unicode + return p_unicode|0x01000000; +} diff --git a/platform/x11/key_mapping_x11.h b/platform/x11/key_mapping_x11.h new file mode 100644 index 0000000000..97393d92f3 --- /dev/null +++ b/platform/x11/key_mapping_x11.h @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* key_mapping_x11.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 KEY_MAPPING_X11_H +#define KEY_MAPPING_X11_H + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +#include <X11/Xlib.h> +#include <X11/XF86keysym.h> +#define XK_MISCELLANY +#define XK_LATIN1 +#define XK_XKB_KEYS +#include <X11/keysymdef.h> + +#include "os/keyboard.h" + +class KeyMappingX11 { + KeyMappingX11() {}; +public: + static unsigned int get_keycode(KeySym p_keysym); + static KeySym get_keysym(unsigned int p_code); + static unsigned int get_unicode_from_keysym(KeySym p_keysym); + static KeySym get_keysym_from_unicode(unsigned int p_unicode); + +}; + + +#endif diff --git a/platform/x11/logo.png b/platform/x11/logo.png Binary files differnew file mode 100644 index 0000000000..c40214d6de --- /dev/null +++ b/platform/x11/logo.png diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp new file mode 100644 index 0000000000..79dd6f1234 --- /dev/null +++ b/platform/x11/os_x11.cpp @@ -0,0 +1,1300 @@ +/*************************************************************************/ +/* os_x11.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 "servers/visual/visual_server_raster.h" +#include "drivers/gles2/rasterizer_gles2.h" +#include "drivers/gles1/rasterizer_gles1.h" +#include "os_x11.h" +#include "key_mapping_x11.h" +#include <stdio.h> +#include <stdlib.h> +#include "print_string.h" +#include "servers/physics/physics_server_sw.h" + +#include "X11/Xutil.h" +#include "main/main.h" + + + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <linux/joystick.h> + +//stupid linux.h +#ifdef KEY_TAB +#undef KEY_TAB +#endif + + +#include <X11/Xatom.h> +#include "os/pc_joystick_map.h" + +#undef CursorShape + +int OS_X11::get_video_driver_count() const { + + return 2; +} +const char * OS_X11::get_video_driver_name(int p_driver) const { + + return p_driver==0?"GLES2":"GLES1"; +} +OS::VideoMode OS_X11::get_default_video_mode() const { + + return OS::VideoMode(800,600,false); +} + +void OS_X11::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + last_button_state=0; + dpad_last[0]=0; + dpad_last[1]=0; + + xmbstring=NULL; + event_id=0; + x11_window=0; + last_click_ms=0; + args=OS::get_singleton()->get_cmdline_args(); + current_videomode=p_desired; + main_loop=NULL; + last_timestamp=0; + last_mouse_pos_valid=false; + last_keyrelease_time=0; + + if (get_render_thread_mode()==RENDER_SEPARATE_THREAD) { + XInitThreads(); + } + + /** XLIB INITIALIZATION **/ + x11_display = XOpenDisplay(NULL); + + char * modifiers = XSetLocaleModifiers ("@im=none"); + ERR_FAIL_COND( modifiers == NULL ); + + xim = XOpenIM (x11_display, NULL, NULL, NULL); + + + if (xim == NULL) { + WARN_PRINT("XOpenIM failed"); + xim_style=NULL; + } else { + ::XIMStyles *xim_styles=NULL; + xim_style=0; + char *imvalret=NULL; + imvalret = XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL); + if (imvalret != NULL || xim_styles == NULL) { + fprintf (stderr, "Input method doesn't support any styles\n"); + } + + if (xim_styles) { + xim_style = 0; + for (int i=0;i<xim_styles->count_styles;i++) { + + if (xim_styles->supported_styles[i] == + (XIMPreeditNothing | XIMStatusNothing)) { + + xim_style = xim_styles->supported_styles[i]; + break; + } + } + + XFree (xim_styles); + } + } + + /* + char* windowid = getenv("GODOT_WINDOWID"); + if (windowid) { + + //freopen("/home/punto/stdout", "w", stdout); + //reopen("/home/punto/stderr", "w", stderr); + x11_window = atol(windowid); + + XWindowAttributes xwa; + XGetWindowAttributes(x11_display,x11_window,&xwa); + + current_videomode.width = xwa.width; + current_videomode.height = xwa.height; + }; + */ + + // maybe contextgl wants to be in charge of creating the window + //print_line("def videomode "+itos(current_videomode.width)+","+itos(current_videomode.height)); +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) + context_gl = memnew( ContextGL_X11( x11_display, x11_window,current_videomode, false ) ); + context_gl->initialize(); + + if (p_video_driver == 0) { + rasterizer = memnew( RasterizerGLES2 ); + } else { + rasterizer = memnew( RasterizerGLES1 ); + }; + +#endif + visual_server = memnew( VisualServerRaster(rasterizer) ); + + if (get_render_thread_mode()!=RENDER_THREAD_UNSAFE) { + + visual_server =memnew(VisualServerWrapMT(visual_server,get_render_thread_mode()==RENDER_SEPARATE_THREAD)); + } + + 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->init(); + spatial_sound_server = memnew( SpatialSoundServerSW ); + spatial_sound_server->init(); + spatial_sound_2d_server = memnew( SpatialSound2DServerSW ); + spatial_sound_2d_server->init(); + + + ERR_FAIL_COND(!visual_server); + ERR_FAIL_COND(x11_window==0); + + XSetWindowAttributes new_attr; + + new_attr.event_mask=KeyPressMask | KeyReleaseMask | ButtonPressMask | + ButtonReleaseMask | EnterWindowMask | + LeaveWindowMask | PointerMotionMask | + Button1MotionMask | + Button2MotionMask | Button3MotionMask | + Button4MotionMask | Button5MotionMask | + ButtonMotionMask | KeymapStateMask | + ExposureMask | VisibilityChangeMask | + StructureNotifyMask | + SubstructureNotifyMask | SubstructureRedirectMask | + FocusChangeMask | PropertyChangeMask | + ColormapChangeMask | OwnerGrabButtonMask; + + XChangeWindowAttributes(x11_display, x11_window,CWEventMask,&new_attr); + + wm_delete = XInternAtom(x11_display, "WM_DELETE_WINDOW", true); + XSetWMProtocols(x11_display, x11_window, &wm_delete, 1); + + + if (xim && xim_style) { + + xic = XCreateIC (xim,XNInputStyle, xim_style,XNClientWindow,x11_window,XNFocusWindow, x11_window, (char*)NULL); + } else { + + xic=NULL; + WARN_PRINT("XCreateIC couldn't create xic"); + + } + + XcursorSetTheme(x11_display,"default"); + cursor_size = XcursorGetDefaultSize(x11_display); + cursor_theme = XcursorGetTheme(x11_display); + + if (!cursor_theme) { + print_line("not found theme"); + cursor_theme="default"; + } + + for(int i=0;i<CURSOR_MAX;i++) { + + cursors[i]=None; + } + + current_cursor=CURSOR_ARROW; + + if (cursor_theme) { + //print_line("cursor theme: "+String(cursor_theme)); + for(int i=0;i<CURSOR_MAX;i++) { + + static const char *cursor_file[]={ + "left_ptr", + "xterm", + "hand2", + "cross", + "watch", + "left_ptr_watch", + "fleur", + "hand1", + "X_cursor", + "sb_v_double_arrow", + "sb_h_double_arrow", + "size_bdiag", + "size_fdiag", + "hand1", + "sb_v_double_arrow", + "sb_h_double_arrow", + "question_arrow" + }; + + XcursorImage *img = XcursorLibraryLoadImage(cursor_file[i],cursor_theme,cursor_size); + if (img) { + cursors[i]=XcursorImageLoadCursor(x11_display,img); + //print_line("found cursor: "+String(cursor_file[i])+" id "+itos(cursors[i])); + } else { + if (OS::is_stdout_verbose()) + print_line("failed cursor: "+String(cursor_file[i])); + } + } + + } + + + { + Pixmap cursormask; + XGCValues xgc; + GC gc; + XColor col; + Cursor cursor; + + cursormask = XCreatePixmap(x11_display, RootWindow(x11_display,DefaultScreen(x11_display)), 1, 1, 1); + xgc.function = GXclear; + gc = XCreateGC(x11_display, cursormask, GCFunction, &xgc); + XFillRectangle(x11_display, cursormask, gc, 0, 0, 1, 1); + col.pixel = 0; + col.red = 0; + col.flags = 4; + cursor = XCreatePixmapCursor(x11_display, + cursormask, cursormask, + &col, &col, 0, 0); + XFreePixmap(x11_display, cursormask); + XFreeGC(x11_display, gc); + + + + if (cursor == None) + { + ERR_PRINT("FAILED CREATING CURSOR"); + } + + null_cursor=cursor; + } + set_cursor_shape(CURSOR_BUSY); + + + visual_server->init(); + // + physics_server = memnew( PhysicsServerSW ); + physics_server->init(); + physics_2d_server = memnew( Physics2DServerSW ); + physics_2d_server->init(); + + input = memnew( InputDefault ); + + probe_joystick(); + + _ensure_data_dir(); + + net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False); + + + //printf("got map notify\n"); + +} +void OS_X11::finalize() { + + if(main_loop) + memdelete(main_loop); + main_loop=NULL; + + spatial_sound_server->finish(); + memdelete(spatial_sound_server); + spatial_sound_2d_server->finish(); + memdelete(spatial_sound_2d_server); + + //if (debugger_connection_console) { +// memdelete(debugger_connection_console); +//} + + audio_server->finish(); + memdelete(audio_server); + memdelete(sample_manager); + + visual_server->finish(); + memdelete(visual_server); + memdelete(rasterizer); + + physics_server->finish(); + memdelete(physics_server); + + physics_2d_server->finish(); + memdelete(physics_2d_server); + + memdelete(input); + +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) + memdelete(context_gl); +#endif + + + XCloseDisplay(x11_display); + if (xmbstring) + memfree(xmbstring); + + args.clear(); +} + + +void OS_X11::set_mouse_mode(MouseMode p_mode) { + + print_line("WUTF "+itos(p_mode)+" old "+itos(mouse_mode)); + if (p_mode==mouse_mode) + return; + + if (mouse_mode==MOUSE_MODE_CAPTURED) + XUngrabPointer(x11_display, CurrentTime); + if (mouse_mode!=MOUSE_MODE_VISIBLE && p_mode==MOUSE_MODE_VISIBLE) + XUndefineCursor(x11_display,x11_window); + if (p_mode!=MOUSE_MODE_VISIBLE && mouse_mode==MOUSE_MODE_VISIBLE) { + XDefineCursor(x11_display,x11_window,null_cursor); + } + + mouse_mode=p_mode; + + if (mouse_mode==MOUSE_MODE_CAPTURED) { + if (XGrabPointer(x11_display, x11_window, True, + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask, GrabModeAsync, GrabModeAsync, + x11_window, None, CurrentTime) != + GrabSuccess) { + ERR_PRINT("NO GRAB"); + } + + center.x = current_videomode.width/2; + center.y = current_videomode.height/2; + XWarpPointer(x11_display, None, x11_window, + 0,0,0,0, (int)center.x, (int)center.y); + } + +} + +OS::MouseMode OS_X11::get_mouse_mode() const { + + return mouse_mode; +} + + + +int OS_X11::get_mouse_button_state() const { + + return last_button_state; +} + +Point2 OS_X11::get_mouse_pos() const { + + return last_mouse_pos; +} + +void OS_X11::set_window_title(const String& p_title) { + + XStoreName(x11_display,x11_window,p_title.utf8().get_data()); +} + +void OS_X11::set_video_mode(const VideoMode& p_video_mode,int p_screen) { + + +} +OS::VideoMode OS_X11::get_video_mode(int p_screen) const { + + return current_videomode; +} +void OS_X11::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const { + + +} + + +InputModifierState OS_X11::get_key_modifier_state(unsigned int p_x11_state) { + + InputModifierState state; + + state.shift = (p_x11_state&ShiftMask); + state.control = (p_x11_state&ControlMask); + state.alt = (p_x11_state&Mod1Mask /*|| p_x11_state&Mod5Mask*/); //altgr should not count as alt + state.meta = (p_x11_state&Mod4Mask); + + return state; +} + +unsigned int OS_X11::get_mouse_button_state(unsigned int p_x11_state) { + + unsigned int state=0; + + if (p_x11_state&Button1Mask) { + + state|=1<<0; + } + + if (p_x11_state&Button3Mask) { + + state|=1<<1; + } + + if (p_x11_state&Button2Mask) { + + state|=1<<2; + } + + if (p_x11_state&Button4Mask) { + + state|=1<<3; + } + + if (p_x11_state&Button5Mask) { + + state|=1<<4; + } + + last_button_state=state; + return state; +} + +void OS_X11::handle_key_event(XKeyEvent *p_event) { + + + // X11 functions don't know what const is + XKeyEvent *xkeyevent = p_event; + + // This code was pretty difficult to write. + // The docs stink and every toolkit seems to + // do it in a different way. + + /* Phase 1, obtain a proper keysym */ + + // This was also very difficult to figure out. + // You'd expect you could just use Keysym provided by + // XKeycodeToKeysym to obtain internationalized + // input.. WRONG!! + // you must use XLookupString (???) which not only wastes + // cycles generating an unnecesary string, but also + // still works in half the cases. (won't handle deadkeys) + // For more complex input methods (deadkeys and more advanced) + // you have to use XmbLookupString (??). + // So.. then you have to chosse which of both results + // you want to keep. + // This is a real bizarreness and cpu waster. + + KeySym keysym_keycode=0; // keysym used to find a keycode + KeySym keysym_unicode=0; // keysym used to find unicode + + int nbytes=0; // bytes the string takes + + // XLookupString returns keysyms usable as nice scancodes/ + char str[256+1]; + nbytes=XLookupString(xkeyevent, str, 256, &keysym_keycode, NULL); + + // Meanwhile, XLookupString returns keysyms useful for unicode. + + + if (!xmbstring) { + // keep a temporary buffer for the string + xmbstring=(char*)memalloc(sizeof(char)*8); + xmblen=8; + } + + if (xkeyevent->type == KeyPress && xic) { + + Status status; + do { + + int mnbytes = XmbLookupString (xic, xkeyevent, xmbstring, xmblen - 1, &keysym_unicode, &status); + xmbstring[mnbytes] = '\0'; + + if (status == XBufferOverflow) { + xmblen = mnbytes + 1; + xmbstring = (char*)memrealloc (xmbstring, xmblen); + } + } while (status == XBufferOverflow); + } + + + /* Phase 2, obtain a pigui keycode from the keysym */ + + // KeyMappingX11 just translated the X11 keysym to a PIGUI + // keysym, so it works in all platforms the same. + + unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode); + + /* Phase 3, obtain an unicode character from the keysym */ + + // KeyMappingX11 also translates keysym to unicode. + // It does a binary search on a table to translate + // most properly. + //print_line("keysym_unicode: "+rtos(keysym_unicode)); + unsigned int unicode = keysym_unicode>0? KeyMappingX11::get_unicode_from_keysym(keysym_unicode):0; + + + /* Phase 4, determine if event must be filtered */ + + // This seems to be a side-effect of using XIM. + // XEventFilter looks like a core X11 funciton, + // but it's actually just used to see if we must + // ignore a deadkey, or events XIM determines + // must not reach the actual gui. + // Guess it was a design problem of the extension + + bool keypress = xkeyevent->type == KeyPress; + + if (xkeyevent->type == KeyPress && xic) { + if (XFilterEvent((XEvent*)xkeyevent, x11_window)) + return; + } + + if (keycode==0 && unicode==0) + return; + + /* Phase 5, determine modifier mask */ + + // No problems here, except I had no way to + // know Mod1 was ALT and Mod4 was META (applekey/winkey) + // just tried Mods until i found them. + + //print_line("mod1: "+itos(xkeyevent->state&Mod1Mask)+" mod 5: "+itos(xkeyevent->state&Mod5Mask)); + + InputModifierState state = get_key_modifier_state(xkeyevent->state); + + /* Phase 6, determine echo character */ + + // Echo characters in X11 are a keyrelease and a keypress + // one after the other with the (almot) same timestamp. + // To detect them, i use XPeekEvent and check that their + // difference in time is below a treshold. + + bool echo=false; + + if (xkeyevent->type == KeyPress) { + + // saved the time of the last keyrelease to see + // if it's the same as this keypress. + if (xkeyevent->time==last_keyrelease_time) + echo=true; + + } else { + + // make sure there are events pending, + // so this call won't block. + if (XPending(x11_display)>0) { + XEvent peek_event; + XPeekEvent(x11_display, &peek_event); + + // I'm using a treshold of 5 msecs, + // since sometimes there seems to be a little + // jitter. I'm still not convinced that all this approach + // is correct, but the xorg developers are + // not very helpful today. + + ::Time tresh=ABS(peek_event.xkey.time-xkeyevent->time); + if (peek_event.type == KeyPress && tresh<5 ) + echo=true; + + // use the time from peek_event so it always works + last_keyrelease_time=peek_event.xkey.time; + } else { + last_keyrelease_time=xkeyevent->time; + } + + // save the time to check for echo when keypress happens + + } + + + /* Phase 7, send event to Window */ + + InputEvent event; + event.ID=++event_id; + event.type = InputEvent::KEY; + event.device=0; + event.key.mod=state; + event.key.pressed=keypress; + + if (keycode>='a' && keycode<='z') + keycode-='a'-'A'; + + event.key.scancode=keycode; + event.key.unicode=unicode; + event.key.echo=echo; + + if (event.key.scancode==KEY_BACKTAB) { + //make it consistent accross platforms. + event.key.scancode=KEY_TAB; + event.key.mod.shift=true; + } + + //printf("key: %x\n",event.key.scancode); + input->parse_input_event( event); + + +} + +void OS_X11::process_xevents() { + + //printf("checking events %i\n", XPending(x11_display)); + + bool do_mouse_warp=false; + + while (XPending(x11_display) > 0) { + XEvent event; + XNextEvent(x11_display, &event); + + switch (event.type) { + case Expose: + Main::force_redraw(); + break; + + case NoExpose: + minimized = true; + break; + + case VisibilityNotify: { + + XVisibilityEvent * visibility = (XVisibilityEvent *)&event; + minimized = (visibility->state == VisibilityFullyObscured); + + } break; + + case FocusIn: + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); + if (mouse_mode==MOUSE_MODE_CAPTURED) { + XGrabPointer(x11_display, x11_window, True, + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask, GrabModeAsync, GrabModeAsync, + x11_window, None, CurrentTime); + } + break; + + case FocusOut: + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); + if (mouse_mode==MOUSE_MODE_CAPTURED) { + //dear X11, I try, I really try, but you never work, you do whathever you want. + XUngrabPointer(x11_display, CurrentTime); + } + break; + + case ConfigureNotify: + /* call resizeGLScene only if our window-size changed */ + + if ((event.xconfigure.width == current_videomode.width) && + (event.xconfigure.height == current_videomode.height)) + break; + + current_videomode.width=event.xconfigure.width; + current_videomode.height=event.xconfigure.height; + break; + case ButtonPress: + case ButtonRelease: { + + /* exit in case of a mouse button press */ + last_timestamp=event.xbutton.time; + if (mouse_mode==MOUSE_MODE_CAPTURED) { + event.xbutton.x=last_mouse_pos.x; + event.xbutton.y=last_mouse_pos.y; + } + + InputEvent mouse_event; + mouse_event.ID=++event_id; + mouse_event.type = InputEvent::MOUSE_BUTTON; + mouse_event.device=0; + mouse_event.mouse_button.mod = get_key_modifier_state(event.xbutton.state); + mouse_event.mouse_button.button_mask = get_mouse_button_state(event.xbutton.state); + mouse_event.mouse_button.x=event.xbutton.x; + mouse_event.mouse_button.y=event.xbutton.y; + mouse_event.mouse_button.global_x=event.xbutton.x; + mouse_event.mouse_button.global_y=event.xbutton.y; + mouse_event.mouse_button.button_index=event.xbutton.button; + if (mouse_event.mouse_button.button_index==2) + mouse_event.mouse_button.button_index=3; + else if (mouse_event.mouse_button.button_index==3) + mouse_event.mouse_button.button_index=2; + + mouse_event.mouse_button.pressed=(event.type==ButtonPress); + + + if (event.type==ButtonPress && event.xbutton.button==1) { + + uint64_t diff = get_ticks_usec()/1000 - last_click_ms; + + if (diff<400 && Point2(last_click_pos).distance_to(Point2(event.xbutton.x,event.xbutton.y))<5) { + + last_click_ms=0; + last_click_pos = Point2(-100,-100); + mouse_event.mouse_button.doubleclick=true; + mouse_event.ID=++event_id; + + } else { + last_click_ms+=diff; + last_click_pos = Point2(event.xbutton.x,event.xbutton.y); + } + } + + input->parse_input_event( mouse_event); + + + } break; + case MotionNotify: { + + + last_timestamp=event.xmotion.time; + + // Motion is also simple. + // A little hack is in order + // to be able to send relative motion events. + + Point2i pos( event.xmotion.x, event.xmotion.y ); + + if (mouse_mode==MOUSE_MODE_CAPTURED) { +#if 1 + Vector2 c = Point2i(current_videomode.width/2,current_videomode.height/2); + if (pos==Point2i(current_videomode.width/2,current_videomode.height/2)) { + //this sucks, it's a hack, etc and is a little inaccurate, etc. + //but nothing I can do, X11 sucks. + + center=pos; + break; + } + + Point2i ncenter = pos; + pos = last_mouse_pos + ( pos-center ); + center=ncenter; + do_mouse_warp=true; +#else + //Dear X11, thanks for making my life miserable + + center.x = current_videomode.width/2; + center.y = current_videomode.height/2; + pos = last_mouse_pos + ( pos-center ); + if (pos==last_mouse_pos) + break; + XWarpPointer(x11_display, None, x11_window, + 0,0,0,0, (int)center.x, (int)center.y); +#endif + + } + + + if (!last_mouse_pos_valid) { + + last_mouse_pos=pos; + last_mouse_pos_valid=true; + } + + Point2i rel = pos - last_mouse_pos; + + InputEvent motion_event; + motion_event.ID=++event_id; + motion_event.type=InputEvent::MOUSE_MOTION; + motion_event.device=0; + + motion_event.mouse_motion.mod = get_key_modifier_state(event.xmotion.state); + motion_event.mouse_motion.button_mask = get_mouse_button_state(event.xmotion.state); + motion_event.mouse_motion.x=pos.x; + motion_event.mouse_motion.y=pos.y; + input->set_mouse_pos(pos); + motion_event.mouse_motion.global_x=pos.x; + motion_event.mouse_motion.global_y=pos.y; + motion_event.mouse_motion.speed_x=input->get_mouse_speed().x; + motion_event.mouse_motion.speed_y=input->get_mouse_speed().y; + + motion_event.mouse_motion.relative_x=rel.x; + motion_event.mouse_motion.relative_y=rel.y; + + last_mouse_pos=pos; + + input->parse_input_event( motion_event); + + } break; + case KeyPress: + case KeyRelease: { + + last_timestamp=event.xkey.time; + + // key event is a little complex, so + // it will be handled in it's own function. + handle_key_event( (XKeyEvent*)&event ); + } break; + case SelectionRequest: { + + XSelectionRequestEvent *req; + XEvent e, respond; + e = event; + + req=&(e.xselectionrequest); + if (req->target == XA_STRING || req->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) || + req->target == XInternAtom(x11_display, "UTF8_STRING", 0)) + { + CharString clip = OS::get_clipboard().utf8(); + XChangeProperty (x11_display, + req->requestor, + req->property, + req->target, + 8, + PropModeReplace, + (unsigned char*)clip.get_data(), + clip.length()); + respond.xselection.property=req->property; + } else if (req->target == XInternAtom(x11_display, "TARGETS", 0)) { + + Atom data[2]; + data[0] = XInternAtom(x11_display, "UTF8_STRING", 0); + data[1] = XA_STRING; + XChangeProperty (x11_display, req->requestor, req->property, req->target, + 8, PropModeReplace, (unsigned char *) &data, + sizeof (data)); + respond.xselection.property=req->property; + + } else { + printf ("No String %x\n", + (int)req->target); + respond.xselection.property= None; + } + respond.xselection.type= SelectionNotify; + respond.xselection.display= req->display; + respond.xselection.requestor= req->requestor; + respond.xselection.selection=req->selection; + respond.xselection.target= req->target; + respond.xselection.time = req->time; + XSendEvent (x11_display, req->requestor,0,0,&respond); + XFlush (x11_display); + } break; + + + case ClientMessage: + + if ((unsigned int)event.xclient.data.l[0]==(unsigned int)wm_delete) + main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); + break; + default: + break; + } + } + + XFlush(x11_display); + + if (do_mouse_warp) { + + XWarpPointer(x11_display, None, x11_window, + 0,0,0,0, (int)current_videomode.width/2, (int)current_videomode.height/2); + + } +} + +MainLoop *OS_X11::get_main_loop() const { + + return main_loop; +} + +void OS_X11::delete_main_loop() { + + if (main_loop) + memdelete(main_loop); + main_loop=NULL; +} + +void OS_X11::set_main_loop( MainLoop * p_main_loop ) { + + main_loop=p_main_loop; + input->set_main_loop(p_main_loop); +} + +bool OS_X11::can_draw() const { + + return !minimized; +}; + +void OS_X11::set_clipboard(const String& p_text) { + + OS::set_clipboard(p_text); + + XSetSelectionOwner(x11_display, XA_PRIMARY, x11_window, CurrentTime); + XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, CurrentTime); +}; + +static String _get_clipboard(Atom p_source, Window x11_window, ::Display* x11_display, String p_internal_clipboard) { + + String ret; + + Atom type; + Atom selection = XA_PRIMARY; + int format, result; + unsigned long len, bytes_left, dummy; + unsigned char *data; + Window Sown = XGetSelectionOwner (x11_display, p_source); + + if (Sown == x11_window) { + + printf("returning internal clipboard\n"); + return p_internal_clipboard; + }; + + if (Sown != None) { + XConvertSelection (x11_display, p_source, XA_STRING, selection, + x11_window, CurrentTime); + XFlush (x11_display); + while (true) { + XEvent event; + XNextEvent(x11_display, &event); + if (event.type == SelectionNotify && event.xselection.requestor == x11_window) { + break; + }; + }; + + // + // Do not get any data, see how much data is there + // + XGetWindowProperty (x11_display, x11_window, + selection, // Tricky.. + 0, 0, // offset - len + 0, // Delete 0==FALSE + AnyPropertyType, //flag + &type, // return type + &format, // return format + &len, &bytes_left, //that + &data); + // DATA is There + if (bytes_left > 0) + { + result = XGetWindowProperty (x11_display, x11_window, + selection, 0,bytes_left,0, + AnyPropertyType, &type,&format, + &len, &dummy, &data); + if (result == Success) { + ret.parse_utf8((const char*)data); + } else printf ("FAIL\n"); + XFree (data); + } + } + + return ret; + +}; + +String OS_X11::get_clipboard() const { + + String ret; + ret = _get_clipboard(XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, x11_display, OS::get_clipboard()); + + if (ret == "") { + ret = _get_clipboard(XA_PRIMARY, x11_window, x11_display, OS::get_clipboard()); + }; + + return ret; +}; + +String OS_X11::get_name() { + + return "X11"; +} + + +void OS_X11::close_joystick(int p_id) { + + if (p_id == -1) { + for (int i=0; i<JOYSTICKS_MAX; i++) { + + close_joystick(i); + }; + return; + }; + + + if (joysticks[p_id].fd != -1) { + close(joysticks[p_id].fd); + joysticks[p_id].fd = -1; + }; +}; + +void OS_X11::probe_joystick(int p_id) { + + if (p_id == -1) { + + for (int i=0; i<JOYSTICKS_MAX; i++) { + + probe_joystick(i); + }; + return; + }; + + close_joystick(p_id); + + const char *joy_names[] = { + "/dev/input/js%d", + "/dev/js%d", + NULL + }; + + int i=0; + while(joy_names[i]) { + + char fname[64]; + sprintf(fname, joy_names[i], p_id); + int fd = open(fname, O_RDONLY); + if (fd != -1) { + + fcntl( fd, F_SETFL, O_NONBLOCK ); + joysticks[p_id] = Joystick(); // this will reset the axis array + joysticks[p_id].fd = fd; + break; // don't try the next name + }; + + ++i; + }; +}; + +void OS_X11::move_window_to_foreground() { + + XRaiseWindow(x11_display,x11_window); +} + +void OS_X11::process_joysticks() { + + int bytes; + js_event events[32]; + InputEvent ievent; + for (int i=0; i<JOYSTICKS_MAX; i++) { + + if (joysticks[i].fd == -1) + continue; + ievent.device = i; + + while ( (bytes = read(joysticks[i].fd, &events, sizeof(events))) > 0) { + + int ev_count = bytes / sizeof(js_event); + for (int j=0; j<ev_count; j++) { + + js_event& event = events[j]; + + //printf("got event on joystick %i, %i, %i, %i, %i\n", i, joysticks[i].fd, event.type, event.number, event.value); + if (event.type & JS_EVENT_INIT) + continue; + + switch (event.type & ~JS_EVENT_INIT) { + + case JS_EVENT_AXIS: + + if (joysticks[i].last_axis[event.number] != event.value) { + + if (event.number==5 || event.number==6) { + + int axis=event.number-5; + int val = event.value; + if (val<0) + val=-1; + if (val>0) + val=+1; + + InputEvent ev; + ev.type = InputEvent::JOYSTICK_BUTTON; + ev.ID = ++event_id; + + + if (val!=dpad_last[axis]) { + + int prev_val = dpad_last[axis]; + if (prev_val!=0) { + + ev.joy_button.pressed=false; + ev.joy_button.pressure=0.0; + if (event.number==5) + ev.joy_button.button_index=JOY_DPAD_LEFT+(prev_val+1)/2; + if (event.number==6) + ev.joy_button.button_index=JOY_DPAD_UP+(prev_val+1)/2; + + input->parse_input_event( ev ); + } + } + + if (val!=0) { + + ev.joy_button.pressed=true; + ev.joy_button.pressure=1.0; + if (event.number==5) + ev.joy_button.button_index=JOY_DPAD_LEFT+(val+1)/2; + if (event.number==6) + ev.joy_button.button_index=JOY_DPAD_UP+(val+1)/2; + + input->parse_input_event( ev ); + } + + + dpad_last[axis]=val; + + } + //print_line("ev: "+itos(event.number)+" val: "+ rtos((float)event.value / (float)MAX_JOY_AXIS)); + if (event.number >= JOY_AXIS_MAX) + break; + //ERR_FAIL_COND(event.number >= JOY_AXIS_MAX); + ievent.type = InputEvent::JOYSTICK_MOTION; + ievent.ID = ++event_id; + ievent.joy_motion.axis = _pc_joystick_get_native_axis(event.number); + ievent.joy_motion.axis_value = (float)event.value / (float)MAX_JOY_AXIS; + joysticks[i].last_axis[event.number] = event.value; + input->parse_input_event( ievent ); + }; + break; + + case JS_EVENT_BUTTON: + + + ievent.type = InputEvent::JOYSTICK_BUTTON; + ievent.ID = ++event_id; + ievent.joy_button.button_index = _pc_joystick_get_native_button(event.number); + ievent.joy_button.pressed = event.value; + input->parse_input_event( ievent ); + break; + }; + }; + }; + }; +}; + +void OS_X11::set_cursor_shape(CursorShape p_shape) { + + ERR_FAIL_INDEX(p_shape,CURSOR_MAX); + + if (p_shape==current_cursor) + return; + if (mouse_mode==MOUSE_MODE_VISIBLE) { + if (cursors[p_shape]!=None) + XDefineCursor(x11_display,x11_window,cursors[p_shape]); + else if (cursors[CURSOR_ARROW]!=None) + XDefineCursor(x11_display,x11_window,cursors[CURSOR_ARROW]); + } + + + current_cursor=p_shape; +} + + +void OS_X11::release_rendering_thread() { + + context_gl->release_current(); + +} + +void OS_X11::make_rendering_thread() { + + context_gl->make_current(); +} + +void OS_X11::swap_buffers() { + + context_gl->swap_buffers(); +} + + +void OS_X11::set_icon(const Image& p_icon) { + + //does not work, if anyone knows why, please fix + if (!p_icon.empty()) { + + Image img=p_icon; + img.convert(Image::FORMAT_RGBA); + + + int w = img.get_width(); + int h = img.get_height(); + + Vector<long> pd; + + pd.resize((2+w*h)*sizeof(long)); + + print_line("***** SET ICON ***** "+itos(w)+" "+itos(h)); + + pd[0]=w; + pd[1]=h; + + DVector<uint8_t>::Read r = img.get_data().read(); + + uint32_t *wr=(uint32_t*)&pd[2]; + + for(int i=0;i<w*h;i++) { + + uint32_t v=0; + v|=r[i*4+3]; + v<<=8; + v|=r[i*4+0]; + v<<=8; + v|=r[i*4+1]; + v<<=8; + v|=r[i*4+2]; + wr[i]=v; + } + + XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) pd.ptr(), + (2+w*h)); + } else { + XDeleteProperty(x11_display, x11_window, net_wm_icon); + } + XFlush(x11_display); + +} + + +void OS_X11::run() { + + force_quit = false; + + if (!main_loop) + return; + + main_loop->init(); + +// uint64_t last_ticks=get_ticks_usec(); + +// int frames=0; +// uint64_t frame=0; + + while (!force_quit) { + + process_xevents(); // get rid of pending events + process_joysticks(); + if (Main::iteration()==true) + break; + }; + + main_loop->finish(); +} + +OS_X11::OS_X11() { + +#ifdef RTAUDIO_ENABLED + AudioDriverManagerSW::add_driver(&driver_rtaudio); +#endif + +#ifdef ALSA_ENABLED + AudioDriverManagerSW::add_driver(&driver_alsa); +#endif + + minimized = false; + xim_style=NULL; + mouse_mode=MOUSE_MODE_VISIBLE; + + +}; diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h new file mode 100644 index 0000000000..ee50bdea4a --- /dev/null +++ b/platform/x11/os_x11.h @@ -0,0 +1,203 @@ +/*************************************************************************/ +/* os_x11.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 OS_X11_H +#define OS_X11_H + + +#include "os/input.h" +#include "drivers/unix/os_unix.h" +#include "context_gl_x11.h" +#include "servers/visual_server.h" +#include "servers/visual/visual_server_wrap_mt.h" +#include "servers/visual/rasterizer.h" +#include "servers/physics_server.h" +#include "servers/audio/audio_server_sw.h" +#include "servers/audio/sample_manager_sw.h" +#include "servers/spatial_sound/spatial_sound_server_sw.h" +#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" +#include "drivers/rtaudio/audio_driver_rtaudio.h" +#include "drivers/alsa/audio_driver_alsa.h" +#include "servers/physics_2d/physics_2d_server_sw.h" + +#include <X11/keysym.h> +#include <X11/Xlib.h> +#include <X11/Xcursor/Xcursor.h> + +//bitch +#undef CursorShape +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class OS_X11 : public OS_Unix { + + Atom wm_delete; +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) + ContextGL_X11 *context_gl; +#endif + Rasterizer *rasterizer; + VisualServer *visual_server; + VideoMode current_videomode; + List<String> args; + Window x11_window; + MainLoop *main_loop; + ::Display* x11_display; + char *xmbstring; + int xmblen; + unsigned long last_timestamp; + ::Time last_keyrelease_time; + ::XIC xic; + ::XIM xim; + ::XIMStyle xim_style; + Point2i last_mouse_pos; + bool last_mouse_pos_valid; + Point2i last_click_pos; + uint64_t last_click_ms; + unsigned int event_id; + uint32_t last_button_state; + + PhysicsServer *physics_server; + unsigned int get_mouse_button_state(unsigned int p_x11_state); + InputModifierState get_key_modifier_state(unsigned int p_x11_state); + Physics2DServer *physics_2d_server; + + MouseMode mouse_mode; + Point2i center; + + void handle_key_event(XKeyEvent *p_event); + void process_xevents(); + virtual void delete_main_loop(); + IP_Unix *ip_unix; + + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSoundServerSW *spatial_sound_server; + SpatialSound2DServerSW *spatial_sound_2d_server; + + bool force_quit; + bool minimized; + int dpad_last[2]; + + + const char *cursor_theme; + int cursor_size; + Cursor cursors[CURSOR_MAX]; + Cursor null_cursor; + CursorShape current_cursor; + + InputDefault *input; + +#ifdef RTAUDIO_ENABLED + AudioDriverRtAudio driver_rtaudio; +#endif + +#ifdef ALSA_ENABLED + AudioDriverALSA driver_alsa; +#endif + + enum { + JOYSTICKS_MAX = 8, + MAX_JOY_AXIS = 32768, // I've no idea + }; + + struct Joystick { + + int fd; + int last_axis[JOY_AXIS_MAX]; + + Joystick() { + fd = -1; + for (int i=0; i<JOY_AXIS_MAX; i++) { + + last_axis[i] = 0; + }; + }; + }; + + Atom net_wm_icon; + + + int joystick_count; + Joystick joysticks[JOYSTICKS_MAX]; + + +protected: + + 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 void initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver); + virtual void finalize(); + + virtual void set_main_loop( MainLoop * p_main_loop ); + + void probe_joystick(int p_id = -1); + void process_joysticks(); + void close_joystick(int p_id = -1); + +public: + + virtual String get_name(); + + virtual void set_cursor_shape(CursorShape p_shape); + + void set_mouse_mode(MouseMode p_mode); + MouseMode get_mouse_mode() 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_icon(const Image& p_icon); + + virtual MainLoop *get_main_loop() const; + + virtual bool can_draw() const; + + virtual void set_clipboard(const String& p_text); + virtual String get_clipboard() const; + + virtual void release_rendering_thread(); + virtual void make_rendering_thread(); + virtual void swap_buffers(); + + + 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 void move_window_to_foreground(); + + void run(); + + OS_X11(); +}; + +#endif diff --git a/platform/x11/platform_config.h b/platform/x11/platform_config.h new file mode 100644 index 0000000000..d14f3e3f9a --- /dev/null +++ b/platform/x11/platform_config.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* platform_config.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. */ +/*************************************************************************/ +#include <alloca.h> +#define GLES2_INCLUDE_H "gl_context/glew.h" +#define GLES1_INCLUDE_H "gl_context/glew.h" + |