1 namespace gui::drivers;
13 #include <sys/prctl.h>
15 #include <android/configuration.h>
16 #include <android/looper.h>
17 #include <android/native_activity.h>
18 #include <android/sensor.h>
19 #include <android/log.h>
20 #include <android/window.h>
26 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "ecere-app", __VA_ARGS__))
27 #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "ecere-app", __VA_ARGS__))
28 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "ecere-app", __VA_ARGS__))
30 #define LOGV(...) ((void)0)
32 #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "ecere-app", __VA_ARGS__))
35 // *** NATIVE APP GLUE ********
36 enum LooperID { main = 1, input = 2, user = 3 };
37 enum AppCommand : byte
39 error = 0, inputChanged, initWindow, termWindow, windowResized, windowRedrawNeeded,
40 contentRectChanged, gainedFocus, lostFocus,
41 configChanged, lowMemory, start, resume, saveState, pause, stop, destroy
44 class AndroidPollSource
49 virtual void any_object::process();
52 class AndroidAppGlue : Thread
55 virtual void onAppCmd(AppCommand cmd);
56 virtual int onInputEvent(AInputEvent* event);
59 ANativeActivity* activity;
60 AConfiguration* config;
65 AInputQueue* inputQueue;
66 ANativeWindow* window;
68 AppCommand activityState;
69 bool destroyRequested;
76 int msgread, msgwrite;
80 config = AConfiguration_new();
81 AConfiguration_fromAssetManager(config, activity->assetManager);
85 looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
86 ALooper_addFd(looper, msgread, LooperID::main, ALOOPER_EVENT_INPUT, null, cmdPollSource);
104 AInputQueue_detachLooper(inputQueue);
105 AConfiguration_delete(config);
111 AndroidPollSource cmdPollSource
117 AppCommand cmd = read_cmd();
123 AndroidPollSource inputPollSource
129 AInputEvent* event = null;
130 if(AInputQueue_getEvent(inputQueue, &event) >= 0)
133 LOGV("New input event: type=%d\n", AInputEvent_getType(event));
134 if(AInputQueue_preDispatchEvent(inputQueue, event))
136 /*handled = */onInputEvent(event);
137 //AInputQueue_finishEvent(inputQueue, event, handled);
140 LOGE("Failure reading next input event: %s\n", strerror(errno));
147 AInputQueue* pendingInputQueue;
148 ANativeWindow* pendingWindow;
149 ARect pendingContentRect;
151 void free_saved_state()
161 AppCommand read_cmd()
164 if(read(msgread, &cmd, sizeof(cmd)) == sizeof(cmd))
171 LOGE("No data on command pipe!");
175 void print_cur_config()
177 char lang[2], country[2];
178 AConfiguration_getLanguage(config, lang);
179 AConfiguration_getCountry(config, country);
181 LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
182 "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
183 "modetype=%d modenight=%d",
184 AConfiguration_getMcc(config),
185 AConfiguration_getMnc(config),
186 lang[0], lang[1], country[0], country[1],
187 AConfiguration_getOrientation(config),
188 AConfiguration_getTouchscreen(config),
189 AConfiguration_getDensity(config),
190 AConfiguration_getKeyboard(config),
191 AConfiguration_getNavigation(config),
192 AConfiguration_getKeysHidden(config),
193 AConfiguration_getNavHidden(config),
194 AConfiguration_getSdkVersion(config),
195 AConfiguration_getScreenSize(config),
196 AConfiguration_getScreenLong(config),
197 AConfiguration_getUiModeType(config),
198 AConfiguration_getUiModeNight(config));
201 void pre_exec_cmd(AppCommand cmd)
203 //PrintLn("pre_exec_cmd: ", cmd);
209 AInputQueue_detachLooper(inputQueue);
210 inputQueue = pendingInputQueue;
212 AInputQueue_attachLooper(inputQueue, looper, LooperID::input, null, inputPollSource);
218 window = pendingWindow;
235 AConfiguration_fromAssetManager(config, activity->assetManager);
239 destroyRequested = true;
244 void post_exec_cmd(AppCommand cmd)
246 //PrintLn("post_exec_cmd: ", cmd);
267 void write_cmd(AppCommand cmd)
269 if(write(msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd))
270 LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
273 void set_input(AInputQueue* inputQueue)
276 pendingInputQueue = inputQueue;
277 write_cmd(inputChanged);
278 while(inputQueue != pendingInputQueue)
283 void set_window(ANativeWindow* window)
287 write_cmd(termWindow);
288 pendingWindow = window;
290 write_cmd(initWindow);
291 while(window != pendingWindow)
296 void set_activity_state(AppCommand cmd)
300 while(activityState != cmd)
316 void setSavedState(void * state, uint size)
323 savedState = malloc(size);
324 savedStateSize = size;
325 memcpy(savedState, state, size);
335 LOGE("could not create pipe: %s", strerror(errno));
336 msgread = msgpipe[0];
337 msgwrite = msgpipe[1];
341 // Wait for thread to start.
343 while(!running) cond.Wait(mutex);
349 static void onDestroy(ANativeActivity* activity)
351 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
352 LOGI("Destroy: %p\n", activity);
355 delete androidActivity;
356 delete __androidCurrentModule;
360 static void onStart(ANativeActivity* activity)
362 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
363 LOGI("Start: %p\n", activity);
364 app.set_activity_state(start);
367 static void onResume(ANativeActivity* activity)
369 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
370 LOGI("Resume: %p\n", activity);
371 app.set_activity_state(resume);
374 static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen)
376 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
377 void* savedState = null;
378 LOGI("SaveInstanceState: %p\n", activity);
380 app.stateSaved = false;
381 app.write_cmd(saveState);
382 while(!app.stateSaved)
383 app.cond.Wait(app.mutex);
386 savedState = app.savedState;
387 *outLen = app.savedStateSize;
388 app.savedState = null;
389 app.savedStateSize = 0;
395 static void onPause(ANativeActivity* activity)
397 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
398 LOGI("Pause: %p\n", activity);
399 app.set_activity_state(pause);
402 static void onStop(ANativeActivity* activity)
404 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
405 LOGI("Stop: %p\n", activity);
406 app.set_activity_state(stop);
409 static void onConfigurationChanged(ANativeActivity* activity)
411 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
412 LOGI("ConfigurationChanged: %p\n", activity);
413 app.write_cmd(configChanged);
416 static void onLowMemory(ANativeActivity* activity)
418 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
419 LOGI("LowMemory: %p\n", activity);
420 app.write_cmd(lowMemory);
423 static void onWindowFocusChanged(ANativeActivity* activity, int focused)
425 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
426 LOGI("WindowFocusChanged: %p -- %d\n", activity, focused);
427 app.write_cmd(focused ? gainedFocus : lostFocus);
430 static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window)
432 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
433 LOGI("NativeWindowCreated: %p -- %p\n", activity, window);
434 app.set_window(window);
437 static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window)
439 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
440 LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window);
441 app.set_window(null);
444 static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue)
446 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
447 LOGI("InputQueueCreated: %p -- %p\n", activity, queue);
448 app.set_input(queue);
451 static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue)
453 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
454 LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue);
455 app.inputQueue = null;
459 default dllexport void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize)
464 // Determine our package name
465 JNIEnv* env=activity->env;
472 // *** Reinitialize static global variables ***
474 guiApplicationInitialized = false;
476 desktopW = 0; desktopH = 0;
477 clipBoardData = null;
479 __androidCurrentModule = null;
481 prctl(PR_SET_DUMPABLE, 1);
483 LOGI("Creating: %p\n", activity);
485 //(*activity->vm)->AttachCurrentThread(activity->vm, &env, 0);
486 clazz = (*env)->GetObjectClass(env, activity->clazz);
487 methodID = (*env)->GetMethodID(env, clazz, "getPackageName", "()Ljava/lang/String;");
488 result = (*env)->CallObjectMethod(env, activity->clazz, methodID);
489 str = (*env)->GetStringUTFChars(env, (jstring)result, &isCopy);
490 // (*activity->vm)->DetachCurrentThread(activity->vm);
491 moduleName = strstr(str, "com.ecere.");
492 if(moduleName) moduleName += 10;
493 androidArgv[0] = moduleName;
495 // Create a base Application class
496 __androidCurrentModule = __ecere_COM_Initialize(true, 1, androidArgv);
498 eModule_Load(__androidCurrentModule, "ecere", publicAccess);
502 if(activity->internalDataPath) PrintLn("internalDataPath is ", activity->internalDataPath);
503 if(activity->externalDataPath) PrintLn("externalDataPath is ", activity->externalDataPath);
506 PrintLn("cwd is ", GetWorkingDir(tmp, sizeof(tmp)));
510 ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_FULLSCREEN|AWINDOW_FLAG_KEEP_SCREEN_ON, 0 );
511 app = AndroidActivity { activity = activity, moduleName = moduleName };
513 app.setSavedState(savedState, savedStateSize);
514 activity->callbacks->onDestroy = onDestroy;
515 activity->callbacks->onStart = onStart;
516 activity->callbacks->onResume = onResume;
517 activity->callbacks->onSaveInstanceState = onSaveInstanceState;
518 activity->callbacks->onPause = onPause;
519 activity->callbacks->onStop = onStop;
520 activity->callbacks->onConfigurationChanged = onConfigurationChanged;
521 activity->callbacks->onLowMemory = onLowMemory;
522 activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
523 activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
524 activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
525 activity->callbacks->onInputQueueCreated = onInputQueueCreated;
526 activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
527 activity->instance = app;
531 // *** END OF NATIVE APP GLUE ******
534 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
535 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp;
536 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown;
537 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
538 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove;
539 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick;
540 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown;
541 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp;
542 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick;
543 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown;
544 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp;
545 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick;
546 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown;
547 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp;
550 static Module __androidCurrentModule;
551 static char * androidArgv[1];
553 static int desktopW, desktopH;
554 static char * clipBoardData;
555 static int mouseX, mouseY;
557 class AndroidInterface : Interface
559 class_property(name) = "Android";
561 // --- User Interface System ---
564 setlocale(LC_ALL, "en_US.UTF-8");
573 #define DBLCLICK_DELAY 300 // 0.3 second
574 #define DBLCLICK_DELTA 1
576 bool ProcessInput(bool processAll)
578 bool eventAvailable = false;
580 if(androidActivity.ident < 0)
581 androidActivity.ident = (LooperID)ALooper_pollAll(0, null, &androidActivity.events, (void**)&androidActivity.source);
583 if(gotInit && androidActivity.window)
585 int w = ANativeWindow_getWidth(androidActivity.window);
586 int h = ANativeWindow_getHeight(androidActivity.window);
587 if(desktopW != w || desktopH != h)
589 guiApp.SetDesktopPosition(0, 0, w, h, true);
592 guiApp.desktop.Update(null);
596 while(androidActivity.ident >= 0)
598 AndroidPollSource source = androidActivity.source;
600 androidActivity.source = null;
602 source.process(source.userData);
604 // If a sensor has data, process it now.
606 if(androidActivity.ident == user)
608 if(androidActivity.accelerometerSensor)
611 while (ASensorEventQueue_getEvents(androidActivity.sensorEventQueue, &event, 1) > 0)
612 LOGI("accelerometer: x=%f y=%f z=%f", event.acceleration.x, event.acceleration.y, event.acceleration.z);
617 eventAvailable = true;
618 if(androidActivity.destroyRequested)
620 guiApp.desktop.Destroy(0);
621 eventAvailable = true;
622 androidActivity.ident = (LooperID)-1;
625 androidActivity.ident = (LooperID)ALooper_pollAll(0, null, &androidActivity.events, (void**)&androidActivity.source);
627 androidActivity.ident = (LooperID)-1;
629 return eventAvailable;
634 androidActivity.ident = (LooperID)ALooper_pollAll((int)(1000/18.2f), null, &androidActivity.events, (void**)&androidActivity.source);
635 // guiApp.WaitEvent();
638 void Lock(Window window)
643 void Unlock(Window window)
648 const char ** GraphicsDrivers(int * numDrivers)
650 static const char *graphicsDrivers[] = { "OpenGL" };
651 *numDrivers = sizeof(graphicsDrivers) / sizeof(char *);
652 return (const char **)graphicsDrivers;
655 void GetCurrentMode(bool * fullScreen, int * resolution, int * colorDepth, int * refreshRate)
660 void EnsureFullScreen(bool *fullScreen)
665 bool ScreenMode(bool fullScreen, int resolution, int colorDepth, int refreshRate, bool * textMode)
672 // --- Window Creation ---
673 void * CreateRootWindow(Window window)
675 return androidActivity.window;
678 void DestroyRootWindow(Window window)
683 // -- Window manipulation ---
685 void SetRootWindowCaption(Window window, const char * name)
690 void PositionRootWindow(Window window, int x, int y, int w, int h, bool move, bool resize)
695 void OrderRootWindow(Window window, bool topMost)
700 void SetRootWindowColor(Window window)
705 void OffsetWindow(Window window, int * x, int * y)
710 void UpdateRootWindow(Window window)
712 if(!window.parent || !window.parent.display)
716 Box box = window.box;
717 box.left -= window.clientStart.x;
718 box.top -= window.clientStart.y;
719 box.right -= window.clientStart.x;
720 box.bottom -= window.clientStart.y;
721 // Logf("Update root window %s\n", window.name);
723 box.left += window.clientStart.x;
724 box.top += window.clientStart.y;
725 box.right += window.clientStart.x;
726 box.bottom += window.clientStart.y;
727 window.UpdateDirty(box);
733 void SetRootWindowState(Window window, WindowState state, bool visible)
737 void FlashRootWindow(Window window)
742 void ActivateRootWindow(Window window)
747 // --- Mouse-based window movement ---
749 void StartMoving(Window window, int x, int y, bool fromKeyBoard)
754 void StopMoving(Window window)
759 // -- Mouse manipulation ---
761 void GetMousePosition(int *x, int *y)
767 void SetMousePosition(int x, int y)
773 void SetMouseRange(Window window, Box box)
777 void SetMouseCapture(Window window)
781 // -- Mouse cursor ---
783 void SetMouseCursor(Window window, int cursor)
793 void SetCaret(int x, int y, int size)
795 Window caretOwner = guiApp.caretOwner;
796 Window window = caretOwner ? caretOwner.rootWindow : null;
797 if(window && window.windowData)
802 void ClearClipboard()
806 delete clipBoardData;
810 bool AllocateClipboard(ClipBoard clipBoard, uint size)
813 if((clipBoard.text = new0 byte[size]))
818 bool SaveClipboard(ClipBoard clipBoard)
824 delete clipBoardData;
826 clipBoardData = clipBoard.text;
827 clipBoard.text = null;
833 bool LoadClipboard(ClipBoard clipBoard)
837 // The data is inside this client...
840 clipBoard.text = new char[strlen(clipBoardData)+1];
841 strcpy(clipBoard.text, clipBoardData);
844 // The data is with another client...
851 void UnloadClipboard(ClipBoard clipBoard)
853 delete clipBoard.text;
856 // --- State based input ---
858 bool AcquireInput(Window window, bool state)
863 bool GetMouseState(MouseButtons * buttons, int * x, int * y)
871 bool GetJoystickState(int device, Joystick joystick)
877 bool GetKeyState(Key key)
879 bool keyState = false;
883 void SetTimerResolution(uint hertz)
885 // timerDelay = hertz ? (1000000 / hertz) : MAXINT;
888 bool SetIcon(Window window, BitmapResource resource)
893 if(bitmap.Load(resource.fileName, null, null))
909 static AndroidActivity androidActivity;
911 default const char * AndroidInterface_GetLibLocation(Application a)
913 static char loc[MAX_LOCATION];
914 // sprintf(loc, "/data/data/com.ecere.%s/lib/lib", a.argv[0]);
915 sprintf(loc, "/data/app/com.ecere.%s-1/lib/arm64/lib", a.argv[0]);
921 default float AMotionEvent_getAxisValue(const AInputEvent* motion_event,
922 int32_t axis, size_t pointer_index);
925 static define AMETA_META_ON = 0x00010000;
926 static define AMETA_META_LEFT_ON = 0x00020000;
927 static define AMETA_META_RIGHT_ON = 0x00040000;
929 static Key keyCodeTable[] =
931 0, //AKEYCODE_UNKNOWN = 0,
932 0, //AKEYCODE_SOFT_LEFT = 1,
933 0, //AKEYCODE_SOFT_RIGHT = 2,
934 0, //AKEYCODE_HOME = 3,
935 0, //AKEYCODE_BACK = 4,
936 0, //AKEYCODE_CALL = 5,
937 0, //AKEYCODE_ENDCALL = 6,
938 k0, //AKEYCODE_0 = 7,
939 k1, //AKEYCODE_1 = 8,
940 k2, //AKEYCODE_2 = 9,
941 k3, //AKEYCODE_3 = 10,
942 k4, //AKEYCODE_4 = 11,
943 k5, //AKEYCODE_5 = 12,
944 k6, //AKEYCODE_6 = 13,
945 k7, //AKEYCODE_7 = 14,
946 k8, //AKEYCODE_8 = 15,
947 k9, //AKEYCODE_9 = 16,
948 keyPadStar, //AKEYCODE_STAR = 17,
949 Key { k3, shift = true }, //AKEYCODE_POUND = 18,
950 up, //AKEYCODE_DPAD_UP = 19,
951 down, //AKEYCODE_DPAD_DOWN = 20,
952 left, //AKEYCODE_DPAD_LEFT = 21,
953 right, //AKEYCODE_DPAD_RIGHT = 22,
954 keyPad5, //AKEYCODE_DPAD_CENTER = 23,
955 0, //AKEYCODE_VOLUME_UP = 24,
956 0, //AKEYCODE_VOLUME_DOWN = 25,
957 0, //AKEYCODE_POWER = 26,
958 0, //AKEYCODE_CAMERA = 27,
959 0, //AKEYCODE_CLEAR = 28,
960 a, //AKEYCODE_A = 29,
961 b, //AKEYCODE_B = 30,
962 c, //AKEYCODE_C = 31,
963 d, //AKEYCODE_D = 32,
964 e, //AKEYCODE_E = 33,
965 f, //AKEYCODE_F = 34,
966 g, //AKEYCODE_G = 35,
967 h, //AKEYCODE_H = 36,
968 i, //AKEYCODE_I = 37,
969 j, //AKEYCODE_J = 38,
970 k, //AKEYCODE_K = 39,
971 l, //AKEYCODE_L = 40,
972 m, //AKEYCODE_M = 41,
973 n, //AKEYCODE_N = 42,
974 o, //AKEYCODE_O = 43,
975 p, //AKEYCODE_P = 44,
976 q, //AKEYCODE_Q = 45,
977 r, //AKEYCODE_R = 46,
978 s, //AKEYCODE_S = 47,
979 t, //AKEYCODE_T = 48,
980 u, //AKEYCODE_U = 49,
981 v, //AKEYCODE_V = 50,
982 w, //AKEYCODE_W = 51,
983 x, //AKEYCODE_X = 52,
984 y, //AKEYCODE_Y = 53,
985 z, //AKEYCODE_Z = 54,
986 comma, //AKEYCODE_COMMA = 55,
987 period, //AKEYCODE_PERIOD = 56,
988 leftAlt, //AKEYCODE_ALT_LEFT = 57,
989 rightAlt, //AKEYCODE_ALT_RIGHT = 58,
990 leftShift, //AKEYCODE_SHIFT_LEFT = 59,
991 rightShift, //AKEYCODE_SHIFT_RIGHT = 60,
992 tab, //AKEYCODE_TAB = 61,
993 space, //AKEYCODE_SPACE = 62,
994 0, //AKEYCODE_SYM = 63,
995 0, //AKEYCODE_EXPLORER = 64,
996 0, //AKEYCODE_ENVELOPE = 65,
997 enter, //AKEYCODE_ENTER = 66,
998 backSpace, //AKEYCODE_DEL = 67,
999 backQuote, //AKEYCODE_GRAVE = 68,
1000 minus, //AKEYCODE_MINUS = 69,
1001 plus, //AKEYCODE_EQUALS = 70,
1002 leftBracket, //AKEYCODE_LEFT_BRACKET = 71,
1003 rightBracket, //AKEYCODE_RIGHT_BRACKET = 72,
1004 backSlash, //AKEYCODE_BACKSLASH = 73,
1005 semicolon, //AKEYCODE_SEMICOLON = 74,
1006 quote, //AKEYCODE_APOSTROPHE = 75,
1007 slash, //AKEYCODE_SLASH = 76,
1008 Key { k2, shift = true }, //AKEYCODE_AT = 77,
1009 0, //AKEYCODE_NUM = 78, // Interpreted as an Alt
1010 0, //AKEYCODE_HEADSETHOOK = 79,
1011 0, //AKEYCODE_FOCUS = 80, // *Camera* focus
1012 keyPadPlus, //AKEYCODE_PLUS = 81,
1013 0, //AKEYCODE_MENU = 82,
1014 0, //AKEYCODE_NOTIFICATION = 83,
1015 0, //AKEYCODE_SEARCH = 84,
1016 0, //AKEYCODE_MEDIA_PLAY_PAUSE= 85,
1017 0, //AKEYCODE_MEDIA_STOP = 86,
1018 0, //AKEYCODE_MEDIA_NEXT = 87,
1019 0, //AKEYCODE_MEDIA_PREVIOUS = 88,
1020 0, //AKEYCODE_MEDIA_REWIND = 89,
1021 0, //AKEYCODE_MEDIA_FAST_FORWARD = 90,
1022 0, //AKEYCODE_MUTE = 91,
1023 0, //AKEYCODE_PAGE_UP = 92,
1024 0, //AKEYCODE_PAGE_DOWN = 93,
1025 0, //AKEYCODE_PICTSYMBOLS = 94,
1026 0, //AKEYCODE_SWITCH_CHARSET = 95,
1027 0, //AKEYCODE_BUTTON_A = 96,
1028 0, //AKEYCODE_BUTTON_B = 97,
1029 0, //AKEYCODE_BUTTON_C = 98,
1030 0, //AKEYCODE_BUTTON_X = 99,
1031 0, //AKEYCODE_BUTTON_Y = 100,
1032 0, //AKEYCODE_BUTTON_Z = 101,
1033 0, //AKEYCODE_BUTTON_L1 = 102,
1034 0, //AKEYCODE_BUTTON_R1 = 103,
1035 0, //AKEYCODE_BUTTON_L2 = 104,
1036 0, //AKEYCODE_BUTTON_R2 = 105,
1037 0, //AKEYCODE_BUTTON_THUMBL = 106,
1038 0, //AKEYCODE_BUTTON_THUMBR = 107,
1039 0, //AKEYCODE_BUTTON_START = 108,
1040 0, //AKEYCODE_BUTTON_SELECT = 109,
1041 0, //AKEYCODE_BUTTON_MODE = 110,
1042 escape, //AKEYCODE_BUTTON_ESCAPE = 111,
1043 del, //AKEYCODE_BUTTON_ESCAPE = 112,
1044 leftControl, // = 113
1045 rightControl, // = 114
1047 scrollLock, // = 116
1048 0, // = 117 KEYCODE_META_LEFT
1049 0, // = 118 KEYCODE_META_RIGHT
1050 0, // = 119 KEYCODE_FUNCTION
1051 printScreen, // = 120 KEYCODE_SYSRQ
1052 pauseBreak, // = 121
1058 // Why don't we have this in the NDK :(
1059 // default int32_t AKeyEvent_getUnichar(const AInputEvent* key_event);
1061 class AndroidActivity : AndroidAppGlue
1063 AndroidPollSource source;
1067 ASensorManager* sensorManager;
1068 const ASensor* accelerometerSensor;
1069 ASensorEventQueue* sensorEventQueue;
1073 int onInputEvent(AInputEvent* event)
1075 Window window = guiApp.desktop;
1076 uint type = AInputEvent_getType(event);
1077 if(type == AINPUT_EVENT_TYPE_MOTION)
1079 uint actionAndIndex = AMotionEvent_getAction(event);
1080 //uint source = AInputEvent_getSource(event);
1081 uint action = actionAndIndex & AMOTION_EVENT_ACTION_MASK;
1082 //uint index = (actionAndIndex & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
1083 //uint flags = AMotionEvent_getFlags(event);
1084 uint meta = AMotionEvent_getMetaState(event);
1085 //uint edge = AMotionEvent_getEdgeFlags(event);
1086 //int64 downTime = AMotionEvent_getDownTime(event); // nanotime
1087 //int64 eventTime = AMotionEvent_getDownTime(event);
1089 Modifiers keyFlags = 0;
1090 int x = (int)AMotionEvent_getX(event, 0);
1091 int y = (int)AMotionEvent_getY(event, 0);
1092 bool shift = (meta & AMETA_SHIFT_ON) ? true : false;
1093 bool alt = (meta & AMETA_ALT_ON) ? true : false;
1094 //bool sym = (meta & AMETA_SYM_ON) ? true : false;
1096 keyFlags.shift = shift;
1099 //PrintLn("Got a motion input event: ", action);
1101 if(action == 8) //AMOTION_EVENT_ACTION_SCROLL)
1102 axis = AMotionEvent_getAxisValue(event, 9, index); //AMOTION_EVENT_AXIS_VSCROLL);
1105 AInputQueue_finishEvent(inputQueue, event, 1);
1109 case 8: //AMOTION_EVENT_ACTION_SCROLL:
1110 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, (axis < 0) ? wheelUp : wheelDown, 0);
1113 case AMOTION_EVENT_ACTION_DOWN:
1114 mouseX = x, mouseY = y;
1115 window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, x, y, &keyFlags, false, true);
1117 case AMOTION_EVENT_ACTION_UP:
1118 mouseX = x, mouseY = y;
1119 window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp, x, y, &keyFlags, false, true);
1121 case AMOTION_EVENT_ACTION_MOVE:
1122 mouseX = x, mouseY = y;
1123 window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove, x, y, &keyFlags, false, true);
1125 case AMOTION_EVENT_ACTION_CANCEL: break;
1126 case AMOTION_EVENT_ACTION_OUTSIDE: break;
1127 case AMOTION_EVENT_ACTION_POINTER_DOWN: break;
1128 case AMOTION_EVENT_ACTION_POINTER_UP: break;
1132 else if(type == AINPUT_EVENT_TYPE_KEY)
1134 uint action = AKeyEvent_getAction(event);
1135 //uint flags = AKeyEvent_getFlags(event);
1136 uint keyCode = AKeyEvent_getKeyCode(event);
1137 uint meta = AKeyEvent_getMetaState(event);
1138 Key key = keyCodeTable[keyCode];
1139 bool shift = (meta & AMETA_SHIFT_ON) ? true : false;
1140 bool alt = (meta & AMETA_ALT_ON || meta & AMETA_ALT_LEFT_ON || meta & AMETA_ALT_RIGHT_ON) ? true : false;
1141 //bool metaMeta = (meta & AMETA_META_ON || meta & AMETA_META_LEFT_ON || meta & AMETA_META_RIGHT_ON) ? true : false;
1142 //bool sym = (meta & AMETA_SYM_ON) ? true : false;
1143 //unichar ch = AKeyEvent_getUnichar(event);
1149 AInputQueue_finishEvent(inputQueue, event, 1);
1151 // PrintLn("Got a key: action = ", action, ", flags = ", flags, ", keyCode = ", keyCode, ", meta = ", meta, ": key = ", (int)key);
1155 if(action == AKEY_EVENT_ACTION_DOWN || action == AKEY_EVENT_ACTION_MULTIPLE)
1157 /*if(key == wheelDown || key == wheelUp)
1158 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, key, ch);
1161 char c = Interface::TranslateKey(key.code, shift);
1163 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, key, ch);
1166 else if(action == AKEY_EVENT_ACTION_UP)
1167 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, key, ch);
1172 AInputQueue_finishEvent(inputQueue, event, 0);
1176 void onAppCmd(AppCommand cmd)
1181 setSavedState(&state, sizeof(state));
1188 ANativeWindow_setBuffersGeometry(window, 0, 0, 0); //format);
1189 w = ANativeWindow_getWidth(window);
1190 h = ANativeWindow_getHeight(window);
1191 guiApp.Initialize(false);
1192 guiApp.desktop.windowHandle = window;
1193 guiApp.interfaceDriver = null;
1194 guiApp.SwitchMode(true, null, 0, 0, 0, null, false);
1196 if(desktopW != w || desktopH != h)
1198 guiApp.SetDesktopPosition(0, 0, w, h, true);
1202 guiApp.desktop.Update(null);
1206 guiApp.desktop.UnloadGraphics(false);
1209 guiApp.desktop.Update(null);
1210 guiApp.SetAppFocus(true);
1212 if(accelerometerSensor)
1214 ASensorEventQueue_enableSensor(sensorEventQueue, accelerometerSensor);
1215 ASensorEventQueue_setEventRate(sensorEventQueue, accelerometerSensor, (1000L/60)*1000);
1221 if(accelerometerSensor)
1222 ASensorEventQueue_disableSensor(sensorEventQueue, accelerometerSensor);
1224 guiApp.SetAppFocus(false);
1225 guiApp.desktop.Update(null);
1229 guiApp.desktop.UpdateDisplay();
1236 androidActivity = this;
1237 /* Let's have fun with sensors when we have an actual device to play with
1238 sensorManager = ASensorManager_getInstance();
1239 accelerometerSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER);
1240 sensorEventQueue = ASensorManager_createEventQueue(sensorManager, looper, LooperID::user, null, null);
1244 state = *(SavedState*)savedState;
1249 // Evolve the Application class into a GuiApplication
1250 eInstance_Evolve((Instance *)&__androidCurrentModule, class(GuiApplication));
1252 // Wait for the initWindow command:
1253 guiApp.interfaceDriver = class(AndroidInterface);
1256 // Can't call the GuiApplication here, because GuiApplication::Initialize() has not been called yet
1257 guiApp.interfaceDriver.Wait();
1258 guiApp.interfaceDriver.ProcessInput(true);
1261 // Invoke __ecereDll_Load() in lib[our package name].so
1262 app = eModule_Load(__androidCurrentModule, moduleName, publicAccess);
1266 // Find out if any GuiApplication class was defined in our module
1267 for(c = app.classes.first; c && !eClass_IsDerived(c, class(GuiApplication)); c = c.next);
1268 if(!c) c = class(GuiApplication);
1270 guiApp.lockMutex.Release(); // TOCHECK: Seems the evolve is losing our mutex lock here ?
1272 // Evolve the Application into it
1273 eInstance_Evolve((Instance *)&__androidCurrentModule, c);
1274 guiApp = (GuiApplication)__androidCurrentModule;
1277 const String skin = guiApp.skin;
1278 *&guiApp.currentSkin = null;
1279 guiApp.SelectSkin(skin);
1282 guiApp.lockMutex.Wait();
1285 ((void (*)(void *))(void *)__androidCurrentModule._vTbl[12])(__androidCurrentModule);
1288 if(!destroyRequested)
1289 ANativeActivity_finish(activity);
1290 while(!destroyRequested)
1292 guiApp.interfaceDriver.Wait();
1293 guiApp.interfaceDriver.ProcessInput(true);