1 namespace gui::drivers;
14 #include <android/configuration.h>
15 #include <android/looper.h>
16 #include <android/native_activity.h>
17 #include <android/sensor.h>
18 #include <android/log.h>
19 #include <android/window.h>
25 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "ecere-app", __VA_ARGS__))
26 #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "ecere-app", __VA_ARGS__))
27 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "ecere-app", __VA_ARGS__))
29 #define LOGV(...) ((void)0)
31 #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "ecere-app", __VA_ARGS__))
34 // *** NATIVE APP GLUE ********
35 enum LooperID { main = 1, input = 2, user = 3 };
36 enum AppCommand : byte
38 error = 0, inputChanged, initWindow, termWindow, windowResized, windowRedrawNeeded,
39 contentRectChanged, gainedFocus, lostFocus,
40 configChanged, lowMemory, start, resume, saveState, pause, stop, destroy
43 class AndroidPollSource
48 virtual void any_object::process();
51 class AndroidAppGlue : Thread
54 virtual void onAppCmd(AppCommand cmd);
55 virtual int onInputEvent(AInputEvent* event);
58 ANativeActivity* activity;
59 AConfiguration* config;
64 AInputQueue* inputQueue;
65 ANativeWindow* window;
67 AppCommand activityState;
68 bool destroyRequested;
75 int msgread, msgwrite;
79 config = AConfiguration_new();
80 AConfiguration_fromAssetManager(config, activity->assetManager);
84 looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
85 ALooper_addFd(looper, msgread, LooperID::main, ALOOPER_EVENT_INPUT, null, cmdPollSource);
103 AInputQueue_detachLooper(inputQueue);
104 AConfiguration_delete(config);
110 AndroidPollSource cmdPollSource
116 AppCommand cmd = read_cmd();
122 AndroidPollSource inputPollSource
128 AInputEvent* event = null;
129 if(AInputQueue_getEvent(inputQueue, &event) >= 0)
132 LOGV("New input event: type=%d\n", AInputEvent_getType(event));
133 if(AInputQueue_preDispatchEvent(inputQueue, event))
135 /*handled = */onInputEvent(event);
136 //AInputQueue_finishEvent(inputQueue, event, handled);
139 LOGE("Failure reading next input event: %s\n", strerror(errno));
146 AInputQueue* pendingInputQueue;
147 ANativeWindow* pendingWindow;
148 ARect pendingContentRect;
150 void free_saved_state()
160 AppCommand read_cmd()
163 if(read(msgread, &cmd, sizeof(cmd)) == sizeof(cmd))
170 LOGE("No data on command pipe!");
174 void print_cur_config()
176 char lang[2], country[2];
177 AConfiguration_getLanguage(config, lang);
178 AConfiguration_getCountry(config, country);
180 LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
181 "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
182 "modetype=%d modenight=%d",
183 AConfiguration_getMcc(config),
184 AConfiguration_getMnc(config),
185 lang[0], lang[1], country[0], country[1],
186 AConfiguration_getOrientation(config),
187 AConfiguration_getTouchscreen(config),
188 AConfiguration_getDensity(config),
189 AConfiguration_getKeyboard(config),
190 AConfiguration_getNavigation(config),
191 AConfiguration_getKeysHidden(config),
192 AConfiguration_getNavHidden(config),
193 AConfiguration_getSdkVersion(config),
194 AConfiguration_getScreenSize(config),
195 AConfiguration_getScreenLong(config),
196 AConfiguration_getUiModeType(config),
197 AConfiguration_getUiModeNight(config));
200 void pre_exec_cmd(AppCommand cmd)
202 PrintLn("pre_exec_cmd: ", cmd);
208 AInputQueue_detachLooper(inputQueue);
209 inputQueue = pendingInputQueue;
211 AInputQueue_attachLooper(inputQueue, looper, LooperID::input, null, inputPollSource);
217 window = pendingWindow;
234 AConfiguration_fromAssetManager(config, activity->assetManager);
238 destroyRequested = true;
243 void post_exec_cmd(AppCommand cmd)
245 PrintLn("post_exec_cmd: ", cmd);
266 void write_cmd(AppCommand cmd)
268 if(write(msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd))
269 LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
272 void set_input(AInputQueue* inputQueue)
275 pendingInputQueue = inputQueue;
276 write_cmd(inputChanged);
277 while(inputQueue != pendingInputQueue)
282 void set_window(ANativeWindow* window)
286 write_cmd(termWindow);
287 pendingWindow = window;
289 write_cmd(initWindow);
290 while(window != pendingWindow)
295 void set_activity_state(AppCommand cmd)
299 while(activityState != cmd)
315 void setSavedState(void * state, uint size)
322 savedState = malloc(size);
323 savedStateSize = size;
324 memcpy(savedState, state, size);
334 LOGE("could not create pipe: %s", strerror(errno));
335 msgread = msgpipe[0];
336 msgwrite = msgpipe[1];
340 // Wait for thread to start.
342 while(!running) cond.Wait(mutex);
348 static void onDestroy(ANativeActivity* activity)
350 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
351 LOGI("Destroy: %p\n", activity);
354 delete androidActivity;
355 delete __androidCurrentModule;
359 static void onStart(ANativeActivity* activity)
361 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
362 LOGI("Start: %p\n", activity);
363 app.set_activity_state(start);
366 static void onResume(ANativeActivity* activity)
368 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
369 LOGI("Resume: %p\n", activity);
370 app.set_activity_state(resume);
373 static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen)
375 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
376 void* savedState = null;
377 LOGI("SaveInstanceState: %p\n", activity);
379 app.stateSaved = false;
380 app.write_cmd(saveState);
381 while(!app.stateSaved)
382 app.cond.Wait(app.mutex);
385 savedState = app.savedState;
386 *outLen = app.savedStateSize;
387 app.savedState = null;
388 app.savedStateSize = 0;
394 static void onPause(ANativeActivity* activity)
396 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
397 LOGI("Pause: %p\n", activity);
398 app.set_activity_state(pause);
401 static void onStop(ANativeActivity* activity)
403 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
404 LOGI("Stop: %p\n", activity);
405 app.set_activity_state(stop);
408 static void onConfigurationChanged(ANativeActivity* activity)
410 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
411 LOGI("ConfigurationChanged: %p\n", activity);
412 app.write_cmd(configChanged);
415 static void onLowMemory(ANativeActivity* activity)
417 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
418 LOGI("LowMemory: %p\n", activity);
419 app.write_cmd(lowMemory);
422 static void onWindowFocusChanged(ANativeActivity* activity, int focused)
424 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
425 LOGI("WindowFocusChanged: %p -- %d\n", activity, focused);
426 app.write_cmd(focused ? gainedFocus : lostFocus);
429 static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window)
431 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
432 LOGI("NativeWindowCreated: %p -- %p\n", activity, window);
433 app.set_window(window);
436 static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window)
438 AndroidAppGlue app = (AndroidAppGlue)activity->instance;
439 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 LOGI("Creating: %p\n", activity);
483 //(*activity->vm)->AttachCurrentThread(activity->vm, &env, 0);
484 clazz = (*env)->GetObjectClass(env, activity->clazz);
485 methodID = (*env)->GetMethodID(env, clazz, "getPackageName", "()Ljava/lang/String;");
486 result = (*env)->CallObjectMethod(env, activity->clazz, methodID);
487 str = (*env)->GetStringUTFChars(env, (jstring)result, &isCopy);
488 // (*activity->vm)->DetachCurrentThread(activity->vm);
489 moduleName = strstr(str, "com.ecere.");
490 if(moduleName) moduleName += 10;
491 androidArgv[0] = moduleName;
493 // Create a base Application class
494 __androidCurrentModule = __ecere_COM_Initialize(true, 1, androidArgv);
496 eModule_Load(__androidCurrentModule, "ecere", publicAccess);
499 if(activity->internalDataPath) PrintLn("internalDataPath is ", activity->internalDataPath);
500 if(activity->externalDataPath) PrintLn("externalDataPath is ", activity->externalDataPath);
503 PrintLn("cwd is ", GetWorkingDir(tmp, sizeof(tmp)));
506 ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_FULLSCREEN|AWINDOW_FLAG_KEEP_SCREEN_ON, 0 );
507 app = AndroidActivity { activity = activity, moduleName = moduleName };
509 app.setSavedState(savedState, (uint)savedStateSize);
510 activity->callbacks->onDestroy = onDestroy;
511 activity->callbacks->onStart = onStart;
512 activity->callbacks->onResume = onResume;
513 activity->callbacks->onSaveInstanceState = onSaveInstanceState;
514 activity->callbacks->onPause = onPause;
515 activity->callbacks->onStop = onStop;
516 activity->callbacks->onConfigurationChanged = onConfigurationChanged;
517 activity->callbacks->onLowMemory = onLowMemory;
518 activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
519 activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
520 activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
521 activity->callbacks->onInputQueueCreated = onInputQueueCreated;
522 activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
523 activity->instance = app;
527 // *** END OF NATIVE APP GLUE ******
530 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
531 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp;
532 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown;
533 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
534 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove;
535 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick;
536 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown;
537 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp;
538 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick;
539 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown;
540 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp;
541 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick;
542 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown;
543 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp;
546 static Module __androidCurrentModule;
547 static char * androidArgv[1];
549 static int desktopW, desktopH;
550 static char * clipBoardData;
551 static int mouseX, mouseY;
553 class AndroidInterface : Interface
555 class_property(name) = "Android";
557 // --- User Interface System ---
560 setlocale(LC_ALL, "en_US.UTF-8");
569 #define DBLCLICK_DELAY 300 // 0.3 second
570 #define DBLCLICK_DELTA 1
572 bool ProcessInput(bool processAll)
574 bool eventAvailable = false;
576 if(androidActivity.ident < 0)
577 androidActivity.ident = (LooperID)ALooper_pollAll(0, null, &androidActivity.events, (void**)&androidActivity.source);
579 if(gotInit && androidActivity.window)
581 int w = ANativeWindow_getWidth(androidActivity.window);
582 int h = ANativeWindow_getHeight(androidActivity.window);
583 if(desktopW != w || desktopH != h)
585 guiApp.SetDesktopPosition(0, 0, w, h, true);
588 guiApp.desktop.Update(null);
592 while(androidActivity.ident >= 0)
594 AndroidPollSource source = androidActivity.source;
596 androidActivity.source = null;
598 source.process(source.userData);
600 // If a sensor has data, process it now.
602 if(androidActivity.ident == user)
604 if(androidActivity.accelerometerSensor)
607 while (ASensorEventQueue_getEvents(androidActivity.sensorEventQueue, &event, 1) > 0)
608 LOGI("accelerometer: x=%f y=%f z=%f", event.acceleration.x, event.acceleration.y, event.acceleration.z);
613 eventAvailable = true;
614 if(androidActivity.destroyRequested)
616 guiApp.desktop.Destroy(0);
617 eventAvailable = true;
618 androidActivity.ident = (LooperID)-1;
621 androidActivity.ident = (LooperID)ALooper_pollAll(0, null, &androidActivity.events, (void**)&androidActivity.source);
623 androidActivity.ident = (LooperID)-1;
625 return eventAvailable;
630 androidActivity.ident = (LooperID)ALooper_pollAll((int)(1000/18.2f), null, &androidActivity.events, (void**)&androidActivity.source);
631 // guiApp.WaitEvent();
634 void Lock(Window window)
639 void Unlock(Window window)
644 const char ** GraphicsDrivers(int * numDrivers)
646 static const char *graphicsDrivers[] = { "OpenGL" };
647 *numDrivers = sizeof(graphicsDrivers) / sizeof(char *);
648 return (const char **)graphicsDrivers;
651 void GetCurrentMode(bool * fullScreen, int * resolution, int * colorDepth, int * refreshRate)
656 void EnsureFullScreen(bool *fullScreen)
661 bool ScreenMode(bool fullScreen, int resolution, int colorDepth, int refreshRate, bool * textMode)
668 // --- Window Creation ---
669 void * CreateRootWindow(Window window)
671 return androidActivity.window;
674 void DestroyRootWindow(Window window)
679 // -- Window manipulation ---
681 void SetRootWindowCaption(Window window, const char * name)
686 void PositionRootWindow(Window window, int x, int y, int w, int h, bool move, bool resize)
691 void OrderRootWindow(Window window, bool topMost)
696 void SetRootWindowColor(Window window)
701 void OffsetWindow(Window window, int * x, int * y)
706 void UpdateRootWindow(Window window)
708 if(!window.parent || !window.parent.display)
712 Box box = window.box;
713 box.left -= window.clientStart.x;
714 box.top -= window.clientStart.y;
715 box.right -= window.clientStart.x;
716 box.bottom -= window.clientStart.y;
717 // Logf("Update root window %s\n", window.name);
719 box.left += window.clientStart.x;
720 box.top += window.clientStart.y;
721 box.right += window.clientStart.x;
722 box.bottom += window.clientStart.y;
723 window.UpdateDirty(box);
729 void SetRootWindowState(Window window, WindowState state, bool visible)
733 void FlashRootWindow(Window window)
738 void ActivateRootWindow(Window window)
743 // --- Mouse-based window movement ---
745 void StartMoving(Window window, int x, int y, bool fromKeyBoard)
750 void StopMoving(Window window)
755 // -- Mouse manipulation ---
757 void GetMousePosition(int *x, int *y)
763 void SetMousePosition(int x, int y)
769 void SetMouseRange(Window window, Box box)
773 void SetMouseCapture(Window window)
777 // -- Mouse cursor ---
779 void SetMouseCursor(Window window, int cursor)
789 void SetCaret(int x, int y, int size)
791 Window caretOwner = guiApp.caretOwner;
792 Window window = caretOwner ? caretOwner.rootWindow : null;
793 if(window && window.windowData)
798 void ClearClipboard()
802 delete clipBoardData;
806 bool AllocateClipboard(ClipBoard clipBoard, uint size)
809 if((clipBoard.text = new0 byte[size]))
814 bool SaveClipboard(ClipBoard clipBoard)
820 delete clipBoardData;
822 clipBoardData = clipBoard.text;
823 clipBoard.text = null;
829 bool LoadClipboard(ClipBoard clipBoard)
833 // The data is inside this client...
836 clipBoard.text = new char[strlen(clipBoardData)+1];
837 strcpy(clipBoard.text, clipBoardData);
840 // The data is with another client...
847 void UnloadClipboard(ClipBoard clipBoard)
849 delete clipBoard.text;
852 // --- State based input ---
854 bool AcquireInput(Window window, bool state)
859 bool GetMouseState(MouseButtons * buttons, int * x, int * y)
867 bool GetJoystickState(int device, Joystick joystick)
873 bool GetKeyState(Key key)
875 bool keyState = false;
879 void SetTimerResolution(uint hertz)
881 // timerDelay = hertz ? (1000000 / hertz) : MAXINT;
884 bool SetIcon(Window window, BitmapResource resource)
889 if(bitmap.Load(resource.fileName, null, null))
905 static AndroidActivity androidActivity;
907 default const char * AndroidInterface_GetLibLocation()
911 static char loc[MAX_LOCATION];
912 sprintf(loc, "/data/data/com.ecere.%s/lib/lib", androidActivity.moduleName);
920 default float AMotionEvent_getAxisValue(const AInputEvent* motion_event,
921 int32_t axis, size_t pointer_index);
924 static define AMETA_META_ON = 0x00010000;
925 static define AMETA_META_LEFT_ON = 0x00020000;
926 static define AMETA_META_RIGHT_ON = 0x00040000;
928 static Key keyCodeTable[] =
930 0, //AKEYCODE_UNKNOWN = 0,
931 0, //AKEYCODE_SOFT_LEFT = 1,
932 0, //AKEYCODE_SOFT_RIGHT = 2,
933 0, //AKEYCODE_HOME = 3,
934 0, //AKEYCODE_BACK = 4,
935 0, //AKEYCODE_CALL = 5,
936 0, //AKEYCODE_ENDCALL = 6,
937 k0, //AKEYCODE_0 = 7,
938 k1, //AKEYCODE_1 = 8,
939 k2, //AKEYCODE_2 = 9,
940 k3, //AKEYCODE_3 = 10,
941 k4, //AKEYCODE_4 = 11,
942 k5, //AKEYCODE_5 = 12,
943 k6, //AKEYCODE_6 = 13,
944 k7, //AKEYCODE_7 = 14,
945 k8, //AKEYCODE_8 = 15,
946 k9, //AKEYCODE_9 = 16,
947 keyPadStar, //AKEYCODE_STAR = 17,
948 Key { k3, shift = true }, //AKEYCODE_POUND = 18,
949 up, //AKEYCODE_DPAD_UP = 19,
950 down, //AKEYCODE_DPAD_DOWN = 20,
951 left, //AKEYCODE_DPAD_LEFT = 21,
952 right, //AKEYCODE_DPAD_RIGHT = 22,
953 keyPad5, //AKEYCODE_DPAD_CENTER = 23,
954 0, //AKEYCODE_VOLUME_UP = 24,
955 0, //AKEYCODE_VOLUME_DOWN = 25,
956 0, //AKEYCODE_POWER = 26,
957 0, //AKEYCODE_CAMERA = 27,
958 0, //AKEYCODE_CLEAR = 28,
959 a, //AKEYCODE_A = 29,
960 b, //AKEYCODE_B = 30,
961 c, //AKEYCODE_C = 31,
962 d, //AKEYCODE_D = 32,
963 e, //AKEYCODE_E = 33,
964 f, //AKEYCODE_F = 34,
965 g, //AKEYCODE_G = 35,
966 h, //AKEYCODE_H = 36,
967 i, //AKEYCODE_I = 37,
968 j, //AKEYCODE_J = 38,
969 k, //AKEYCODE_K = 39,
970 l, //AKEYCODE_L = 40,
971 m, //AKEYCODE_M = 41,
972 n, //AKEYCODE_N = 42,
973 o, //AKEYCODE_O = 43,
974 p, //AKEYCODE_P = 44,
975 q, //AKEYCODE_Q = 45,
976 r, //AKEYCODE_R = 46,
977 s, //AKEYCODE_S = 47,
978 t, //AKEYCODE_T = 48,
979 u, //AKEYCODE_U = 49,
980 v, //AKEYCODE_V = 50,
981 w, //AKEYCODE_W = 51,
982 x, //AKEYCODE_X = 52,
983 y, //AKEYCODE_Y = 53,
984 z, //AKEYCODE_Z = 54,
985 comma, //AKEYCODE_COMMA = 55,
986 period, //AKEYCODE_PERIOD = 56,
987 leftAlt, //AKEYCODE_ALT_LEFT = 57,
988 rightAlt, //AKEYCODE_ALT_RIGHT = 58,
989 leftShift, //AKEYCODE_SHIFT_LEFT = 59,
990 rightShift, //AKEYCODE_SHIFT_RIGHT = 60,
991 tab, //AKEYCODE_TAB = 61,
992 space, //AKEYCODE_SPACE = 62,
993 0, //AKEYCODE_SYM = 63,
994 0, //AKEYCODE_EXPLORER = 64,
995 0, //AKEYCODE_ENVELOPE = 65,
996 enter, //AKEYCODE_ENTER = 66,
997 backSpace, //AKEYCODE_DEL = 67,
998 backQuote, //AKEYCODE_GRAVE = 68,
999 minus, //AKEYCODE_MINUS = 69,
1000 plus, //AKEYCODE_EQUALS = 70,
1001 leftBracket, //AKEYCODE_LEFT_BRACKET = 71,
1002 rightBracket, //AKEYCODE_RIGHT_BRACKET = 72,
1003 backSlash, //AKEYCODE_BACKSLASH = 73,
1004 semicolon, //AKEYCODE_SEMICOLON = 74,
1005 quote, //AKEYCODE_APOSTROPHE = 75,
1006 slash, //AKEYCODE_SLASH = 76,
1007 Key { k2, shift = true }, //AKEYCODE_AT = 77,
1008 0, //AKEYCODE_NUM = 78, // Interpreted as an Alt
1009 0, //AKEYCODE_HEADSETHOOK = 79,
1010 0, //AKEYCODE_FOCUS = 80, // *Camera* focus
1011 keyPadPlus, //AKEYCODE_PLUS = 81,
1012 0, //AKEYCODE_MENU = 82,
1013 0, //AKEYCODE_NOTIFICATION = 83,
1014 0, //AKEYCODE_SEARCH = 84,
1015 0, //AKEYCODE_MEDIA_PLAY_PAUSE= 85,
1016 0, //AKEYCODE_MEDIA_STOP = 86,
1017 0, //AKEYCODE_MEDIA_NEXT = 87,
1018 0, //AKEYCODE_MEDIA_PREVIOUS = 88,
1019 0, //AKEYCODE_MEDIA_REWIND = 89,
1020 0, //AKEYCODE_MEDIA_FAST_FORWARD = 90,
1021 0, //AKEYCODE_MUTE = 91,
1022 0, //AKEYCODE_PAGE_UP = 92,
1023 0, //AKEYCODE_PAGE_DOWN = 93,
1024 0, //AKEYCODE_PICTSYMBOLS = 94,
1025 0, //AKEYCODE_SWITCH_CHARSET = 95,
1026 0, //AKEYCODE_BUTTON_A = 96,
1027 0, //AKEYCODE_BUTTON_B = 97,
1028 0, //AKEYCODE_BUTTON_C = 98,
1029 0, //AKEYCODE_BUTTON_X = 99,
1030 0, //AKEYCODE_BUTTON_Y = 100,
1031 0, //AKEYCODE_BUTTON_Z = 101,
1032 0, //AKEYCODE_BUTTON_L1 = 102,
1033 0, //AKEYCODE_BUTTON_R1 = 103,
1034 0, //AKEYCODE_BUTTON_L2 = 104,
1035 0, //AKEYCODE_BUTTON_R2 = 105,
1036 0, //AKEYCODE_BUTTON_THUMBL = 106,
1037 0, //AKEYCODE_BUTTON_THUMBR = 107,
1038 0, //AKEYCODE_BUTTON_START = 108,
1039 0, //AKEYCODE_BUTTON_SELECT = 109,
1040 0, //AKEYCODE_BUTTON_MODE = 110,
1041 escape, //AKEYCODE_BUTTON_ESCAPE = 111,
1042 del, //AKEYCODE_BUTTON_ESCAPE = 112,
1043 leftControl, // = 113
1044 rightControl, // = 114
1046 scrollLock, // = 116
1047 0, // = 117 KEYCODE_META_LEFT
1048 0, // = 118 KEYCODE_META_RIGHT
1049 0, // = 119 KEYCODE_FUNCTION
1050 printScreen, // = 120 KEYCODE_SYSRQ
1051 pauseBreak, // = 121
1057 // Why don't we have this in the NDK :(
1058 // default int32_t AKeyEvent_getUnichar(const AInputEvent* key_event);
1060 static Array<TouchPointerInfo> buildPointerInfo(AInputEvent * event)
1062 uint count = (uint)AMotionEvent_getPointerCount(event);
1063 Array<TouchPointerInfo> infos { size = count };
1065 for(i = 0; i < count; i++)
1067 infos[i].x = (int)AMotionEvent_getX(event, i);
1068 infos[i].y = (int)AMotionEvent_getY(event, i);
1069 infos[i].id = (int)AMotionEvent_getPointerId(event, i);
1070 infos[i].pressure = AMotionEvent_getPressure(event, i);
1071 infos[i].size = AMotionEvent_getSize(event, i);
1076 class AndroidActivity : AndroidAppGlue
1078 AndroidPollSource source;
1082 ASensorManager* sensorManager;
1083 const ASensor* accelerometerSensor;
1084 ASensorEventQueue* sensorEventQueue;
1088 int onInputEvent(AInputEvent* event)
1090 static Time lastTime = 0;
1091 Window window = guiApp.desktop;
1092 uint type = AInputEvent_getType(event);
1093 if(type == AINPUT_EVENT_TYPE_MOTION)
1095 uint actionAndIndex = AMotionEvent_getAction(event);
1096 //uint source = AInputEvent_getSource(event);
1097 uint action = actionAndIndex & AMOTION_EVENT_ACTION_MASK;
1098 //uint index = (actionAndIndex & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
1099 //uint flags = AMotionEvent_getFlags(event);
1100 uint meta = AMotionEvent_getMetaState(event);
1101 //uint edge = AMotionEvent_getEdgeFlags(event);
1102 //int64 downTime = AMotionEvent_getDownTime(event); // nanotime
1103 //int64 eventTime = AMotionEvent_getDownTime(event);
1105 Modifiers keyFlags = 0;
1106 int x = (int)AMotionEvent_getX(event, 0);
1107 int y = (int)AMotionEvent_getY(event, 0);
1108 bool shift = (meta & AMETA_SHIFT_ON) ? true : false;
1109 bool alt = (meta & AMETA_ALT_ON) ? true : false;
1110 //bool sym = (meta & AMETA_SYM_ON) ? true : false;
1112 keyFlags.shift = shift;
1115 //PrintLn("Got a motion input event: ", action);
1117 if(action == 8) //AMOTION_EVENT_ACTION_SCROLL)
1118 axis = AMotionEvent_getAxisValue(event, 9, index); //AMOTION_EVENT_AXIS_VSCROLL);
1121 AInputQueue_finishEvent(inputQueue, event, 1);
1125 case 8: //AMOTION_EVENT_ACTION_SCROLL:
1126 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, (axis < 0) ? wheelUp : wheelDown, 0);
1129 case AMOTION_EVENT_ACTION_DOWN:
1131 Time time = GetTime();
1133 if(Abs(x - mouseX) < 40 && Abs(y - mouseY) < 40 && time - lastTime < 0.3)
1134 if(!window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick, x, y, &keyFlags, false, true))
1137 mouseX = x, mouseY = y;
1139 window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, x, y, &keyFlags, false, true);
1142 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1143 window.MultiTouchMessage(down, infos, &keyFlags, false, true);
1148 case AMOTION_EVENT_ACTION_UP:
1149 mouseX = x, mouseY = y;
1150 if(window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp, x, y, &keyFlags, false, true))
1152 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1153 window.MultiTouchMessage(up, infos, &keyFlags, false, true);
1157 case AMOTION_EVENT_ACTION_MOVE:
1158 mouseX = x, mouseY = y;
1159 if(window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove, x, y, &keyFlags, false, true))
1161 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1162 window.MultiTouchMessage(move, infos, &keyFlags, false, true);
1166 case AMOTION_EVENT_ACTION_CANCEL: break;
1167 case AMOTION_EVENT_ACTION_OUTSIDE: break;
1168 case AMOTION_EVENT_ACTION_POINTER_DOWN:
1170 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1171 window.MultiTouchMessage(pointerDown, infos, &keyFlags, false, true);
1175 case AMOTION_EVENT_ACTION_POINTER_UP:
1177 Array<TouchPointerInfo> infos = buildPointerInfo(event);
1178 window.MultiTouchMessage(pointerUp, infos, &keyFlags, false, true);
1185 else if(type == AINPUT_EVENT_TYPE_KEY)
1187 uint action = AKeyEvent_getAction(event);
1188 //uint flags = AKeyEvent_getFlags(event);
1189 uint keyCode = AKeyEvent_getKeyCode(event);
1190 uint meta = AKeyEvent_getMetaState(event);
1191 Key key = keyCodeTable[keyCode];
1192 bool shift = (meta & AMETA_SHIFT_ON) ? true : false;
1193 bool alt = (meta & AMETA_ALT_ON || meta & AMETA_ALT_LEFT_ON || meta & AMETA_ALT_RIGHT_ON) ? true : false;
1194 //bool metaMeta = (meta & AMETA_META_ON || meta & AMETA_META_LEFT_ON || meta & AMETA_META_RIGHT_ON) ? true : false;
1195 //bool sym = (meta & AMETA_SYM_ON) ? true : false;
1196 //unichar ch = AKeyEvent_getUnichar(event);
1202 AInputQueue_finishEvent(inputQueue, event, 1);
1204 // PrintLn("Got a key: action = ", action, ", flags = ", flags, ", keyCode = ", keyCode, ", meta = ", meta, ": key = ", (int)key);
1208 if(action == AKEY_EVENT_ACTION_DOWN || action == AKEY_EVENT_ACTION_MULTIPLE)
1210 /*if(key == wheelDown || key == wheelUp)
1211 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, key, ch);
1214 char c = Interface::TranslateKey(key.code, shift);
1216 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, key, ch);
1219 else if(action == AKEY_EVENT_ACTION_UP)
1220 window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, key, ch);
1225 AInputQueue_finishEvent(inputQueue, event, 0);
1229 void onAppCmd(AppCommand cmd)
1234 setSavedState(&state, sizeof(state));
1241 ANativeWindow_setBuffersGeometry(window, 0, 0, 0); //format);
1242 w = ANativeWindow_getWidth(window);
1243 h = ANativeWindow_getHeight(window);
1244 guiApp.Initialize(false);
1245 guiApp.desktop.windowHandle = window;
1246 guiApp.interfaceDriver = null;
1247 guiApp.SwitchMode(true, null, 0, 0, 0, null, false);
1249 if(desktopW != w || desktopH != h)
1251 guiApp.SetDesktopPosition(0, 0, w, h, true);
1255 guiApp.desktop.Update(null);
1259 guiApp.desktop.UnloadGraphics(false);
1262 guiApp.desktop.Update(null);
1263 guiApp.SetAppFocus(true);
1265 if(accelerometerSensor)
1267 ASensorEventQueue_enableSensor(sensorEventQueue, accelerometerSensor);
1268 ASensorEventQueue_setEventRate(sensorEventQueue, accelerometerSensor, (1000L/60)*1000);
1274 if(accelerometerSensor)
1275 ASensorEventQueue_disableSensor(sensorEventQueue, accelerometerSensor);
1277 guiApp.SetAppFocus(false);
1278 guiApp.desktop.Update(null);
1282 guiApp.desktop.UpdateDisplay();
1289 androidActivity = this;
1290 /* Let's have fun with sensors when we have an actual device to play with
1291 sensorManager = ASensorManager_getInstance();
1292 accelerometerSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER);
1293 sensorEventQueue = ASensorManager_createEventQueue(sensorManager, looper, LooperID::user, null, null);
1297 state = *(SavedState*)savedState;
1302 // Evolve the Application class into a GuiApplication
1303 eInstance_Evolve((Instance *)&__androidCurrentModule, class(GuiApplication));
1305 // Wait for the initWindow command:
1306 guiApp.interfaceDriver = class(AndroidInterface);
1309 // Can't call the GuiApplication here, because GuiApplication::Initialize() has not been called yet
1310 guiApp.interfaceDriver.Wait();
1311 guiApp.interfaceDriver.ProcessInput(true);
1314 // Invoke __ecereDll_Load() in lib[our package name].so
1315 app = eModule_Load(__androidCurrentModule, moduleName, publicAccess);
1319 // Find out if any GuiApplication class was defined in our module
1320 for(c = app.classes.first; c && !eClass_IsDerived(c, class(GuiApplication)); c = c.next);
1321 if(!c) c = class(GuiApplication);
1323 guiApp.lockMutex.Release(); // TOCHECK: Seems the evolve is losing our mutex lock here ?
1325 // Evolve the Application into it
1326 eInstance_Evolve((Instance *)&__androidCurrentModule, c);
1327 guiApp = (GuiApplication)__androidCurrentModule;
1330 const String skin = guiApp.skin;
1331 *&guiApp.currentSkin = null;
1332 guiApp.SelectSkin(skin);
1335 guiApp.lockMutex.Wait();
1338 ((void (*)(void *))(void *)__androidCurrentModule._vTbl[12])(__androidCurrentModule);
1341 if(!destroyRequested)
1342 ANativeActivity_finish(activity);
1343 while(!destroyRequested)
1345 guiApp.interfaceDriver.Wait();
1346 guiApp.interfaceDriver.ProcessInput(true);