source: trunk/src/platform/android/androidapp.cpp @ 2579

Last change on this file since 2579 was 2579, checked in by sam, 9 years ago

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

  • Property svn:keywords set to Id
File size: 8.3 KB
Line 
1//
2// Lol Engine
3//
4// Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net>
5//   This program is free software; you can redistribute it and/or
6//   modify it under the terms of the Do What The Fuck You Want To
7//   Public License, Version 2, as published by Sam Hocevar. See
8//   http://www.wtfpl.net/ for more details.
9//
10
11#if defined HAVE_CONFIG_H
12#   include "config.h"
13#endif
14
15#if defined __ANDROID__
16
17#include <jni.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}
26
27#include "core.h"
28#include "androidapp.h"
29
30using 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}
53
54/* One of these wrappers will be overridden by the user's version */
55void lol_android_main(void) __attribute__((weak));
56void lol_android_main(int argc, char **argv) __attribute__((weak));
57void lol_android_main(int argc, char **argv, char **envp) __attribute__((weak));
58
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
263    int argc = 1;
264    char *argv[] = { "", nullptr };
265    char *env[] = { nullptr };
266
267    /* Call the user's main() function. One of these will work. */
268    lol_android_main();
269    lol_android_main(argc, argv);
270    lol_android_main(argc, argv, env);
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);
311}
312
313/*
314 * Fake main() wrappers that let us call the user’s main() from within
315 * a separate thread.
316 */
317void lol_android_main(void)
318{
319}
320
321void lol_android_main(int argc, char **argv)
322{
323    UNUSED(argc, argv);
324}
325
326void lol_android_main(int argc, char **argv, char **envp)
327{
328    UNUSED(argc, argv, envp);
329}
330
331#endif /* __ANDROID__ */
332
Note: See TracBrowser for help on using the repository browser.