no known regressions yet.legacy
@@ -1,19 +1,22 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | |||
package="net.lolengine" | |||
package="net.lolengine.lol_@PROGRAM@" | |||
android:versionCode="1" | |||
android:versionName="1.0"> | |||
<application android:label="@string/app_name" | |||
android:icon="@drawable/icon"> | |||
<activity android:name=".LolActivity" | |||
<activity android:name="net.lolengine.LolActivity" | |||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" | |||
android:screenOrientation="landscape" | |||
android:label="@string/app_name" | |||
android:launchMode="singleTask"> | |||
<meta-data android:name="android.app.lib_name" | |||
android:value="@PROGRAM@" /> | |||
<intent-filter> | |||
<action android:name="android.intent.action.MAIN" /> | |||
<category android:name="android.intent.category.LAUNCHER" /> | |||
</intent-filter> | |||
</activity> | |||
</application> | |||
<uses-sdk android:minSdkVersion="8" /> | |||
<uses-sdk android:minSdkVersion="9" /> | |||
</manifest> |
@@ -10,65 +10,32 @@ | |||
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.Resources; /* getResources() */ | |||
import android.graphics.Bitmap; | |||
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) | |||
{ | |||
@@ -94,154 +61,5 @@ public class LolActivity extends Activity | |||
{ | |||
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)" | |||
$(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 | |||
$(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" | |||
$(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" | |||
$(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" | |||
cp "$(top_srcdir)/build/android/icon.png" "$(d)/res/drawable/" | |||
$(MKDIR_P) "$(d)/libs/armeabi" | |||
$(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" | |||
android update project -t android-8 -p "$(d)" | |||
android update project -t android-9 -n "$(p)" -p "$(d)" | |||
ant debug -f "$(d)/build.xml" | |||
touch $@ | |||
else | |||
@@ -183,10 +183,10 @@ configure() | |||
./configure --host=armv7-apple-darwin10 CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS" CC="$CC" CXX="$CXX" | |||
;; | |||
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" | |||
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" | |||
# FIXME: is this needed? | |||
# ant debug | |||
@@ -312,7 +312,7 @@ dnl Use Android? FIXME: super hacks! | |||
ac_cv_my_have_android="no" | |||
AC_CHECK_LIB(log, __android_log_vprint, | |||
[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") | |||
@@ -1,7 +1,7 @@ | |||
// | |||
// 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 | |||
// modify it under the terms of the Do What The Fuck You Want To | |||
// Public License, Version 2, as published by Sam Hocevar. See | |||
@@ -15,166 +15,314 @@ | |||
#if defined __ANDROID__ | |||
#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 "loldebug.h" | |||
#include "androidapp.h" | |||
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 | |||
{ | |||
JavaVM *g_vm; | |||
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 | |||
* a separate thread. | |||
*/ | |||
void lol_android_main(void) {} | |||
void lol_android_main(void) | |||
{ | |||
} | |||
void lol_android_main(int argc, char **argv) | |||
{ | |||
UNUSED(argc, argv); | |||
} | |||
void lol_android_main(int argc, char **argv, char **envp) | |||
{ | |||
UNUSED(argc, argv, envp); | |||
@@ -32,8 +32,6 @@ public: | |||
void ShowPointer(bool show); | |||
void Tick(); | |||
static void *MainRun(void *data); | |||
private: | |||
AndroidAppData *m_data; | |||
}; | |||