Ignore:
Timestamp:
Mar 11, 2013, 7:25:21 PM (10 years ago)
Author:
sam
Message:

android: switch to NativeActivity instead of rolling our own Java crap;
no known regressions yet.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/platform/android/androidapp.cpp

    r2576 r2579  
    22// Lol Engine
    33//
    4 // Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
     4// Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net>
    55//   This program is free software; you can redistribute it and/or
    66//   modify it under the terms of the Do What The Fuck You Want To
     
    1616
    1717#include <jni.h>
    18 #include <android/log.h>
     18
     19#include <EGL/egl.h>
     20#include <GLES/gl.h>
     21
     22extern "C" {
     23#include <android_native_app_glue.h>
     24#include <android_native_app_glue.c>
     25}
    1926
    2027#include "core.h"
    21 #include "loldebug.h"
    2228#include "androidapp.h"
    2329
    2430using namespace lol;
     31
     32namespace lol
     33{
     34JavaVM *g_vm;
     35jobject g_activity;
     36}; /* namespace lol */
     37
     38extern "C" jint
     39JNI_OnLoad(JavaVM* vm, void* reserved)
     40{
     41    Log::Info("Java layer loading library, vm=0x%08lx", (long)(intptr_t)vm);
     42    g_vm = vm;
     43    return JNI_VERSION_1_4;
     44}
     45
     46extern "C" void
     47Java_net_lolengine_LolActivity_nativeInit(JNIEnv* env, jobject thiz)
     48{
     49    Log::Info("Java layer initialising activity 0x%08lx", (long)thiz);
     50    env->NewGlobalRef(thiz); /* FIXME: never released! */
     51    g_activity = thiz;
     52}
    2553
    2654/* One of these wrappers will be overridden by the user's version */
     
    2957void lol_android_main(int argc, char **argv, char **envp) __attribute__((weak));
    3058
    31 namespace lol
    32 {
    33 JavaVM *g_vm;
    34 jobject g_activity;
    35 Queue<int> g_main_queue;
    36 Thread *g_main_thread;
    37 float g_fps;
    38 
    39 AndroidApp::AndroidApp(char const *title, ivec2 res, float fps)
    40   : m_data(0)
    41 {
    42     g_fps = fps;
    43 }
    44 
    45 void AndroidApp::ShowPointer(bool show)
    46 {
    47 }
    48 
    49 /* This is a fake Tick() method. We just wait until we're called and
    50  * signal nativeInit() that all the user's initialisation code was
    51  * called. Then we sit here forever, the Java layer is in charge of
    52  * calling TickDraw(). */
    53 void AndroidApp::Tick()
    54 {
    55     static int init = 0;
    56     if (!init)
    57     {
    58         init = 1;
    59         g_main_queue.Push(1);
    60         g_main_queue.Push(1);
    61     }
    62 
    63     /* Do nothing while the real render thread does the job. The
    64      * real stuff happens in nativeRender() */
    65     Timer t;
    66     t.Wait(0.5f);
    67 }
    68 
    69 void *AndroidApp::MainRun(void *data)
    70 {
     59/**
     60 * Our saved state data.
     61 */
     62struct SavedState
     63{
     64    ivec2 position;
     65};
     66
     67/**
     68 * Shared state for our app.
     69 */
     70class lol::AndroidAppData
     71{
     72public:
     73    android_app* m_native_app;
     74
     75    EGLDisplay display;
     76    EGLSurface surface;
     77    EGLContext context;
     78
     79    SavedState m_state;
     80};
     81
     82/**
     83 * Initialize an EGL context for the current display.
     84 */
     85static int engine_init_display(struct AndroidAppData* engine)
     86{
     87    /*
     88     * Here specify the attributes of the desired configuration.
     89     * Below, we select an EGLConfig with at least 8 bits per color
     90     * component compatible with on-screen windows
     91     */
     92    const EGLint attribs[] =
     93    {
     94        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
     95        EGL_BUFFER_SIZE, 16,
     96        EGL_DEPTH_SIZE, 16,
     97        EGL_RED_SIZE, 4,
     98        EGL_GREEN_SIZE, 4,
     99        EGL_BLUE_SIZE, 4,
     100        EGL_ALPHA_SIZE, 4,
     101        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
     102        EGL_NONE
     103    };
     104    EGLint w, h, dummy, format;
     105    EGLint numConfigs;
     106    EGLConfig config;
     107    EGLSurface surface;
     108    EGLContext context;
     109
     110    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     111
     112    eglInitialize(display, 0, 0);
     113    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
     114    eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
     115
     116    ANativeWindow_setBuffersGeometry(engine->m_native_app->window,
     117                                     0, 0, format);
     118    surface = eglCreateWindowSurface(display, config,
     119                                     engine->m_native_app->window, nullptr);
     120
     121    EGLint ctxattr[] =
     122    {
     123        EGL_CONTEXT_CLIENT_VERSION, 2,
     124        EGL_NONE
     125    };
     126    context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctxattr);
     127
     128    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
     129    {
     130        Log::Error("unable to eglMakeCurrent");
     131        return -1;
     132    }
     133
     134    eglQuerySurface(display, surface, EGL_WIDTH, &w);
     135    eglQuerySurface(display, surface, EGL_HEIGHT, &h);
     136
     137    engine->display = display;
     138    engine->context = context;
     139    engine->surface = surface;
     140
     141    /* Launch our ticker */
     142    Log::Info("Java layer initialising renderer at %g fps", 60.0f);
     143    Ticker::Setup(60.0f);
     144    Video::Setup(ivec2(w, h));
     145
     146    return 0;
     147}
     148
     149static void engine_draw_frame(AndroidAppData* engine)
     150{
     151    if (!engine->display)
     152        return;
     153
     154    Ticker::TickDraw();
     155
     156    eglSwapBuffers(engine->display, engine->surface);
     157}
     158
     159/**
     160 * Tear down the EGL context currently associated with the display.
     161 */
     162static void engine_term_display(AndroidAppData* engine)
     163{
     164    if (engine->display != EGL_NO_DISPLAY)
     165    {
     166        eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
     167        if (engine->context != EGL_NO_CONTEXT)
     168        {
     169            eglDestroyContext(engine->display, engine->context);
     170        }
     171        if (engine->surface != EGL_NO_SURFACE)
     172        {
     173            eglDestroySurface(engine->display, engine->surface);
     174        }
     175        eglTerminate(engine->display);
     176    }
     177    engine->display = EGL_NO_DISPLAY;
     178    engine->context = EGL_NO_CONTEXT;
     179    engine->surface = EGL_NO_SURFACE;
     180}
     181
     182/**
     183 * Process the next input event.
     184 */
     185static int32_t engine_handle_input(android_app* native_app, AInputEvent* event)
     186{
     187    AndroidAppData* engine = (AndroidAppData*)native_app->userData;
     188    switch (AInputEvent_getType(event))
     189    {
     190    case AINPUT_EVENT_TYPE_MOTION:
     191        Input::SetMousePos(ivec2(AMotionEvent_getX(event, 0),
     192                                 AMotionEvent_getY(event, 0)));
     193        switch (AKeyEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK)
     194        {
     195        case AMOTION_EVENT_ACTION_DOWN:
     196            Input::SetMouseButton(0);
     197            break;
     198        case AMOTION_EVENT_ACTION_UP:
     199            Input::UnsetMouseButton(0);
     200            break;
     201        }
     202        return 1;
     203    }
     204    return 0;
     205}
     206
     207/**
     208 * Process the next main command.
     209 */
     210static void engine_handle_cmd(android_app* native_app, int32_t cmd)
     211{
     212    AndroidAppData* engine = (AndroidAppData*)native_app->userData;
     213    switch (cmd)
     214    {
     215        case APP_CMD_SAVE_STATE:
     216            /* The system has asked us to save our current state. Do so. */
     217            engine->m_native_app->savedState = malloc(sizeof(SavedState));
     218            *((SavedState*)engine->m_native_app->savedState) = engine->m_state;
     219            engine->m_native_app->savedStateSize = sizeof(SavedState);
     220            break;
     221        case APP_CMD_INIT_WINDOW:
     222            /* The window is being shown, get it ready. */
     223            if (engine->m_native_app->window != nullptr)
     224            {
     225                engine_init_display(engine);
     226                engine_draw_frame(engine);
     227            }
     228            break;
     229        case APP_CMD_TERM_WINDOW:
     230            /* The window is being hidden or closed, clean it up. */
     231            engine_term_display(engine);
     232            break;
     233        case APP_CMD_GAINED_FOCUS:
     234            break;
     235        case APP_CMD_LOST_FOCUS:
     236            /* FIXME: stop animating */
     237            engine_draw_frame(engine);
     238            break;
     239    }
     240}
     241
     242/* FIXME: find a better way to pass this to the AndroidApp ctor. */
     243AndroidAppData *g_data;
     244
     245void android_main(android_app* native_app)
     246{
     247    g_data = new AndroidAppData();
     248
     249    /* Make sure glue isn't stripped */
     250    app_dummy();
     251
     252    native_app->userData = g_data;
     253    native_app->onAppCmd = engine_handle_cmd;
     254    native_app->onInputEvent = engine_handle_input;
     255    g_data->m_native_app = native_app;
     256
     257    if (native_app->savedState != nullptr)
     258    {
     259        /* We are starting with a previous saved state; restore from it */
     260        g_data->m_state = *(SavedState*)native_app->savedState;
     261    }
     262
    71263    int argc = 1;
    72264    char *argv[] = { "", nullptr };
     
    77269    lol_android_main(argc, argv);
    78270    lol_android_main(argc, argv, env);
    79 
    80     return nullptr;
    81 }
    82 
    83 AndroidApp::~AndroidApp()
    84 {
    85 }
    86 
    87 };
    88 
    89 extern "C" jint
    90 JNI_OnLoad(JavaVM* vm, void* reserved)
    91 {
    92     Log::Info("Java layer loading library, vm=0x%08lx", (long)(intptr_t)vm);
    93     g_vm = vm;
    94     return JNI_VERSION_1_4;
    95 }
    96 
    97 extern "C" void
    98 Java_net_lolengine_LolActivity_nativeInit(JNIEnv* env, jobject thiz)
    99 {
    100     Log::Info("Java layer initialising activity 0x%08lx", (long)thiz);
    101     env->NewGlobalRef(thiz); /* FIXME: never released! */
    102     g_activity = thiz;
    103 }
    104 
    105 extern "C" void
    106 Java_net_lolengine_LolRenderer_nativeInit(JNIEnv* env)
    107 {
    108     /* Initialise app thread and wait for it to be ready, ie. set
    109      * the FPS value at least. */
    110     g_main_thread = new Thread(lol::AndroidApp::MainRun, nullptr);
    111     g_main_queue.Pop();
    112 
    113     /* Launch our ticker */
    114     Log::Info("Java layer initialising renderer at %g fps", g_fps);
    115     Ticker::Setup(g_fps);
    116     Video::Setup(ivec2(320, 200));
    117 
    118     /* Wake up app thread */
    119     g_main_queue.Pop();
    120 }
    121 
    122 extern "C" void
    123 Java_net_lolengine_LolRenderer_nativeResize(JNIEnv* env, jobject thiz,
    124                                                 jint w, jint h)
    125 {
    126     Log::Info("Java layer resizing to %i x %i", w, h);
    127     Video::Setup(ivec2(w, h));
    128 }
    129 
    130 extern "C" void
    131 Java_net_lolengine_LolRenderer_nativeDone(JNIEnv* env)
    132 {
    133     /* FIXME: clean up */
    134     delete g_main_thread;
    135 }
    136 
    137 extern "C" void
    138 Java_net_lolengine_LolView_nativePause(JNIEnv* env)
    139 {
    140     /* TODO: unimplemented */
    141 }
    142 
    143 extern "C" void
    144 Java_net_lolengine_LolView_nativeDown(JNIEnv* env)
    145 {
    146     Input::SetMouseButton(0);
    147 }
    148 
    149 extern "C" void
    150 Java_net_lolengine_LolView_nativeUp(JNIEnv* env)
    151 {
    152     Input::UnsetMouseButton(0);
    153 }
    154 
    155 extern "C" void
    156 Java_net_lolengine_LolView_nativeMove(JNIEnv* env, jobject thiz,
    157                                           jint x, jint y)
    158 {
    159     Input::SetMousePos(ivec2(x, y));
    160 }
    161 
    162 /* Call to render the next GL frame */
    163 extern "C" void
    164 Java_net_lolengine_LolRenderer_nativeRender(JNIEnv* env)
    165 {
    166     Ticker::TickDraw();
     271}
     272
     273lol::AndroidApp::AndroidApp(char const *title, ivec2 res, float fps)
     274  : m_data(g_data)
     275{
     276    ;
     277}
     278
     279void lol::AndroidApp::ShowPointer(bool show)
     280{
     281}
     282
     283lol::AndroidApp::~AndroidApp()
     284{
     285    engine_term_display(m_data);
     286    delete m_data;
     287}
     288
     289void lol::AndroidApp::Tick()
     290{
     291    /* Read all pending events. */
     292    int ident;
     293    int events;
     294    struct android_poll_source* source;
     295
     296    /* Loop until all events are read, then continue to draw the next
     297     * frame of animation. */
     298    while ((ident = ALooper_pollAll(0, nullptr, &events,
     299                                    (void**)&source)) >= 0)
     300    {
     301        /* Process this event */
     302        if (source)
     303            source->process(m_data->m_native_app, source);
     304
     305        /* Check if we are exiting */
     306        if (m_data->m_native_app->destroyRequested != 0)
     307            Ticker::Shutdown();
     308    }
     309
     310    engine_draw_frame(m_data);
    167311}
    168312
     
    171315 * a separate thread.
    172316 */
    173 void lol_android_main(void) {}
     317void lol_android_main(void)
     318{
     319}
     320
    174321void lol_android_main(int argc, char **argv)
    175322{
    176323    UNUSED(argc, argv);
    177324}
     325
    178326void lol_android_main(int argc, char **argv, char **envp)
    179327{
Note: See TracChangeset for help on using the changeset viewer.