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 { size = count };
1118 for(i = 0; i < count; i++)
1120 infos[i].point = { (int)AMotionEvent_getX(event, i), (int)AMotionEvent_getY(event, i) };
1121 infos[i].id = (int)AMotionEvent_getPointerId(event, i);
1122 infos[i].pressure = AMotionEvent_getPressure(event, i);
1123 infos[i].size = AMotionEvent_getSize(event, i);
1128 class AndroidActivity : AndroidAppGlue
1130 AndroidPollSource source;
1134 ASensorManager* sensorManager;
1135 const ASensor* accelerometerSensor;
1136 ASensorEventQueue* sensorEventQueue;
1140 int onInputEvent(AInputEvent* event)
1142 static Time lastTime = 0;
1143 Window window = guiApp.desktop;
1144 uint type = AInputEvent_getType(event);
1145 if(type == AINPUT_EVENT_TYPE_MOTION)
1147 uint actionAndIndex = AMotionEvent_getAction(event);
1148 //uint source = AInputEvent_getSource(event);
1149 uint action = actionAndIndex & AMOTION_EVENT_ACTION_MASK;
1150 //uint index = (actionAndIndex & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
1151 //uint flags = AMotionEvent_getFlags(event);
1152 uint meta = AMotionEvent_getMetaState(event);
1153 //uint edge = AMotionEvent_getEdgeFlags(event);
1154 //int64 downTime = AMotionEvent_getDownTime(event); // nanotime
1155 //int64 eventTime = AMotionEvent_getDownTime(event);
1157 Modifiers keyFlags = 0;
1158 int x = (int)AMotionEvent_getX(event, 0);
1159 int y = (int)AMotionEvent_getY(event, 0);
1160 bool shift = (meta & AMETA_SHIFT_ON) ? true : false;
1161 bool alt = (meta & AMETA_ALT_ON) ? true : false;
1162 //bool sym = (meta & AMETA_SYM_ON) ? true : false;
1164 keyFlags.shift = shift;
1167 //PrintLn("Got a motion input event: ", action);
1169 if(action == 8) //AMOTION_EVENT_ACTION_SCROLL)
1170 axis = AMotionEvent_getAxisValue(event, 9, index); //AMOTION_EVENT_AXIS_VSCROLL);
1173 AInputQueue_finishEvent(inputQueue, event, 1);
1177 case 8: //AMOTION_EVENT_ACTION_SCROLL:
1178 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, (axis < 0) ? wheelUp : wheelDown, 0);
1181 case AMOTION_EVENT_ACTION_DOWN:
1183 Time time = GetTime();
1185 if(Abs(x - mouseX) < 40 && Abs(y - mouseY) < 40 && time - lastTime < 0.3)
1186 if(!window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick, x, y, &keyFlags, false, true))
1189 mouseX = x, mouseY = y;
1191 // TOCHECK: Should we do result = here?
1192 window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, x, y, &keyFlags, false, true);
1195 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1196 window.MultiTouchMessage(down, infos, &keyFlags, false, true);
1201 case AMOTION_EVENT_ACTION_UP:
1202 mouseX = x, mouseY = y;
1203 if(window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp, x, y, &keyFlags, false, true))
1205 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1206 window.MultiTouchMessage(up, infos, &keyFlags, false, true);
1210 case AMOTION_EVENT_ACTION_MOVE:
1211 mouseX = x, mouseY = y;
1212 if(window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove, x, y, &keyFlags, false, true))
1214 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1215 window.MultiTouchMessage(move, infos, &keyFlags, false, true);
1219 case AMOTION_EVENT_ACTION_CANCEL: break;
1220 case AMOTION_EVENT_ACTION_OUTSIDE: break;
1221 case AMOTION_EVENT_ACTION_POINTER_DOWN:
1223 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1224 window.MultiTouchMessage(pointerDown, infos, &keyFlags, false, true);
1228 case AMOTION_EVENT_ACTION_POINTER_UP:
1230 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1231 window.MultiTouchMessage(pointerUp, infos, &keyFlags, false, true);
1238 else if(type == AINPUT_EVENT_TYPE_KEY)
1240 uint action = AKeyEvent_getAction(event);
1241 //uint flags = AKeyEvent_getFlags(event);
1242 uint keyCode = AKeyEvent_getKeyCode(event);
1243 uint meta = AKeyEvent_getMetaState(event);
1244 Key key = keyCodeTable[keyCode];
1245 bool shift = (meta & AMETA_SHIFT_ON) ? true : false;
1246 bool alt = (meta & AMETA_ALT_ON || meta & AMETA_ALT_LEFT_ON || meta & AMETA_ALT_RIGHT_ON) ? true : false;
1247 //bool metaMeta = (meta & AMETA_META_ON || meta & AMETA_META_LEFT_ON || meta & AMETA_META_RIGHT_ON) ? true : false;
1248 //bool sym = (meta & AMETA_SYM_ON) ? true : false;
1249 //unichar ch = AKeyEvent_getUnichar(event);
1255 AInputQueue_finishEvent(inputQueue, event, 1);
1257 // PrintLn("Got a key: action = ", action, ", flags = ", flags, ", keyCode = ", keyCode, ", meta = ", meta, ": key = ", (int)key);
1261 if(action == AKEY_EVENT_ACTION_DOWN || action == AKEY_EVENT_ACTION_MULTIPLE)
1263 /*if(key == wheelDown || key == wheelUp)
1264 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, key, ch);
1267 char c = Interface::TranslateKey(key.code, shift);
1269 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, key, ch);
1272 else if(action == AKEY_EVENT_ACTION_UP)
1273 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, key, ch);
1278 AInputQueue_finishEvent(inputQueue, event, 0);
1282 void onAppCmd(AppCommand cmd)
1287 setSavedState(&state, sizeof(state));
1294 ANativeWindow_setBuffersGeometry(window, 0, 0, 0); //format);
1295 w = ANativeWindow_getWidth(window);
1296 h = ANativeWindow_getHeight(window);
1297 guiApp.Initialize(false);
1298 guiApp.desktop.windowHandle = window;
1299 guiApp.interfaceDriver = null;
1300 guiApp.SwitchMode(true, null, 0, 0, 0, null, false);
1302 if(desktopW != w || desktopH != h)
1304 guiApp.SetDesktopPosition(0, 0, w, h, true);
1308 guiApp.desktop.Update(null);
1312 guiApp.desktop.UnloadGraphics(false);
1315 guiApp.desktop.Update(null);
1316 guiApp.SetAppFocus(true);
1318 if(accelerometerSensor)
1320 ASensorEventQueue_enableSensor(sensorEventQueue, accelerometerSensor);
1321 ASensorEventQueue_setEventRate(sensorEventQueue, accelerometerSensor, (1000L/60)*1000);
1327 if(accelerometerSensor)
1328 ASensorEventQueue_disableSensor(sensorEventQueue, accelerometerSensor);
1330 guiApp.SetAppFocus(false);
1331 guiApp.desktop.Update(null);
1335 guiApp.desktop.UpdateDisplay();
1342 androidActivity = this;
1343 /* Let's have fun with sensors when we have an actual device to play with
1344 sensorManager = ASensorManager_getInstance();
1345 accelerometerSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER);
1346 sensorEventQueue = ASensorManager_createEventQueue(sensorManager, looper, LooperID::user, null, null);
1350 state = *(SavedState*)savedState;
1355 // Evolve the Application class into a GuiApplication
1356 eInstance_Evolve((Instance *)&__androidCurrentModule, class(GuiApplication));
1358 // Wait for the initWindow command:
1359 guiApp.interfaceDriver = class(AndroidInterface);
1362 // Can't call the GuiApplication here, because GuiApplication::Initialize() has not been called yet
1363 guiApp.interfaceDriver.Wait();
1364 guiApp.interfaceDriver.ProcessInput(true);
1367 // Invoke __ecereDll_Load() in lib[our package name].so
1368 app = eModule_Load(__androidCurrentModule, moduleName, publicAccess);
1372 // Find out if any GuiApplication class was defined in our module
1373 for(c = app.classes.first; c && !eClass_IsDerived(c, class(GuiApplication)); c = c.next);
1374 if(!c) c = class(GuiApplication);
1376 guiApp.lockMutex.Release(); // TOCHECK: Seems the evolve is losing our mutex lock here ?
1378 // Evolve the Application into it
1379 eInstance_Evolve((Instance *)&__androidCurrentModule, c);
1380 guiApp = (GuiApplication)__androidCurrentModule;
1383 const String skin = guiApp.skin;
1384 *&guiApp.currentSkin = null;
1385 guiApp.SelectSkin(skin);
1388 guiApp.lockMutex.Wait();
1391 ((void (*)(void *))(void *)__androidCurrentModule._vTbl[12])(__androidCurrentModule);
1394 if(!destroyRequested)
1395 ANativeActivity_finish(activity);
1396 while(!destroyRequested)
1398 guiApp.interfaceDriver.Wait();
1399 guiApp.interfaceDriver.ProcessInput(true);