no known regressions yet.legacy
| @@ -1,19 +1,22 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| package="net.lolengine" | |||||
| package="net.lolengine.lol_@PROGRAM@" | |||||
| android:versionCode="1" | android:versionCode="1" | ||||
| android:versionName="1.0"> | android:versionName="1.0"> | ||||
| <application android:label="@string/app_name" | <application android:label="@string/app_name" | ||||
| android:icon="@drawable/icon"> | android:icon="@drawable/icon"> | ||||
| <activity android:name=".LolActivity" | |||||
| <activity android:name="net.lolengine.LolActivity" | |||||
| android:theme="@android:style/Theme.NoTitleBar.Fullscreen" | android:theme="@android:style/Theme.NoTitleBar.Fullscreen" | ||||
| android:screenOrientation="landscape" | |||||
| android:label="@string/app_name" | android:label="@string/app_name" | ||||
| android:launchMode="singleTask"> | android:launchMode="singleTask"> | ||||
| <meta-data android:name="android.app.lib_name" | |||||
| android:value="@PROGRAM@" /> | |||||
| <intent-filter> | <intent-filter> | ||||
| <action android:name="android.intent.action.MAIN" /> | <action android:name="android.intent.action.MAIN" /> | ||||
| <category android:name="android.intent.category.LAUNCHER" /> | <category android:name="android.intent.category.LAUNCHER" /> | ||||
| </intent-filter> | </intent-filter> | ||||
| </activity> | </activity> | ||||
| </application> | </application> | ||||
| <uses-sdk android:minSdkVersion="8" /> | |||||
| <uses-sdk android:minSdkVersion="9" /> | |||||
| </manifest> | </manifest> | ||||
| @@ -10,65 +10,32 @@ | |||||
| package net.lolengine; | package net.lolengine; | ||||
| import android.app.Activity; | |||||
| import android.content.Context; | |||||
| import android.content.pm.ActivityInfo; | |||||
| import android.app.NativeActivity; | |||||
| import android.content.res.AssetManager; /* getAssets() */ | import android.content.res.AssetManager; /* getAssets() */ | ||||
| import android.content.res.Resources; /* getResources() */ | |||||
| import android.graphics.Bitmap; | import android.graphics.Bitmap; | ||||
| import android.graphics.BitmapFactory; | import android.graphics.BitmapFactory; | ||||
| import android.opengl.GLSurfaceView; | |||||
| import android.os.Bundle; | |||||
| import android.util.Log; | |||||
| import android.view.MotionEvent; | |||||
| import android.view.WindowManager; | |||||
| import android.view.Window; | |||||
| 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; | |||||
| /* FIXME: this needs to have a different name for each project */ | |||||
| public class LolActivity extends Activity | |||||
| public class LolActivity extends NativeActivity | |||||
| { | { | ||||
| @Override protected void onCreate(Bundle savedInstanceState) | |||||
| { | |||||
| super.onCreate(savedInstanceState); | |||||
| getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, | |||||
| WindowManager.LayoutParams.FLAG_FULLSCREEN); | |||||
| setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); | |||||
| getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, | |||||
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); | |||||
| requestWindowFeature(Window.FEATURE_NO_TITLE); | |||||
| nativeInit(); | |||||
| mView = new LolView(getApplication()); | |||||
| setContentView(mView); | |||||
| } | |||||
| @Override protected void onPause() | |||||
| static | |||||
| { | { | ||||
| super.onPause(); | |||||
| mView.onPause(); | |||||
| /* Need to preload libstlport_shared.so somewhere; also need to | |||||
| * preload lib@PROGRAM@.so otherwise nativeInit() can’t be found. */ | |||||
| System.loadLibrary("stlport_shared"); | |||||
| System.loadLibrary("@PROGRAM@"); | |||||
| } | } | ||||
| @Override protected void onResume() | |||||
| public LolActivity() | |||||
| { | { | ||||
| super.onResume(); | |||||
| mView.onResume(); | |||||
| nativeInit(); | |||||
| } | } | ||||
| private LolView mView; | |||||
| private native void nativeInit(); | |||||
| static | |||||
| { | |||||
| System.loadLibrary("stlport_shared"); | |||||
| System.loadLibrary("@PROGRAM@"); | |||||
| } | |||||
| /* | |||||
| * Bitmap loading helpers | |||||
| */ | |||||
| public Bitmap openImage(String name) | public Bitmap openImage(String name) | ||||
| { | { | ||||
| @@ -94,154 +61,5 @@ public class LolActivity extends Activity | |||||
| { | { | ||||
| bmp.recycle(); | bmp.recycle(); | ||||
| } | } | ||||
| private native void nativeInit(); | |||||
| } | |||||
| class LolView extends GLSurfaceView | |||||
| { | |||||
| public LolView(Context context) | |||||
| { | |||||
| super(context); | |||||
| setEGLContextFactory(new ContextFactory()); | |||||
| //setEGLConfigChooser(new ConfigChooser(4, 4, 4, 0, 8, 0)); | |||||
| setEGLConfigChooser(new ConfigChooser(4, 4, 4, 4, 8, 0)); | |||||
| setRenderer(new LolRenderer()); | |||||
| } | |||||
| private static class ContextFactory implements GLSurfaceView.EGLContextFactory | |||||
| { | |||||
| private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; | |||||
| public EGLContext createContext(EGL10 egl, EGLDisplay dpy, EGLConfig cfg) | |||||
| { | |||||
| Log.w("LOL", "creating OpenGL ES 2.0 context"); | |||||
| int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; | |||||
| EGLContext ctx = egl.eglCreateContext(dpy, cfg, EGL10.EGL_NO_CONTEXT, attrib_list); | |||||
| return ctx; | |||||
| } | |||||
| public void destroyContext(EGL10 egl, EGLDisplay dpy, EGLContext ctx) | |||||
| { | |||||
| egl.eglDestroyContext(dpy, ctx); | |||||
| } | |||||
| } | |||||
| private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser | |||||
| { | |||||
| public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) | |||||
| { | |||||
| mRed = r; | |||||
| mGreen = g; | |||||
| mBlue = b; | |||||
| mAlpha = a; | |||||
| mDepth = depth; | |||||
| mStencil = stencil; | |||||
| } | |||||
| private static int EGL_OPENGL_ES2_BIT = 4; | |||||
| private static int[] s_configAttribs2 = | |||||
| { | |||||
| // EGL10.EGL_BUFFER_SIZE, 16, | |||||
| EGL10.EGL_DEPTH_SIZE, 8, | |||||
| EGL10.EGL_RED_SIZE, 4, | |||||
| EGL10.EGL_GREEN_SIZE, 4, | |||||
| EGL10.EGL_BLUE_SIZE, 4, | |||||
| // EGL10.EGL_ALPHA_SIZE, 4, | |||||
| EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, | |||||
| EGL10.EGL_NONE | |||||
| }; | |||||
| public EGLConfig chooseConfig(EGL10 egl, EGLDisplay dpy) | |||||
| { | |||||
| int[] num_config = new int[1]; | |||||
| egl.eglChooseConfig(dpy, s_configAttribs2, null, 0, num_config); | |||||
| int n = num_config[0]; | |||||
| if (n <= 0) | |||||
| throw new IllegalArgumentException("No GLES configs"); | |||||
| EGLConfig[] configs = new EGLConfig[n]; | |||||
| egl.eglChooseConfig(dpy, s_configAttribs2, configs, n, num_config); | |||||
| return choose(egl, dpy, configs); | |||||
| } | |||||
| public EGLConfig choose(EGL10 egl, EGLDisplay dpy, EGLConfig[] configs) | |||||
| { | |||||
| for(EGLConfig cfg : configs) | |||||
| { | |||||
| /* Do not complain if we get more bits than we asked. */ | |||||
| if (get(egl, dpy, cfg, EGL10.EGL_STENCIL_SIZE, 0) >= mStencil | |||||
| && get(egl, dpy, cfg, EGL10.EGL_DEPTH_SIZE, 0) >= mDepth | |||||
| && get(egl, dpy, cfg, EGL10.EGL_RED_SIZE, 0) >= mRed | |||||
| && get(egl, dpy, cfg, EGL10.EGL_GREEN_SIZE, 0) >= mGreen | |||||
| && get(egl, dpy, cfg, EGL10.EGL_BLUE_SIZE, 0) >= mBlue | |||||
| && get(egl, dpy, cfg, EGL10.EGL_ALPHA_SIZE, 0) >= mAlpha) | |||||
| return cfg; | |||||
| } | |||||
| return null; | |||||
| } | |||||
| private int get(EGL10 egl, EGLDisplay dpy, EGLConfig cfg, | |||||
| int attr, int defval) | |||||
| { | |||||
| int[] value = new int[1]; | |||||
| if (egl.eglGetConfigAttrib(dpy, cfg, attr, value)) | |||||
| return value[0]; | |||||
| return defval; | |||||
| } | |||||
| protected int mRed, mGreen, mBlue, mAlpha, mDepth, mStencil; | |||||
| } | |||||
| public boolean onTouchEvent(final MotionEvent ev) | |||||
| { | |||||
| final int action = ev.getAction(); | |||||
| switch (action) | |||||
| { | |||||
| case MotionEvent.ACTION_DOWN: | |||||
| nativeMove((int)ev.getX(), (int)ev.getY()); | |||||
| nativeDown(); | |||||
| break; | |||||
| case MotionEvent.ACTION_UP: | |||||
| nativeMove((int)ev.getX(), (int)ev.getY()); | |||||
| nativeUp(); | |||||
| break; | |||||
| case MotionEvent.ACTION_MOVE: | |||||
| nativeMove((int)ev.getX(), (int)ev.getY()); | |||||
| break; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| private static native void nativePause(); | |||||
| private static native void nativeDown(); | |||||
| private static native void nativeUp(); | |||||
| private static native void nativeMove(int x, int y); | |||||
| } | |||||
| class LolRenderer implements GLSurfaceView.Renderer | |||||
| { | |||||
| public void onSurfaceCreated(GL10 gl, EGLConfig config) | |||||
| { | |||||
| nativeInit(); | |||||
| } | |||||
| public void onSurfaceChanged(GL10 gl, int w, int h) | |||||
| { | |||||
| Log.w("LOL", String.format("resizing to %dx%d", w, h)); | |||||
| nativeResize(w, h); | |||||
| } | |||||
| public void onDrawFrame(GL10 gl) | |||||
| { | |||||
| nativeRender(); | |||||
| } | |||||
| private static native void nativeInit(); | |||||
| private static native void nativeResize(int w, int h); | |||||
| private static native void nativeRender(); | |||||
| private static native void nativeDone(); | |||||
| } | } | ||||
| @@ -116,17 +116,17 @@ all-local-android: $(foreach p, $(PROGRAMS:%$(EXEEXT)=%), .$(p).androidstamp) | |||||
| rm -rf "$(d)" | rm -rf "$(d)" | ||||
| $(MKDIR_P) "$(d)" $(sort $(foreach f, $($(p)_DATA), "$(d)/assets/$(dir $(f))")) | $(MKDIR_P) "$(d)" $(sort $(foreach f, $($(p)_DATA), "$(d)/assets/$(dir $(f))")) | ||||
| $(foreach f, $($(p)_DATA), $(LN_S) "$(abs_srcdir)/$(f)" "$(d)/assets/$(f)" &&) true | $(foreach f, $($(p)_DATA), $(LN_S) "$(abs_srcdir)/$(f)" "$(d)/assets/$(f)" &&) true | ||||
| $(SED) -e 's,@PROGRAM@,$(p),' "$(top_srcdir)/build/android/AndroidManifest.xml" > "$(d)/AndroidManifest.xml" | |||||
| $(SED) -e 's,@PROGRAM@,$(subst -,_,$(p)),' "$(top_srcdir)/build/android/AndroidManifest.xml" > "$(d)/AndroidManifest.xml" | |||||
| $(MKDIR_P) "$(d)/src/net/lolengine" | $(MKDIR_P) "$(d)/src/net/lolengine" | ||||
| $(SED) -e 's,@PROGRAM@,$(p),' "$(top_srcdir)/build/android/LolActivity.java" > "$(d)/src/net/lolengine/LolActivity.java" | |||||
| $(SED) -e 's,@PROGRAM@,$(subst -,_,$(p)),' "$(top_srcdir)/build/android/LolActivity.java" > "$(d)/src/net/lolengine/LolActivity.java" | |||||
| $(MKDIR_P) "$(d)/res/values" | $(MKDIR_P) "$(d)/res/values" | ||||
| $(SED) -e 's,@PROGRAM@,$(p),' "$(top_srcdir)/build/android/strings.xml" > "$(d)/res/values/strings.xml" | |||||
| $(SED) -e 's,@PROGRAM@,$(subst -,_,$(p)),' "$(top_srcdir)/build/android/strings.xml" > "$(d)/res/values/strings.xml" | |||||
| $(MKDIR_P) "$(d)/res/drawable" | $(MKDIR_P) "$(d)/res/drawable" | ||||
| cp "$(top_srcdir)/build/android/icon.png" "$(d)/res/drawable/" | cp "$(top_srcdir)/build/android/icon.png" "$(d)/res/drawable/" | ||||
| $(MKDIR_P) "$(d)/libs/armeabi" | $(MKDIR_P) "$(d)/libs/armeabi" | ||||
| $(LN_S) "$(abs_builddir)/$(p)$(EXEEXT)" "$(d)/libs/armeabi/lib$(p).so" | $(LN_S) "$(abs_builddir)/$(p)$(EXEEXT)" "$(d)/libs/armeabi/lib$(p).so" | ||||
| $(LN_S) "$${ANDROID_NDK_ROOT}/sources/cxx-stl/stlport/libs/armeabi/libstlport_shared.so" "$(d)/libs/armeabi" | $(LN_S) "$${ANDROID_NDK_ROOT}/sources/cxx-stl/stlport/libs/armeabi/libstlport_shared.so" "$(d)/libs/armeabi" | ||||
| android update project -t android-8 -p "$(d)" | |||||
| android update project -t android-9 -n "$(p)" -p "$(d)" | |||||
| ant debug -f "$(d)/build.xml" | ant debug -f "$(d)/build.xml" | ||||
| touch $@ | touch $@ | ||||
| else | else | ||||
| @@ -183,10 +183,10 @@ configure() | |||||
| ./configure --host=armv7-apple-darwin10 CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS" CC="$CC" CXX="$CXX" | ./configure --host=armv7-apple-darwin10 CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS" CC="$CC" CXX="$CXX" | ||||
| ;; | ;; | ||||
| android-arm) | android-arm) | ||||
| CPPFLAGS="$CPPFLAGS -Wno-psabi -I$ANDROID_NDK_ROOT/sources/cxx-stl/stlport/stlport -fpic -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64" | |||||
| CPPFLAGS="$CPPFLAGS -Wno-psabi -I$ANDROID_NDK_ROOT/sources/cxx-stl/stlport/stlport -I$ANDROID_NDK_ROOT/sources/android/native_app_glue -fpic -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64" | |||||
| CFLAGS="$CFLAGS -march=armv5te -mtune=xscale -msoft-float -mthumb" | CFLAGS="$CFLAGS -march=armv5te -mtune=xscale -msoft-float -mthumb" | ||||
| CXXFLAGS="$CXXFLAGS -march=armv5te -mtune=xscale -msoft-float -mthumb -fno-rtti -fno-exceptions" | CXXFLAGS="$CXXFLAGS -march=armv5te -mtune=xscale -msoft-float -mthumb -fno-rtti -fno-exceptions" | ||||
| LOL_LIBS="$LOL_LIBS -L$ANDROID_NDK_ROOT/sources/cxx-stl/stlport/libs/armeabi -lstlport_shared -lm -fpic -XCClinker -shared" | |||||
| LOL_LIBS="$LOL_LIBS -L$ANDROID_NDK_ROOT/sources/cxx-stl/stlport/libs/armeabi -lstlport_shared -lm -fpic -XCClinker -shared -u ANativeActivity_onCreate" | |||||
| PKG_CONFIG_PATH="$PKG_CONFIG_PATH" ./configure --host=arm-linux-androideabi ac_cv_exeext=.so CPPFLAGS="$CPPFLAGS" CFLAGS="$CFLAGS" CXXFLAGS="$CXXFLAGS" LDFLAGS="$LDFLAGS" LOL_LIBS="$LOL_LIBS" | PKG_CONFIG_PATH="$PKG_CONFIG_PATH" ./configure --host=arm-linux-androideabi ac_cv_exeext=.so CPPFLAGS="$CPPFLAGS" CFLAGS="$CFLAGS" CXXFLAGS="$CXXFLAGS" LDFLAGS="$LDFLAGS" LOL_LIBS="$LOL_LIBS" | ||||
| # FIXME: is this needed? | # FIXME: is this needed? | ||||
| # ant debug | # ant debug | ||||
| @@ -312,7 +312,7 @@ dnl Use Android? FIXME: super hacks! | |||||
| ac_cv_my_have_android="no" | ac_cv_my_have_android="no" | ||||
| AC_CHECK_LIB(log, __android_log_vprint, | AC_CHECK_LIB(log, __android_log_vprint, | ||||
| [ac_cv_my_have_android="yes" | [ac_cv_my_have_android="yes" | ||||
| LOL_LIBS="${LOL_LIBS} -llog -module"]) | |||||
| LOL_LIBS="${LOL_LIBS} -llog -landroid -module -lEGL -lGLESv2"]) | |||||
| AM_CONDITIONAL(USE_ANDROID, test "${ac_cv_my_have_android}" != "no") | AM_CONDITIONAL(USE_ANDROID, test "${ac_cv_my_have_android}" != "no") | ||||
| @@ -1,7 +1,7 @@ | |||||
| // | // | ||||
| // Lol Engine | // Lol Engine | ||||
| // | // | ||||
| // Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net> | |||||
| // Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net> | |||||
| // This program is free software; you can redistribute it and/or | // This program is free software; you can redistribute it and/or | ||||
| // modify it under the terms of the Do What The Fuck You Want To | // modify it under the terms of the Do What The Fuck You Want To | ||||
| // Public License, Version 2, as published by Sam Hocevar. See | // Public License, Version 2, as published by Sam Hocevar. See | ||||
| @@ -15,166 +15,314 @@ | |||||
| #if defined __ANDROID__ | #if defined __ANDROID__ | ||||
| #include <jni.h> | #include <jni.h> | ||||
| #include <android/log.h> | |||||
| #include <EGL/egl.h> | |||||
| #include <GLES/gl.h> | |||||
| extern "C" { | |||||
| #include <android_native_app_glue.h> | |||||
| #include <android_native_app_glue.c> | |||||
| } | |||||
| #include "core.h" | #include "core.h" | ||||
| #include "loldebug.h" | |||||
| #include "androidapp.h" | #include "androidapp.h" | ||||
| using namespace lol; | using namespace lol; | ||||
| /* One of these wrappers will be overridden by the user's version */ | |||||
| void lol_android_main(void) __attribute__((weak)); | |||||
| void lol_android_main(int argc, char **argv) __attribute__((weak)); | |||||
| void lol_android_main(int argc, char **argv, char **envp) __attribute__((weak)); | |||||
| namespace lol | namespace lol | ||||
| { | { | ||||
| JavaVM *g_vm; | JavaVM *g_vm; | ||||
| jobject g_activity; | jobject g_activity; | ||||
| Queue<int> g_main_queue; | |||||
| Thread *g_main_thread; | |||||
| float g_fps; | |||||
| }; /* namespace lol */ | |||||
| AndroidApp::AndroidApp(char const *title, ivec2 res, float fps) | |||||
| : m_data(0) | |||||
| extern "C" jint | |||||
| JNI_OnLoad(JavaVM* vm, void* reserved) | |||||
| { | { | ||||
| g_fps = fps; | |||||
| Log::Info("Java layer loading library, vm=0x%08lx", (long)(intptr_t)vm); | |||||
| g_vm = vm; | |||||
| return JNI_VERSION_1_4; | |||||
| } | } | ||||
| void AndroidApp::ShowPointer(bool show) | |||||
| extern "C" void | |||||
| Java_net_lolengine_LolActivity_nativeInit(JNIEnv* env, jobject thiz) | |||||
| { | { | ||||
| Log::Info("Java layer initialising activity 0x%08lx", (long)thiz); | |||||
| env->NewGlobalRef(thiz); /* FIXME: never released! */ | |||||
| g_activity = thiz; | |||||
| } | } | ||||
| /* This is a fake Tick() method. We just wait until we're called and | |||||
| * signal nativeInit() that all the user's initialisation code was | |||||
| * called. Then we sit here forever, the Java layer is in charge of | |||||
| * calling TickDraw(). */ | |||||
| void AndroidApp::Tick() | |||||
| /* One of these wrappers will be overridden by the user's version */ | |||||
| void lol_android_main(void) __attribute__((weak)); | |||||
| void lol_android_main(int argc, char **argv) __attribute__((weak)); | |||||
| void lol_android_main(int argc, char **argv, char **envp) __attribute__((weak)); | |||||
| /** | |||||
| * Our saved state data. | |||||
| */ | |||||
| struct SavedState | |||||
| { | |||||
| ivec2 position; | |||||
| }; | |||||
| /** | |||||
| * Shared state for our app. | |||||
| */ | |||||
| class lol::AndroidAppData | |||||
| { | |||||
| public: | |||||
| android_app* m_native_app; | |||||
| EGLDisplay display; | |||||
| EGLSurface surface; | |||||
| EGLContext context; | |||||
| SavedState m_state; | |||||
| }; | |||||
| /** | |||||
| * Initialize an EGL context for the current display. | |||||
| */ | |||||
| static int engine_init_display(struct AndroidAppData* engine) | |||||
| { | { | ||||
| static int init = 0; | |||||
| if (!init) | |||||
| /* | |||||
| * 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 attribs[] = | |||||
| { | |||||
| EGL_SURFACE_TYPE, EGL_WINDOW_BIT, | |||||
| EGL_BUFFER_SIZE, 16, | |||||
| EGL_DEPTH_SIZE, 16, | |||||
| EGL_RED_SIZE, 4, | |||||
| EGL_GREEN_SIZE, 4, | |||||
| EGL_BLUE_SIZE, 4, | |||||
| EGL_ALPHA_SIZE, 4, | |||||
| EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, | |||||
| EGL_NONE | |||||
| }; | |||||
| EGLint w, h, dummy, format; | |||||
| EGLint numConfigs; | |||||
| EGLConfig config; | |||||
| EGLSurface surface; | |||||
| EGLContext context; | |||||
| EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |||||
| eglInitialize(display, 0, 0); | |||||
| eglChooseConfig(display, attribs, &config, 1, &numConfigs); | |||||
| eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format); | |||||
| ANativeWindow_setBuffersGeometry(engine->m_native_app->window, | |||||
| 0, 0, format); | |||||
| surface = eglCreateWindowSurface(display, config, | |||||
| engine->m_native_app->window, nullptr); | |||||
| EGLint ctxattr[] = | |||||
| { | { | ||||
| init = 1; | |||||
| g_main_queue.Push(1); | |||||
| g_main_queue.Push(1); | |||||
| EGL_CONTEXT_CLIENT_VERSION, 2, | |||||
| EGL_NONE | |||||
| }; | |||||
| context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctxattr); | |||||
| if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) | |||||
| { | |||||
| Log::Error("unable to eglMakeCurrent"); | |||||
| return -1; | |||||
| } | } | ||||
| /* Do nothing while the real render thread does the job. The | |||||
| * real stuff happens in nativeRender() */ | |||||
| Timer t; | |||||
| t.Wait(0.5f); | |||||
| eglQuerySurface(display, surface, EGL_WIDTH, &w); | |||||
| eglQuerySurface(display, surface, EGL_HEIGHT, &h); | |||||
| engine->display = display; | |||||
| engine->context = context; | |||||
| engine->surface = surface; | |||||
| /* Launch our ticker */ | |||||
| Log::Info("Java layer initialising renderer at %g fps", 60.0f); | |||||
| Ticker::Setup(60.0f); | |||||
| Video::Setup(ivec2(w, h)); | |||||
| return 0; | |||||
| } | } | ||||
| void *AndroidApp::MainRun(void *data) | |||||
| static void engine_draw_frame(AndroidAppData* engine) | |||||
| { | { | ||||
| int argc = 1; | |||||
| char *argv[] = { "", nullptr }; | |||||
| char *env[] = { nullptr }; | |||||
| if (!engine->display) | |||||
| return; | |||||
| /* Call the user's main() function. One of these will work. */ | |||||
| lol_android_main(); | |||||
| lol_android_main(argc, argv); | |||||
| lol_android_main(argc, argv, env); | |||||
| Ticker::TickDraw(); | |||||
| return nullptr; | |||||
| eglSwapBuffers(engine->display, engine->surface); | |||||
| } | } | ||||
| AndroidApp::~AndroidApp() | |||||
| /** | |||||
| * Tear down the EGL context currently associated with the display. | |||||
| */ | |||||
| static void engine_term_display(AndroidAppData* 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->display = EGL_NO_DISPLAY; | |||||
| engine->context = EGL_NO_CONTEXT; | |||||
| engine->surface = EGL_NO_SURFACE; | |||||
| } | } | ||||
| }; | |||||
| extern "C" jint | |||||
| JNI_OnLoad(JavaVM* vm, void* reserved) | |||||
| /** | |||||
| * Process the next input event. | |||||
| */ | |||||
| static int32_t engine_handle_input(android_app* native_app, AInputEvent* event) | |||||
| { | { | ||||
| Log::Info("Java layer loading library, vm=0x%08lx", (long)(intptr_t)vm); | |||||
| g_vm = vm; | |||||
| return JNI_VERSION_1_4; | |||||
| AndroidAppData* engine = (AndroidAppData*)native_app->userData; | |||||
| switch (AInputEvent_getType(event)) | |||||
| { | |||||
| case AINPUT_EVENT_TYPE_MOTION: | |||||
| Input::SetMousePos(ivec2(AMotionEvent_getX(event, 0), | |||||
| AMotionEvent_getY(event, 0))); | |||||
| switch (AKeyEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK) | |||||
| { | |||||
| case AMOTION_EVENT_ACTION_DOWN: | |||||
| Input::SetMouseButton(0); | |||||
| break; | |||||
| case AMOTION_EVENT_ACTION_UP: | |||||
| Input::UnsetMouseButton(0); | |||||
| break; | |||||
| } | |||||
| return 1; | |||||
| } | |||||
| return 0; | |||||
| } | } | ||||
| extern "C" void | |||||
| Java_net_lolengine_LolActivity_nativeInit(JNIEnv* env, jobject thiz) | |||||
| /** | |||||
| * Process the next main command. | |||||
| */ | |||||
| static void engine_handle_cmd(android_app* native_app, int32_t cmd) | |||||
| { | { | ||||
| Log::Info("Java layer initialising activity 0x%08lx", (long)thiz); | |||||
| env->NewGlobalRef(thiz); /* FIXME: never released! */ | |||||
| g_activity = thiz; | |||||
| AndroidAppData* engine = (AndroidAppData*)native_app->userData; | |||||
| switch (cmd) | |||||
| { | |||||
| case APP_CMD_SAVE_STATE: | |||||
| /* The system has asked us to save our current state. Do so. */ | |||||
| engine->m_native_app->savedState = malloc(sizeof(SavedState)); | |||||
| *((SavedState*)engine->m_native_app->savedState) = engine->m_state; | |||||
| engine->m_native_app->savedStateSize = sizeof(SavedState); | |||||
| break; | |||||
| case APP_CMD_INIT_WINDOW: | |||||
| /* The window is being shown, get it ready. */ | |||||
| if (engine->m_native_app->window != nullptr) | |||||
| { | |||||
| engine_init_display(engine); | |||||
| engine_draw_frame(engine); | |||||
| } | |||||
| break; | |||||
| case APP_CMD_TERM_WINDOW: | |||||
| /* The window is being hidden or closed, clean it up. */ | |||||
| engine_term_display(engine); | |||||
| break; | |||||
| case APP_CMD_GAINED_FOCUS: | |||||
| break; | |||||
| case APP_CMD_LOST_FOCUS: | |||||
| /* FIXME: stop animating */ | |||||
| engine_draw_frame(engine); | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| extern "C" void | |||||
| Java_net_lolengine_LolRenderer_nativeInit(JNIEnv* env) | |||||
| /* FIXME: find a better way to pass this to the AndroidApp ctor. */ | |||||
| AndroidAppData *g_data; | |||||
| void android_main(android_app* native_app) | |||||
| { | { | ||||
| /* Initialise app thread and wait for it to be ready, ie. set | |||||
| * the FPS value at least. */ | |||||
| g_main_thread = new Thread(lol::AndroidApp::MainRun, nullptr); | |||||
| g_main_queue.Pop(); | |||||
| g_data = new AndroidAppData(); | |||||
| /* Launch our ticker */ | |||||
| Log::Info("Java layer initialising renderer at %g fps", g_fps); | |||||
| Ticker::Setup(g_fps); | |||||
| Video::Setup(ivec2(320, 200)); | |||||
| /* Make sure glue isn't stripped */ | |||||
| app_dummy(); | |||||
| /* Wake up app thread */ | |||||
| g_main_queue.Pop(); | |||||
| } | |||||
| native_app->userData = g_data; | |||||
| native_app->onAppCmd = engine_handle_cmd; | |||||
| native_app->onInputEvent = engine_handle_input; | |||||
| g_data->m_native_app = native_app; | |||||
| extern "C" void | |||||
| Java_net_lolengine_LolRenderer_nativeResize(JNIEnv* env, jobject thiz, | |||||
| jint w, jint h) | |||||
| { | |||||
| Log::Info("Java layer resizing to %i x %i", w, h); | |||||
| Video::Setup(ivec2(w, h)); | |||||
| } | |||||
| if (native_app->savedState != nullptr) | |||||
| { | |||||
| /* We are starting with a previous saved state; restore from it */ | |||||
| g_data->m_state = *(SavedState*)native_app->savedState; | |||||
| } | |||||
| extern "C" void | |||||
| Java_net_lolengine_LolRenderer_nativeDone(JNIEnv* env) | |||||
| { | |||||
| /* FIXME: clean up */ | |||||
| delete g_main_thread; | |||||
| } | |||||
| int argc = 1; | |||||
| char *argv[] = { "", nullptr }; | |||||
| char *env[] = { nullptr }; | |||||
| extern "C" void | |||||
| Java_net_lolengine_LolView_nativePause(JNIEnv* env) | |||||
| { | |||||
| /* TODO: unimplemented */ | |||||
| /* Call the user's main() function. One of these will work. */ | |||||
| lol_android_main(); | |||||
| lol_android_main(argc, argv); | |||||
| lol_android_main(argc, argv, env); | |||||
| } | } | ||||
| extern "C" void | |||||
| Java_net_lolengine_LolView_nativeDown(JNIEnv* env) | |||||
| lol::AndroidApp::AndroidApp(char const *title, ivec2 res, float fps) | |||||
| : m_data(g_data) | |||||
| { | { | ||||
| Input::SetMouseButton(0); | |||||
| ; | |||||
| } | } | ||||
| extern "C" void | |||||
| Java_net_lolengine_LolView_nativeUp(JNIEnv* env) | |||||
| void lol::AndroidApp::ShowPointer(bool show) | |||||
| { | { | ||||
| Input::UnsetMouseButton(0); | |||||
| } | } | ||||
| extern "C" void | |||||
| Java_net_lolengine_LolView_nativeMove(JNIEnv* env, jobject thiz, | |||||
| jint x, jint y) | |||||
| lol::AndroidApp::~AndroidApp() | |||||
| { | { | ||||
| Input::SetMousePos(ivec2(x, y)); | |||||
| engine_term_display(m_data); | |||||
| delete m_data; | |||||
| } | } | ||||
| /* Call to render the next GL frame */ | |||||
| extern "C" void | |||||
| Java_net_lolengine_LolRenderer_nativeRender(JNIEnv* env) | |||||
| void lol::AndroidApp::Tick() | |||||
| { | { | ||||
| Ticker::TickDraw(); | |||||
| /* Read all pending events. */ | |||||
| int ident; | |||||
| int events; | |||||
| struct android_poll_source* source; | |||||
| /* Loop until all events are read, then continue to draw the next | |||||
| * frame of animation. */ | |||||
| while ((ident = ALooper_pollAll(0, nullptr, &events, | |||||
| (void**)&source)) >= 0) | |||||
| { | |||||
| /* Process this event */ | |||||
| if (source) | |||||
| source->process(m_data->m_native_app, source); | |||||
| /* Check if we are exiting */ | |||||
| if (m_data->m_native_app->destroyRequested != 0) | |||||
| Ticker::Shutdown(); | |||||
| } | |||||
| engine_draw_frame(m_data); | |||||
| } | } | ||||
| /* | /* | ||||
| * Fake main() wrappers that let us call the user’s main() from within | * Fake main() wrappers that let us call the user’s main() from within | ||||
| * a separate thread. | * a separate thread. | ||||
| */ | */ | ||||
| void lol_android_main(void) {} | |||||
| void lol_android_main(void) | |||||
| { | |||||
| } | |||||
| void lol_android_main(int argc, char **argv) | void lol_android_main(int argc, char **argv) | ||||
| { | { | ||||
| UNUSED(argc, argv); | UNUSED(argc, argv); | ||||
| } | } | ||||
| void lol_android_main(int argc, char **argv, char **envp) | void lol_android_main(int argc, char **argv, char **envp) | ||||
| { | { | ||||
| UNUSED(argc, argv, envp); | UNUSED(argc, argv, envp); | ||||
| @@ -32,8 +32,6 @@ public: | |||||
| void ShowPointer(bool show); | void ShowPointer(bool show); | ||||
| void Tick(); | void Tick(); | ||||
| static void *MainRun(void *data); | |||||
| private: | private: | ||||
| AndroidAppData *m_data; | AndroidAppData *m_data; | ||||
| }; | }; | ||||