Fix compiler warnings
[freeglut] / src / android / fg_main_android.c
1 /*
2  * fg_main_android.c
3  *
4  * The Android-specific windows message processing methods.
5  *
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7  * Written by Pawel W. Olszta, <olszta@sourceforge.net>
8  * Copied for Platform code by Evan Felix <karcaw at gmail.com>
9  * Copyright (C) 2012  Sylvain Beucler
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
24  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
25  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  */
28
29 #include <GL/freeglut.h>
30 #include "fg_internal.h"
31 #include "fg_main.h"
32
33 #include <android/log.h>
34 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "FreeGLUT", __VA_ARGS__))
35 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "FreeGLUT", __VA_ARGS__))
36 #include <android/native_app_glue/android_native_app_glue.h>
37 #include <android/keycodes.h>
38
39 static struct touchscreen touchscreen;
40 static unsigned char key_a2fg[256];
41
42 /* Cf. http://developer.android.com/reference/android/view/KeyEvent.html */
43 /* These codes are missing in <android/keycodes.h> */
44 /* Don't convert to enum, since it may conflict with future version of
45    that <android/keycodes.h> */
46 #define AKEYCODE_FORWARD_DEL 112
47 #define AKEYCODE_CTRL_LEFT 113
48 #define AKEYCODE_CTRL_RIGHT 114
49 #define AKEYCODE_MOVE_HOME 122
50 #define AKEYCODE_MOVE_END 123
51 #define AKEYCODE_INSERT 124
52 #define AKEYCODE_ESCAPE 127
53 #define AKEYCODE_F1 131
54 #define AKEYCODE_F2 132
55 #define AKEYCODE_F3 133
56 #define AKEYCODE_F4 134
57 #define AKEYCODE_F5 135
58 #define AKEYCODE_F6 136
59 #define AKEYCODE_F7 137
60 #define AKEYCODE_F8 138
61 #define AKEYCODE_F9 139
62 #define AKEYCODE_F10 140
63 #define AKEYCODE_F11 141
64 #define AKEYCODE_F12 142
65
66 #define EVENT_HANDLED 1
67 #define EVENT_NOT_HANDLED 0
68
69 /**
70  * Initialize Android keycode to GLUT keycode mapping
71  */
72 static void key_init() {
73   memset(key_a2fg, 0, sizeof(key_a2fg));
74
75   key_a2fg[AKEYCODE_F1]  = GLUT_KEY_F1;
76   key_a2fg[AKEYCODE_F2]  = GLUT_KEY_F2;
77   key_a2fg[AKEYCODE_F3]  = GLUT_KEY_F3;
78   key_a2fg[AKEYCODE_F4]  = GLUT_KEY_F4;
79   key_a2fg[AKEYCODE_F5]  = GLUT_KEY_F5;
80   key_a2fg[AKEYCODE_F6]  = GLUT_KEY_F6;
81   key_a2fg[AKEYCODE_F7]  = GLUT_KEY_F7;
82   key_a2fg[AKEYCODE_F8]  = GLUT_KEY_F8;
83   key_a2fg[AKEYCODE_F9]  = GLUT_KEY_F9;
84   key_a2fg[AKEYCODE_F10] = GLUT_KEY_F10;
85   key_a2fg[AKEYCODE_F11] = GLUT_KEY_F11;
86   key_a2fg[AKEYCODE_F12] = GLUT_KEY_F12;
87
88   key_a2fg[AKEYCODE_PAGE_UP]   = GLUT_KEY_PAGE_UP;
89   key_a2fg[AKEYCODE_PAGE_DOWN] = GLUT_KEY_PAGE_DOWN;
90   key_a2fg[AKEYCODE_MOVE_HOME] = GLUT_KEY_HOME;
91   key_a2fg[AKEYCODE_MOVE_END]  = GLUT_KEY_END;
92   key_a2fg[AKEYCODE_INSERT]    = GLUT_KEY_INSERT;
93
94   key_a2fg[AKEYCODE_DPAD_UP]    = GLUT_KEY_UP;
95   key_a2fg[AKEYCODE_DPAD_DOWN]  = GLUT_KEY_DOWN;
96   key_a2fg[AKEYCODE_DPAD_LEFT]  = GLUT_KEY_LEFT;
97   key_a2fg[AKEYCODE_DPAD_RIGHT] = GLUT_KEY_RIGHT;
98
99   key_a2fg[AKEYCODE_ALT_LEFT]    = GLUT_KEY_ALT_L;
100   key_a2fg[AKEYCODE_ALT_RIGHT]   = GLUT_KEY_ALT_R;
101   key_a2fg[AKEYCODE_SHIFT_LEFT]  = GLUT_KEY_SHIFT_L;
102   key_a2fg[AKEYCODE_SHIFT_RIGHT] = GLUT_KEY_SHIFT_R;
103   key_a2fg[AKEYCODE_CTRL_LEFT]   = GLUT_KEY_CTRL_L;
104   key_a2fg[AKEYCODE_CTRL_RIGHT]  = GLUT_KEY_CTRL_R;
105 }
106
107 /**
108  * Convert an Android key event to ASCII.
109  */
110 static unsigned char key_ascii(struct android_app* app, AInputEvent* event) {
111   int32_t code = AKeyEvent_getKeyCode(event);
112
113   /* Handle a few special cases: */
114   switch (code) {
115   case AKEYCODE_DEL:
116     return 8;
117   case AKEYCODE_FORWARD_DEL:
118     return 127;
119   case AKEYCODE_ESCAPE:
120     return 27;
121   }
122
123   /* Get usable JNI context */
124   JNIEnv* env = app->activity->env;
125   JavaVM* vm = app->activity->vm;
126   (*vm)->AttachCurrentThread(vm, &env, NULL);
127
128   jclass KeyEventClass = (*env)->FindClass(env, "android/view/KeyEvent");
129   jmethodID KeyEventConstructor = (*env)->GetMethodID(env, KeyEventClass, "<init>", "(II)V");
130   jobject keyEvent = (*env)->NewObject(env, KeyEventClass, KeyEventConstructor,
131                                        AKeyEvent_getAction(event), AKeyEvent_getKeyCode(event));
132   jmethodID KeyEvent_getUnicodeChar = (*env)->GetMethodID(env, KeyEventClass, "getUnicodeChar", "(I)I");
133   int ascii = (*env)->CallIntMethod(env, keyEvent, KeyEvent_getUnicodeChar, AKeyEvent_getMetaState(event));
134
135   /* LOGI("getUnicodeChar(%d) = %d ('%c')", AKeyEvent_getKeyCode(event), ascii, ascii); */
136
137   return ascii;
138 }
139
140 /*
141  * Handle a window configuration change. When no reshape
142  * callback is hooked, the viewport size is updated to
143  * match the new window size.
144  */
145 void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height )
146 {
147   fprintf(stderr, "fgPlatformReshapeWindow: STUB\n");
148 }
149
150 /*
151  * A static helper function to execute display callback for a window
152  */
153 void fgPlatformDisplayWindow ( SFG_Window *window )
154 {
155   fghRedrawWindow ( window ) ;
156 }
157
158 unsigned long fgPlatformSystemTime ( void )
159 {
160   struct timeval now;
161   gettimeofday( &now, NULL );
162   return now.tv_usec/1000 + now.tv_sec*1000;
163 }
164
165 /*
166  * Does the magic required to relinquish the CPU until something interesting
167  * happens.
168  */
169 void fgPlatformSleepForEvents( long msec )
170 {
171   /* fprintf(stderr, "fgPlatformSleepForEvents: STUB\n"); */
172 }
173
174 /**
175  * Process the next input event.
176  */
177 int32_t handle_input(struct android_app* app, AInputEvent* event) {
178   SFG_Window* window = fgStructure.CurrentWindow;
179
180   /* FIXME: in Android, when key is repeated, down and up events
181      happen most often at the exact same time.  This makes it
182      impossible to animate based on key press time. */
183   /* e.g. down/up/wait/down/up rather than down/wait/down/wait/up */
184   
185   if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {
186     /* LOGI("action: %d", AKeyEvent_getAction(event)); */
187     /* LOGI("keycode: %d", code); */
188     int32_t code = AKeyEvent_getKeyCode(event);
189
190     if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) {
191       int32_t keypress = 0;
192       unsigned char ascii = 0;
193       if ((keypress = key_a2fg[code]) && FETCH_WCB(*window, Special)) {
194         INVOKE_WCB(*window, Special, (keypress, window->State.MouseX, window->State.MouseY));
195         return EVENT_HANDLED;
196       } else if ((ascii = key_ascii(app, event)) && FETCH_WCB(*window, Keyboard)) {
197         INVOKE_WCB(*window, Keyboard, (ascii, window->State.MouseX, window->State.MouseY));
198         return EVENT_HANDLED;
199       }
200     }
201     else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_UP) {
202       int32_t keypress = 0;
203       unsigned char ascii = 0;
204       if ((keypress = key_a2fg[code]) && FETCH_WCB(*window, Special)) {
205         INVOKE_WCB(*window, SpecialUp, (keypress, window->State.MouseX, window->State.MouseY));
206         return EVENT_HANDLED;
207       } else if ((ascii = key_ascii(app, event)) && FETCH_WCB(*window, Keyboard)) {
208         INVOKE_WCB(*window, KeyboardUp, (ascii, window->State.MouseX, window->State.MouseY));
209         return EVENT_HANDLED;
210       }
211     }
212   }
213
214   if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
215     int32_t action = AMotionEvent_getAction(event);
216     float x = AMotionEvent_getX(event, 0);
217     float y = AMotionEvent_getY(event, 0);
218     LOGI("motion %.01f,%.01f action=%d", x, y, AMotionEvent_getAction(event));
219     
220     /* Virtual arrows PAD */
221     /* Don't interfere with existing mouse move event */
222     if (!touchscreen.in_mmotion) {
223       struct vpad_state prev_vpad = touchscreen.vpad;
224       touchscreen.vpad.left = touchscreen.vpad.right
225         = touchscreen.vpad.up = touchscreen.vpad.down = false;
226
227       /* int32_t width = ANativeWindow_getWidth(window->Window.Handle); */
228       int32_t height = ANativeWindow_getHeight(window->Window.Handle);
229       if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_MOVE) {
230         if ((x > 0 && x < 100) && (y > (height - 100) && y < height))
231           touchscreen.vpad.left = true;
232         if ((x > 200 && x < 300) && (y > (height - 100) && y < height))
233           touchscreen.vpad.right = true;
234         if ((x > 100 && x < 200) && (y > (height - 100) && y < height))
235           touchscreen.vpad.down = true;
236         if ((x > 100 && x < 200) && (y > (height - 200) && y < (height - 100)))
237           touchscreen.vpad.up = true;
238       }
239       if (action == AMOTION_EVENT_ACTION_DOWN && 
240           (touchscreen.vpad.left || touchscreen.vpad.right || touchscreen.vpad.down || touchscreen.vpad.up))
241         touchscreen.vpad.on = true;
242       if (action == AMOTION_EVENT_ACTION_UP)
243         touchscreen.vpad.on = false;
244       if (prev_vpad.left != touchscreen.vpad.left
245           || prev_vpad.right != touchscreen.vpad.right
246           || prev_vpad.up != touchscreen.vpad.up
247           || prev_vpad.down != touchscreen.vpad.down
248           || prev_vpad.on != touchscreen.vpad.on) {
249         if (FETCH_WCB(*window, Special)) {
250           if (prev_vpad.left == false && touchscreen.vpad.left == true)
251             INVOKE_WCB(*window, Special, (GLUT_KEY_LEFT, x, y));
252           else if (prev_vpad.right == false && touchscreen.vpad.right == true)
253             INVOKE_WCB(*window, Special, (GLUT_KEY_RIGHT, x, y));
254           else if (prev_vpad.up == false && touchscreen.vpad.up == true)
255             INVOKE_WCB(*window, Special, (GLUT_KEY_UP, x, y));
256           else if (prev_vpad.down == false && touchscreen.vpad.down == true)
257             INVOKE_WCB(*window, Special, (GLUT_KEY_DOWN, x, y));
258         }
259         if (FETCH_WCB(*window, SpecialUp)) {
260           if (prev_vpad.left == true && touchscreen.vpad.left == false)
261             INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_LEFT, x, y));
262           if (prev_vpad.right == true && touchscreen.vpad.right == false)
263             INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_RIGHT, x, y));
264           if (prev_vpad.up == true && touchscreen.vpad.up == false)
265             INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_UP, x, y));
266           if (prev_vpad.down == true && touchscreen.vpad.down == false)
267             INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_DOWN, x, y));
268         }
269         return EVENT_HANDLED;
270       }
271     }
272     
273     /* Normal mouse events */
274     if (!touchscreen.vpad.on) {
275       window->State.MouseX = x;
276       window->State.MouseY = y;
277       LOGI("Changed mouse position: %f,%f", x, y);
278       if (action == AMOTION_EVENT_ACTION_DOWN && FETCH_WCB(*window, Mouse)) {
279         touchscreen.in_mmotion = true;
280         INVOKE_WCB(*window, Mouse, (GLUT_LEFT_BUTTON, GLUT_DOWN, x, y));
281       } else if (action == AMOTION_EVENT_ACTION_UP && FETCH_WCB(*window, Mouse)) {
282         touchscreen.in_mmotion = false;
283         INVOKE_WCB(*window, Mouse, (GLUT_LEFT_BUTTON, GLUT_UP, x, y));
284       } else if (action == AMOTION_EVENT_ACTION_MOVE && FETCH_WCB(*window, Motion)) {
285         INVOKE_WCB(*window, Motion, (x, y));
286       }
287     }
288     
289     return EVENT_HANDLED;
290   }
291
292   /* Let Android handle other events (e.g. Back and Menu buttons) */
293   return EVENT_NOT_HANDLED;
294 }
295
296 /**
297  * Process the next main command.
298  */
299 void handle_cmd(struct android_app* app, int32_t cmd) {
300   switch (cmd) {
301   case APP_CMD_SAVE_STATE:
302     /* The system has asked us to save our current state.  Do so. */
303     LOGI("handle_cmd: APP_CMD_SAVE_STATE");
304     break;
305   case APP_CMD_INIT_WINDOW:
306     /* The window is being shown, get it ready. */
307     LOGI("handle_cmd: APP_CMD_INIT_WINDOW");
308     fgDisplay.pDisplay.single_window->Window.Handle = app->window;
309     /* glPlatformOpenWindow was waiting for Handle to be defined and
310        will now return from fgPlatformProcessSingleEvent() */
311     break;
312   case APP_CMD_TERM_WINDOW:
313     /* The window is being hidden or closed, clean it up. */
314     LOGI("handle_cmd: APP_CMD_TERM_WINDOW");
315     fgDestroyWindow(fgDisplay.pDisplay.single_window);
316     break;
317   case APP_CMD_DESTROY:
318     /* Not reached because GLUT exit()s when last window is closed */
319     LOGI("handle_cmd: APP_CMD_DESTROY");
320     break;
321   case APP_CMD_GAINED_FOCUS:
322     LOGI("handle_cmd: APP_CMD_GAINED_FOCUS");
323     break;
324   case APP_CMD_LOST_FOCUS:
325     LOGI("handle_cmd: APP_CMD_LOST_FOCUS");
326     break;
327   case APP_CMD_CONFIG_CHANGED:
328     /* Handle rotation / orientation change */
329     LOGI("handle_cmd: APP_CMD_CONFIG_CHANGED");
330     break;
331   case APP_CMD_WINDOW_RESIZED:
332     LOGI("handle_cmd: APP_CMD_WINDOW_RESIZED");
333     if (fgDisplay.pDisplay.single_window->Window.pContext.egl.Surface != EGL_NO_SURFACE)
334       /* Make ProcessSingleEvent detect the new size, only available
335          after the next SwapBuffer */
336       glutPostRedisplay();
337     break;
338   default:
339     LOGI("handle_cmd: unhandled cmd=%d", cmd);
340   }
341 }
342
343 void fgPlatformProcessSingleEvent ( void )
344 {
345   static int32_t last_width = -1;
346   static int32_t last_height = -1;
347
348   /* When the screen is resized, the window handle still points to the
349      old window until the next SwapBuffer, while it's crucial to set
350      the size (onShape) correctly before the next onDisplay callback.
351      Plus we don't know if the next SwapBuffer already occurred at the
352      time we process the event (e.g. during onDisplay). */
353   /* So we do the check each time rather than on event. */
354   /* Interestingly, on a Samsung Galaxy S/PowerVR SGX540 GPU/Android
355      2.3, that next SwapBuffer is fake (but still necessary to get the
356      new size). */
357   SFG_Window* window = fgDisplay.pDisplay.single_window;
358   if (window != NULL && window->Window.Handle != NULL) {
359     int32_t width = ANativeWindow_getWidth(window->Window.Handle);
360     int32_t height = ANativeWindow_getHeight(window->Window.Handle);
361     if (width != last_width || height != last_height) {
362       last_width = width;
363       last_height = height;
364       LOGI("width=%d, height=%d", width, height);
365       if( FETCH_WCB( *window, Reshape ) )
366         INVOKE_WCB( *window, Reshape, ( width, height ) );
367       else
368         glViewport( 0, 0, width, height );
369       glutPostRedisplay();
370     }
371   }
372
373   /* Read pending event. */
374   int ident;
375   int events;
376   struct android_poll_source* source;
377   /* This is called "ProcessSingleEvent" but this means we'd only
378      process ~60 (screen Hz) mouse events per second, plus other ports
379      are processing all events already.  So let's process all pending
380      events. */
381   /* if ((ident=ALooper_pollOnce(0, NULL, &events, (void**)&source)) >= 0) { */
382   while ((ident=ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0) {
383     /* Process this event. */
384     if (source != NULL) {
385       source->process(source->app, source);
386     }
387   }
388 }
389
390 void fgPlatformMainLoopPreliminaryWork ( void )
391 {
392   printf("fgPlatformMainLoopPreliminaryWork\n");
393
394   key_init();
395
396   /* Make sure glue isn't stripped. */
397   /* JNI entry points need to be bundled even when linking statically */
398   app_dummy();
399 }
400
401 void fgPlatformDeinitialiseInputDevices ( void )
402 {
403   fprintf(stderr, "fgPlatformDeinitialiseInputDevices: STUB\n");
404 }