3 namespace gui::drivers;
16 #include <android/configuration.h>
17 #include <android/looper.h>
18 #include <android/native_activity.h>
19 #include <android/sensor.h>
20 #include <android/log.h>
21 #include <android/window.h>
27 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "ecere-app", __VA_ARGS__))
28 #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "ecere-app", __VA_ARGS__))
29 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "ecere-app", __VA_ARGS__))
31 #define LOGV(...) ((void)0)
33 #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "ecere-app", __VA_ARGS__))
36 // *** NATIVE APP GLUE ********
37 enum LooperID { main = 1, input = 2, user = 3 };
38 enum AppCommand : byte
40 error = 0, inputChanged, initWindow, termWindow, windowResized, windowRedrawNeeded,
41 contentRectChanged, gainedFocus, lostFocus,
42 configChanged, lowMemory, start, resume, saveState, pause, stop, destroy
45 class AndroidPollSource
50 virtual void any_object::process();
53 class AndroidAppGlue : Thread
56 virtual void onAppCmd(AppCommand cmd);
57 virtual int onInputEvent(AInputEvent* event);
60 ANativeActivity* activity;
61 AConfiguration* config;
66 AInputQueue* inputQueue;
67 ANativeWindow* window;
69 AppCommand activityState;
70 bool destroyRequested;
77 int msgread, msgwrite;
81 config = AConfiguration_new();
82 AConfiguration_fromAssetManager(config, activity->assetManager);
86 looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
87 ALooper_addFd(looper, msgread, LooperID::main, ALOOPER_EVENT_INPUT, null, cmdPollSource);
105 AInputQueue_detachLooper(inputQueue);
106 AConfiguration_delete(config);
112 AndroidPollSource cmdPollSource
118 AppCommand cmd = read_cmd();
124 AndroidPollSource inputPollSource
130 AInputEvent* event = null;
131 if(AInputQueue_getEvent(inputQueue, &event) >= 0)
134 LOGV("New input event: type=%d\n", AInputEvent_getType(event));
135 if(AInputQueue_preDispatchEvent(inputQueue, event))
137 /*handled = */onInputEvent(event);
138 //AInputQueue_finishEvent(inputQueue, event, handled);
141 LOGE("Failure reading next input event: %s\n", strerror(errno));
148 AInputQueue* pendingInputQueue;
149 ANativeWindow* pendingWindow;
150 ARect pendingContentRect;
152 void free_saved_state()
162 AppCommand read_cmd()
165 if(read(msgread, &cmd, sizeof(cmd)) == sizeof(cmd))
172 LOGE("No data on command pipe!");
176 void print_cur_config()
178 char lang[2], country[2];
179 AConfiguration_getLanguage(config, lang);
180 AConfiguration_getCountry(config, country);
182 LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
183 "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
184 "modetype=%d modenight=%d",
185 AConfiguration_getMcc(config),
186 AConfiguration_getMnc(config),
187 lang[0], lang[1], country[0], country[1],
188 AConfiguration_getOrientation(config),
189 AConfiguration_getTouchscreen(config),
190 AConfiguration_getDensity(config),
191 AConfiguration_getKeyboard(config),
192 AConfiguration_getNavigation(config),
193 AConfiguration_getKeysHidden(config),
194 AConfiguration_getNavHidden(config),
195 AConfiguration_getSdkVersion(config),
196 AConfiguration_getScreenSize(config),
197 AConfiguration_getScreenLong(config),
198 AConfiguration_getUiModeType(config),
199 AConfiguration_getUiModeNight(config));
202 void pre_exec_cmd(AppCommand cmd)
204 PrintLn("pre_exec_cmd: ", cmd);
210 AInputQueue_detachLooper(inputQueue);
211 inputQueue = pendingInputQueue;
213 AInputQueue_attachLooper(inputQueue, looper, LooperID::input, null, inputPollSource);
219 window = pendingWindow;
236 AConfiguration_fromAssetManager(config, activity->assetManager);
240 destroyRequested = true;
245 void post_exec_cmd(AppCommand cmd)
247 PrintLn("post_exec_cmd: ", cmd);
268 void write_cmd(AppCommand cmd)
270 if(write(msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd))
271 LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
274 void set_input(AInputQueue* inputQueue)
277 pendingInputQueue = inputQueue;
278 write_cmd(inputChanged);
279 while(inputQueue != pendingInputQueue)
284 void set_window(ANativeWindow* window)
288 write_cmd(termWindow);
289 pendingWindow = window;
291 write_cmd(initWindow);
292 while(window != pendingWindow)
297 void set_activity_state(AppCommand cmd)
301 while(activityState != cmd)
317 void setSavedState(void * state, uint size)
324 savedState = malloc(size);
325 savedStateSize = size;
326 memcpy(savedState, state, size);
336 LOGE("could not create pipe: %s", strerror(errno));
337 msgread = msgpipe[0];
338 msgwrite = msgpipe[1];
342 // Wait for thread to start.
344 while(!running) cond.Wait(mutex);
350 static void onDestroy(ANativeActivity* activity)
352 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
353 LOGI("Destroy: %p\n", activity);
356 delete androidActivity;
357 delete __androidCurrentModule;
361 static void onStart(ANativeActivity* activity)
363 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
364 LOGI("Start: %p\n", activity);
365 app.set_activity_state(start);
368 static void onResume(ANativeActivity* activity)
370 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
371 LOGI("Resume: %p\n", activity);
372 app.set_activity_state(resume);
375 static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen)
377 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
378 void* savedState = null;
379 LOGI("SaveInstanceState: %p\n", activity);
381 app.stateSaved = false;
382 app.write_cmd(saveState);
383 while(!app.stateSaved)
384 app.cond.Wait(app.mutex);
387 savedState = app.savedState;
388 *outLen = app.savedStateSize;
389 app.savedState = null;
390 app.savedStateSize = 0;
396 static void onPause(ANativeActivity* activity)
398 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
399 LOGI("Pause: %p\n", activity);
400 app.set_activity_state(pause);
403 static void onStop(ANativeActivity* activity)
405 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
406 LOGI("Stop: %p\n", activity);
407 app.set_activity_state(stop);
410 static void onConfigurationChanged(ANativeActivity* activity)
412 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
413 LOGI("ConfigurationChanged: %p\n", activity);
414 app.write_cmd(configChanged);
417 static void onLowMemory(ANativeActivity* activity)
419 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
420 LOGI("LowMemory: %p\n", activity);
421 app.write_cmd(lowMemory);
424 static void onWindowFocusChanged(ANativeActivity* activity, int focused)
426 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
427 LOGI("WindowFocusChanged: %p -- %d\n", activity, focused);
428 app.write_cmd(focused ? gainedFocus : lostFocus);
431 static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window)
433 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
434 LOGI("NativeWindowCreated: %p -- %p\n", activity, window);
435 app.set_window(window);
438 static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window)
440 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
441 LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window);
443 app.set_window(null);
446 static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue)
448 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
449 LOGI("InputQueueCreated: %p -- %p\n", activity, queue);
450 app.set_input(queue);
453 static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue)
455 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
456 LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue);
457 app.inputQueue = null;
461 default dllexport void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize)
466 // Determine our package name
467 JNIEnv* env=activity->env;
474 // *** Reinitialize static global variables ***
476 guiApplicationInitialized = false;
478 desktopW = 0; desktopH = 0;
479 clipBoardData = null;
481 __androidCurrentModule = null;
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);
501 if(activity->internalDataPath) PrintLn("internalDataPath is ", activity->internalDataPath);
502 if(activity->externalDataPath) PrintLn("externalDataPath is ", activity->externalDataPath);
505 PrintLn("cwd is ", GetWorkingDir(tmp, sizeof(tmp)));
508 ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_FULLSCREEN|AWINDOW_FLAG_KEEP_SCREEN_ON, 0 );
509 app = AndroidActivity { activity = activity, moduleName = moduleName };
511 app.setSavedState(savedState, (uint)savedStateSize);
512 activity->callbacks->onDestroy = onDestroy;
513 activity->callbacks->onStart = onStart;
514 activity->callbacks->onResume = onResume;
515 activity->callbacks->onSaveInstanceState = onSaveInstanceState;
516 activity->callbacks->onPause = onPause;
517 activity->callbacks->onStop = onStop;
518 activity->callbacks->onConfigurationChanged = onConfigurationChanged;
519 activity->callbacks->onLowMemory = onLowMemory;
520 activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
521 activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
522 activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
523 activity->callbacks->onInputQueueCreated = onInputQueueCreated;
524 activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
525 activity->instance = app;
529 // *** END OF NATIVE APP GLUE ******
532 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
533 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp;
534 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown;
535 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
536 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove;
537 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick;
538 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown;
539 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp;
540 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick;
541 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown;
542 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp;
543 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick;
544 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown;
545 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp;
548 static Module __androidCurrentModule;
549 static char * androidArgv[1];
551 static int desktopW, desktopH;
552 static char * clipBoardData;
553 static int mouseX, mouseY;
555 class AndroidInterface : Interface
557 class_property(name) = "Android";
559 // --- User Interface System ---
562 setlocale(LC_ALL, "en_US.UTF-8");
571 #define DBLCLICK_DELAY 300 // 0.3 second
572 #define DBLCLICK_DELTA 1
574 bool ProcessInput(bool processAll)
576 bool eventAvailable = false;
578 if(androidActivity.ident < 0)
579 androidActivity.ident = (LooperID)ALooper_pollAll(0, null, &androidActivity.events, (void**)&androidActivity.source);
581 if(gotInit && androidActivity.window)
583 int w = ANativeWindow_getWidth(androidActivity.window);
584 int h = ANativeWindow_getHeight(androidActivity.window);
585 if(desktopW != w || desktopH != h)
587 guiApp.SetDesktopPosition(0, 0, w, h, true);
590 guiApp.desktop.Update(null);
594 while(androidActivity.ident >= 0)
596 AndroidPollSource source = androidActivity.source;
598 androidActivity.source = null;
600 source.process(source.userData);
602 // If a sensor has data, process it now.
604 if(androidActivity.ident == user)
606 if(androidActivity.accelerometerSensor)
609 while (ASensorEventQueue_getEvents(androidActivity.sensorEventQueue, &event, 1) > 0)
610 LOGI("accelerometer: x=%f y=%f z=%f", event.acceleration.x, event.acceleration.y, event.acceleration.z);
615 eventAvailable = true;
616 if(androidActivity.destroyRequested)
618 guiApp.desktop.Destroy(0);
619 eventAvailable = true;
620 androidActivity.ident = (LooperID)-1;
623 androidActivity.ident = (LooperID)ALooper_pollAll(0, null, &androidActivity.events, (void**)&androidActivity.source);
625 androidActivity.ident = (LooperID)-1;
627 return eventAvailable;
632 androidActivity.ident = (LooperID)ALooper_pollAll((int)(1000/18.2f), null, &androidActivity.events, (void**)&androidActivity.source);
633 // guiApp.WaitEvent();
636 void Lock(Window window)
641 void Unlock(Window window)
646 const char ** GraphicsDrivers(int * numDrivers)
648 static const char *graphicsDrivers[] = { "OpenGL" };
649 *numDrivers = sizeof(graphicsDrivers) / sizeof(char *);
650 return (const char **)graphicsDrivers;
653 void GetCurrentMode(bool * fullScreen, int * resolution, int * colorDepth, int * refreshRate)
658 void EnsureFullScreen(bool *fullScreen)
663 bool ScreenMode(bool fullScreen, int resolution, int colorDepth, int refreshRate, bool * textMode)
670 // --- Window Creation ---
671 void * CreateRootWindow(Window window)
673 return androidActivity.window;
676 void DestroyRootWindow(Window window)
681 // -- Window manipulation ---
683 void SetRootWindowCaption(Window window, const char * name)
688 void PositionRootWindow(Window window, int x, int y, int w, int h, bool move, bool resize)
693 void OrderRootWindow(Window window, bool topMost)
698 void SetRootWindowColor(Window window)
703 void OffsetWindow(Window window, int * x, int * y)
708 void UpdateRootWindow(Window window)
710 if(!window.parent || !window.parent.display)
714 Box box = window.box;
715 box.left -= window.clientStart.x;
716 box.top -= window.clientStart.y;
717 box.right -= window.clientStart.x;
718 box.bottom -= window.clientStart.y;
719 // Logf("Update root window %s\n", window.name);
721 box.left += window.clientStart.x;
722 box.top += window.clientStart.y;
723 box.right += window.clientStart.x;
724 box.bottom += window.clientStart.y;
725 window.UpdateDirty(box);
731 void SetRootWindowState(Window window, WindowState state, bool visible)
735 void FlashRootWindow(Window window)
740 void ActivateRootWindow(Window window)
745 // --- Mouse-based window movement ---
747 void StartMoving(Window window, int x, int y, bool fromKeyBoard)
752 void StopMoving(Window window)
757 // -- Mouse manipulation ---
759 void GetMousePosition(int *x, int *y)
765 void SetMousePosition(int x, int y)
771 void SetMouseRange(Window window, Box box)
775 void SetMouseCapture(Window window)
779 // -- Mouse cursor ---
781 void SetMouseCursor(Window window, int cursor)
791 void SetCaret(int x, int y, int size)
793 Window caretOwner = guiApp.caretOwner;
794 Window window = caretOwner ? caretOwner.rootWindow : null;
795 if(window && window.windowData)
800 void ClearClipboard()
804 delete clipBoardData;
808 bool AllocateClipboard(ClipBoard clipBoard, uint size)
811 if((clipBoard.text = new0 byte[size]))
816 bool SaveClipboard(ClipBoard clipBoard)
822 delete clipBoardData;
824 clipBoardData = clipBoard.text;
825 clipBoard.text = null;
831 bool LoadClipboard(ClipBoard clipBoard)
835 // The data is inside this client...
838 clipBoard.text = new char[strlen(clipBoardData)+1];
839 strcpy(clipBoard.text, clipBoardData);
842 // The data is with another client...
849 void UnloadClipboard(ClipBoard clipBoard)
851 delete clipBoard.text;
854 // --- State based input ---
856 bool AcquireInput(Window window, bool state)
861 bool GetMouseState(MouseButtons * buttons, int * x, int * y)
869 bool GetJoystickState(int device, Joystick joystick)
875 bool GetKeyState(Key key)
877 bool keyState = false;
881 void SetTimerResolution(uint hertz)
883 // timerDelay = hertz ? (1000000 / hertz) : MAXINT;
886 bool SetIcon(Window window, BitmapResource resource)
891 if(bitmap.Load(resource.fileName, null, null))
907 static AndroidActivity androidActivity;
909 default const char * AndroidInterface_GetLibLocation()
913 static char loc[MAX_LOCATION];
914 sprintf(loc, "/data/data/com.ecere.%s/lib/lib", androidActivity.moduleName);
922 default float AMotionEvent_getAxisValue(const AInputEvent* motion_event,
923 int32_t axis, size_t pointer_index);
926 static define AMETA_META_ON = 0x00010000;
927 static define AMETA_META_LEFT_ON = 0x00020000;
928 static define AMETA_META_RIGHT_ON = 0x00040000;
930 static Key keyCodeTable[] =
932 0, //AKEYCODE_UNKNOWN = 0,
933 0, //AKEYCODE_SOFT_LEFT = 1,
934 0, //AKEYCODE_SOFT_RIGHT = 2,
935 0, //AKEYCODE_HOME = 3,
936 0, //AKEYCODE_BACK = 4,
937 0, //AKEYCODE_CALL = 5,
938 0, //AKEYCODE_ENDCALL = 6,
939 k0, //AKEYCODE_0 = 7,
940 k1, //AKEYCODE_1 = 8,
941 k2, //AKEYCODE_2 = 9,
942 k3, //AKEYCODE_3 = 10,
943 k4, //AKEYCODE_4 = 11,
944 k5, //AKEYCODE_5 = 12,
945 k6, //AKEYCODE_6 = 13,
946 k7, //AKEYCODE_7 = 14,
947 k8, //AKEYCODE_8 = 15,
948 k9, //AKEYCODE_9 = 16,
949 keyPadStar, //AKEYCODE_STAR = 17,
950 Key { k3, shift = true }, //AKEYCODE_POUND = 18,
951 up, //AKEYCODE_DPAD_UP = 19,
952 down, //AKEYCODE_DPAD_DOWN = 20,
953 left, //AKEYCODE_DPAD_LEFT = 21,
954 right, //AKEYCODE_DPAD_RIGHT = 22,
955 keyPad5, //AKEYCODE_DPAD_CENTER = 23,
956 0, //AKEYCODE_VOLUME_UP = 24,
957 0, //AKEYCODE_VOLUME_DOWN = 25,
958 0, //AKEYCODE_POWER = 26,
959 0, //AKEYCODE_CAMERA = 27,
960 0, //AKEYCODE_CLEAR = 28,
961 a, //AKEYCODE_A = 29,
962 b, //AKEYCODE_B = 30,
963 c, //AKEYCODE_C = 31,
964 d, //AKEYCODE_D = 32,
965 e, //AKEYCODE_E = 33,
966 f, //AKEYCODE_F = 34,
967 g, //AKEYCODE_G = 35,
968 h, //AKEYCODE_H = 36,
969 i, //AKEYCODE_I = 37,
970 j, //AKEYCODE_J = 38,
971 k, //AKEYCODE_K = 39,
972 l, //AKEYCODE_L = 40,
973 m, //AKEYCODE_M = 41,
974 n, //AKEYCODE_N = 42,
975 o, //AKEYCODE_O = 43,
976 p, //AKEYCODE_P = 44,
977 q, //AKEYCODE_Q = 45,
978 r, //AKEYCODE_R = 46,
979 s, //AKEYCODE_S = 47,
980 t, //AKEYCODE_T = 48,
981 u, //AKEYCODE_U = 49,
982 v, //AKEYCODE_V = 50,
983 w, //AKEYCODE_W = 51,
984 x, //AKEYCODE_X = 52,
985 y, //AKEYCODE_Y = 53,
986 z, //AKEYCODE_Z = 54,
987 comma, //AKEYCODE_COMMA = 55,
988 period, //AKEYCODE_PERIOD = 56,
989 leftAlt, //AKEYCODE_ALT_LEFT = 57,
990 rightAlt, //AKEYCODE_ALT_RIGHT = 58,
991 leftShift, //AKEYCODE_SHIFT_LEFT = 59,
992 rightShift, //AKEYCODE_SHIFT_RIGHT = 60,
993 tab, //AKEYCODE_TAB = 61,
994 space, //AKEYCODE_SPACE = 62,
995 0, //AKEYCODE_SYM = 63,
996 0, //AKEYCODE_EXPLORER = 64,
997 0, //AKEYCODE_ENVELOPE = 65,
998 enter, //AKEYCODE_ENTER = 66,
999 backSpace, //AKEYCODE_DEL = 67,
1000 backQuote, //AKEYCODE_GRAVE = 68,
1001 minus, //AKEYCODE_MINUS = 69,
1002 plus, //AKEYCODE_EQUALS = 70,
1003 leftBracket, //AKEYCODE_LEFT_BRACKET = 71,
1004 rightBracket, //AKEYCODE_RIGHT_BRACKET = 72,
1005 backSlash, //AKEYCODE_BACKSLASH = 73,
1006 semicolon, //AKEYCODE_SEMICOLON = 74,
1007 quote, //AKEYCODE_APOSTROPHE = 75,
1008 slash, //AKEYCODE_SLASH = 76,
1009 Key { k2, shift = true }, //AKEYCODE_AT = 77,
1010 0, //AKEYCODE_NUM = 78, // Interpreted as an Alt
1011 0, //AKEYCODE_HEADSETHOOK = 79,
1012 0, //AKEYCODE_FOCUS = 80, // *Camera* focus
1013 keyPadPlus, //AKEYCODE_PLUS = 81,
1014 0, //AKEYCODE_MENU = 82,
1015 0, //AKEYCODE_NOTIFICATION = 83,
1016 0, //AKEYCODE_SEARCH = 84,
1017 0, //AKEYCODE_MEDIA_PLAY_PAUSE= 85,
1018 0, //AKEYCODE_MEDIA_STOP = 86,
1019 0, //AKEYCODE_MEDIA_NEXT = 87,
1020 0, //AKEYCODE_MEDIA_PREVIOUS = 88,
1021 0, //AKEYCODE_MEDIA_REWIND = 89,
1022 0, //AKEYCODE_MEDIA_FAST_FORWARD = 90,
1023 0, //AKEYCODE_MUTE = 91,
1024 0, //AKEYCODE_PAGE_UP = 92,
1025 0, //AKEYCODE_PAGE_DOWN = 93,
1026 0, //AKEYCODE_PICTSYMBOLS = 94,
1027 0, //AKEYCODE_SWITCH_CHARSET = 95,
1028 0, //AKEYCODE_BUTTON_A = 96,
1029 0, //AKEYCODE_BUTTON_B = 97,
1030 0, //AKEYCODE_BUTTON_C = 98,
1031 0, //AKEYCODE_BUTTON_X = 99,
1032 0, //AKEYCODE_BUTTON_Y = 100,
1033 0, //AKEYCODE_BUTTON_Z = 101,
1034 0, //AKEYCODE_BUTTON_L1 = 102,
1035 0, //AKEYCODE_BUTTON_R1 = 103,
1036 0, //AKEYCODE_BUTTON_L2 = 104,
1037 0, //AKEYCODE_BUTTON_R2 = 105,
1038 0, //AKEYCODE_BUTTON_THUMBL = 106,
1039 0, //AKEYCODE_BUTTON_THUMBR = 107,
1040 0, //AKEYCODE_BUTTON_START = 108,
1041 0, //AKEYCODE_BUTTON_SELECT = 109,
1042 0, //AKEYCODE_BUTTON_MODE = 110,
1043 escape, //AKEYCODE_BUTTON_ESCAPE = 111,
1044 del, //AKEYCODE_BUTTON_ESCAPE = 112,
1045 leftControl, // = 113
1046 rightControl, // = 114
1048 scrollLock, // = 116
1049 0, // = 117 KEYCODE_META_LEFT
1050 0, // = 118 KEYCODE_META_RIGHT
1051 0, // = 119 KEYCODE_FUNCTION
1052 printScreen, // = 120 KEYCODE_SYSRQ
1053 pauseBreak, // = 121
1059 // Why don't we have this in the NDK :(
1060 // default int32_t AKeyEvent_getUnichar(const AInputEvent* key_event);
1062 static Array<TouchPointerInfo> buildPointerInfo(AInputEvent * event)
1064 uint count = (uint)AMotionEvent_getPointerCount(event);
1065 Array<TouchPointerInfo> infos { size = count };
1067 for(i = 0; i < count; i++)
1069 infos[i].point = { (int)AMotionEvent_getX(event, i), (int)AMotionEvent_getY(event, i) };
1070 infos[i].id = (int)AMotionEvent_getPointerId(event, i);
1071 infos[i].pressure = AMotionEvent_getPressure(event, i);
1072 infos[i].size = AMotionEvent_getSize(event, i);
1077 class AndroidActivity : AndroidAppGlue
1079 AndroidPollSource source;
1083 ASensorManager* sensorManager;
1084 const ASensor* accelerometerSensor;
1085 ASensorEventQueue* sensorEventQueue;
1089 int onInputEvent(AInputEvent* event)
1091 static Time lastTime = 0;
1092 Window window = guiApp.desktop;
1093 uint type = AInputEvent_getType(event);
1094 if(type == AINPUT_EVENT_TYPE_MOTION)
1096 uint actionAndIndex = AMotionEvent_getAction(event);
1097 //uint source = AInputEvent_getSource(event);
1098 uint action = actionAndIndex & AMOTION_EVENT_ACTION_MASK;
1099 //uint index = (actionAndIndex & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
1100 //uint flags = AMotionEvent_getFlags(event);
1101 uint meta = AMotionEvent_getMetaState(event);
1102 //uint edge = AMotionEvent_getEdgeFlags(event);
1103 //int64 downTime = AMotionEvent_getDownTime(event); // nanotime
1104 //int64 eventTime = AMotionEvent_getDownTime(event);
1106 Modifiers keyFlags = 0;
1107 int x = (int)AMotionEvent_getX(event, 0);
1108 int y = (int)AMotionEvent_getY(event, 0);
1109 bool shift = (meta & AMETA_SHIFT_ON) ? true : false;
1110 bool alt = (meta & AMETA_ALT_ON) ? true : false;
1111 //bool sym = (meta & AMETA_SYM_ON) ? true : false;
1113 keyFlags.shift = shift;
1116 //PrintLn("Got a motion input event: ", action);
1118 if(action == 8) //AMOTION_EVENT_ACTION_SCROLL)
1119 axis = AMotionEvent_getAxisValue(event, 9, index); //AMOTION_EVENT_AXIS_VSCROLL);
1122 AInputQueue_finishEvent(inputQueue, event, 1);
1126 case 8: //AMOTION_EVENT_ACTION_SCROLL:
1127 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, (axis < 0) ? wheelUp : wheelDown, 0);
1130 case AMOTION_EVENT_ACTION_DOWN:
1132 Time time = GetTime();
1134 if(Abs(x - mouseX) < 40 && Abs(y - mouseY) < 40 && time - lastTime < 0.3)
1135 if(!window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick, x, y, &keyFlags, false, true))
1138 mouseX = x, mouseY = y;
1140 // TOCHECK: Should we do result = here?
1141 window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, x, y, &keyFlags, false, true);
1144 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1145 window.MultiTouchMessage(down, infos, &keyFlags, false, true);
1150 case AMOTION_EVENT_ACTION_UP:
1151 mouseX = x, mouseY = y;
1152 if(window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp, x, y, &keyFlags, false, true))
1154 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1155 window.MultiTouchMessage(up, infos, &keyFlags, false, true);
1159 case AMOTION_EVENT_ACTION_MOVE:
1160 mouseX = x, mouseY = y;
1161 if(window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove, x, y, &keyFlags, false, true))
1163 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1164 window.MultiTouchMessage(move, infos, &keyFlags, false, true);
1168 case AMOTION_EVENT_ACTION_CANCEL: break;
1169 case AMOTION_EVENT_ACTION_OUTSIDE: break;
1170 case AMOTION_EVENT_ACTION_POINTER_DOWN:
1172 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1173 window.MultiTouchMessage(pointerDown, infos, &keyFlags, false, true);
1177 case AMOTION_EVENT_ACTION_POINTER_UP:
1179 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1180 window.MultiTouchMessage(pointerUp, infos, &keyFlags, false, true);
1187 else if(type == AINPUT_EVENT_TYPE_KEY)
1189 uint action = AKeyEvent_getAction(event);
1190 //uint flags = AKeyEvent_getFlags(event);
1191 uint keyCode = AKeyEvent_getKeyCode(event);
1192 uint meta = AKeyEvent_getMetaState(event);
1193 Key key = keyCodeTable[keyCode];
1194 bool shift = (meta & AMETA_SHIFT_ON) ? true : false;
1195 bool alt = (meta & AMETA_ALT_ON || meta & AMETA_ALT_LEFT_ON || meta & AMETA_ALT_RIGHT_ON) ? true : false;
1196 //bool metaMeta = (meta & AMETA_META_ON || meta & AMETA_META_LEFT_ON || meta & AMETA_META_RIGHT_ON) ? true : false;
1197 //bool sym = (meta & AMETA_SYM_ON) ? true : false;
1198 //unichar ch = AKeyEvent_getUnichar(event);
1204 AInputQueue_finishEvent(inputQueue, event, 1);
1206 // PrintLn("Got a key: action = ", action, ", flags = ", flags, ", keyCode = ", keyCode, ", meta = ", meta, ": key = ", (int)key);
1210 if(action == AKEY_EVENT_ACTION_DOWN || action == AKEY_EVENT_ACTION_MULTIPLE)
1212 /*if(key == wheelDown || key == wheelUp)
1213 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, key, ch);
1216 char c = Interface::TranslateKey(key.code, shift);
1218 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, key, ch);
1221 else if(action == AKEY_EVENT_ACTION_UP)
1222 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, key, ch);
1227 AInputQueue_finishEvent(inputQueue, event, 0);
1231 void onAppCmd(AppCommand cmd)
1236 setSavedState(&state, sizeof(state));
1243 ANativeWindow_setBuffersGeometry(window, 0, 0, 0); //format);
1244 w = ANativeWindow_getWidth(window);
1245 h = ANativeWindow_getHeight(window);
1246 guiApp.Initialize(false);
1247 guiApp.desktop.windowHandle = window;
1248 guiApp.interfaceDriver = null;
1249 guiApp.SwitchMode(true, null, 0, 0, 0, null, false);
1251 if(desktopW != w || desktopH != h)
1253 guiApp.SetDesktopPosition(0, 0, w, h, true);
1257 guiApp.desktop.Update(null);
1261 guiApp.desktop.UnloadGraphics(false);
1264 guiApp.desktop.Update(null);
1265 guiApp.SetAppFocus(true);
1267 if(accelerometerSensor)
1269 ASensorEventQueue_enableSensor(sensorEventQueue, accelerometerSensor);
1270 ASensorEventQueue_setEventRate(sensorEventQueue, accelerometerSensor, (1000L/60)*1000);
1276 if(accelerometerSensor)
1277 ASensorEventQueue_disableSensor(sensorEventQueue, accelerometerSensor);
1279 guiApp.SetAppFocus(false);
1280 guiApp.desktop.Update(null);
1284 guiApp.desktop.UpdateDisplay();
1291 androidActivity = this;
1292 /* Let's have fun with sensors when we have an actual device to play with
1293 sensorManager = ASensorManager_getInstance();
1294 accelerometerSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER);
1295 sensorEventQueue = ASensorManager_createEventQueue(sensorManager, looper, LooperID::user, null, null);
1299 state = *(SavedState*)savedState;
1304 // Evolve the Application class into a GuiApplication
1305 eInstance_Evolve((Instance *)&__androidCurrentModule, class(GuiApplication));
1307 // Wait for the initWindow command:
1308 guiApp.interfaceDriver = class(AndroidInterface);
1311 // Can't call the GuiApplication here, because GuiApplication::Initialize() has not been called yet
1312 guiApp.interfaceDriver.Wait();
1313 guiApp.interfaceDriver.ProcessInput(true);
1316 // Invoke __ecereDll_Load() in lib[our package name].so
1317 app = eModule_Load(__androidCurrentModule, moduleName, publicAccess);
1321 // Find out if any GuiApplication class was defined in our module
1322 for(c = app.classes.first; c && !eClass_IsDerived(c, class(GuiApplication)); c = c.next);
1323 if(!c) c = class(GuiApplication);
1325 guiApp.lockMutex.Release(); // TOCHECK: Seems the evolve is losing our mutex lock here ?
1327 // Evolve the Application into it
1328 eInstance_Evolve((Instance *)&__androidCurrentModule, c);
1329 guiApp = (GuiApplication)__androidCurrentModule;
1332 const String skin = guiApp.skin;
1333 *&guiApp.currentSkin = null;
1334 guiApp.SelectSkin(skin);
1337 guiApp.lockMutex.Wait();
1340 ((void (*)(void *))(void *)__androidCurrentModule._vTbl[12])(__androidCurrentModule);
1343 if(!destroyRequested)
1344 ANativeActivity_finish(activity);
1345 while(!destroyRequested)
1347 guiApp.interfaceDriver.Wait();
1348 guiApp.interfaceDriver.ProcessInput(true);