3 namespace gui::drivers;
15 #include <sys/prctl.h>
17 #include <android/configuration.h>
18 #include <android/looper.h>
19 #include <android/native_activity.h>
20 #include <android/sensor.h>
21 #include <android/log.h>
22 #include <android/window.h>
28 #define printf(...) ((void)__android_log_print(ANDROID_LOG_INFO, "ecere-app", __VA_ARGS__))
30 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "ecere-app", __VA_ARGS__))
31 #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "ecere-app", __VA_ARGS__))
32 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "ecere-app", __VA_ARGS__))
34 #define LOGV(...) ((void)0)
36 #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "ecere-app", __VA_ARGS__))
39 // *** NATIVE APP GLUE ********
40 enum LooperID { main = 1, input = 2, user = 3 };
41 enum AppCommand : byte
43 error = 0, inputChanged, initWindow, termWindow, windowResized, windowRedrawNeeded,
44 contentRectChanged, gainedFocus, lostFocus,
45 configChanged, lowMemory, start, resume, saveState, pause, stop, destroy
48 class AndroidPollSource
53 virtual void any_object::process();
56 static const char * packagePath;
58 class AndroidAppGlue : Thread
61 virtual void onAppCmd(AppCommand cmd);
62 virtual int onInputEvent(AInputEvent* event);
65 ANativeActivity* activity;
66 AConfiguration* config;
71 AInputQueue* inputQueue;
72 ANativeWindow* window;
74 AppCommand activityState;
75 bool destroyRequested;
82 int msgread, msgwrite;
86 config = AConfiguration_new();
87 AConfiguration_fromAssetManager(config, activity->assetManager);
91 looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
92 ALooper_addFd(looper, msgread, LooperID::main, ALOOPER_EVENT_INPUT, null, cmdPollSource);
110 AInputQueue_detachLooper(inputQueue);
111 AConfiguration_delete(config);
117 AndroidPollSource cmdPollSource
123 AppCommand cmd = read_cmd();
129 AndroidPollSource inputPollSource
135 AInputEvent* event = null;
136 if(AInputQueue_getEvent(inputQueue, &event) >= 0)
139 LOGV("New input event: type=%d\n", AInputEvent_getType(event));
140 if(AInputQueue_preDispatchEvent(inputQueue, event))
142 /*handled = */onInputEvent(event);
143 //AInputQueue_finishEvent(inputQueue, event, handled);
146 LOGE("Failure reading next input event: %s\n", strerror(errno));
153 AInputQueue* pendingInputQueue;
154 ANativeWindow* pendingWindow;
155 ARect pendingContentRect;
157 void free_saved_state()
167 AppCommand read_cmd()
170 if(read(msgread, &cmd, sizeof(cmd)) == sizeof(cmd))
177 LOGE("No data on command pipe!");
181 void print_cur_config()
183 char lang[2], country[2];
184 AConfiguration_getLanguage(config, lang);
185 AConfiguration_getCountry(config, country);
187 LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
188 "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
189 "modetype=%d modenight=%d",
190 AConfiguration_getMcc(config),
191 AConfiguration_getMnc(config),
192 lang[0], lang[1], country[0], country[1],
193 AConfiguration_getOrientation(config),
194 AConfiguration_getTouchscreen(config),
195 AConfiguration_getDensity(config),
196 AConfiguration_getKeyboard(config),
197 AConfiguration_getNavigation(config),
198 AConfiguration_getKeysHidden(config),
199 AConfiguration_getNavHidden(config),
200 AConfiguration_getSdkVersion(config),
201 AConfiguration_getScreenSize(config),
202 AConfiguration_getScreenLong(config),
203 AConfiguration_getUiModeType(config),
204 AConfiguration_getUiModeNight(config));
207 void pre_exec_cmd(AppCommand cmd)
209 //PrintLn("pre_exec_cmd: ", cmd);
215 AInputQueue_detachLooper(inputQueue);
216 inputQueue = pendingInputQueue;
218 AInputQueue_attachLooper(inputQueue, looper, LooperID::input, null, inputPollSource);
224 window = pendingWindow;
241 AConfiguration_fromAssetManager(config, activity->assetManager);
245 destroyRequested = true;
250 void post_exec_cmd(AppCommand cmd)
252 //PrintLn("post_exec_cmd: ", cmd);
273 void write_cmd(AppCommand cmd)
275 if(write(msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd))
276 LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
279 void set_input(AInputQueue* inputQueue)
282 pendingInputQueue = inputQueue;
283 write_cmd(inputChanged);
284 while(inputQueue != pendingInputQueue)
289 void set_window(ANativeWindow* window)
293 write_cmd(termWindow);
294 pendingWindow = window;
296 write_cmd(initWindow);
297 while(window != pendingWindow)
302 void set_activity_state(AppCommand cmd)
306 while(activityState != cmd)
322 void setSavedState(void * state, uint size)
329 savedState = malloc(size);
330 savedStateSize = size;
331 memcpy(savedState, state, size);
341 LOGE("could not create pipe: %s", strerror(errno));
342 msgread = msgpipe[0];
343 msgwrite = msgpipe[1];
347 // Wait for thread to start.
349 while(!running) cond.Wait(mutex);
355 static void onDestroy(ANativeActivity* activity)
357 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
358 LOGI("Destroy: %p\n", activity);
361 delete androidActivity;
362 delete __androidCurrentModule;
366 static void onStart(ANativeActivity* activity)
368 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
369 LOGI("Start: %p\n", activity);
370 app.set_activity_state(start);
373 static void onResume(ANativeActivity* activity)
375 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
376 LOGI("Resume: %p\n", activity);
377 app.set_activity_state(resume);
380 static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen)
382 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
383 void* savedState = null;
384 LOGI("SaveInstanceState: %p\n", activity);
386 app.stateSaved = false;
387 app.write_cmd(saveState);
388 while(!app.stateSaved)
389 app.cond.Wait(app.mutex);
392 savedState = app.savedState;
393 *outLen = app.savedStateSize;
394 app.savedState = null;
395 app.savedStateSize = 0;
401 static void onPause(ANativeActivity* activity)
403 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
404 LOGI("Pause: %p\n", activity);
405 app.set_activity_state(pause);
408 static void onStop(ANativeActivity* activity)
410 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
411 LOGI("Stop: %p\n", activity);
412 app.set_activity_state(stop);
415 static void onConfigurationChanged(ANativeActivity* activity)
417 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
418 LOGI("ConfigurationChanged: %p\n", activity);
419 app.write_cmd(configChanged);
422 static void onLowMemory(ANativeActivity* activity)
424 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
425 LOGI("LowMemory: %p\n", activity);
426 app.write_cmd(lowMemory);
429 static void onWindowFocusChanged(ANativeActivity* activity, int focused)
431 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
432 LOGI("WindowFocusChanged: %p -- %d\n", activity, focused);
433 app.write_cmd(focused ? gainedFocus : lostFocus);
436 static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window)
438 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
439 LOGI("NativeWindowCreated: %p -- %p\n", activity, window);
440 app.set_window(window);
443 static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window)
445 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
446 LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window);
448 app.set_window(null);
451 static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue)
453 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
454 LOGI("InputQueueCreated: %p -- %p\n", activity, queue);
455 app.set_input(queue);
458 static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue)
460 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
461 LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue);
462 app.inputQueue = null;
466 default dllexport void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize)
471 // Determine our package name
472 JNIEnv* env=activity->env;
479 // *** Reinitialize static global variables ***
481 guiApplicationInitialized = false;
483 desktopW = 0; desktopH = 0;
484 clipBoardData = null;
486 __androidCurrentModule = null;
488 prctl(PR_SET_DUMPABLE, 1);
490 LOGI("Creating: %p\n", activity);
492 //(*activity->vm)->AttachCurrentThread(activity->vm, &env, 0);
493 clazz = (*env)->GetObjectClass(env, activity->clazz);
494 methodID = (*env)->GetMethodID(env, clazz, "getPackageName", "()Ljava/lang/String;");
495 result = (*env)->CallObjectMethod(env, activity->clazz, methodID);
496 str = (*env)->GetStringUTFChars(env, (jstring)result, &isCopy);
498 moduleName = strstr(str, "com.ecere.");
499 if(moduleName) moduleName += 10;
500 androidArgv[0] = moduleName;
502 methodID = (*env)->GetMethodID(env, clazz, "getPackageCodePath", "()Ljava/lang/String;");
503 result = (*env)->CallObjectMethod(env, activity->clazz, methodID);
504 str = (*env)->GetStringUTFChars(env, (jstring)result, &isCopy);
506 // (*activity->vm)->DetachCurrentThread(activity->vm);
507 LOGI("packagePath: %s\n", packagePath);
509 // Create a base Application class
510 __androidCurrentModule = __ecere_COM_Initialize(true, 1, androidArgv);
512 eModule_Load(__androidCurrentModule, "ecere", publicAccess);
515 if(activity->internalDataPath) PrintLn("internalDataPath is ", activity->internalDataPath);
516 if(activity->externalDataPath) PrintLn("externalDataPath is ", activity->externalDataPath);
519 PrintLn("cwd is ", GetWorkingDir(tmp, sizeof(tmp)));
523 ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_FULLSCREEN|AWINDOW_FLAG_KEEP_SCREEN_ON, 0 );
524 app = AndroidActivity { activity = activity, moduleName = moduleName };
527 app.setSavedState(savedState, (uint)savedStateSize);
528 activity->callbacks->onDestroy = onDestroy;
529 activity->callbacks->onStart = onStart;
530 activity->callbacks->onResume = onResume;
531 activity->callbacks->onSaveInstanceState = onSaveInstanceState;
532 activity->callbacks->onPause = onPause;
533 activity->callbacks->onStop = onStop;
534 activity->callbacks->onConfigurationChanged = onConfigurationChanged;
535 activity->callbacks->onLowMemory = onLowMemory;
536 activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
537 activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
538 activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
539 activity->callbacks->onInputQueueCreated = onInputQueueCreated;
540 activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
541 activity->instance = app;
545 // *** END OF NATIVE APP GLUE ******
548 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
549 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp;
550 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown;
551 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
552 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove;
553 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick;
554 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown;
555 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp;
556 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick;
557 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown;
558 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp;
559 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick;
560 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown;
561 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp;
564 static Module __androidCurrentModule;
565 static char * androidArgv[1];
567 static int desktopW, desktopH;
568 static char * clipBoardData;
569 static int mouseX, mouseY;
571 class AndroidInterface : Interface
573 class_property(name) = "Android";
575 // --- User Interface System ---
578 setlocale(LC_ALL, "en_US.UTF-8");
587 #define DBLCLICK_DELAY 300 // 0.3 second
588 #define DBLCLICK_DELTA 1
590 bool ProcessInput(bool processAll)
592 bool eventAvailable = false;
594 if(androidActivity.ident < 0)
595 androidActivity.ident = (LooperID)ALooper_pollAll(0, null, &androidActivity.events, (void**)&androidActivity.source);
597 if(gotInit && androidActivity.window)
599 int w = ANativeWindow_getWidth(androidActivity.window);
600 int h = ANativeWindow_getHeight(androidActivity.window);
601 if(desktopW != w || desktopH != h)
603 guiApp.SetDesktopPosition(0, 0, w, h, true);
606 guiApp.desktop.Update(null);
610 while(androidActivity.ident >= 0)
612 AndroidPollSource source = androidActivity.source;
614 androidActivity.source = null;
616 source.process(source.userData);
618 // If a sensor has data, process it now.
620 if(androidActivity.ident == user)
622 if(androidActivity.accelerometerSensor)
625 while (ASensorEventQueue_getEvents(androidActivity.sensorEventQueue, &event, 1) > 0)
626 LOGI("accelerometer: x=%f y=%f z=%f", event.acceleration.x, event.acceleration.y, event.acceleration.z);
631 eventAvailable = true;
632 if(androidActivity.destroyRequested)
634 guiApp.desktop.Destroy(0);
635 eventAvailable = true;
636 androidActivity.ident = (LooperID)-1;
639 androidActivity.ident = (LooperID)ALooper_pollAll(0, null, &androidActivity.events, (void**)&androidActivity.source);
641 androidActivity.ident = (LooperID)-1;
643 return eventAvailable;
648 androidActivity.ident = (LooperID)ALooper_pollAll((int)(1000/18.2f), null, &androidActivity.events, (void**)&androidActivity.source);
649 // guiApp.WaitEvent();
652 void Lock(Window window)
657 void Unlock(Window window)
662 const char ** GraphicsDrivers(int * numDrivers)
664 static const char *graphicsDrivers[] = { "OpenGL" };
665 *numDrivers = sizeof(graphicsDrivers) / sizeof(char *);
666 return (const char **)graphicsDrivers;
669 void GetCurrentMode(bool * fullScreen, int * resolution, int * colorDepth, int * refreshRate)
674 void EnsureFullScreen(bool *fullScreen)
679 bool ScreenMode(bool fullScreen, int resolution, int colorDepth, int refreshRate, bool * textMode)
686 // --- Window Creation ---
687 void * CreateRootWindow(Window window)
689 return androidActivity.window;
692 void DestroyRootWindow(Window window)
697 // -- Window manipulation ---
699 void SetRootWindowCaption(Window window, const char * name)
704 void PositionRootWindow(Window window, int x, int y, int w, int h, bool move, bool resize)
709 void OrderRootWindow(Window window, bool topMost)
714 void SetRootWindowColor(Window window)
719 void OffsetWindow(Window window, int * x, int * y)
724 void UpdateRootWindow(Window window)
726 if(!window.parent || !window.parent.display)
730 Box box = window.box;
731 box.left -= window.clientStart.x;
732 box.top -= window.clientStart.y;
733 box.right -= window.clientStart.x;
734 box.bottom -= window.clientStart.y;
735 // Logf("Update root window %s\n", window.name);
737 box.left += window.clientStart.x;
738 box.top += window.clientStart.y;
739 box.right += window.clientStart.x;
740 box.bottom += window.clientStart.y;
741 window.UpdateDirty(box);
747 void SetRootWindowState(Window window, WindowState state, bool visible)
751 void FlashRootWindow(Window window)
756 void ActivateRootWindow(Window window)
761 // --- Mouse-based window movement ---
763 void StartMoving(Window window, int x, int y, bool fromKeyBoard)
768 void StopMoving(Window window)
773 // -- Mouse manipulation ---
775 void GetMousePosition(int *x, int *y)
781 void SetMousePosition(int x, int y)
787 void SetMouseRange(Window window, Box box)
791 void SetMouseCapture(Window window)
795 // -- Mouse cursor ---
797 void SetMouseCursor(Window window, int cursor)
807 void SetCaret(int x, int y, int size)
809 Window caretOwner = guiApp.caretOwner;
810 Window window = caretOwner ? caretOwner.rootWindow : null;
811 if(window && window.windowData)
816 void ClearClipboard()
820 delete clipBoardData;
824 bool AllocateClipboard(ClipBoard clipBoard, uint size)
827 if((clipBoard.text = new0 byte[size]))
832 bool SaveClipboard(ClipBoard clipBoard)
838 delete clipBoardData;
840 clipBoardData = clipBoard.text;
841 clipBoard.text = null;
847 bool LoadClipboard(ClipBoard clipBoard)
851 // The data is inside this client...
854 clipBoard.text = new char[strlen(clipBoardData)+1];
855 strcpy(clipBoard.text, clipBoardData);
858 // The data is with another client...
865 void UnloadClipboard(ClipBoard clipBoard)
867 delete clipBoard.text;
870 // --- State based input ---
872 bool AcquireInput(Window window, bool state)
877 bool GetMouseState(MouseButtons * buttons, int * x, int * y)
885 bool GetJoystickState(int device, Joystick joystick)
891 bool GetKeyState(Key key)
893 bool keyState = false;
897 void SetTimerResolution(uint hertz)
899 // timerDelay = hertz ? (1000000 / hertz) : MAXINT;
902 bool SetIcon(Window window, BitmapResource resource)
907 if(bitmap.Load(resource.fileName, null, null))
923 static AndroidActivity androidActivity;
925 default const char * AndroidInterface_GetLibLocation(Application a)
927 static char loc[MAX_LOCATION] = "", mod[MAX_LOCATION];
929 #if defined(__LP64__)
930 static const char * arch = "arm64";
932 static const char * arch = "armeabi";
939 StripLastDirectory(packagePath, loc);
940 strcatf(loc, "/lib/%s/lib", useArch ? arch : "");
941 sprintf(mod, "%s%s.so", loc, a.argv[0]);
942 found = FileExists(mod).isFile;
948 for(i = 0; !found && i < 10; i++)
951 sprintf(loc, "/data/%s/com.ecere.%s-%d/lib/%s/lib", useApp ? "app" : "data", a.argv[0], i, useArch ? arch : "");
953 sprintf(loc, "/data/%s/com.ecere.%s/lib/%s/lib", useApp ? "app" : "data", a.argv[0], useArch ? arch : "");
954 sprintf(mod, "%s%s.so", loc, a.argv[0]);
955 found = FileExists(mod).isFile;
973 default float AMotionEvent_getAxisValue(const AInputEvent* motion_event,
974 int32_t axis, size_t pointer_index);
977 static define AMETA_META_ON = 0x00010000;
978 static define AMETA_META_LEFT_ON = 0x00020000;
979 static define AMETA_META_RIGHT_ON = 0x00040000;
981 static Key keyCodeTable[] =
983 0, //AKEYCODE_UNKNOWN = 0,
984 0, //AKEYCODE_SOFT_LEFT = 1,
985 0, //AKEYCODE_SOFT_RIGHT = 2,
986 0, //AKEYCODE_HOME = 3,
987 0, //AKEYCODE_BACK = 4,
988 0, //AKEYCODE_CALL = 5,
989 0, //AKEYCODE_ENDCALL = 6,
990 k0, //AKEYCODE_0 = 7,
991 k1, //AKEYCODE_1 = 8,
992 k2, //AKEYCODE_2 = 9,
993 k3, //AKEYCODE_3 = 10,
994 k4, //AKEYCODE_4 = 11,
995 k5, //AKEYCODE_5 = 12,
996 k6, //AKEYCODE_6 = 13,
997 k7, //AKEYCODE_7 = 14,
998 k8, //AKEYCODE_8 = 15,
999 k9, //AKEYCODE_9 = 16,
1000 keyPadStar, //AKEYCODE_STAR = 17,
1001 Key { k3, shift = true }, //AKEYCODE_POUND = 18,
1002 up, //AKEYCODE_DPAD_UP = 19,
1003 down, //AKEYCODE_DPAD_DOWN = 20,
1004 left, //AKEYCODE_DPAD_LEFT = 21,
1005 right, //AKEYCODE_DPAD_RIGHT = 22,
1006 keyPad5, //AKEYCODE_DPAD_CENTER = 23,
1007 0, //AKEYCODE_VOLUME_UP = 24,
1008 0, //AKEYCODE_VOLUME_DOWN = 25,
1009 0, //AKEYCODE_POWER = 26,
1010 0, //AKEYCODE_CAMERA = 27,
1011 0, //AKEYCODE_CLEAR = 28,
1012 a, //AKEYCODE_A = 29,
1013 b, //AKEYCODE_B = 30,
1014 c, //AKEYCODE_C = 31,
1015 d, //AKEYCODE_D = 32,
1016 e, //AKEYCODE_E = 33,
1017 f, //AKEYCODE_F = 34,
1018 g, //AKEYCODE_G = 35,
1019 h, //AKEYCODE_H = 36,
1020 i, //AKEYCODE_I = 37,
1021 j, //AKEYCODE_J = 38,
1022 k, //AKEYCODE_K = 39,
1023 l, //AKEYCODE_L = 40,
1024 m, //AKEYCODE_M = 41,
1025 n, //AKEYCODE_N = 42,
1026 o, //AKEYCODE_O = 43,
1027 p, //AKEYCODE_P = 44,
1028 q, //AKEYCODE_Q = 45,
1029 r, //AKEYCODE_R = 46,
1030 s, //AKEYCODE_S = 47,
1031 t, //AKEYCODE_T = 48,
1032 u, //AKEYCODE_U = 49,
1033 v, //AKEYCODE_V = 50,
1034 w, //AKEYCODE_W = 51,
1035 x, //AKEYCODE_X = 52,
1036 y, //AKEYCODE_Y = 53,
1037 z, //AKEYCODE_Z = 54,
1038 comma, //AKEYCODE_COMMA = 55,
1039 period, //AKEYCODE_PERIOD = 56,
1040 leftAlt, //AKEYCODE_ALT_LEFT = 57,
1041 rightAlt, //AKEYCODE_ALT_RIGHT = 58,
1042 leftShift, //AKEYCODE_SHIFT_LEFT = 59,
1043 rightShift, //AKEYCODE_SHIFT_RIGHT = 60,
1044 tab, //AKEYCODE_TAB = 61,
1045 space, //AKEYCODE_SPACE = 62,
1046 0, //AKEYCODE_SYM = 63,
1047 0, //AKEYCODE_EXPLORER = 64,
1048 0, //AKEYCODE_ENVELOPE = 65,
1049 enter, //AKEYCODE_ENTER = 66,
1050 backSpace, //AKEYCODE_DEL = 67,
1051 backQuote, //AKEYCODE_GRAVE = 68,
1052 minus, //AKEYCODE_MINUS = 69,
1053 plus, //AKEYCODE_EQUALS = 70,
1054 leftBracket, //AKEYCODE_LEFT_BRACKET = 71,
1055 rightBracket, //AKEYCODE_RIGHT_BRACKET = 72,
1056 backSlash, //AKEYCODE_BACKSLASH = 73,
1057 semicolon, //AKEYCODE_SEMICOLON = 74,
1058 quote, //AKEYCODE_APOSTROPHE = 75,
1059 slash, //AKEYCODE_SLASH = 76,
1060 Key { k2, shift = true }, //AKEYCODE_AT = 77,
1061 0, //AKEYCODE_NUM = 78, // Interpreted as an Alt
1062 0, //AKEYCODE_HEADSETHOOK = 79,
1063 0, //AKEYCODE_FOCUS = 80, // *Camera* focus
1064 keyPadPlus, //AKEYCODE_PLUS = 81,
1065 0, //AKEYCODE_MENU = 82,
1066 0, //AKEYCODE_NOTIFICATION = 83,
1067 0, //AKEYCODE_SEARCH = 84,
1068 0, //AKEYCODE_MEDIA_PLAY_PAUSE= 85,
1069 0, //AKEYCODE_MEDIA_STOP = 86,
1070 0, //AKEYCODE_MEDIA_NEXT = 87,
1071 0, //AKEYCODE_MEDIA_PREVIOUS = 88,
1072 0, //AKEYCODE_MEDIA_REWIND = 89,
1073 0, //AKEYCODE_MEDIA_FAST_FORWARD = 90,
1074 0, //AKEYCODE_MUTE = 91,
1075 0, //AKEYCODE_PAGE_UP = 92,
1076 0, //AKEYCODE_PAGE_DOWN = 93,
1077 0, //AKEYCODE_PICTSYMBOLS = 94,
1078 0, //AKEYCODE_SWITCH_CHARSET = 95,
1079 0, //AKEYCODE_BUTTON_A = 96,
1080 0, //AKEYCODE_BUTTON_B = 97,
1081 0, //AKEYCODE_BUTTON_C = 98,
1082 0, //AKEYCODE_BUTTON_X = 99,
1083 0, //AKEYCODE_BUTTON_Y = 100,
1084 0, //AKEYCODE_BUTTON_Z = 101,
1085 0, //AKEYCODE_BUTTON_L1 = 102,
1086 0, //AKEYCODE_BUTTON_R1 = 103,
1087 0, //AKEYCODE_BUTTON_L2 = 104,
1088 0, //AKEYCODE_BUTTON_R2 = 105,
1089 0, //AKEYCODE_BUTTON_THUMBL = 106,
1090 0, //AKEYCODE_BUTTON_THUMBR = 107,
1091 0, //AKEYCODE_BUTTON_START = 108,
1092 0, //AKEYCODE_BUTTON_SELECT = 109,
1093 0, //AKEYCODE_BUTTON_MODE = 110,
1094 escape, //AKEYCODE_BUTTON_ESCAPE = 111,
1095 del, //AKEYCODE_BUTTON_ESCAPE = 112,
1096 leftControl, // = 113
1097 rightControl, // = 114
1099 scrollLock, // = 116
1100 0, // = 117 KEYCODE_META_LEFT
1101 0, // = 118 KEYCODE_META_RIGHT
1102 0, // = 119 KEYCODE_FUNCTION
1103 printScreen, // = 120 KEYCODE_SYSRQ
1104 pauseBreak, // = 121
1110 // Why don't we have this in the NDK :(
1111 // default int32_t AKeyEvent_getUnichar(const AInputEvent* key_event);
1113 static Array<TouchPointerInfo> buildPointerInfo(AInputEvent * event)
1115 uint count = (uint)AMotionEvent_getPointerCount(event);
1116 Array<TouchPointerInfo> infos = null;
1120 infos = { size = count };
1121 for(i = 0; i < count; i++)
1123 infos[i].point = { (int)AMotionEvent_getX(event, i), (int)AMotionEvent_getY(event, i) };
1124 infos[i].id = (int)AMotionEvent_getPointerId(event, i);
1125 infos[i].pressure = AMotionEvent_getPressure(event, i);
1126 infos[i].size = AMotionEvent_getSize(event, i);
1132 class AndroidActivity : AndroidAppGlue
1134 AndroidPollSource source;
1138 ASensorManager* sensorManager;
1139 const ASensor* accelerometerSensor;
1140 ASensorEventQueue* sensorEventQueue;
1144 int onInputEvent(AInputEvent* event)
1146 static Time lastTime = 0;
1147 Window window = guiApp.desktop;
1148 uint type = AInputEvent_getType(event);
1149 if(type == AINPUT_EVENT_TYPE_MOTION)
1151 uint actionAndIndex = AMotionEvent_getAction(event);
1152 //uint source = AInputEvent_getSource(event);
1153 uint action = actionAndIndex & AMOTION_EVENT_ACTION_MASK;
1154 //uint index = (actionAndIndex & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
1155 //uint flags = AMotionEvent_getFlags(event);
1156 uint meta = AMotionEvent_getMetaState(event);
1157 //uint edge = AMotionEvent_getEdgeFlags(event);
1158 //int64 downTime = AMotionEvent_getDownTime(event); // nanotime
1159 //int64 eventTime = AMotionEvent_getDownTime(event);
1161 Modifiers keyFlags = 0;
1162 uint count = (uint)AMotionEvent_getPointerCount(event);
1163 int x = count ? (int)AMotionEvent_getX(event, 0) : 0;
1164 int y = count ? (int)AMotionEvent_getY(event, 0) : 0;
1165 bool shift = (meta & AMETA_SHIFT_ON) ? true : false;
1166 bool alt = (meta & AMETA_ALT_ON) ? true : false;
1167 //bool sym = (meta & AMETA_SYM_ON) ? true : false;
1169 keyFlags.shift = shift;
1172 //PrintLn("Got a motion input event: ", action);
1174 if(action == 8) //AMOTION_EVENT_ACTION_SCROLL)
1175 axis = AMotionEvent_getAxisValue(event, 9, index); //AMOTION_EVENT_AXIS_VSCROLL);
1178 AInputQueue_finishEvent(inputQueue, event, 1);
1182 case 8: //AMOTION_EVENT_ACTION_SCROLL:
1183 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, (axis < 0) ? wheelUp : wheelDown, 0);
1186 case AMOTION_EVENT_ACTION_DOWN:
1188 Time time = GetTime();
1190 if(Abs(x - mouseX) < 40 && Abs(y - mouseY) < 40 && time - lastTime < 0.3)
1191 if(!window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick, x, y, &keyFlags, false, true))
1194 mouseX = x, mouseY = y;
1196 // TOCHECK: Should we do result = here?
1197 window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, x, y, &keyFlags, false, true);
1200 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1201 window.MultiTouchMessage(down, infos, &keyFlags, false, true);
1206 case AMOTION_EVENT_ACTION_UP:
1207 mouseX = x, mouseY = y;
1208 if(window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp, x, y, &keyFlags, false, true))
1210 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1211 window.MultiTouchMessage(up, infos, &keyFlags, false, true);
1215 case AMOTION_EVENT_ACTION_MOVE:
1216 mouseX = x, mouseY = y;
1217 if(window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove, x, y, &keyFlags, false, true))
1219 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1220 window.MultiTouchMessage(move, infos, &keyFlags, false, true);
1224 case AMOTION_EVENT_ACTION_CANCEL: break;
1225 case AMOTION_EVENT_ACTION_OUTSIDE: break;
1226 case AMOTION_EVENT_ACTION_POINTER_DOWN:
1228 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1229 window.MultiTouchMessage(pointerDown, infos, &keyFlags, false, true);
1233 case AMOTION_EVENT_ACTION_POINTER_UP:
1235 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1236 window.MultiTouchMessage(pointerUp, infos, &keyFlags, false, true);
1243 else if(type == AINPUT_EVENT_TYPE_KEY)
1245 uint action = AKeyEvent_getAction(event);
1246 //uint flags = AKeyEvent_getFlags(event);
1247 uint keyCode = AKeyEvent_getKeyCode(event);
1248 uint meta = AKeyEvent_getMetaState(event);
1249 Key key = keyCodeTable[keyCode];
1250 bool shift = (meta & AMETA_SHIFT_ON) ? true : false;
1251 bool alt = (meta & AMETA_ALT_ON || meta & AMETA_ALT_LEFT_ON || meta & AMETA_ALT_RIGHT_ON) ? true : false;
1252 //bool metaMeta = (meta & AMETA_META_ON || meta & AMETA_META_LEFT_ON || meta & AMETA_META_RIGHT_ON) ? true : false;
1253 //bool sym = (meta & AMETA_SYM_ON) ? true : false;
1254 //unichar ch = AKeyEvent_getUnichar(event);
1260 AInputQueue_finishEvent(inputQueue, event, 1);
1262 // PrintLn("Got a key: action = ", action, ", flags = ", flags, ", keyCode = ", keyCode, ", meta = ", meta, ": key = ", (int)key);
1266 if(action == AKEY_EVENT_ACTION_DOWN || action == AKEY_EVENT_ACTION_MULTIPLE)
1268 /*if(key == wheelDown || key == wheelUp)
1269 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, key, ch);
1272 char c = Interface::TranslateKey(key.code, shift);
1274 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, key, ch);
1277 else if(action == AKEY_EVENT_ACTION_UP)
1278 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, key, ch);
1283 AInputQueue_finishEvent(inputQueue, event, 0);
1287 void onAppCmd(AppCommand cmd)
1292 setSavedState(&state, sizeof(state));
1299 ANativeWindow_setBuffersGeometry(window, 0, 0, 0); //format);
1300 w = ANativeWindow_getWidth(window);
1301 h = ANativeWindow_getHeight(window);
1302 guiApp.Initialize(false);
1303 guiApp.desktop.windowHandle = window;
1304 guiApp.interfaceDriver = null;
1305 guiApp.SwitchMode(true, null, 0, 0, 0, null, false);
1307 if(desktopW != w || desktopH != h)
1309 guiApp.SetDesktopPosition(0, 0, w, h, true);
1313 guiApp.desktop.Update(null);
1317 guiApp.desktop.UnloadGraphics(false);
1320 guiApp.desktop.Update(null);
1321 guiApp.SetAppFocus(true);
1323 if(accelerometerSensor)
1325 ASensorEventQueue_enableSensor(sensorEventQueue, accelerometerSensor);
1326 ASensorEventQueue_setEventRate(sensorEventQueue, accelerometerSensor, (1000L/60)*1000);
1332 if(accelerometerSensor)
1333 ASensorEventQueue_disableSensor(sensorEventQueue, accelerometerSensor);
1335 guiApp.SetAppFocus(false);
1336 guiApp.desktop.Update(null);
1340 guiApp.desktop.UpdateDisplay();
1347 androidActivity = this;
1348 /* Let's have fun with sensors when we have an actual device to play with
1349 sensorManager = ASensorManager_getInstance();
1350 accelerometerSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER);
1351 sensorEventQueue = ASensorManager_createEventQueue(sensorManager, looper, LooperID::user, null, null);
1355 state = *(SavedState*)savedState;
1360 // Evolve the Application class into a GuiApplication
1361 eInstance_Evolve((Instance *)&__androidCurrentModule, class(GuiApplication));
1363 // Wait for the initWindow command:
1364 guiApp.interfaceDriver = class(AndroidInterface);
1367 // Can't call the GuiApplication here, because GuiApplication::Initialize() has not been called yet
1368 guiApp.interfaceDriver.Wait();
1369 guiApp.interfaceDriver.ProcessInput(true);
1372 // Invoke __ecereDll_Load() in lib[our package name].so
1373 app = eModule_Load(__androidCurrentModule, moduleName, publicAccess);
1377 // Find out if any GuiApplication class was defined in our module
1378 for(c = app.classes.first; c && !eClass_IsDerived(c, class(GuiApplication)); c = c.next);
1379 if(!c) c = class(GuiApplication);
1381 guiApp.lockMutex.Release(); // TOCHECK: Seems the evolve is losing our mutex lock here ?
1383 // Evolve the Application into it
1384 eInstance_Evolve((Instance *)&__androidCurrentModule, c);
1385 guiApp = (GuiApplication)__androidCurrentModule;
1388 const String skin = guiApp.skin;
1389 *&guiApp.currentSkin = null;
1390 guiApp.SelectSkin(skin);
1393 guiApp.lockMutex.Wait();
1396 ((void (*)(void *))(void *)__androidCurrentModule._vTbl[12])(__androidCurrentModule);
1399 if(!destroyRequested)
1400 ANativeActivity_finish(activity);
1401 while(!destroyRequested)
1403 guiApp.interfaceDriver.Wait();
1404 guiApp.interfaceDriver.ProcessInput(true);