screen-relative events
[andemo] / src / android / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <time.h>
4 #include <unistd.h>
5 #include <sys/time.h>
6 #include <EGL/egl.h>
7 #include <GLES2/gl2.h>
8 #include <android/window.h>
9 #include "android_native_app_glue.h"
10 #include "demo.h"
11 #include "logger.h"
12 #include "demosys.h"
13
14 static void handle_command(struct android_app *app, int32_t cmd);
15 static int handle_input(struct android_app *app, AInputEvent *ev);
16 static int handle_touch_input(struct android_app *app, AInputEvent *ev);
17 static int init_gl(void);
18 static void destroy_gl(void);
19 static unsigned long get_time_msec(void);
20 static void hide_navbar(struct android_app *state);
21
22 struct android_app *app;
23
24 static EGLDisplay dpy;
25 static EGLSurface surf;
26 static EGLContext ctx;
27 static int init_done, paused;
28
29 static int width, height;
30
31 static long start_time;
32
33
34 void android_main(struct android_app *app_ptr)
35 {
36         app = app_ptr;
37
38         app->onAppCmd = handle_command;
39         app->onInputEvent = handle_input;
40
41         hide_navbar(app);
42
43         start_logger();
44
45         printf("Running %d bit version\n", (int)sizeof(void*) << 3);
46
47         for(;;) {
48                 int num_events;
49                 struct android_poll_source *pollsrc;
50
51                 while(ALooper_pollAll(0, 0, &num_events, (void**)&pollsrc) >= 0) {
52                         if(pollsrc) {
53                                 pollsrc->process(app, pollsrc);
54                         }
55                 }
56
57                 if(app->destroyRequested) {
58                         return;
59                 }
60                 if(init_done && !paused) {
61                         time_msec = (long)get_time_msec() - start_time;
62                         demo_display();
63                         eglSwapBuffers(dpy, surf);
64                 }
65         }
66 }
67
68 void swap_buffers(void)
69 {
70         eglSwapBuffers(dpy, surf);
71 }
72
73 static void handle_command(struct android_app *app, int32_t cmd)
74 {
75         int xsz, ysz;
76
77         switch(cmd) {
78         case APP_CMD_PAUSE:
79                 paused = 1;     /* TODO: handle timers */
80                 dsys_stop();
81                 break;
82         case APP_CMD_RESUME:
83                 paused = 0;
84                 dsys_run();
85                 break;
86
87         case APP_CMD_INIT_WINDOW:
88                 ANativeActivity_setWindowFlags(app->activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0);
89                 if(init_gl() == -1) {
90                         exit(1);
91                 }
92                 if(demo_init() == -1) {
93                         exit(1);
94                 }
95                 demo_reshape(width, height);
96                 start_time = (long)get_time_msec();
97                 init_done = 1;
98                 dsys_run();
99                 break;
100
101         case APP_CMD_TERM_WINDOW:
102                 init_done = 0;
103                 demo_cleanup();
104                 destroy_gl();
105                 break;
106
107         case APP_CMD_WINDOW_RESIZED:
108         case APP_CMD_CONFIG_CHANGED:
109                 xsz = ANativeWindow_getWidth(app->window);
110                 ysz = ANativeWindow_getHeight(app->window);
111                 if(xsz != width || ysz != height) {
112                         printf("reshape(%d, %d)\n", xsz, ysz);
113                         demo_reshape(xsz, ysz);
114                         width = xsz;
115                         height = ysz;
116                 }
117                 break;
118
119         /*
120         case APP_CMD_SAVE_STATE:
121         case APP_CMD_GAINED_FOCUS:
122         case APP_CMD_LOST_FOCUS:
123         */
124         default:
125                 break;
126         }
127 }
128
129 static int handle_input(struct android_app *app, AInputEvent *ev)
130 {
131         int evtype = AInputEvent_getType(ev);
132
133         switch(evtype) {
134         case AINPUT_EVENT_TYPE_MOTION:
135                 return handle_touch_input(app, ev);
136
137         default:
138                 break;
139         }
140         return 0;
141 }
142
143 static int handle_touch_input(struct android_app *app, AInputEvent *ev)
144 {
145         int i, pcount, x, y, idx;
146         unsigned int action;
147         static int prev_pos[2];
148
149         action = AMotionEvent_getAction(ev);
150
151         idx = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
152                 AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
153
154         x = AMotionEvent_getX(ev, idx);
155         y = AMotionEvent_getY(ev, idx);
156
157         switch(action & AMOTION_EVENT_ACTION_MASK) {
158         case AMOTION_EVENT_ACTION_DOWN:
159         case AMOTION_EVENT_ACTION_POINTER_DOWN:
160                 demo_mouse(0, 1, x, y);
161
162                 prev_pos[0] = x;
163                 prev_pos[1] = y;
164                 break;
165
166         case AMOTION_EVENT_ACTION_UP:
167         case AMOTION_EVENT_ACTION_POINTER_UP:
168                 demo_mouse(0, 0, x, y);
169
170                 prev_pos[0] = x;
171                 prev_pos[1] = y;
172                 break;
173
174         case AMOTION_EVENT_ACTION_MOVE:
175                 pcount = AMotionEvent_getPointerCount(ev);
176                 for(i=0; i<pcount; i++) {
177                         int id = AMotionEvent_getPointerId(ev, i);
178                         if(id == 0) {
179                                 demo_motion(x, y);
180                                 prev_pos[0] = x;
181                                 prev_pos[1] = y;
182                                 break;
183                         }
184                 }
185                 break;
186
187         default:
188                 break;
189         }
190
191         return 1;
192 }
193
194 static int init_gl(void)
195 {
196         static const int eglattr[] = {
197                 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
198                 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
199                 EGL_RED_SIZE, 5,
200                 EGL_GREEN_SIZE, 5,
201                 EGL_BLUE_SIZE, 5,
202                 EGL_DEPTH_SIZE, 16,
203                 EGL_NONE
204         };
205         static const int ctxattr[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
206
207         EGLConfig eglcfg;
208         int count, vis;
209
210         dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
211         if(!dpy || !eglInitialize(dpy, 0, 0)) {
212                 fprintf(stderr, "failed to initialize EGL\n");
213                 destroy_gl();
214                 return -1;
215         }
216
217         if(!eglChooseConfig(dpy, eglattr, &eglcfg, 1, &count)) {
218                 fprintf(stderr, "no matching EGL config found\n");
219                 destroy_gl();
220                 return -1;
221         }
222
223         /* configure the native window visual according to the chosen EGL config */
224         eglGetConfigAttrib(dpy, &eglcfg, EGL_NATIVE_VISUAL_ID, &vis);
225         ANativeWindow_setBuffersGeometry(app->window, 0, 0, vis);
226
227         if(!(surf = eglCreateWindowSurface(dpy, eglcfg, app->window, 0))) {
228                 fprintf(stderr, "failed to create window\n");
229                 destroy_gl();
230                 return -1;
231         }
232
233         if(!(ctx = eglCreateContext(dpy, eglcfg, EGL_NO_CONTEXT, ctxattr))) {
234                 fprintf(stderr, "failed to create OpenGL ES context\n");
235                 destroy_gl();
236                 return -1;
237         }
238         eglMakeCurrent(dpy, surf, surf, ctx);
239
240         eglQuerySurface(dpy, surf, EGL_WIDTH, &width);
241         eglQuerySurface(dpy, surf, EGL_HEIGHT, &height);
242         return 0;
243 }
244
245 static void destroy_gl(void)
246 {
247         if(!dpy) return;
248
249         eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
250
251         if(ctx) {
252                 eglDestroyContext(dpy, ctx);
253                 ctx = 0;
254         }
255         if(surf) {
256                 eglDestroySurface(dpy, surf);
257                 surf = 0;
258         }
259
260         eglTerminate(dpy);
261         dpy = 0;
262 }
263
264 static unsigned long get_time_msec(void)
265 {
266         struct timespec ts;
267         static struct timespec ts0;
268
269         clock_gettime(CLOCK_MONOTONIC, &ts);
270         if(ts0.tv_sec == 0 && ts0.tv_nsec == 0) {
271                 ts0 = ts;
272                 return 0;
273         }
274         return (ts.tv_sec - ts0.tv_sec) * 1000 + (ts.tv_nsec - ts0.tv_nsec) / 1000000;
275 }
276
277 static void hide_navbar(struct android_app *state)
278 {
279         JNIEnv *env;
280         jclass cactivity, cwin, cview;
281         jobject win, view;
282         jmethodID get_window, get_decor_view, set_system_ui_visibility;
283         jfieldID field_flag_fs, field_flag_hidenav, field_flag_immersive;
284         int flag_fs, flag_hidenav, flag_immersive;
285
286         (*state->activity->vm)->AttachCurrentThread(state->activity->vm, &env, 0);
287
288         cactivity = (*env)->FindClass(env, "android/app/NativeActivity");
289         get_window = (*env)->GetMethodID(env, cactivity, "getWindow", "()Landroid/view/Window;");
290
291         cwin = (*env)->FindClass(env, "android/view/Window");
292         get_decor_view = (*env)->GetMethodID(env, cwin, "getDecorView", "()Landroid/view/View;");
293
294         cview = (*env)->FindClass(env, "android/view/View");
295         set_system_ui_visibility = (*env)->GetMethodID(env, cview, "setSystemUiVisibility", "(I)V");
296
297         win = (*env)->CallObjectMethod(env, state->activity->clazz, get_window);
298         view = (*env)->CallObjectMethod(env, win, get_decor_view);
299
300         field_flag_fs = (*env)->GetStaticFieldID(env, cview, "SYSTEM_UI_FLAG_FULLSCREEN", "I");
301         field_flag_hidenav = (*env)->GetStaticFieldID(env, cview, "SYSTEM_UI_FLAG_HIDE_NAVIGATION", "I");
302         field_flag_immersive = (*env)->GetStaticFieldID(env, cview, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY", "I");
303
304         flag_fs = (*env)->GetStaticIntField(env, cview, field_flag_fs);
305         flag_hidenav = (*env)->GetStaticIntField(env, cview, field_flag_hidenav);
306         flag_immersive = (*env)->GetStaticIntField(env, cview, field_flag_immersive);
307
308         (*env)->CallVoidMethod(env, view, set_system_ui_visibility, flag_fs | flag_hidenav | flag_immersive);
309
310         (*state->activity->vm)->DetachCurrentThread(state->activity->vm);
311 }