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

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

android: open files through the asset manager, meaning Lua works.

  • Property svn:keywords set to Id
File size: 8.4 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#include <android/asset_manager_jni.h>
19
20#include <EGL/egl.h>
21#include <GLES/gl.h>
22
23extern "C" {
24#include <android_native_app_glue.h>
25#include <android_native_app_glue.c>
26}
27
28#include "core.h"
29#include "androidapp.h"
30
31using namespace lol;
32
33namespace lol
34{
35JavaVM *g_vm;
36jobject g_activity;
37AAssetManager *g_assets;
38}; /* namespace lol */
39
40extern "C" jint
41JNI_OnLoad(JavaVM* vm, void* reserved)
42{
43    Log::Info("Java layer loading library, vm=0x%08lx", (long)(intptr_t)vm);
44    g_vm = vm;
45    return JNI_VERSION_1_4;
46}
47
48extern "C" void
49Java_net_lolengine_LolActivity_nativeInit(JNIEnv* env, jobject thiz,
50                                          jobject assets)
51{
52    Log::Info("Java layer initialising activity 0x%08lx", (long)thiz);
53    env->NewGlobalRef(thiz); /* FIXME: never released! */
54    g_activity = thiz;
55    env->NewGlobalRef(assets); /* FIXME: never released! */
56    g_assets = AAssetManager_fromJava(env, assets);
57}
58
59/* One of these wrappers will be overridden by the user's version */
60void lol_android_main(void) __attribute__((weak));
61void lol_android_main(int argc, char **argv) __attribute__((weak));
62void lol_android_main(int argc, char **argv, char **envp) __attribute__((weak));
63
64/**
65 * Our saved state data.
66 */
67struct SavedState
68{
69    ivec2 position;
70};
71
72/**
73 * Shared state for our app.
74 */
75class lol::AndroidAppData
76{
77public:
78    int CreateDisplay();
79    void DestroyDisplay();
80    void DrawFrame();
81
82    static void StaticHandleCommand(android_app* native_app, int32_t cmd)
83    {
84        return ((AndroidAppData*)native_app->userData)->HandleCommand(cmd);
85    }
86
87    static int32_t StaticHandleInput(android_app* native_app, AInputEvent* ev)
88    {
89        return ((AndroidAppData*)native_app->userData)->HandleInput(ev);
90    }
91
92    android_app* m_native_app;
93
94    SavedState m_state;
95
96private:
97    void HandleCommand(int32_t cmd);
98    int32_t HandleInput(AInputEvent* event);
99
100    EGLDisplay m_display;
101    EGLSurface m_surface;
102    EGLContext m_context;
103};
104
105/**
106 * Initialize an EGL context for the current display.
107 */
108int lol::AndroidAppData::CreateDisplay()
109{
110    /* FIXME: there is a lot of code common to eglapp.cpp here. */
111    const EGLint attribs[] =
112    {
113        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
114        EGL_BUFFER_SIZE, 16,
115        EGL_DEPTH_SIZE, 16,
116        EGL_RED_SIZE, 4,
117        EGL_GREEN_SIZE, 4,
118        EGL_BLUE_SIZE, 4,
119        EGL_ALPHA_SIZE, 4,
120        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
121        EGL_NONE
122    };
123    EGLint w, h, dummy, format;
124    EGLint numConfigs;
125    EGLConfig config;
126
127    m_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
128
129    eglInitialize(m_display, 0, 0);
130    eglChooseConfig(m_display, attribs, &config, 1, &numConfigs);
131    eglGetConfigAttrib(m_display, config, EGL_NATIVE_VISUAL_ID, &format);
132
133    ANativeWindow_setBuffersGeometry(m_native_app->window,
134                                     0, 0, format);
135    m_surface = eglCreateWindowSurface(m_display, config,
136                                       m_native_app->window, nullptr);
137
138    EGLint ctxattr[] =
139    {
140        EGL_CONTEXT_CLIENT_VERSION, 2,
141        EGL_NONE
142    };
143    m_context = eglCreateContext(m_display, config, EGL_NO_CONTEXT, ctxattr);
144
145    if (eglMakeCurrent(m_display, m_surface, m_surface, m_context) == EGL_FALSE)
146    {
147        Log::Error("unable to eglMakeCurrent");
148        return -1;
149    }
150
151    eglQuerySurface(m_display, m_surface, EGL_WIDTH, &w);
152    eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &h);
153
154    /* Launch our ticker */
155    Log::Info("Java layer initialising renderer at %g fps", 60.0f);
156    Ticker::Setup(60.0f);
157    Video::Setup(ivec2(w, h));
158
159    return 0;
160}
161
162void lol::AndroidAppData::DrawFrame()
163{
164    if (!m_display)
165        return;
166
167    Ticker::TickDraw();
168
169    eglSwapBuffers(m_display, m_surface);
170}
171
172/**
173 * Tear down the EGL context currently associated with the display.
174 */
175void lol::AndroidAppData::DestroyDisplay()
176{
177    if (m_display != EGL_NO_DISPLAY)
178    {
179        eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
180        if (m_context != EGL_NO_CONTEXT)
181        {
182            eglDestroyContext(m_display, m_context);
183        }
184        if (m_surface != EGL_NO_SURFACE)
185        {
186            eglDestroySurface(m_display, m_surface);
187        }
188        eglTerminate(m_display);
189    }
190    m_display = EGL_NO_DISPLAY;
191    m_context = EGL_NO_CONTEXT;
192    m_surface = EGL_NO_SURFACE;
193}
194
195/**
196 * Process the next input event.
197 */
198int32_t lol::AndroidAppData::HandleInput(AInputEvent* event)
199{
200    switch (AInputEvent_getType(event))
201    {
202    case AINPUT_EVENT_TYPE_MOTION:
203        Input::SetMousePos(ivec2(AMotionEvent_getX(event, 0),
204                                 AMotionEvent_getY(event, 0)));
205        switch (AKeyEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK)
206        {
207        case AMOTION_EVENT_ACTION_DOWN:
208            Input::SetMouseButton(0);
209            break;
210        case AMOTION_EVENT_ACTION_UP:
211            Input::UnsetMouseButton(0);
212            break;
213        }
214        return 1;
215    }
216    return 0;
217}
218
219/**
220 * Process the next main command.
221 */
222void lol::AndroidAppData::HandleCommand(int32_t cmd)
223{
224    switch (cmd)
225    {
226        case APP_CMD_SAVE_STATE:
227            /* The system has asked us to save our current state. Do so. */
228            m_native_app->savedState = malloc(sizeof(SavedState));
229            *((SavedState*)m_native_app->savedState) = m_state;
230            m_native_app->savedStateSize = sizeof(SavedState);
231            break;
232        case APP_CMD_INIT_WINDOW:
233            /* The window is being shown, get it ready. */
234            if (m_native_app->window != nullptr)
235            {
236                CreateDisplay();
237                DrawFrame();
238            }
239            break;
240        case APP_CMD_TERM_WINDOW:
241            /* The window is being hidden or closed, clean it up. */
242            DestroyDisplay();
243            break;
244        case APP_CMD_GAINED_FOCUS:
245            break;
246        case APP_CMD_LOST_FOCUS:
247            /* FIXME: stop animating */
248            DrawFrame();
249            break;
250    }
251}
252
253/* FIXME: find a better way to pass this to the AndroidApp ctor. */
254AndroidAppData *g_data;
255
256void android_main(android_app* native_app)
257{
258    g_data = new AndroidAppData();
259    g_data->m_native_app = native_app;
260
261    /* Make sure glue isn't stripped */
262    app_dummy();
263
264    native_app->userData = g_data;
265    native_app->onAppCmd = lol::AndroidAppData::StaticHandleCommand;
266    native_app->onInputEvent = lol::AndroidAppData::StaticHandleInput;
267
268    if (native_app->savedState != nullptr)
269    {
270        /* We are starting with a previous saved state; restore from it */
271        g_data->m_state = *(SavedState*)native_app->savedState;
272    }
273
274    int argc = 1;
275    char *argv[] = { "", nullptr };
276    char *env[] = { nullptr };
277
278    /* Call the user's main() function. One of these will work. */
279    lol_android_main();
280    lol_android_main(argc, argv);
281    lol_android_main(argc, argv, env);
282}
283
284lol::AndroidApp::AndroidApp(char const *title, ivec2 res, float fps)
285  : m_data(g_data)
286{
287    ;
288}
289
290void lol::AndroidApp::ShowPointer(bool show)
291{
292}
293
294lol::AndroidApp::~AndroidApp()
295{
296    m_data->DestroyDisplay();
297    delete m_data;
298}
299
300void lol::AndroidApp::Tick()
301{
302    /* Read all pending events. */
303    int ident;
304    int events;
305    struct android_poll_source* source;
306
307    /* Loop until all events are read, then continue to draw the next
308     * frame of animation. */
309    while ((ident = ALooper_pollAll(0, nullptr, &events,
310                                    (void**)&source)) >= 0)
311    {
312        /* Process this event */
313        if (source)
314            source->process(m_data->m_native_app, source);
315
316        /* Check if we are exiting */
317        if (m_data->m_native_app->destroyRequested != 0)
318            Ticker::Shutdown();
319    }
320
321    m_data->DrawFrame();
322}
323
324/*
325 * Fake main() wrappers that let us call the user’s main() from within
326 * a separate thread.
327 */
328void lol_android_main(void)
329{
330}
331
332void lol_android_main(int argc, char **argv)
333{
334    UNUSED(argc, argv);
335}
336
337void lol_android_main(int argc, char **argv, char **envp)
338{
339    UNUSED(argc, argv, envp);
340}
341
342#endif /* __ANDROID__ */
343
Note: See TracBrowser for help on using the repository browser.