a79b8a3b2b90e1cff092f87c83ab1b202107b713
[sdk] / ecere / src / gui / drivers / AndroidInterface.ec
1 #define _Noreturn
2
3 namespace gui::drivers;
4
5 import "Window"
6 import "Interface"
7 import "Condition"
8
9 #define uint _uint
10 #define set _set
11 #include <errno.h>
12 #include <locale.h>
13 #include <pthread.h>
14 #include <unistd.h>
15 #include <sys/prctl.h>
16
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>
23
24 #include <jni.h>
25 #undef set
26 #undef uint
27
28 #define printf(...) ((void)__android_log_print(ANDROID_LOG_INFO, "ecere-app", __VA_ARGS__))
29
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__))
33 #ifndef _DEBUG
34 #define LOGV(...)  ((void)0)
35 #else
36 #define LOGV(...)  ((void)__android_log_print(ANDROID_LOG_VERBOSE, "ecere-app", __VA_ARGS__))
37 #endif
38
39 // *** NATIVE APP GLUE ********
40 enum LooperID { main = 1, input = 2, user = 3 };
41 enum AppCommand : byte
42 {
43    error = 0, inputChanged, initWindow, termWindow, windowResized, windowRedrawNeeded,
44    contentRectChanged, gainedFocus, lostFocus,
45    configChanged, lowMemory, start, resume, saveState, pause, stop, destroy
46 };
47
48 class AndroidPollSource
49 {
50 public:
51    void * userData;
52    LooperID id;
53    virtual void any_object::process();
54 };
55
56 static const char * packagePath;
57
58 class AndroidAppGlue : Thread
59 {
60    void* userData;
61    virtual void onAppCmd(AppCommand cmd);
62    virtual int onInputEvent(AInputEvent* event);
63    virtual void main();
64
65    ANativeActivity* activity;
66    AConfiguration* config;
67    void* savedState;
68    uint savedStateSize;
69
70    ALooper* looper;
71    AInputQueue* inputQueue;
72    ANativeWindow* window;
73    ARect contentRect;
74    AppCommand activityState;
75    bool destroyRequested;
76    char * moduleName;
77
78 private:
79    Mutex mutex { };
80    Condition cond { };
81
82    int msgread, msgwrite;
83
84    unsigned int Main()
85    {
86       config = AConfiguration_new();
87       AConfiguration_fromAssetManager(config, activity->assetManager);
88
89       print_cur_config();
90
91       looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
92       ALooper_addFd(looper, msgread, LooperID::main, ALOOPER_EVENT_INPUT, null, cmdPollSource);
93
94       mutex.Wait();
95       running = true;
96       cond.Signal();
97       mutex.Release();
98
99       main();
100
101       destroy();
102       return 0;
103    }
104
105    void destroy()
106    {
107       free_saved_state();
108       mutex.Wait();
109       if(inputQueue)
110          AInputQueue_detachLooper(inputQueue);
111       AConfiguration_delete(config);
112       destroyed = true;
113       cond.Signal();
114       mutex.Release();
115    }
116
117    AndroidPollSource cmdPollSource
118    {
119       this, main;
120
121       void process()
122       {
123          AppCommand cmd = read_cmd();
124          pre_exec_cmd(cmd);
125          onAppCmd(cmd);
126          post_exec_cmd(cmd);
127       }
128    };
129    AndroidPollSource inputPollSource
130    {
131       this, input;
132
133       void process()
134       {
135          AInputEvent* event = null;
136          if(AInputQueue_getEvent(inputQueue, &event) >= 0)
137          {
138             //int handled = 0;
139             LOGV("New input event: type=%d\n", AInputEvent_getType(event));
140             if(AInputQueue_preDispatchEvent(inputQueue, event))
141                return;
142             /*handled = */onInputEvent(event);
143             //AInputQueue_finishEvent(inputQueue, event, handled);
144          }
145          else
146             LOGE("Failure reading next input event: %s\n", strerror(errno));
147       }
148    };
149
150    bool running;
151    bool stateSaved;
152    bool destroyed;
153    AInputQueue* pendingInputQueue;
154    ANativeWindow* pendingWindow;
155    ARect pendingContentRect;
156
157    void free_saved_state()
158    {
159       mutex.Wait();
160       if(savedState)
161          free(savedState);
162       savedState = 0;
163       savedStateSize = 0;
164       mutex.Release();
165    }
166
167    AppCommand read_cmd()
168    {
169       AppCommand cmd;
170       if(read(msgread, &cmd, sizeof(cmd)) == sizeof(cmd))
171       {
172          if(cmd == saveState)
173             free_saved_state();
174          return cmd;
175       }
176       else
177          LOGE("No data on command pipe!");
178       return error;
179    }
180
181    void print_cur_config()
182    {
183       char lang[2], country[2];
184       AConfiguration_getLanguage(config, lang);
185       AConfiguration_getCountry(config, country);
186
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));
205    }
206
207    void pre_exec_cmd(AppCommand cmd)
208    {
209       //PrintLn("pre_exec_cmd: ", cmd);
210       switch(cmd)
211       {
212          case inputChanged:
213             mutex.Wait();
214             if(inputQueue)
215                AInputQueue_detachLooper(inputQueue);
216             inputQueue = pendingInputQueue;
217             if(inputQueue)
218                AInputQueue_attachLooper(inputQueue, looper, LooperID::input, null, inputPollSource);
219             cond.Signal();
220             mutex.Release();
221             break;
222          case initWindow:
223             mutex.Wait();
224             window = pendingWindow;
225             cond.Signal();
226             mutex.Release();
227             break;
228          case termWindow:
229             cond.Signal();
230             break;
231          case resume:
232          case start:
233          case pause:
234          case stop:
235             mutex.Wait();
236             activityState = cmd;
237             cond.Signal();
238             mutex.Release();
239             break;
240          case configChanged:
241             AConfiguration_fromAssetManager(config, activity->assetManager);
242             print_cur_config();
243             break;
244          case destroy:
245             destroyRequested = true;
246             break;
247       }
248    }
249
250    void post_exec_cmd(AppCommand cmd)
251    {
252       //PrintLn("post_exec_cmd: ", cmd);
253       switch(cmd)
254       {
255          case termWindow:
256             mutex.Wait();
257             window = null;
258             cond.Signal();
259             mutex.Release();
260             break;
261          case saveState:
262             mutex.Wait();
263             stateSaved = true;
264             cond.Signal();
265             mutex.Release();
266             break;
267          case resume:
268             free_saved_state();
269             break;
270       }
271    }
272
273    void write_cmd(AppCommand cmd)
274    {
275       if(write(msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd))
276          LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
277    }
278
279    void set_input(AInputQueue* inputQueue)
280    {
281       mutex.Wait();
282       pendingInputQueue = inputQueue;
283       write_cmd(inputChanged);
284       while(inputQueue != pendingInputQueue)
285          cond.Wait(mutex);
286       mutex.Release();
287    }
288
289    void set_window(ANativeWindow* window)
290    {
291       mutex.Wait();
292       if(pendingWindow)
293          write_cmd(termWindow);
294       pendingWindow = window;
295       if(window)
296          write_cmd(initWindow);
297       while(window != pendingWindow)
298          cond.Wait(mutex);
299       mutex.Release();
300    }
301
302    void set_activity_state(AppCommand cmd)
303    {
304       mutex.Wait();
305       write_cmd(cmd);
306       while(activityState != cmd)
307          cond.Wait(mutex);
308       mutex.Release();
309    }
310
311    void cleanup()
312    {
313       mutex.Wait();
314       write_cmd(destroy);
315       while(!destroyed)
316          cond.Wait(mutex);
317       mutex.Release();
318       close(msgread);
319       close(msgwrite);
320    }
321
322    void setSavedState(void * state, uint size)
323    {
324       if(savedState)
325          free(savedState);
326       savedState = null;
327       if(state)
328       {
329          savedState = malloc(size);
330          savedStateSize = size;
331          memcpy(savedState, state, size);
332       }
333       else
334          savedStateSize = 0;
335    }
336
337    public void Create()
338    {
339       int msgpipe[2];
340       if(pipe(msgpipe))
341          LOGE("could not create pipe: %s", strerror(errno));
342       msgread = msgpipe[0];
343       msgwrite = msgpipe[1];
344
345       Thread::Create();
346
347       // Wait for thread to start.
348       mutex.Wait();
349       while(!running) cond.Wait(mutex);
350       mutex.Release();
351    }
352 }
353
354 // Callbacks
355 static void onDestroy(ANativeActivity* activity)
356 {
357    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
358    LOGI("Destroy: %p\n", activity);
359    app.cleanup();
360    app.Wait();
361    delete androidActivity;
362    delete __androidCurrentModule;
363    LOGI("THE END.");
364 }
365
366 static void onStart(ANativeActivity* activity)
367 {
368    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
369    LOGI("Start: %p\n", activity);
370    app.set_activity_state(start);
371 }
372
373 static void onResume(ANativeActivity* activity)
374 {
375    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
376    LOGI("Resume: %p\n", activity);
377    app.set_activity_state(resume);
378 }
379
380 static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen)
381 {
382    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
383    void* savedState = null;
384    LOGI("SaveInstanceState: %p\n", activity);
385    app.mutex.Wait();
386    app.stateSaved = false;
387    app.write_cmd(saveState);
388    while(!app.stateSaved)
389       app.cond.Wait(app.mutex);
390    if(app.savedState)
391    {
392       savedState = app.savedState;
393       *outLen = app.savedStateSize;
394       app.savedState = null;
395       app.savedStateSize = 0;
396    }
397    app.mutex.Release();
398    return savedState;
399 }
400
401 static void onPause(ANativeActivity* activity)
402 {
403    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
404    LOGI("Pause: %p\n", activity);
405    app.set_activity_state(pause);
406 }
407
408 static void onStop(ANativeActivity* activity)
409 {
410    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
411    LOGI("Stop: %p\n", activity);
412    app.set_activity_state(stop);
413 }
414
415 static void onConfigurationChanged(ANativeActivity* activity)
416 {
417    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
418    LOGI("ConfigurationChanged: %p\n", activity);
419    app.write_cmd(configChanged);
420 }
421
422 static void onLowMemory(ANativeActivity* activity)
423 {
424    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
425    LOGI("LowMemory: %p\n", activity);
426    app.write_cmd(lowMemory);
427 }
428
429 static void onWindowFocusChanged(ANativeActivity* activity, int focused)
430 {
431    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
432    LOGI("WindowFocusChanged: %p -- %d\n", activity, focused);
433    app.write_cmd(focused ? gainedFocus : lostFocus);
434 }
435
436 static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window)
437 {
438    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
439    LOGI("NativeWindowCreated: %p -- %p\n", activity, window);
440    app.set_window(window);
441 }
442
443 static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window)
444 {
445    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
446    LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window);
447    app.window = null;
448    app.set_window(null);
449 }
450
451 static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue)
452 {
453    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
454    LOGI("InputQueueCreated: %p -- %p\n", activity, queue);
455    app.set_input(queue);
456 }
457
458 static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue)
459 {
460    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
461    LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue);
462    app.inputQueue = null;
463    app.set_input(null);
464 }
465
466 default dllexport void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize)
467 {
468    AndroidAppGlue app;
469    char * moduleName;
470
471    // Determine our package name
472    JNIEnv* env=activity->env;
473    jclass clazz;
474    const char* str;
475    jboolean isCopy;
476    jmethodID methodID;
477    jobject result;
478
479    // *** Reinitialize static global variables ***
480    gotInit = false;
481    guiApplicationInitialized = false;
482    guiApp = null;
483    desktopW = 0; desktopH = 0;
484    clipBoardData = null;
485    __thisModule = null;
486    __androidCurrentModule = null;
487
488    prctl(PR_SET_DUMPABLE, 1);
489
490    LOGI("Creating: %p\n", activity);
491
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);
497
498    moduleName = strstr(str, "com.ecere.");
499    if(moduleName) moduleName += 10;
500    androidArgv[0] = moduleName;
501
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);
505    packagePath = str;
506    // (*activity->vm)->DetachCurrentThread(activity->vm);
507    LOGI("packagePath: %s\n", packagePath);
508
509    // Create a base Application class
510    __androidCurrentModule = __ecere_COM_Initialize(true, 1, androidArgv);
511    // Load up Ecere
512    eModule_Load(__androidCurrentModule, "ecere", publicAccess);
513
514    /*
515    if(activity->internalDataPath) PrintLn("internalDataPath is ", activity->internalDataPath);
516    if(activity->externalDataPath) PrintLn("externalDataPath is ", activity->externalDataPath);
517    {
518       char tmp[256];
519       PrintLn("cwd is ", GetWorkingDir(tmp, sizeof(tmp)));
520    }
521    */
522
523    ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_FULLSCREEN|AWINDOW_FLAG_KEEP_SCREEN_ON, 0 );
524    app = AndroidActivity { activity = activity, moduleName = moduleName };
525
526    incref app;
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;
542    app.Create();
543 }
544
545 // *** END OF NATIVE APP GLUE ******
546
547 default:
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;
562 private:
563
564 static Module __androidCurrentModule;
565 static char * androidArgv[1];
566
567 static int desktopW, desktopH;
568 static char * clipBoardData;
569 static int mouseX, mouseY;
570
571 class AndroidInterface : Interface
572 {
573    class_property(name) = "Android";
574
575    // --- User Interface System ---
576    bool Initialize()
577    {
578       setlocale(LC_ALL, "en_US.UTF-8");
579       return true;
580    }
581
582    void Terminate()
583    {
584
585    }
586
587    #define DBLCLICK_DELAY  300   // 0.3 second
588    #define DBLCLICK_DELTA  1
589
590    bool ProcessInput(bool processAll)
591    {
592       bool eventAvailable = false;
593
594       if(androidActivity.ident < 0)
595          androidActivity.ident = (LooperID)ALooper_pollAll(0, null, &androidActivity.events, (void**)&androidActivity.source);
596
597       if(gotInit && androidActivity.window)
598       {
599          int w = ANativeWindow_getWidth(androidActivity.window);
600          int h = ANativeWindow_getHeight(androidActivity.window);
601          if(desktopW != w || desktopH != h)
602          {
603             guiApp.SetDesktopPosition(0, 0, w, h, true);
604             desktopW = w;
605             desktopH = h;
606             guiApp.desktop.Update(null);
607          }
608       }
609
610       while(androidActivity.ident >= 0)
611       {
612          AndroidPollSource source = androidActivity.source;
613
614          androidActivity.source = null;
615          if(source)
616             source.process(source.userData);
617
618          // If a sensor has data, process it now.
619          /*
620          if(androidActivity.ident == user)
621          {
622             if(androidActivity.accelerometerSensor)
623             {
624                ASensorEvent event;
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);
627             }
628          }
629          */
630
631          eventAvailable = true;
632          if(androidActivity.destroyRequested)
633          {
634             guiApp.desktop.Destroy(0);
635             eventAvailable = true;
636             androidActivity.ident = (LooperID)-1;
637          }
638          else if(processAll)
639             androidActivity.ident = (LooperID)ALooper_pollAll(0, null, &androidActivity.events, (void**)&androidActivity.source);
640          else
641             androidActivity.ident = (LooperID)-1;
642       }
643       return eventAvailable;
644    }
645
646    void Wait()
647    {
648       androidActivity.ident = (LooperID)ALooper_pollAll((int)(1000/18.2f), null, &androidActivity.events, (void**)&androidActivity.source);
649       // guiApp.WaitEvent();
650    }
651
652    void Lock(Window window)
653    {
654
655    }
656
657    void Unlock(Window window)
658    {
659
660    }
661
662    const char ** GraphicsDrivers(int * numDrivers)
663    {
664       static const char *graphicsDrivers[] = { "OpenGL" };
665       *numDrivers = sizeof(graphicsDrivers) / sizeof(char *);
666       return (const char **)graphicsDrivers;
667    }
668
669    void GetCurrentMode(bool * fullScreen, int * resolution, int * colorDepth, int * refreshRate)
670    {
671       *fullScreen = true;
672    }
673
674    void EnsureFullScreen(bool *fullScreen)
675    {
676       *fullScreen = true;
677    }
678
679    bool ScreenMode(bool fullScreen, int resolution, int colorDepth, int refreshRate, bool * textMode)
680    {
681       bool result = true;
682
683       return result;
684    }
685
686    // --- Window Creation ---
687    void * CreateRootWindow(Window window)
688    {
689       return androidActivity.window;
690    }
691
692    void DestroyRootWindow(Window window)
693    {
694
695    }
696
697    // -- Window manipulation ---
698
699    void SetRootWindowCaption(Window window, const char * name)
700    {
701
702    }
703
704    void PositionRootWindow(Window window, int x, int y, int w, int h, bool move, bool resize)
705    {
706
707    }
708
709    void OrderRootWindow(Window window, bool topMost)
710    {
711
712    }
713
714    void SetRootWindowColor(Window window)
715    {
716
717    }
718
719    void OffsetWindow(Window window, int * x, int * y)
720    {
721
722    }
723
724    void UpdateRootWindow(Window window)
725    {
726       if(!window.parent || !window.parent.display)
727       {
728          if(window.visible)
729          {
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);
736             window.Update(null);
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);
742          }
743       }
744    }
745
746
747    void SetRootWindowState(Window window, WindowState state, bool visible)
748    {
749    }
750
751    void FlashRootWindow(Window window)
752    {
753
754    }
755
756    void ActivateRootWindow(Window window)
757    {
758
759    }
760
761    // --- Mouse-based window movement ---
762
763    void StartMoving(Window window, int x, int y, bool fromKeyBoard)
764    {
765
766    }
767
768    void StopMoving(Window window)
769    {
770
771    }
772
773    // -- Mouse manipulation ---
774
775    void GetMousePosition(int *x, int *y)
776    {
777       *x = mouseX;
778       *y = mouseY;
779    }
780
781    void SetMousePosition(int x, int y)
782    {
783       mouseX = x;
784       mouseY = y;
785    }
786
787    void SetMouseRange(Window window, Box box)
788    {
789    }
790
791    void SetMouseCapture(Window window)
792    {
793    }
794
795    // -- Mouse cursor ---
796
797    void SetMouseCursor(Window window, int cursor)
798    {
799       if(cursor == -1)
800       {
801
802       }
803    }
804
805    // --- Caret ---
806
807    void SetCaret(int x, int y, int size)
808    {
809       Window caretOwner = guiApp.caretOwner;
810       Window window = caretOwner ? caretOwner.rootWindow : null;
811       if(window && window.windowData)
812       {
813       }
814    }
815
816    void ClearClipboard()
817    {
818       if(clipBoardData)
819       {
820          delete clipBoardData;
821       }
822    }
823
824    bool AllocateClipboard(ClipBoard clipBoard, uint size)
825    {
826       bool result = false;
827       if((clipBoard.text = new0 byte[size]))
828          result = true;
829       return result;
830    }
831
832    bool SaveClipboard(ClipBoard clipBoard)
833    {
834       bool result = false;
835       if(clipBoard.text)
836       {
837          if(clipBoardData)
838             delete clipBoardData;
839
840          clipBoardData = clipBoard.text;
841          clipBoard.text = null;
842          result = true;
843       }
844       return result;
845    }
846
847    bool LoadClipboard(ClipBoard clipBoard)
848    {
849       bool result = false;
850
851       // The data is inside this client...
852       if(clipBoardData)
853       {
854          clipBoard.text = new char[strlen(clipBoardData)+1];
855          strcpy(clipBoard.text, clipBoardData);
856          result = true;
857       }
858       // The data is with another client...
859       else
860       {
861       }
862       return result;
863    }
864
865    void UnloadClipboard(ClipBoard clipBoard)
866    {
867       delete clipBoard.text;
868    }
869
870    // --- State based input ---
871
872    bool AcquireInput(Window window, bool state)
873    {
874       return false;
875    }
876
877    bool GetMouseState(MouseButtons * buttons, int * x, int * y)
878    {
879       bool result = false;
880       if(x) *x = 0;
881       if(y) *y = 0;
882       return result;
883    }
884
885    bool GetJoystickState(int device, Joystick joystick)
886    {
887       bool result = false;
888       return result;
889    }
890
891    bool GetKeyState(Key key)
892    {
893       bool keyState = false;
894       return keyState;
895    }
896
897    void SetTimerResolution(uint hertz)
898    {
899       // timerDelay = hertz ? (1000000 / hertz) : MAXINT;
900    }
901
902    bool SetIcon(Window window, BitmapResource resource)
903    {
904       if(resource)
905       {
906          /*Bitmap bitmap { };
907          if(bitmap.Load(resource.fileName, null, null))
908          {
909          }
910          delete bitmap;*/
911       }
912       return true;
913    }
914 }
915
916 struct SavedState
917 {
918     float angle;
919     int x;
920     int y;
921 };
922
923 static AndroidActivity androidActivity;
924
925 default const char * AndroidInterface_GetLibLocation(Application a)
926 {
927    static char loc[MAX_LOCATION] = "", mod[MAX_LOCATION];
928    bool found = false;
929 #if defined(__LP64__)
930    static const char * arch = "arm64";
931 #else
932    static const char * arch = "armeabi";
933 #endif
934    int i;
935    bool useArch = true;
936
937    while(!found)
938    {
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;
943       if(!found)
944       {
945          bool useApp = true;
946          while(!found)
947          {
948             for(i = 0; !found && i < 10; i++)
949             {
950                if(i)
951                   sprintf(loc, "/data/%s/com.ecere.%s-%d/lib/%s/lib", useApp ? "app" : "data", a.argv[0], i, useArch ? arch : "");
952                else
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;
956             }
957             if(useApp)
958                useApp = false;
959             else
960                break;
961          }
962       }
963       if(useArch)
964          useArch = false;
965       else
966          break;
967    }
968    return loc;
969 }
970
971 static bool gotInit;
972
973 default float AMotionEvent_getAxisValue(const AInputEvent* motion_event,
974         int32_t axis, size_t pointer_index);
975
976
977 static define AMETA_META_ON       = 0x00010000;
978 static define AMETA_META_LEFT_ON  = 0x00020000;
979 static define AMETA_META_RIGHT_ON = 0x00040000;
980
981 static Key keyCodeTable[] =
982 {
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
1098     capsLock, // = 115
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
1105     home, // = 122
1106     end, // = 123
1107     insert // = 124
1108 };
1109
1110 // Why don't we have this in the NDK :(
1111 // default int32_t AKeyEvent_getUnichar(const AInputEvent* key_event);
1112
1113 static Array<TouchPointerInfo> buildPointerInfo(AInputEvent * event)
1114 {
1115    uint count = (uint)AMotionEvent_getPointerCount(event);
1116    Array<TouchPointerInfo> infos { size = count };
1117    int i;
1118    for(i = 0; i < count; i++)
1119    {
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);
1124    }
1125    return infos;
1126 }
1127
1128 class AndroidActivity : AndroidAppGlue
1129 {
1130    AndroidPollSource source;
1131    int events;
1132    LooperID ident;
1133    /*
1134    ASensorManager* sensorManager;
1135    const ASensor* accelerometerSensor;
1136    ASensorEventQueue* sensorEventQueue;
1137    */
1138    SavedState state;
1139
1140    int onInputEvent(AInputEvent* event)
1141    {
1142       static Time lastTime = 0;
1143       Window window = guiApp.desktop;
1144       uint type = AInputEvent_getType(event);
1145       if(type == AINPUT_EVENT_TYPE_MOTION)
1146       {
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);
1156          //float axis;
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;
1163
1164          keyFlags.shift = shift;
1165          keyFlags.alt = alt;
1166
1167          //PrintLn("Got a motion input event: ", action);
1168          /*
1169          if(action == 8) //AMOTION_EVENT_ACTION_SCROLL)
1170             axis = AMotionEvent_getAxisValue(event, 9, index); //AMOTION_EVENT_AXIS_VSCROLL);
1171          */
1172
1173          AInputQueue_finishEvent(inputQueue, event, 1);
1174          switch(action)
1175          {
1176             /*
1177             case 8: //AMOTION_EVENT_ACTION_SCROLL:
1178                window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, (axis < 0) ? wheelUp : wheelDown, 0);
1179                break;
1180                */
1181             case AMOTION_EVENT_ACTION_DOWN:
1182             {
1183                Time time = GetTime();
1184                bool result = true;
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))
1187                      result = false;
1188                lastTime = time;
1189                mouseX = x, mouseY = y;
1190                if(result)
1191                   // TOCHECK: Should we do result = here?
1192                   window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, x, y, &keyFlags, false, true);
1193                if(result)
1194                {
1195                   Array<TouchPointerInfo> infos = buildPointerInfo(event);
1196                   window.MultiTouchMessage(down, infos, &keyFlags, false, true);
1197                   delete infos;
1198                }
1199                break;
1200             }
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))
1204                {
1205                   Array<TouchPointerInfo> infos = buildPointerInfo(event);
1206                   window.MultiTouchMessage(up, infos, &keyFlags, false, true);
1207                   delete infos;
1208                }
1209                break;
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))
1213                {
1214                   Array<TouchPointerInfo> infos = buildPointerInfo(event);
1215                   window.MultiTouchMessage(move, infos, &keyFlags, false, true);
1216                   delete infos;
1217                }
1218                break;
1219             case AMOTION_EVENT_ACTION_CANCEL: break;
1220             case AMOTION_EVENT_ACTION_OUTSIDE: break;
1221             case AMOTION_EVENT_ACTION_POINTER_DOWN:
1222             {
1223                Array<TouchPointerInfo> infos = buildPointerInfo(event);
1224                window.MultiTouchMessage(pointerDown, infos, &keyFlags, false, true);
1225                delete infos;
1226                break;
1227             }
1228             case AMOTION_EVENT_ACTION_POINTER_UP:
1229             {
1230                Array<TouchPointerInfo> infos = buildPointerInfo(event);
1231                window.MultiTouchMessage(pointerUp, infos, &keyFlags, false, true);
1232                delete infos;
1233                break;
1234             }
1235          }
1236          return 1;
1237       }
1238       else if(type == AINPUT_EVENT_TYPE_KEY)
1239       {
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);
1250          unichar ch = 0;
1251
1252          key.shift = shift;
1253          key.alt = alt;
1254
1255          AInputQueue_finishEvent(inputQueue, event, 1);
1256
1257          // PrintLn("Got a key: action = ", action, ", flags = ", flags, ", keyCode = ", keyCode, ", meta = ", meta, ": key = ", (int)key);
1258
1259          if(key)
1260          {
1261             if(action == AKEY_EVENT_ACTION_DOWN || action == AKEY_EVENT_ACTION_MULTIPLE)
1262             {
1263                /*if(key == wheelDown || key == wheelUp)
1264                   window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, key, ch);
1265                else*/
1266                {
1267                   char c = Interface::TranslateKey(key.code, shift);
1268                   if(c > 0) ch = c;
1269                   window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, key, ch);
1270                }
1271             }
1272             else if(action == AKEY_EVENT_ACTION_UP)
1273                window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, key, ch);
1274          }
1275          return 1;
1276       }
1277       else
1278          AInputQueue_finishEvent(inputQueue, event, 0);
1279       return 0;
1280    }
1281
1282    void onAppCmd(AppCommand cmd)
1283    {
1284       switch(cmd)
1285       {
1286          case saveState:
1287             setSavedState(&state, sizeof(state));
1288             break;
1289          case initWindow:
1290             if(window)
1291             {
1292                int w, h;
1293                gotInit = true;
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);
1301
1302                if(desktopW != w || desktopH != h)
1303                {
1304                   guiApp.SetDesktopPosition(0, 0, w, h, true);
1305                   desktopW = w;
1306                   desktopH = h;
1307                }
1308                guiApp.desktop.Update(null);
1309             }
1310             break;
1311          case termWindow:
1312             guiApp.desktop.UnloadGraphics(false);
1313             break;
1314          case gainedFocus:
1315             guiApp.desktop.Update(null);
1316             guiApp.SetAppFocus(true);
1317             /*
1318             if(accelerometerSensor)
1319             {
1320                ASensorEventQueue_enableSensor(sensorEventQueue, accelerometerSensor);
1321                ASensorEventQueue_setEventRate(sensorEventQueue, accelerometerSensor, (1000L/60)*1000);
1322             }
1323             */
1324             break;
1325          case lostFocus:
1326             /*
1327             if(accelerometerSensor)
1328                ASensorEventQueue_disableSensor(sensorEventQueue, accelerometerSensor);
1329             */
1330             guiApp.SetAppFocus(false);
1331             guiApp.desktop.Update(null);
1332             break;
1333          case configChanged:
1334             if(window)
1335                guiApp.desktop.UpdateDisplay();
1336             break;
1337       }
1338    }
1339
1340    void main()
1341    {
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);
1347       */
1348
1349       if(savedState)
1350          state = *(SavedState*)savedState;
1351
1352       {
1353          Module app;
1354
1355          // Evolve the Application class into a GuiApplication
1356          eInstance_Evolve((Instance *)&__androidCurrentModule, class(GuiApplication));
1357
1358          // Wait for the initWindow command:
1359          guiApp.interfaceDriver = class(AndroidInterface);
1360          while(!gotInit)
1361          {
1362             // Can't call the GuiApplication here, because GuiApplication::Initialize() has not been called yet
1363             guiApp.interfaceDriver.Wait();
1364             guiApp.interfaceDriver.ProcessInput(true);
1365          }
1366
1367          // Invoke __ecereDll_Load() in lib[our package name].so
1368          app = eModule_Load(__androidCurrentModule, moduleName, publicAccess);
1369          if(app)
1370          {
1371             Class c;
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);
1375
1376             guiApp.lockMutex.Release();   // TOCHECK: Seems the evolve is losing our mutex lock here ?
1377
1378             // Evolve the Application into it
1379             eInstance_Evolve((Instance *)&__androidCurrentModule, c);
1380             guiApp = (GuiApplication)__androidCurrentModule;
1381
1382             {
1383                const String skin = guiApp.skin;
1384                *&guiApp.currentSkin = null;
1385                guiApp.SelectSkin(skin);
1386             }
1387
1388             guiApp.lockMutex.Wait();
1389
1390             // Call Main()
1391             ((void (*)(void *))(void *)__androidCurrentModule._vTbl[12])(__androidCurrentModule);
1392          }
1393
1394          if(!destroyRequested)
1395             ANativeActivity_finish(activity);
1396          while(!destroyRequested)
1397          {
1398             guiApp.interfaceDriver.Wait();
1399             guiApp.interfaceDriver.ProcessInput(true);
1400          }
1401       }
1402    }
1403 }