ndktest

changeset 0:1310df7cdf25

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 23 Apr 2015 20:54:02 +0300
parents
children fe78cf853157
files .hgignore AndroidManifest.xml Makefile README src/android_native_app_glue.c src/android_native_app_glue.h src/app.c src/app.h src/logger.c src/logger.h src/main.c
diffstat 11 files changed, 1310 insertions(+), 0 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/.hgignore	Thu Apr 23 20:54:02 2015 +0300
     1.3 @@ -0,0 +1,10 @@
     1.4 +\.o$
     1.5 +\.swp$
     1.6 +\.d$
     1.7 +^bin/
     1.8 +^libs/
     1.9 +^res/
    1.10 +^build.xml$
    1.11 +\.properties$
    1.12 +\.java$
    1.13 +proguard-project\.txt$
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/AndroidManifest.xml	Thu Apr 23 20:54:02 2015 +0300
     2.3 @@ -0,0 +1,25 @@
     2.4 +<?xml version="1.0" encoding="utf-8"?>
     2.5 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     2.6 +	package="com.mutantstargoat.ndktest"
     2.7 +	android:versionCode="1"
     2.8 +	android:versionName="1.0">
     2.9 +
    2.10 +	<uses-sdk android:minSdkVersion="9"/>
    2.11 +
    2.12 +	<application android:label="@string/app_name"
    2.13 +			android:hasCode="false"
    2.14 +			android:debuggable="true"
    2.15 +			android:icon="@drawable/ic_launcher">
    2.16 +
    2.17 +		<activity android:name="android.app.NativeActivity"
    2.18 +			android:label="@string/app_name">
    2.19 +
    2.20 +			<meta-data android:name="android.app.lib_name" android:value="ndktest"/>
    2.21 +
    2.22 +			<intent-filter>
    2.23 +				<action android:name="android.intent.action.MAIN" />
    2.24 +				<category android:name="android.intent.category.LAUNCHER" />
    2.25 +			</intent-filter>
    2.26 +		</activity>
    2.27 +	</application>
    2.28 +</manifest>
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/Makefile	Thu Apr 23 20:54:02 2015 +0300
     3.3 @@ -0,0 +1,62 @@
     3.4 +android_platform = android-21
     3.5 +
     3.6 +src = $(wildcard src/*.c)
     3.7 +obj = $(src:.c=.o)
     3.8 +name = ndktest
     3.9 +lib = libs/armeabi/lib$(name).so
    3.10 +apk-release = bin/$(name).apk
    3.11 +apk-debug = bin/$(name)-debug.apk
    3.12 +
    3.13 +pkg = com.mutantstargoat.ndktest
    3.14 +act = android.app.NativeActivity
    3.15 +
    3.16 +CC = arm-linux-androideabi-gcc
    3.17 +
    3.18 +android_usr = $(NDK)/platforms/$(android_platform)/arch-arm/usr
    3.19 +android_inc = -I$(android_usr)/include
    3.20 +android_libs = -L$(android_usr)/lib -llog -landroid -lEGL -lGLESv1_CM
    3.21 +
    3.22 +CFLAGS = -std=c99 -pedantic -Wall -g $(android_inc) -Isrc
    3.23 +LDFLAGS = -Wl,--fix-cortex-a8 $(android_libs)
    3.24 +
    3.25 +.PHONY: debug
    3.26 +debug: $(apk-debug)
    3.27 +
    3.28 +.PHONY: release
    3.29 +release: $(apk-release)
    3.30 +
    3.31 +$(apk-debug): $(lib)
    3.32 +	ant debug
    3.33 +
    3.34 +$(apk-release): $(lib)
    3.35 +	ant release
    3.36 +
    3.37 +.PHONY: lib
    3.38 +lib: $(lib)
    3.39 +
    3.40 +$(lib): $(obj)
    3.41 +	@mkdir -p libs/armeabi
    3.42 +	$(CC) -o $@ -shared $(obj) $(LDFLAGS)
    3.43 +
    3.44 +.PHONY: clean
    3.45 +clean:
    3.46 +	rm -f $(obj) $(lib) $(apk-release) $(apk-debug)
    3.47 +
    3.48 +.PHONY: install
    3.49 +install: install-debug
    3.50 +
    3.51 +.PHONY: install-debug
    3.52 +install-debug:
    3.53 +	adb install -r $(apk-debug)
    3.54 +
    3.55 +.PHONY: install-release
    3.56 +install-release:
    3.57 +	adb install -r $(apk-release)
    3.58 +
    3.59 +.PHONY: run
    3.60 +run:
    3.61 +	adb shell am start -n $(pkg)/$(act)
    3.62 +
    3.63 +.PHONY: stop
    3.64 +stop:
    3.65 +	adb shell am force-stop $(pkg)
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/README	Thu Apr 23 20:54:02 2015 +0300
     4.3 @@ -0,0 +1,14 @@
     4.4 +Setup instructions
     4.5 +------------------
     4.6 +$NDK/build/tools/make-standalone-toolchain.sh \
     4.7 +	--platform=android-21 --arch=arm \
     4.8 +	--install-dir=$HOME/devel/ndk-toolchain
     4.9 +
    4.10 +export PATH=$HOME/devel/ndk-toolchain/bin
    4.11 +export CC=arm-linux-androideabi-gcc
    4.12 +
    4.13 + * use -mthumb (so says the doc)
    4.14 + * use LDFLAGS -Wl,--fix-cortex-a8
    4.15 + * use LDFLAGS -lstdc++ to statically link the GNU C++ library
    4.16 + * for armeabi-v7a ABI: -march=armv7-a -mfloat-abi=softfp
    4.17 + * for neon: -march=arm7-a -mfloat-abi=softfp -mfpu=neon
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/src/android_native_app_glue.c	Thu Apr 23 20:54:02 2015 +0300
     5.3 @@ -0,0 +1,440 @@
     5.4 +/*
     5.5 + * Copyright (C) 2010 The Android Open Source Project
     5.6 + *
     5.7 + * Licensed under the Apache License, Version 2.0 (the "License");
     5.8 + * you may not use this file except in compliance with the License.
     5.9 + * You may obtain a copy of the License at
    5.10 + *
    5.11 + *      http://www.apache.org/licenses/LICENSE-2.0
    5.12 + *
    5.13 + * Unless required by applicable law or agreed to in writing, software
    5.14 + * distributed under the License is distributed on an "AS IS" BASIS,
    5.15 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    5.16 + * See the License for the specific language governing permissions and
    5.17 + * limitations under the License.
    5.18 + *
    5.19 + */
    5.20 +
    5.21 +#include <jni.h>
    5.22 +
    5.23 +#include <errno.h>
    5.24 +#include <string.h>
    5.25 +#include <unistd.h>
    5.26 +#include <sys/resource.h>
    5.27 +
    5.28 +#include "android_native_app_glue.h"
    5.29 +#include <android/log.h>
    5.30 +
    5.31 +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
    5.32 +#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__))
    5.33 +
    5.34 +/* For debug builds, always enable the debug traces in this library */
    5.35 +#ifndef NDEBUG
    5.36 +#  define LOGV(...)  ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__))
    5.37 +#else
    5.38 +#  define LOGV(...)  ((void)0)
    5.39 +#endif
    5.40 +
    5.41 +static void free_saved_state(struct android_app* android_app) {
    5.42 +    pthread_mutex_lock(&android_app->mutex);
    5.43 +    if (android_app->savedState != NULL) {
    5.44 +        free(android_app->savedState);
    5.45 +        android_app->savedState = NULL;
    5.46 +        android_app->savedStateSize = 0;
    5.47 +    }
    5.48 +    pthread_mutex_unlock(&android_app->mutex);
    5.49 +}
    5.50 +
    5.51 +int8_t android_app_read_cmd(struct android_app* android_app) {
    5.52 +    int8_t cmd;
    5.53 +    if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) {
    5.54 +        switch (cmd) {
    5.55 +            case APP_CMD_SAVE_STATE:
    5.56 +                free_saved_state(android_app);
    5.57 +                break;
    5.58 +        }
    5.59 +        return cmd;
    5.60 +    } else {
    5.61 +        LOGE("No data on command pipe!");
    5.62 +    }
    5.63 +    return -1;
    5.64 +}
    5.65 +
    5.66 +static void print_cur_config(struct android_app* android_app) {
    5.67 +    char lang[2], country[2];
    5.68 +    AConfiguration_getLanguage(android_app->config, lang);
    5.69 +    AConfiguration_getCountry(android_app->config, country);
    5.70 +
    5.71 +    LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
    5.72 +            "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
    5.73 +            "modetype=%d modenight=%d",
    5.74 +            AConfiguration_getMcc(android_app->config),
    5.75 +            AConfiguration_getMnc(android_app->config),
    5.76 +            lang[0], lang[1], country[0], country[1],
    5.77 +            AConfiguration_getOrientation(android_app->config),
    5.78 +            AConfiguration_getTouchscreen(android_app->config),
    5.79 +            AConfiguration_getDensity(android_app->config),
    5.80 +            AConfiguration_getKeyboard(android_app->config),
    5.81 +            AConfiguration_getNavigation(android_app->config),
    5.82 +            AConfiguration_getKeysHidden(android_app->config),
    5.83 +            AConfiguration_getNavHidden(android_app->config),
    5.84 +            AConfiguration_getSdkVersion(android_app->config),
    5.85 +            AConfiguration_getScreenSize(android_app->config),
    5.86 +            AConfiguration_getScreenLong(android_app->config),
    5.87 +            AConfiguration_getUiModeType(android_app->config),
    5.88 +            AConfiguration_getUiModeNight(android_app->config));
    5.89 +}
    5.90 +
    5.91 +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
    5.92 +    switch (cmd) {
    5.93 +        case APP_CMD_INPUT_CHANGED:
    5.94 +            LOGV("APP_CMD_INPUT_CHANGED\n");
    5.95 +            pthread_mutex_lock(&android_app->mutex);
    5.96 +            if (android_app->inputQueue != NULL) {
    5.97 +                AInputQueue_detachLooper(android_app->inputQueue);
    5.98 +            }
    5.99 +            android_app->inputQueue = android_app->pendingInputQueue;
   5.100 +            if (android_app->inputQueue != NULL) {
   5.101 +                LOGV("Attaching input queue to looper");
   5.102 +                AInputQueue_attachLooper(android_app->inputQueue,
   5.103 +                        android_app->looper, LOOPER_ID_INPUT, NULL,
   5.104 +                        &android_app->inputPollSource);
   5.105 +            }
   5.106 +            pthread_cond_broadcast(&android_app->cond);
   5.107 +            pthread_mutex_unlock(&android_app->mutex);
   5.108 +            break;
   5.109 +
   5.110 +        case APP_CMD_INIT_WINDOW:
   5.111 +            LOGV("APP_CMD_INIT_WINDOW\n");
   5.112 +            pthread_mutex_lock(&android_app->mutex);
   5.113 +            android_app->window = android_app->pendingWindow;
   5.114 +            pthread_cond_broadcast(&android_app->cond);
   5.115 +            pthread_mutex_unlock(&android_app->mutex);
   5.116 +            break;
   5.117 +
   5.118 +        case APP_CMD_TERM_WINDOW:
   5.119 +            LOGV("APP_CMD_TERM_WINDOW\n");
   5.120 +            pthread_cond_broadcast(&android_app->cond);
   5.121 +            break;
   5.122 +
   5.123 +        case APP_CMD_RESUME:
   5.124 +        case APP_CMD_START:
   5.125 +        case APP_CMD_PAUSE:
   5.126 +        case APP_CMD_STOP:
   5.127 +            LOGV("activityState=%d\n", cmd);
   5.128 +            pthread_mutex_lock(&android_app->mutex);
   5.129 +            android_app->activityState = cmd;
   5.130 +            pthread_cond_broadcast(&android_app->cond);
   5.131 +            pthread_mutex_unlock(&android_app->mutex);
   5.132 +            break;
   5.133 +
   5.134 +        case APP_CMD_CONFIG_CHANGED:
   5.135 +            LOGV("APP_CMD_CONFIG_CHANGED\n");
   5.136 +            AConfiguration_fromAssetManager(android_app->config,
   5.137 +                    android_app->activity->assetManager);
   5.138 +            print_cur_config(android_app);
   5.139 +            break;
   5.140 +
   5.141 +        case APP_CMD_DESTROY:
   5.142 +            LOGV("APP_CMD_DESTROY\n");
   5.143 +            android_app->destroyRequested = 1;
   5.144 +            break;
   5.145 +    }
   5.146 +}
   5.147 +
   5.148 +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
   5.149 +    switch (cmd) {
   5.150 +        case APP_CMD_TERM_WINDOW:
   5.151 +            LOGV("APP_CMD_TERM_WINDOW\n");
   5.152 +            pthread_mutex_lock(&android_app->mutex);
   5.153 +            android_app->window = NULL;
   5.154 +            pthread_cond_broadcast(&android_app->cond);
   5.155 +            pthread_mutex_unlock(&android_app->mutex);
   5.156 +            break;
   5.157 +
   5.158 +        case APP_CMD_SAVE_STATE:
   5.159 +            LOGV("APP_CMD_SAVE_STATE\n");
   5.160 +            pthread_mutex_lock(&android_app->mutex);
   5.161 +            android_app->stateSaved = 1;
   5.162 +            pthread_cond_broadcast(&android_app->cond);
   5.163 +            pthread_mutex_unlock(&android_app->mutex);
   5.164 +            break;
   5.165 +
   5.166 +        case APP_CMD_RESUME:
   5.167 +            free_saved_state(android_app);
   5.168 +            break;
   5.169 +    }
   5.170 +}
   5.171 +
   5.172 +void app_dummy() {
   5.173 +
   5.174 +}
   5.175 +
   5.176 +static void android_app_destroy(struct android_app* android_app) {
   5.177 +    LOGV("android_app_destroy!");
   5.178 +    free_saved_state(android_app);
   5.179 +    pthread_mutex_lock(&android_app->mutex);
   5.180 +    if (android_app->inputQueue != NULL) {
   5.181 +        AInputQueue_detachLooper(android_app->inputQueue);
   5.182 +    }
   5.183 +    AConfiguration_delete(android_app->config);
   5.184 +    android_app->destroyed = 1;
   5.185 +    pthread_cond_broadcast(&android_app->cond);
   5.186 +    pthread_mutex_unlock(&android_app->mutex);
   5.187 +    // Can't touch android_app object after this.
   5.188 +}
   5.189 +
   5.190 +static void process_input(struct android_app* app, struct android_poll_source* source) {
   5.191 +    AInputEvent* event = NULL;
   5.192 +    while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
   5.193 +        LOGV("New input event: type=%d\n", AInputEvent_getType(event));
   5.194 +        if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
   5.195 +            continue;
   5.196 +        }
   5.197 +        int32_t handled = 0;
   5.198 +        if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
   5.199 +        AInputQueue_finishEvent(app->inputQueue, event, handled);
   5.200 +    }
   5.201 +}
   5.202 +
   5.203 +static void process_cmd(struct android_app* app, struct android_poll_source* source) {
   5.204 +    int8_t cmd = android_app_read_cmd(app);
   5.205 +    android_app_pre_exec_cmd(app, cmd);
   5.206 +    if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
   5.207 +    android_app_post_exec_cmd(app, cmd);
   5.208 +}
   5.209 +
   5.210 +static void* android_app_entry(void* param) {
   5.211 +    struct android_app* android_app = (struct android_app*)param;
   5.212 +
   5.213 +    android_app->config = AConfiguration_new();
   5.214 +    AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);
   5.215 +
   5.216 +    print_cur_config(android_app);
   5.217 +
   5.218 +    android_app->cmdPollSource.id = LOOPER_ID_MAIN;
   5.219 +    android_app->cmdPollSource.app = android_app;
   5.220 +    android_app->cmdPollSource.process = process_cmd;
   5.221 +    android_app->inputPollSource.id = LOOPER_ID_INPUT;
   5.222 +    android_app->inputPollSource.app = android_app;
   5.223 +    android_app->inputPollSource.process = process_input;
   5.224 +
   5.225 +    ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
   5.226 +    ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
   5.227 +            &android_app->cmdPollSource);
   5.228 +    android_app->looper = looper;
   5.229 +
   5.230 +    pthread_mutex_lock(&android_app->mutex);
   5.231 +    android_app->running = 1;
   5.232 +    pthread_cond_broadcast(&android_app->cond);
   5.233 +    pthread_mutex_unlock(&android_app->mutex);
   5.234 +
   5.235 +    android_main(android_app);
   5.236 +
   5.237 +    android_app_destroy(android_app);
   5.238 +    return NULL;
   5.239 +}
   5.240 +
   5.241 +// --------------------------------------------------------------------
   5.242 +// Native activity interaction (called from main thread)
   5.243 +// --------------------------------------------------------------------
   5.244 +
   5.245 +static struct android_app* android_app_create(ANativeActivity* activity,
   5.246 +        void* savedState, size_t savedStateSize) {
   5.247 +    struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app));
   5.248 +    memset(android_app, 0, sizeof(struct android_app));
   5.249 +    android_app->activity = activity;
   5.250 +
   5.251 +    pthread_mutex_init(&android_app->mutex, NULL);
   5.252 +    pthread_cond_init(&android_app->cond, NULL);
   5.253 +
   5.254 +    if (savedState != NULL) {
   5.255 +        android_app->savedState = malloc(savedStateSize);
   5.256 +        android_app->savedStateSize = savedStateSize;
   5.257 +        memcpy(android_app->savedState, savedState, savedStateSize);
   5.258 +    }
   5.259 +
   5.260 +    int msgpipe[2];
   5.261 +    if (pipe(msgpipe)) {
   5.262 +        LOGE("could not create pipe: %s", strerror(errno));
   5.263 +        return NULL;
   5.264 +    }
   5.265 +    android_app->msgread = msgpipe[0];
   5.266 +    android_app->msgwrite = msgpipe[1];
   5.267 +
   5.268 +    pthread_attr_t attr; 
   5.269 +    pthread_attr_init(&attr);
   5.270 +    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
   5.271 +    pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
   5.272 +
   5.273 +    // Wait for thread to start.
   5.274 +    pthread_mutex_lock(&android_app->mutex);
   5.275 +    while (!android_app->running) {
   5.276 +        pthread_cond_wait(&android_app->cond, &android_app->mutex);
   5.277 +    }
   5.278 +    pthread_mutex_unlock(&android_app->mutex);
   5.279 +
   5.280 +    return android_app;
   5.281 +}
   5.282 +
   5.283 +static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) {
   5.284 +    if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
   5.285 +        LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
   5.286 +    }
   5.287 +}
   5.288 +
   5.289 +static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) {
   5.290 +    pthread_mutex_lock(&android_app->mutex);
   5.291 +    android_app->pendingInputQueue = inputQueue;
   5.292 +    android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
   5.293 +    while (android_app->inputQueue != android_app->pendingInputQueue) {
   5.294 +        pthread_cond_wait(&android_app->cond, &android_app->mutex);
   5.295 +    }
   5.296 +    pthread_mutex_unlock(&android_app->mutex);
   5.297 +}
   5.298 +
   5.299 +static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) {
   5.300 +    pthread_mutex_lock(&android_app->mutex);
   5.301 +    if (android_app->pendingWindow != NULL) {
   5.302 +        android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
   5.303 +    }
   5.304 +    android_app->pendingWindow = window;
   5.305 +    if (window != NULL) {
   5.306 +        android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
   5.307 +    }
   5.308 +    while (android_app->window != android_app->pendingWindow) {
   5.309 +        pthread_cond_wait(&android_app->cond, &android_app->mutex);
   5.310 +    }
   5.311 +    pthread_mutex_unlock(&android_app->mutex);
   5.312 +}
   5.313 +
   5.314 +static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) {
   5.315 +    pthread_mutex_lock(&android_app->mutex);
   5.316 +    android_app_write_cmd(android_app, cmd);
   5.317 +    while (android_app->activityState != cmd) {
   5.318 +        pthread_cond_wait(&android_app->cond, &android_app->mutex);
   5.319 +    }
   5.320 +    pthread_mutex_unlock(&android_app->mutex);
   5.321 +}
   5.322 +
   5.323 +static void android_app_free(struct android_app* android_app) {
   5.324 +    pthread_mutex_lock(&android_app->mutex);
   5.325 +    android_app_write_cmd(android_app, APP_CMD_DESTROY);
   5.326 +    while (!android_app->destroyed) {
   5.327 +        pthread_cond_wait(&android_app->cond, &android_app->mutex);
   5.328 +    }
   5.329 +    pthread_mutex_unlock(&android_app->mutex);
   5.330 +
   5.331 +    close(android_app->msgread);
   5.332 +    close(android_app->msgwrite);
   5.333 +    pthread_cond_destroy(&android_app->cond);
   5.334 +    pthread_mutex_destroy(&android_app->mutex);
   5.335 +    free(android_app);
   5.336 +}
   5.337 +
   5.338 +static void onDestroy(ANativeActivity* activity) {
   5.339 +    LOGV("Destroy: %p\n", activity);
   5.340 +    android_app_free((struct android_app*)activity->instance);
   5.341 +}
   5.342 +
   5.343 +static void onStart(ANativeActivity* activity) {
   5.344 +    LOGV("Start: %p\n", activity);
   5.345 +    android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START);
   5.346 +}
   5.347 +
   5.348 +static void onResume(ANativeActivity* activity) {
   5.349 +    LOGV("Resume: %p\n", activity);
   5.350 +    android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME);
   5.351 +}
   5.352 +
   5.353 +static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
   5.354 +    struct android_app* android_app = (struct android_app*)activity->instance;
   5.355 +    void* savedState = NULL;
   5.356 +
   5.357 +    LOGV("SaveInstanceState: %p\n", activity);
   5.358 +    pthread_mutex_lock(&android_app->mutex);
   5.359 +    android_app->stateSaved = 0;
   5.360 +    android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
   5.361 +    while (!android_app->stateSaved) {
   5.362 +        pthread_cond_wait(&android_app->cond, &android_app->mutex);
   5.363 +    }
   5.364 +
   5.365 +    if (android_app->savedState != NULL) {
   5.366 +        savedState = android_app->savedState;
   5.367 +        *outLen = android_app->savedStateSize;
   5.368 +        android_app->savedState = NULL;
   5.369 +        android_app->savedStateSize = 0;
   5.370 +    }
   5.371 +
   5.372 +    pthread_mutex_unlock(&android_app->mutex);
   5.373 +
   5.374 +    return savedState;
   5.375 +}
   5.376 +
   5.377 +static void onPause(ANativeActivity* activity) {
   5.378 +    LOGV("Pause: %p\n", activity);
   5.379 +    android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE);
   5.380 +}
   5.381 +
   5.382 +static void onStop(ANativeActivity* activity) {
   5.383 +    LOGV("Stop: %p\n", activity);
   5.384 +    android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP);
   5.385 +}
   5.386 +
   5.387 +static void onConfigurationChanged(ANativeActivity* activity) {
   5.388 +    struct android_app* android_app = (struct android_app*)activity->instance;
   5.389 +    LOGV("ConfigurationChanged: %p\n", activity);
   5.390 +    android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
   5.391 +}
   5.392 +
   5.393 +static void onLowMemory(ANativeActivity* activity) {
   5.394 +    struct android_app* android_app = (struct android_app*)activity->instance;
   5.395 +    LOGV("LowMemory: %p\n", activity);
   5.396 +    android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
   5.397 +}
   5.398 +
   5.399 +static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
   5.400 +    LOGV("WindowFocusChanged: %p -- %d\n", activity, focused);
   5.401 +    android_app_write_cmd((struct android_app*)activity->instance,
   5.402 +            focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
   5.403 +}
   5.404 +
   5.405 +static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
   5.406 +    LOGV("NativeWindowCreated: %p -- %p\n", activity, window);
   5.407 +    android_app_set_window((struct android_app*)activity->instance, window);
   5.408 +}
   5.409 +
   5.410 +static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
   5.411 +    LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window);
   5.412 +    android_app_set_window((struct android_app*)activity->instance, NULL);
   5.413 +}
   5.414 +
   5.415 +static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
   5.416 +    LOGV("InputQueueCreated: %p -- %p\n", activity, queue);
   5.417 +    android_app_set_input((struct android_app*)activity->instance, queue);
   5.418 +}
   5.419 +
   5.420 +static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
   5.421 +    LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue);
   5.422 +    android_app_set_input((struct android_app*)activity->instance, NULL);
   5.423 +}
   5.424 +
   5.425 +void ANativeActivity_onCreate(ANativeActivity* activity,
   5.426 +        void* savedState, size_t savedStateSize) {
   5.427 +    LOGV("Creating: %p\n", activity);
   5.428 +    activity->callbacks->onDestroy = onDestroy;
   5.429 +    activity->callbacks->onStart = onStart;
   5.430 +    activity->callbacks->onResume = onResume;
   5.431 +    activity->callbacks->onSaveInstanceState = onSaveInstanceState;
   5.432 +    activity->callbacks->onPause = onPause;
   5.433 +    activity->callbacks->onStop = onStop;
   5.434 +    activity->callbacks->onConfigurationChanged = onConfigurationChanged;
   5.435 +    activity->callbacks->onLowMemory = onLowMemory;
   5.436 +    activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
   5.437 +    activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
   5.438 +    activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
   5.439 +    activity->callbacks->onInputQueueCreated = onInputQueueCreated;
   5.440 +    activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
   5.441 +
   5.442 +    activity->instance = android_app_create(activity, savedState, savedStateSize);
   5.443 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/android_native_app_glue.h	Thu Apr 23 20:54:02 2015 +0300
     6.3 @@ -0,0 +1,349 @@
     6.4 +/*
     6.5 + * Copyright (C) 2010 The Android Open Source Project
     6.6 + *
     6.7 + * Licensed under the Apache License, Version 2.0 (the "License");
     6.8 + * you may not use this file except in compliance with the License.
     6.9 + * You may obtain a copy of the License at
    6.10 + *
    6.11 + *      http://www.apache.org/licenses/LICENSE-2.0
    6.12 + *
    6.13 + * Unless required by applicable law or agreed to in writing, software
    6.14 + * distributed under the License is distributed on an "AS IS" BASIS,
    6.15 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    6.16 + * See the License for the specific language governing permissions and
    6.17 + * limitations under the License.
    6.18 + *
    6.19 + */
    6.20 +
    6.21 +#ifndef _ANDROID_NATIVE_APP_GLUE_H
    6.22 +#define _ANDROID_NATIVE_APP_GLUE_H
    6.23 +
    6.24 +#include <poll.h>
    6.25 +#include <pthread.h>
    6.26 +#include <sched.h>
    6.27 +
    6.28 +#include <android/configuration.h>
    6.29 +#include <android/looper.h>
    6.30 +#include <android/native_activity.h>
    6.31 +
    6.32 +#ifdef __cplusplus
    6.33 +extern "C" {
    6.34 +#endif
    6.35 +
    6.36 +/**
    6.37 + * The native activity interface provided by <android/native_activity.h>
    6.38 + * is based on a set of application-provided callbacks that will be called
    6.39 + * by the Activity's main thread when certain events occur.
    6.40 + *
    6.41 + * This means that each one of this callbacks _should_ _not_ block, or they
    6.42 + * risk having the system force-close the application. This programming
    6.43 + * model is direct, lightweight, but constraining.
    6.44 + *
    6.45 + * The 'android_native_app_glue' static library is used to provide a different
    6.46 + * execution model where the application can implement its own main event
    6.47 + * loop in a different thread instead. Here's how it works:
    6.48 + *
    6.49 + * 1/ The application must provide a function named "android_main()" that
    6.50 + *    will be called when the activity is created, in a new thread that is
    6.51 + *    distinct from the activity's main thread.
    6.52 + *
    6.53 + * 2/ android_main() receives a pointer to a valid "android_app" structure
    6.54 + *    that contains references to other important objects, e.g. the
    6.55 + *    ANativeActivity obejct instance the application is running in.
    6.56 + *
    6.57 + * 3/ the "android_app" object holds an ALooper instance that already
    6.58 + *    listens to two important things:
    6.59 + *
    6.60 + *      - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX
    6.61 + *        declarations below.
    6.62 + *
    6.63 + *      - input events coming from the AInputQueue attached to the activity.
    6.64 + *
    6.65 + *    Each of these correspond to an ALooper identifier returned by
    6.66 + *    ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT,
    6.67 + *    respectively.
    6.68 + *
    6.69 + *    Your application can use the same ALooper to listen to additional
    6.70 + *    file-descriptors.  They can either be callback based, or with return
    6.71 + *    identifiers starting with LOOPER_ID_USER.
    6.72 + *
    6.73 + * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event,
    6.74 + *    the returned data will point to an android_poll_source structure.  You
    6.75 + *    can call the process() function on it, and fill in android_app->onAppCmd
    6.76 + *    and android_app->onInputEvent to be called for your own processing
    6.77 + *    of the event.
    6.78 + *
    6.79 + *    Alternatively, you can call the low-level functions to read and process
    6.80 + *    the data directly...  look at the process_cmd() and process_input()
    6.81 + *    implementations in the glue to see how to do this.
    6.82 + *
    6.83 + * See the sample named "native-activity" that comes with the NDK with a
    6.84 + * full usage example.  Also look at the JavaDoc of NativeActivity.
    6.85 + */
    6.86 +
    6.87 +struct android_app;
    6.88 +
    6.89 +/**
    6.90 + * Data associated with an ALooper fd that will be returned as the "outData"
    6.91 + * when that source has data ready.
    6.92 + */
    6.93 +struct android_poll_source {
    6.94 +    // The identifier of this source.  May be LOOPER_ID_MAIN or
    6.95 +    // LOOPER_ID_INPUT.
    6.96 +    int32_t id;
    6.97 +
    6.98 +    // The android_app this ident is associated with.
    6.99 +    struct android_app* app;
   6.100 +
   6.101 +    // Function to call to perform the standard processing of data from
   6.102 +    // this source.
   6.103 +    void (*process)(struct android_app* app, struct android_poll_source* source);
   6.104 +};
   6.105 +
   6.106 +/**
   6.107 + * This is the interface for the standard glue code of a threaded
   6.108 + * application.  In this model, the application's code is running
   6.109 + * in its own thread separate from the main thread of the process.
   6.110 + * It is not required that this thread be associated with the Java
   6.111 + * VM, although it will need to be in order to make JNI calls any
   6.112 + * Java objects.
   6.113 + */
   6.114 +struct android_app {
   6.115 +    // The application can place a pointer to its own state object
   6.116 +    // here if it likes.
   6.117 +    void* userData;
   6.118 +
   6.119 +    // Fill this in with the function to process main app commands (APP_CMD_*)
   6.120 +    void (*onAppCmd)(struct android_app* app, int32_t cmd);
   6.121 +
   6.122 +    // Fill this in with the function to process input events.  At this point
   6.123 +    // the event has already been pre-dispatched, and it will be finished upon
   6.124 +    // return.  Return 1 if you have handled the event, 0 for any default
   6.125 +    // dispatching.
   6.126 +    int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event);
   6.127 +
   6.128 +    // The ANativeActivity object instance that this app is running in.
   6.129 +    ANativeActivity* activity;
   6.130 +
   6.131 +    // The current configuration the app is running in.
   6.132 +    AConfiguration* config;
   6.133 +
   6.134 +    // This is the last instance's saved state, as provided at creation time.
   6.135 +    // It is NULL if there was no state.  You can use this as you need; the
   6.136 +    // memory will remain around until you call android_app_exec_cmd() for
   6.137 +    // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL.
   6.138 +    // These variables should only be changed when processing a APP_CMD_SAVE_STATE,
   6.139 +    // at which point they will be initialized to NULL and you can malloc your
   6.140 +    // state and place the information here.  In that case the memory will be
   6.141 +    // freed for you later.
   6.142 +    void* savedState;
   6.143 +    size_t savedStateSize;
   6.144 +
   6.145 +    // The ALooper associated with the app's thread.
   6.146 +    ALooper* looper;
   6.147 +
   6.148 +    // When non-NULL, this is the input queue from which the app will
   6.149 +    // receive user input events.
   6.150 +    AInputQueue* inputQueue;
   6.151 +
   6.152 +    // When non-NULL, this is the window surface that the app can draw in.
   6.153 +    ANativeWindow* window;
   6.154 +
   6.155 +    // Current content rectangle of the window; this is the area where the
   6.156 +    // window's content should be placed to be seen by the user.
   6.157 +    ARect contentRect;
   6.158 +
   6.159 +    // Current state of the app's activity.  May be either APP_CMD_START,
   6.160 +    // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below.
   6.161 +    int activityState;
   6.162 +
   6.163 +    // This is non-zero when the application's NativeActivity is being
   6.164 +    // destroyed and waiting for the app thread to complete.
   6.165 +    int destroyRequested;
   6.166 +
   6.167 +    // -------------------------------------------------
   6.168 +    // Below are "private" implementation of the glue code.
   6.169 +
   6.170 +    pthread_mutex_t mutex;
   6.171 +    pthread_cond_t cond;
   6.172 +
   6.173 +    int msgread;
   6.174 +    int msgwrite;
   6.175 +
   6.176 +    pthread_t thread;
   6.177 +
   6.178 +    struct android_poll_source cmdPollSource;
   6.179 +    struct android_poll_source inputPollSource;
   6.180 +
   6.181 +    int running;
   6.182 +    int stateSaved;
   6.183 +    int destroyed;
   6.184 +    int redrawNeeded;
   6.185 +    AInputQueue* pendingInputQueue;
   6.186 +    ANativeWindow* pendingWindow;
   6.187 +    ARect pendingContentRect;
   6.188 +};
   6.189 +
   6.190 +enum {
   6.191 +    /**
   6.192 +     * Looper data ID of commands coming from the app's main thread, which
   6.193 +     * is returned as an identifier from ALooper_pollOnce().  The data for this
   6.194 +     * identifier is a pointer to an android_poll_source structure.
   6.195 +     * These can be retrieved and processed with android_app_read_cmd()
   6.196 +     * and android_app_exec_cmd().
   6.197 +     */
   6.198 +    LOOPER_ID_MAIN = 1,
   6.199 +
   6.200 +    /**
   6.201 +     * Looper data ID of events coming from the AInputQueue of the
   6.202 +     * application's window, which is returned as an identifier from
   6.203 +     * ALooper_pollOnce().  The data for this identifier is a pointer to an
   6.204 +     * android_poll_source structure.  These can be read via the inputQueue
   6.205 +     * object of android_app.
   6.206 +     */
   6.207 +    LOOPER_ID_INPUT = 2,
   6.208 +
   6.209 +    /**
   6.210 +     * Start of user-defined ALooper identifiers.
   6.211 +     */
   6.212 +    LOOPER_ID_USER = 3,
   6.213 +};
   6.214 +
   6.215 +enum {
   6.216 +    /**
   6.217 +     * Command from main thread: the AInputQueue has changed.  Upon processing
   6.218 +     * this command, android_app->inputQueue will be updated to the new queue
   6.219 +     * (or NULL).
   6.220 +     */
   6.221 +    APP_CMD_INPUT_CHANGED,
   6.222 +
   6.223 +    /**
   6.224 +     * Command from main thread: a new ANativeWindow is ready for use.  Upon
   6.225 +     * receiving this command, android_app->window will contain the new window
   6.226 +     * surface.
   6.227 +     */
   6.228 +    APP_CMD_INIT_WINDOW,
   6.229 +
   6.230 +    /**
   6.231 +     * Command from main thread: the existing ANativeWindow needs to be
   6.232 +     * terminated.  Upon receiving this command, android_app->window still
   6.233 +     * contains the existing window; after calling android_app_exec_cmd
   6.234 +     * it will be set to NULL.
   6.235 +     */
   6.236 +    APP_CMD_TERM_WINDOW,
   6.237 +
   6.238 +    /**
   6.239 +     * Command from main thread: the current ANativeWindow has been resized.
   6.240 +     * Please redraw with its new size.
   6.241 +     */
   6.242 +    APP_CMD_WINDOW_RESIZED,
   6.243 +
   6.244 +    /**
   6.245 +     * Command from main thread: the system needs that the current ANativeWindow
   6.246 +     * be redrawn.  You should redraw the window before handing this to
   6.247 +     * android_app_exec_cmd() in order to avoid transient drawing glitches.
   6.248 +     */
   6.249 +    APP_CMD_WINDOW_REDRAW_NEEDED,
   6.250 +
   6.251 +    /**
   6.252 +     * Command from main thread: the content area of the window has changed,
   6.253 +     * such as from the soft input window being shown or hidden.  You can
   6.254 +     * find the new content rect in android_app::contentRect.
   6.255 +     */
   6.256 +    APP_CMD_CONTENT_RECT_CHANGED,
   6.257 +
   6.258 +    /**
   6.259 +     * Command from main thread: the app's activity window has gained
   6.260 +     * input focus.
   6.261 +     */
   6.262 +    APP_CMD_GAINED_FOCUS,
   6.263 +
   6.264 +    /**
   6.265 +     * Command from main thread: the app's activity window has lost
   6.266 +     * input focus.
   6.267 +     */
   6.268 +    APP_CMD_LOST_FOCUS,
   6.269 +
   6.270 +    /**
   6.271 +     * Command from main thread: the current device configuration has changed.
   6.272 +     */
   6.273 +    APP_CMD_CONFIG_CHANGED,
   6.274 +
   6.275 +    /**
   6.276 +     * Command from main thread: the system is running low on memory.
   6.277 +     * Try to reduce your memory use.
   6.278 +     */
   6.279 +    APP_CMD_LOW_MEMORY,
   6.280 +
   6.281 +    /**
   6.282 +     * Command from main thread: the app's activity has been started.
   6.283 +     */
   6.284 +    APP_CMD_START,
   6.285 +
   6.286 +    /**
   6.287 +     * Command from main thread: the app's activity has been resumed.
   6.288 +     */
   6.289 +    APP_CMD_RESUME,
   6.290 +
   6.291 +    /**
   6.292 +     * Command from main thread: the app should generate a new saved state
   6.293 +     * for itself, to restore from later if needed.  If you have saved state,
   6.294 +     * allocate it with malloc and place it in android_app.savedState with
   6.295 +     * the size in android_app.savedStateSize.  The will be freed for you
   6.296 +     * later.
   6.297 +     */
   6.298 +    APP_CMD_SAVE_STATE,
   6.299 +
   6.300 +    /**
   6.301 +     * Command from main thread: the app's activity has been paused.
   6.302 +     */
   6.303 +    APP_CMD_PAUSE,
   6.304 +
   6.305 +    /**
   6.306 +     * Command from main thread: the app's activity has been stopped.
   6.307 +     */
   6.308 +    APP_CMD_STOP,
   6.309 +
   6.310 +    /**
   6.311 +     * Command from main thread: the app's activity is being destroyed,
   6.312 +     * and waiting for the app thread to clean up and exit before proceeding.
   6.313 +     */
   6.314 +    APP_CMD_DESTROY,
   6.315 +};
   6.316 +
   6.317 +/**
   6.318 + * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
   6.319 + * app command message.
   6.320 + */
   6.321 +int8_t android_app_read_cmd(struct android_app* android_app);
   6.322 +
   6.323 +/**
   6.324 + * Call with the command returned by android_app_read_cmd() to do the
   6.325 + * initial pre-processing of the given command.  You can perform your own
   6.326 + * actions for the command after calling this function.
   6.327 + */
   6.328 +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd);
   6.329 +
   6.330 +/**
   6.331 + * Call with the command returned by android_app_read_cmd() to do the
   6.332 + * final post-processing of the given command.  You must have done your own
   6.333 + * actions for the command before calling this function.
   6.334 + */
   6.335 +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd);
   6.336 +
   6.337 +/**
   6.338 + * Dummy function you can call to ensure glue code isn't stripped.
   6.339 + */
   6.340 +void app_dummy();
   6.341 +
   6.342 +/**
   6.343 + * This is the function that application code must implement, representing
   6.344 + * the main entry to the app.
   6.345 + */
   6.346 +extern void android_main(struct android_app* app);
   6.347 +
   6.348 +#ifdef __cplusplus
   6.349 +}
   6.350 +#endif
   6.351 +
   6.352 +#endif /* _ANDROID_NATIVE_APP_GLUE_H */
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/app.c	Thu Apr 23 20:54:02 2015 +0300
     7.3 @@ -0,0 +1,80 @@
     7.4 +#include <stdio.h>
     7.5 +#include <sys/time.h>
     7.6 +#include <GLES/gl.h>
     7.7 +#include "app.h"
     7.8 +
     7.9 +static unsigned int get_msec(void);
    7.10 +
    7.11 +int app_init(void)
    7.12 +{
    7.13 +	printf("app_init called\n");
    7.14 +
    7.15 +	return 0;
    7.16 +}
    7.17 +
    7.18 +void app_cleanup(void)
    7.19 +{
    7.20 +	printf("app_cleanup called\n");
    7.21 +}
    7.22 +
    7.23 +static float vert_col[] = { 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1 };
    7.24 +static float vert_pos[] = { 0.0, 0.6, 0.0, -0.6, -0.4, 0.0, 0.6, -0.4, 0.0 };
    7.25 +
    7.26 +void app_display(void)
    7.27 +{
    7.28 +	unsigned int msec = get_msec();
    7.29 +
    7.30 +	glClearColor(0.1, 0.1, 0.1, 1);
    7.31 +	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    7.32 +
    7.33 +	glMatrixMode(GL_MODELVIEW);
    7.34 +	glLoadIdentity();
    7.35 +	glRotatef(msec / 10.0, 0, 0, 1);
    7.36 +
    7.37 +	glEnableClientState(GL_VERTEX_ARRAY);
    7.38 +	glEnableClientState(GL_COLOR_ARRAY);
    7.39 +	glVertexPointer(3, GL_FLOAT, 0, vert_pos);
    7.40 +	glColorPointer(4, GL_FLOAT, 0, vert_col);
    7.41 +
    7.42 +	glDrawArrays(GL_TRIANGLES, 0, 3);
    7.43 +
    7.44 +	glDisableClientState(GL_VERTEX_ARRAY);
    7.45 +	glDisableClientState(GL_COLOR_ARRAY);
    7.46 +
    7.47 +	eglSwapBuffers(dpy, surf);
    7.48 +}
    7.49 +
    7.50 +void app_resize(int x, int y)
    7.51 +{
    7.52 +	printf("app_resize: %dx%d\n", x, y);
    7.53 +	glViewport(0, 0, x, y);
    7.54 +
    7.55 +	float aspect = (float)x / (float)y;
    7.56 +	glMatrixMode(GL_PROJECTION);
    7.57 +	glLoadIdentity();
    7.58 +	glScalef(1, aspect, 1);
    7.59 +}
    7.60 +
    7.61 +void app_touch(int id, int press, int x, int y)
    7.62 +{
    7.63 +	printf("app_touch: %s, id: %d, pos: %d %d\n", press ? "press" : "release", id, x, y);
    7.64 +}
    7.65 +
    7.66 +void app_drag(int id, int x, int y)
    7.67 +{
    7.68 +	printf("app_drag id: %d, pos: %d %d\n", id, x, y);
    7.69 +}
    7.70 +
    7.71 +static unsigned int get_msec(void)
    7.72 +{
    7.73 +	static struct timeval tv0;
    7.74 +	struct timeval tv;
    7.75 +
    7.76 +	gettimeofday(&tv, 0);
    7.77 +
    7.78 +	if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
    7.79 +		tv0 = tv;
    7.80 +		return 0;
    7.81 +	}
    7.82 +	return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
    7.83 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/src/app.h	Thu Apr 23 20:54:02 2015 +0300
     8.3 @@ -0,0 +1,23 @@
     8.4 +#ifndef APP_H_
     8.5 +#define APP_H_
     8.6 +
     8.7 +#include <EGL/egl.h>
     8.8 +
     8.9 +#ifndef APP_NAME
    8.10 +#define APP_NAME	"gles_test"
    8.11 +#endif
    8.12 +
    8.13 +EGLDisplay dpy;
    8.14 +EGLSurface surf;
    8.15 +EGLContext ctx;
    8.16 +
    8.17 +int app_init(void);
    8.18 +void app_cleanup(void);
    8.19 +
    8.20 +void app_display(void);
    8.21 +void app_resize(int x, int y);
    8.22 +
    8.23 +void app_touch(int id, int press, int x, int y);
    8.24 +void app_drag(int id, int x, int y);
    8.25 +
    8.26 +#endif	/* APP_H_ */
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/src/logger.c	Thu Apr 23 20:54:02 2015 +0300
     9.3 @@ -0,0 +1,61 @@
     9.4 +#include <stdio.h>
     9.5 +#include <assert.h>
     9.6 +#include <unistd.h>
     9.7 +#include <pthread.h>
     9.8 +#include <android/log.h>
     9.9 +#include "logger.h"
    9.10 +#include "app.h"
    9.11 +
    9.12 +static void *thread_func(void *arg);
    9.13 +
    9.14 +static int pfd[2];
    9.15 +static pthread_t thr;
    9.16 +static int initialized;
    9.17 +
    9.18 +int start_logger(void)
    9.19 +{
    9.20 +	if(initialized) {
    9.21 +		return 1;
    9.22 +	}
    9.23 +
    9.24 +	/* set stdout to line-buffered, and stderr to unbuffered */
    9.25 +	setvbuf(stdout, 0, _IOLBF, 0);
    9.26 +	setvbuf(stderr, 0, _IONBF, 0);
    9.27 +
    9.28 +	if(pipe(pfd) == -1) {
    9.29 +		perror("failed to create logging pipe");
    9.30 +		return -1;
    9.31 +	}
    9.32 +	assert(pfd[0] > 2 && pfd[1] > 2);
    9.33 +
    9.34 +	/* redirect stdout & stderr to the write-end of the pipe */
    9.35 +	dup2(pfd[1], 1);
    9.36 +	dup2(pfd[1], 2);
    9.37 +
    9.38 +	/* start the logging thread */
    9.39 +	if(pthread_create(&thr, 0, thread_func, 0) == -1) {
    9.40 +		perror("failed to spawn logging thread");
    9.41 +		return -1;
    9.42 +	}
    9.43 +	pthread_detach(thr);
    9.44 +	return 0;
    9.45 +}
    9.46 +
    9.47 +static void *thread_func(void *arg)
    9.48 +{
    9.49 +	ssize_t rdsz;
    9.50 +	char buf[257];
    9.51 +
    9.52 +	__android_log_print(ANDROID_LOG_DEBUG, APP_NAME, "logger starting up...");
    9.53 +
    9.54 +	while((rdsz = read(pfd[0], buf, sizeof buf - 1)) > 0) {
    9.55 +		if(buf[rdsz - 1] == '\n') {
    9.56 +			--rdsz;
    9.57 +		}
    9.58 +		buf[rdsz] = 0;
    9.59 +		__android_log_write(ANDROID_LOG_DEBUG, APP_NAME, buf);
    9.60 +	}
    9.61 +
    9.62 +	__android_log_print(ANDROID_LOG_DEBUG, APP_NAME, "logger shutting down...");
    9.63 +	return 0;
    9.64 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/src/logger.h	Thu Apr 23 20:54:02 2015 +0300
    10.3 @@ -0,0 +1,6 @@
    10.4 +#ifndef LOGGER_H_
    10.5 +#define LOGGER_H_
    10.6 +
    10.7 +int start_logger(void);
    10.8 +
    10.9 +#endif	/* LOGGER_H_ */
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/src/main.c	Thu Apr 23 20:54:02 2015 +0300
    11.3 @@ -0,0 +1,240 @@
    11.4 +#include <stdio.h>
    11.5 +#include <stdlib.h>
    11.6 +#include <string.h>
    11.7 +#include <GLES/gl.h>
    11.8 +#include <GLES/glext.h>
    11.9 +#include <EGL/egl.h>
   11.10 +#include <android_native_app_glue.h>
   11.11 +#include "app.h"
   11.12 +#include "logger.h"
   11.13 +
   11.14 +static void handle_command(struct android_app *app, int32_t cmd);
   11.15 +static int handle_input(struct android_app *app, AInputEvent *ev);
   11.16 +static int handle_touch_input(struct android_app *app, AInputEvent *ev);
   11.17 +static int init_gl(void);
   11.18 +static void destroy_gl(void);
   11.19 +
   11.20 +static struct android_app *app;
   11.21 +static int win_width, win_height;
   11.22 +
   11.23 +void android_main(struct android_app *app_ptr)
   11.24 +{
   11.25 +	app_dummy();
   11.26 +	app = app_ptr;
   11.27 +
   11.28 +	app->onAppCmd = handle_command;
   11.29 +	app->onInputEvent = handle_input;
   11.30 +
   11.31 +	start_logger();
   11.32 +
   11.33 +	for(;;) {
   11.34 +		int num_events;
   11.35 +		struct android_poll_source *pollsrc;
   11.36 +
   11.37 +		while(ALooper_pollAll(0, 0, &num_events, (void**)&pollsrc) >= 0) {
   11.38 +			if(pollsrc) {
   11.39 +				pollsrc->process(app, pollsrc);
   11.40 +			}
   11.41 +		}
   11.42 +
   11.43 +		if(app->destroyRequested) {
   11.44 +			return;
   11.45 +		}
   11.46 +
   11.47 +		app_display();
   11.48 +	}
   11.49 +}
   11.50 +
   11.51 +static void handle_command(struct android_app *app, int32_t cmd)
   11.52 +{
   11.53 +	switch(cmd) {
   11.54 +	case APP_CMD_SAVE_STATE:
   11.55 +		/* save the application state to be reloaded on restart if needed */
   11.56 +		break;
   11.57 +
   11.58 +	case APP_CMD_INIT_WINDOW:
   11.59 +		if(init_gl() == -1) {
   11.60 +			exit(1);
   11.61 +		}
   11.62 +		/* initialize the application */
   11.63 +		if(app_init() == -1) {
   11.64 +			exit(1);	/* initialization failed, quit */
   11.65 +		}
   11.66 +		break;
   11.67 +
   11.68 +	case APP_CMD_TERM_WINDOW:
   11.69 +		/* cleanup */
   11.70 +		app_cleanup();
   11.71 +		destroy_gl();
   11.72 +		break;
   11.73 +
   11.74 +	case APP_CMD_GAINED_FOCUS:
   11.75 +		/* app focused */
   11.76 +		break;
   11.77 +
   11.78 +	case APP_CMD_LOST_FOCUS:
   11.79 +		/* app lost focus */
   11.80 +		break;
   11.81 +
   11.82 +	case APP_CMD_WINDOW_RESIZED:
   11.83 +	case APP_CMD_CONFIG_CHANGED:
   11.84 +		{
   11.85 +			int nx = ANativeWindow_getWidth(app->window);
   11.86 +			int ny = ANativeWindow_getHeight(app->window);
   11.87 +			if(nx != win_width || ny != win_height) {
   11.88 +				app_resize(nx, ny);
   11.89 +				win_width = nx;
   11.90 +				win_height = ny;
   11.91 +			}
   11.92 +		}
   11.93 +		break;
   11.94 +
   11.95 +	default:
   11.96 +		break;
   11.97 +	}
   11.98 +}
   11.99 +
  11.100 +static int handle_input(struct android_app *app, AInputEvent *ev)
  11.101 +{
  11.102 +	int evtype = AInputEvent_getType(ev);
  11.103 +
  11.104 +	switch(evtype) {
  11.105 +	case AINPUT_EVENT_TYPE_MOTION:
  11.106 +		return handle_touch_input(app, ev);
  11.107 +
  11.108 +	default:
  11.109 +		break;
  11.110 +	}
  11.111 +	return 0;
  11.112 +}
  11.113 +
  11.114 +#define MAX_TOUCH_IDS	32
  11.115 +
  11.116 +static int handle_touch_input(struct android_app *app, AInputEvent *ev)
  11.117 +{
  11.118 +	int x, y, idx, touch_id;
  11.119 +	unsigned int action;
  11.120 +	static int prev_pos[MAX_TOUCH_IDS][2];
  11.121 +
  11.122 +	action = AMotionEvent_getAction(ev);
  11.123 +
  11.124 +	idx = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
  11.125 +		AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
  11.126 +	touch_id = AMotionEvent_getPointerId(ev, idx);
  11.127 +
  11.128 +	x = AMotionEvent_getX(ev, idx);
  11.129 +	y = AMotionEvent_getY(ev, idx);
  11.130 +
  11.131 +	switch(action & AMOTION_EVENT_ACTION_MASK) {
  11.132 +	case AMOTION_EVENT_ACTION_DOWN:
  11.133 +	case AMOTION_EVENT_ACTION_POINTER_DOWN:
  11.134 +		app_touch(touch_id, 1, x, y);
  11.135 +		if(touch_id < MAX_TOUCH_IDS) {
  11.136 +			prev_pos[touch_id][0] = x;
  11.137 +			prev_pos[touch_id][1] = y;
  11.138 +		}
  11.139 +		break;
  11.140 +
  11.141 +	case AMOTION_EVENT_ACTION_UP:
  11.142 +	case AMOTION_EVENT_ACTION_POINTER_UP:
  11.143 +		app_touch(touch_id, 0, x, y);
  11.144 +		if(touch_id < MAX_TOUCH_IDS) {
  11.145 +			prev_pos[touch_id][0] = x;
  11.146 +			prev_pos[touch_id][1] = y;
  11.147 +		}
  11.148 +		break;
  11.149 +
  11.150 +	case AMOTION_EVENT_ACTION_MOVE:
  11.151 +		{
  11.152 +			int i, pcount = AMotionEvent_getPointerCount(ev);
  11.153 +			for(i=0; i<pcount; i++) {
  11.154 +				int id = AMotionEvent_getPointerId(ev, i);
  11.155 +				if(id < MAX_TOUCH_IDS && x != prev_pos[id][0] && y != prev_pos[id][1]) {
  11.156 +					app_drag(id, x, y);
  11.157 +					prev_pos[id][0] = x;
  11.158 +					prev_pos[id][1] = y;
  11.159 +				}
  11.160 +			}
  11.161 +		}
  11.162 +		break;
  11.163 +
  11.164 +	default:
  11.165 +		break;
  11.166 +	}
  11.167 +
  11.168 +	return 1;
  11.169 +}
  11.170 +
  11.171 +
  11.172 +static int init_gl(void)
  11.173 +{
  11.174 +	static const int eglattr[] = {
  11.175 +		EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  11.176 +		EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
  11.177 +		EGL_RED_SIZE, 5,
  11.178 +		EGL_GREEN_SIZE, 5,
  11.179 +		EGL_BLUE_SIZE, 5,
  11.180 +		EGL_DEPTH_SIZE, 16,
  11.181 +		EGL_NONE
  11.182 +	};
  11.183 +	static const int ctxattr[] = { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE };
  11.184 +
  11.185 +	EGLConfig eglcfg;
  11.186 +	int count, vis;
  11.187 +
  11.188 +	dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  11.189 +	if(!dpy || !eglInitialize(dpy, 0, 0)) {
  11.190 +		fprintf(stderr, "failed to initialize EGL\n");
  11.191 +		destroy_gl();
  11.192 +		return -1;
  11.193 +	}
  11.194 +
  11.195 +	if(!eglChooseConfig(dpy, eglattr, &eglcfg, 1, &count)) {
  11.196 +		fprintf(stderr, "no matching EGL config found\n");
  11.197 +		destroy_gl();
  11.198 +		return -1;
  11.199 +	}
  11.200 +
  11.201 +	/* configure the native window visual according to the chosen EGL config */
  11.202 +	eglGetConfigAttrib(dpy, &eglcfg, EGL_NATIVE_VISUAL_ID, &vis);
  11.203 +	ANativeWindow_setBuffersGeometry(app->window, 0, 0, vis);
  11.204 +
  11.205 +	if(!(surf = eglCreateWindowSurface(dpy, eglcfg, app->window, 0))) {
  11.206 +		fprintf(stderr, "failed to create window\n");
  11.207 +		destroy_gl();
  11.208 +		return -1;
  11.209 +	}
  11.210 +
  11.211 +	if(!(ctx = eglCreateContext(dpy, eglcfg, EGL_NO_CONTEXT, ctxattr))) {
  11.212 +		fprintf(stderr, "failed to create OpenGL ES context\n");
  11.213 +		destroy_gl();
  11.214 +		return -1;
  11.215 +	}
  11.216 +	eglMakeCurrent(dpy, surf, surf, ctx);
  11.217 +
  11.218 +	eglQuerySurface(dpy, surf, EGL_WIDTH, &win_width);
  11.219 +	eglQuerySurface(dpy, surf, EGL_HEIGHT, &win_height);
  11.220 +	app_resize(win_width, win_height);
  11.221 +
  11.222 +	return 0;
  11.223 +}
  11.224 +
  11.225 +static void destroy_gl(void)
  11.226 +{
  11.227 +	if(!dpy) return;
  11.228 +
  11.229 +	eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  11.230 +
  11.231 +	if(ctx) {
  11.232 +		eglDestroyContext(dpy, ctx);
  11.233 +		ctx = 0;
  11.234 +	}
  11.235 +	if(surf) {
  11.236 +		eglDestroySurface(dpy, surf);
  11.237 +		surf = 0;
  11.238 +	}
  11.239 +
  11.240 +	eglTerminate(dpy);
  11.241 +	dpy = 0;
  11.242 +}
  11.243 +