ide/designer/CodeEditor: Clean up de-duplications
[sdk] / ecere / src / gui / drivers / AndroidInterface.ec
1 namespace gui::drivers;
2
3 import "Window"
4 import "Interface"
5 import "Condition"
6
7 #define uint _uint
8 #define set _set
9 #include <errno.h>
10 #include <locale.h>
11 #include <pthread.h>
12 #include <unistd.h>
13 #include <sys/prctl.h>
14
15 #include <android/configuration.h>
16 #include <android/looper.h>
17 #include <android/native_activity.h>
18 #include <android/sensor.h>
19 #include <android/log.h>
20 #include <android/window.h>
21
22 #include <jni.h>
23 #undef set
24 #undef uint
25
26 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "ecere-app", __VA_ARGS__))
27 #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "ecere-app", __VA_ARGS__))
28 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "ecere-app", __VA_ARGS__))
29 #ifndef _DEBUG
30 #define LOGV(...)  ((void)0)
31 #else
32 #define LOGV(...)  ((void)__android_log_print(ANDROID_LOG_VERBOSE, "ecere-app", __VA_ARGS__))
33 #endif
34
35 // *** NATIVE APP GLUE ********
36 enum LooperID { main = 1, input = 2, user = 3 };
37 enum AppCommand : byte
38 {
39    error = 0, inputChanged, initWindow, termWindow, windowResized, windowRedrawNeeded,
40    contentRectChanged, gainedFocus, lostFocus,
41    configChanged, lowMemory, start, resume, saveState, pause, stop, destroy
42 };
43
44 class AndroidPollSource
45 {
46 public:
47    void * userData;
48    LooperID id;
49    virtual void any_object::process();
50 };
51
52 class AndroidAppGlue : Thread
53 {
54    void* userData;
55    virtual void onAppCmd(AppCommand cmd);
56    virtual int onInputEvent(AInputEvent* event);
57    virtual void main();
58
59    ANativeActivity* activity;
60    AConfiguration* config;
61    void* savedState;
62    uint savedStateSize;
63
64    ALooper* looper;
65    AInputQueue* inputQueue;
66    ANativeWindow* window;
67    ARect contentRect;
68    AppCommand activityState;
69    bool destroyRequested;
70    char * moduleName;
71
72 private:
73    Mutex mutex { };
74    Condition cond { };
75
76    int msgread, msgwrite;
77
78    unsigned int Main()
79    {
80       config = AConfiguration_new();
81       AConfiguration_fromAssetManager(config, activity->assetManager);
82
83       print_cur_config();
84
85       looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
86       ALooper_addFd(looper, msgread, LooperID::main, ALOOPER_EVENT_INPUT, null, cmdPollSource);
87
88       mutex.Wait();
89       running = true;
90       cond.Signal();
91       mutex.Release();
92
93       main();
94
95       destroy();
96       return 0;
97    }
98
99    void destroy()
100    {
101       free_saved_state();
102       mutex.Wait();
103       if(inputQueue)
104          AInputQueue_detachLooper(inputQueue);
105       AConfiguration_delete(config);
106       destroyed = true;
107       cond.Signal();
108       mutex.Release();
109    }
110
111    AndroidPollSource cmdPollSource
112    {
113       this, main;
114
115       void process()
116       {
117          AppCommand cmd = read_cmd();
118          pre_exec_cmd(cmd);
119          onAppCmd(cmd);
120          post_exec_cmd(cmd);
121       }
122    };
123    AndroidPollSource inputPollSource
124    {
125       this, input;
126
127       void process()
128       {
129          AInputEvent* event = null;
130          if(AInputQueue_getEvent(inputQueue, &event) >= 0)
131          {
132             //int handled = 0;
133             LOGV("New input event: type=%d\n", AInputEvent_getType(event));
134             if(AInputQueue_preDispatchEvent(inputQueue, event))
135                return;
136             /*handled = */onInputEvent(event);
137             //AInputQueue_finishEvent(inputQueue, event, handled);
138          }
139          else
140             LOGE("Failure reading next input event: %s\n", strerror(errno));
141       }
142    };
143
144    bool running;
145    bool stateSaved;
146    bool destroyed;
147    AInputQueue* pendingInputQueue;
148    ANativeWindow* pendingWindow;
149    ARect pendingContentRect;
150
151    void free_saved_state()
152    {
153       mutex.Wait();
154       if(savedState)
155          free(savedState);
156       savedState = 0;
157       savedStateSize = 0;
158       mutex.Release();
159    }
160
161    AppCommand read_cmd()
162    {
163       AppCommand cmd;
164       if(read(msgread, &cmd, sizeof(cmd)) == sizeof(cmd))
165       {
166          if(cmd == saveState)
167             free_saved_state();
168          return cmd;
169       }
170       else
171          LOGE("No data on command pipe!");
172       return error;
173    }
174
175    void print_cur_config()
176    {
177       char lang[2], country[2];
178       AConfiguration_getLanguage(config, lang);
179       AConfiguration_getCountry(config, country);
180
181       LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
182               "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
183               "modetype=%d modenight=%d",
184               AConfiguration_getMcc(config),
185               AConfiguration_getMnc(config),
186               lang[0], lang[1], country[0], country[1],
187               AConfiguration_getOrientation(config),
188               AConfiguration_getTouchscreen(config),
189               AConfiguration_getDensity(config),
190               AConfiguration_getKeyboard(config),
191               AConfiguration_getNavigation(config),
192               AConfiguration_getKeysHidden(config),
193               AConfiguration_getNavHidden(config),
194               AConfiguration_getSdkVersion(config),
195               AConfiguration_getScreenSize(config),
196               AConfiguration_getScreenLong(config),
197               AConfiguration_getUiModeType(config),
198               AConfiguration_getUiModeNight(config));
199    }
200
201    void pre_exec_cmd(AppCommand cmd)
202    {
203       //PrintLn("pre_exec_cmd: ", cmd);
204       switch(cmd)
205       {
206          case inputChanged:
207             mutex.Wait();
208             if(inputQueue)
209                AInputQueue_detachLooper(inputQueue);
210             inputQueue = pendingInputQueue;
211             if(inputQueue)
212                AInputQueue_attachLooper(inputQueue, looper, LooperID::input, null, inputPollSource);
213             cond.Signal();
214             mutex.Release();
215             break;
216          case initWindow:
217             mutex.Wait();
218             window = pendingWindow;
219             cond.Signal();
220             mutex.Release();
221             break;
222          case termWindow:
223             cond.Signal();
224             break;
225          case resume:
226          case start:
227          case pause:
228          case stop:
229             mutex.Wait();
230             activityState = cmd;
231             cond.Signal();
232             mutex.Release();
233             break;
234          case configChanged:
235             AConfiguration_fromAssetManager(config, activity->assetManager);
236             print_cur_config();
237             break;
238          case destroy:
239             destroyRequested = true;
240             break;
241       }
242    }
243
244    void post_exec_cmd(AppCommand cmd)
245    {
246       //PrintLn("post_exec_cmd: ", cmd);
247       switch(cmd)
248       {
249          case termWindow:
250             mutex.Wait();
251             window = null;
252             cond.Signal();
253             mutex.Release();
254             break;
255          case saveState:
256             mutex.Wait();
257             stateSaved = true;
258             cond.Signal();
259             mutex.Release();
260             break;
261          case resume:
262             free_saved_state();
263             break;
264       }
265    }
266
267    void write_cmd(AppCommand cmd)
268    {
269       if(write(msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd))
270          LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
271    }
272
273    void set_input(AInputQueue* inputQueue)
274    {
275       mutex.Wait();
276       pendingInputQueue = inputQueue;
277       write_cmd(inputChanged);
278       while(inputQueue != pendingInputQueue)
279          cond.Wait(mutex);
280       mutex.Release();
281    }
282
283    void set_window(ANativeWindow* window)
284    {
285       mutex.Wait();
286       if(pendingWindow)
287          write_cmd(termWindow);
288       pendingWindow = window;
289       if(window)
290          write_cmd(initWindow);
291       while(window != pendingWindow)
292          cond.Wait(mutex);
293       mutex.Release();
294    }
295
296    void set_activity_state(AppCommand cmd)
297    {
298       mutex.Wait();
299       write_cmd(cmd);
300       while(activityState != cmd)
301          cond.Wait(mutex);
302       mutex.Release();
303    }
304
305    void cleanup()
306    {
307       mutex.Wait();
308       write_cmd(destroy);
309       while(!destroyed)
310          cond.Wait(mutex);
311       mutex.Release();
312       close(msgread);
313       close(msgwrite);
314    }
315
316    void setSavedState(void * state, uint size)
317    {
318       if(savedState)
319          free(savedState);
320       savedState = null;
321       if(state)
322       {
323          savedState = malloc(size);
324          savedStateSize = size;
325          memcpy(savedState, state, size);
326       }
327       else
328          savedStateSize = 0;
329    }
330
331    public void Create()
332    {
333       int msgpipe[2];
334       if(pipe(msgpipe))
335          LOGE("could not create pipe: %s", strerror(errno));
336       msgread = msgpipe[0];
337       msgwrite = msgpipe[1];
338
339       Thread::Create();
340
341       // Wait for thread to start.
342       mutex.Wait();
343       while(!running) cond.Wait(mutex);
344       mutex.Release();
345    }
346 }
347
348 // Callbacks
349 static void onDestroy(ANativeActivity* activity)
350 {
351    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
352    LOGI("Destroy: %p\n", activity);
353    app.cleanup();
354    app.Wait();
355    delete androidActivity;
356    delete __androidCurrentModule;
357    LOGI("THE END.");
358 }
359
360 static void onStart(ANativeActivity* activity)
361 {
362    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
363    LOGI("Start: %p\n", activity);
364    app.set_activity_state(start);
365 }
366
367 static void onResume(ANativeActivity* activity)
368 {
369    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
370    LOGI("Resume: %p\n", activity);
371    app.set_activity_state(resume);
372 }
373
374 static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen)
375 {
376    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
377    void* savedState = null;
378    LOGI("SaveInstanceState: %p\n", activity);
379    app.mutex.Wait();
380    app.stateSaved = false;
381    app.write_cmd(saveState);
382    while(!app.stateSaved)
383       app.cond.Wait(app.mutex);
384    if(app.savedState)
385    {
386       savedState = app.savedState;
387       *outLen = app.savedStateSize;
388       app.savedState = null;
389       app.savedStateSize = 0;
390    }
391    app.mutex.Release();
392    return savedState;
393 }
394
395 static void onPause(ANativeActivity* activity)
396 {
397    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
398    LOGI("Pause: %p\n", activity);
399    app.set_activity_state(pause);
400 }
401
402 static void onStop(ANativeActivity* activity)
403 {
404    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
405    LOGI("Stop: %p\n", activity);
406    app.set_activity_state(stop);
407 }
408
409 static void onConfigurationChanged(ANativeActivity* activity)
410 {
411    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
412    LOGI("ConfigurationChanged: %p\n", activity);
413    app.write_cmd(configChanged);
414 }
415
416 static void onLowMemory(ANativeActivity* activity)
417 {
418    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
419    LOGI("LowMemory: %p\n", activity);
420    app.write_cmd(lowMemory);
421 }
422
423 static void onWindowFocusChanged(ANativeActivity* activity, int focused)
424 {
425    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
426    LOGI("WindowFocusChanged: %p -- %d\n", activity, focused);
427    app.write_cmd(focused ? gainedFocus : lostFocus);
428 }
429
430 static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window)
431 {
432    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
433    LOGI("NativeWindowCreated: %p -- %p\n", activity, window);
434    app.set_window(window);
435 }
436
437 static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window)
438 {
439    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
440    LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window);
441    app.set_window(null);
442 }
443
444 static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue)
445 {
446    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
447    LOGI("InputQueueCreated: %p -- %p\n", activity, queue);
448    app.set_input(queue);
449 }
450
451 static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue)
452 {
453    AndroidAppGlue app = (AndroidAppGlue)activity->instance;
454    LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue);
455    app.inputQueue = null;
456    app.set_input(null);
457 }
458
459 default dllexport void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize)
460 {
461    AndroidAppGlue app;
462    char * moduleName;
463
464    // Determine our package name
465    JNIEnv* env=activity->env;
466    jclass clazz;
467    const char* str;
468    jboolean isCopy;
469    jmethodID methodID;
470    jobject result;
471
472    // *** Reinitialize static global variables ***
473    gotInit = false;
474    guiApplicationInitialized = false;
475    guiApp = null;
476    desktopW = 0; desktopH = 0;
477    clipBoardData = null;
478    __thisModule = null;
479    __androidCurrentModule = null;
480
481    prctl(PR_SET_DUMPABLE, 1);
482
483    LOGI("Creating: %p\n", activity);
484
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;
494
495    // Create a base Application class
496    __androidCurrentModule = __ecere_COM_Initialize(true, 1, androidArgv);
497    // Load up Ecere
498    eModule_Load(__androidCurrentModule, "ecere", publicAccess);
499
500
501    /*
502    if(activity->internalDataPath) PrintLn("internalDataPath is ", activity->internalDataPath);
503    if(activity->externalDataPath) PrintLn("externalDataPath is ", activity->externalDataPath);
504    {
505       char tmp[256];
506       PrintLn("cwd is ", GetWorkingDir(tmp, sizeof(tmp)));
507    }
508    */
509
510    ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_FULLSCREEN|AWINDOW_FLAG_KEEP_SCREEN_ON, 0 );
511    app = AndroidActivity { activity = activity, moduleName = moduleName };
512    incref app;
513    app.setSavedState(savedState, savedStateSize);
514    activity->callbacks->onDestroy = onDestroy;
515    activity->callbacks->onStart = onStart;
516    activity->callbacks->onResume = onResume;
517    activity->callbacks->onSaveInstanceState = onSaveInstanceState;
518    activity->callbacks->onPause = onPause;
519    activity->callbacks->onStop = onStop;
520    activity->callbacks->onConfigurationChanged = onConfigurationChanged;
521    activity->callbacks->onLowMemory = onLowMemory;
522    activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
523    activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
524    activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
525    activity->callbacks->onInputQueueCreated = onInputQueueCreated;
526    activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
527    activity->instance = app;
528    app.Create();
529 }
530
531 // *** END OF NATIVE APP GLUE ******
532
533 default:
534 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
535 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp;
536 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown;
537 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
538 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove;
539 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick;
540 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown;
541 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp;
542 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick;
543 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown;
544 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp;
545 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick;
546 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown;
547 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp;
548 private:
549
550 static Module __androidCurrentModule;
551 static char * androidArgv[1];
552
553 static int desktopW, desktopH;
554 static char * clipBoardData;
555 static int mouseX, mouseY;
556
557 class AndroidInterface : Interface
558 {
559    class_property(name) = "Android";
560
561    // --- User Interface System ---
562    bool Initialize()
563    {
564       setlocale(LC_ALL, "en_US.UTF-8");
565       return true;
566    }
567
568    void Terminate()
569    {
570
571    }
572
573    #define DBLCLICK_DELAY  300   // 0.3 second
574    #define DBLCLICK_DELTA  1
575
576    bool ProcessInput(bool processAll)
577    {
578       bool eventAvailable = false;
579
580       if(androidActivity.ident < 0)
581          androidActivity.ident = (LooperID)ALooper_pollAll(0, null, &androidActivity.events, (void**)&androidActivity.source);
582
583       if(gotInit && androidActivity.window)
584       {
585          int w = ANativeWindow_getWidth(androidActivity.window);
586          int h = ANativeWindow_getHeight(androidActivity.window);
587          if(desktopW != w || desktopH != h)
588          {
589             guiApp.SetDesktopPosition(0, 0, w, h, true);
590             desktopW = w;
591             desktopH = h;
592             guiApp.desktop.Update(null);
593          }
594       }
595
596       while(androidActivity.ident >= 0)
597       {
598          AndroidPollSource source = androidActivity.source;
599
600          androidActivity.source = null;
601          if(source)
602             source.process(source.userData);
603
604          // If a sensor has data, process it now.
605          /*
606          if(androidActivity.ident == user)
607          {
608             if(androidActivity.accelerometerSensor)
609             {
610                ASensorEvent event;
611                while (ASensorEventQueue_getEvents(androidActivity.sensorEventQueue, &event, 1) > 0)
612                   LOGI("accelerometer: x=%f y=%f z=%f", event.acceleration.x, event.acceleration.y, event.acceleration.z);
613             }
614          }
615          */
616
617          eventAvailable = true;
618          if(androidActivity.destroyRequested)
619          {
620             guiApp.desktop.Destroy(0);
621             eventAvailable = true;
622             androidActivity.ident = (LooperID)-1;
623          }
624          else if(processAll)
625             androidActivity.ident = (LooperID)ALooper_pollAll(0, null, &androidActivity.events, (void**)&androidActivity.source);
626          else
627             androidActivity.ident = (LooperID)-1;
628       }
629       return eventAvailable;
630    }
631
632    void Wait()
633    {
634       androidActivity.ident = (LooperID)ALooper_pollAll((int)(1000/18.2f), null, &androidActivity.events, (void**)&androidActivity.source);
635       // guiApp.WaitEvent();
636    }
637
638    void Lock(Window window)
639    {
640
641    }
642
643    void Unlock(Window window)
644    {
645
646    }
647
648    const char ** GraphicsDrivers(int * numDrivers)
649    {
650       static const char *graphicsDrivers[] = { "OpenGL" };
651       *numDrivers = sizeof(graphicsDrivers) / sizeof(char *);
652       return (const char **)graphicsDrivers;
653    }
654
655    void GetCurrentMode(bool * fullScreen, int * resolution, int * colorDepth, int * refreshRate)
656    {
657       *fullScreen = true;
658    }
659
660    void EnsureFullScreen(bool *fullScreen)
661    {
662       *fullScreen = true;
663    }
664
665    bool ScreenMode(bool fullScreen, int resolution, int colorDepth, int refreshRate, bool * textMode)
666    {
667       bool result = true;
668
669       return result;
670    }
671
672    // --- Window Creation ---
673    void * CreateRootWindow(Window window)
674    {
675       return androidActivity.window;
676    }
677
678    void DestroyRootWindow(Window window)
679    {
680
681    }
682
683    // -- Window manipulation ---
684
685    void SetRootWindowCaption(Window window, const char * name)
686    {
687
688    }
689
690    void PositionRootWindow(Window window, int x, int y, int w, int h, bool move, bool resize)
691    {
692
693    }
694
695    void OrderRootWindow(Window window, bool topMost)
696    {
697
698    }
699
700    void SetRootWindowColor(Window window)
701    {
702
703    }
704
705    void OffsetWindow(Window window, int * x, int * y)
706    {
707
708    }
709
710    void UpdateRootWindow(Window window)
711    {
712       if(!window.parent || !window.parent.display)
713       {
714          if(window.visible)
715          {
716             Box box = window.box;
717             box.left -= window.clientStart.x;
718             box.top -= window.clientStart.y;
719             box.right -= window.clientStart.x;
720             box.bottom -= window.clientStart.y;
721             // Logf("Update root window %s\n", window.name);
722             window.Update(null);
723             box.left   += window.clientStart.x;
724             box.top    += window.clientStart.y;
725             box.right  += window.clientStart.x;
726             box.bottom += window.clientStart.y;
727             window.UpdateDirty(box);
728          }
729       }
730    }
731
732
733    void SetRootWindowState(Window window, WindowState state, bool visible)
734    {
735    }
736
737    void FlashRootWindow(Window window)
738    {
739
740    }
741
742    void ActivateRootWindow(Window window)
743    {
744
745    }
746
747    // --- Mouse-based window movement ---
748
749    void StartMoving(Window window, int x, int y, bool fromKeyBoard)
750    {
751
752    }
753
754    void StopMoving(Window window)
755    {
756
757    }
758
759    // -- Mouse manipulation ---
760
761    void GetMousePosition(int *x, int *y)
762    {
763       *x = mouseX;
764       *y = mouseY;
765    }
766
767    void SetMousePosition(int x, int y)
768    {
769       mouseX = x;
770       mouseY = y;
771    }
772
773    void SetMouseRange(Window window, Box box)
774    {
775    }
776
777    void SetMouseCapture(Window window)
778    {
779    }
780
781    // -- Mouse cursor ---
782
783    void SetMouseCursor(Window window, int cursor)
784    {
785       if(cursor == -1)
786       {
787
788       }
789    }
790
791    // --- Caret ---
792
793    void SetCaret(int x, int y, int size)
794    {
795       Window caretOwner = guiApp.caretOwner;
796       Window window = caretOwner ? caretOwner.rootWindow : null;
797       if(window && window.windowData)
798       {
799       }
800    }
801
802    void ClearClipboard()
803    {
804       if(clipBoardData)
805       {
806          delete clipBoardData;
807       }
808    }
809
810    bool AllocateClipboard(ClipBoard clipBoard, uint size)
811    {
812       bool result = false;
813       if((clipBoard.text = new0 byte[size]))
814          result = true;
815       return result;
816    }
817
818    bool SaveClipboard(ClipBoard clipBoard)
819    {
820       bool result = false;
821       if(clipBoard.text)
822       {
823          if(clipBoardData)
824             delete clipBoardData;
825
826          clipBoardData = clipBoard.text;
827          clipBoard.text = null;
828          result = true;
829       }
830       return result;
831    }
832
833    bool LoadClipboard(ClipBoard clipBoard)
834    {
835       bool result = false;
836
837       // The data is inside this client...
838       if(clipBoardData)
839       {
840          clipBoard.text = new char[strlen(clipBoardData)+1];
841          strcpy(clipBoard.text, clipBoardData);
842          result = true;
843       }
844       // The data is with another client...
845       else
846       {
847       }
848       return result;
849    }
850
851    void UnloadClipboard(ClipBoard clipBoard)
852    {
853       delete clipBoard.text;
854    }
855
856    // --- State based input ---
857
858    bool AcquireInput(Window window, bool state)
859    {
860       return false;
861    }
862
863    bool GetMouseState(MouseButtons * buttons, int * x, int * y)
864    {
865       bool result = false;
866       if(x) *x = 0;
867       if(y) *y = 0;
868       return result;
869    }
870
871    bool GetJoystickState(int device, Joystick joystick)
872    {
873       bool result = false;
874       return result;
875    }
876
877    bool GetKeyState(Key key)
878    {
879       bool keyState = false;
880       return keyState;
881    }
882
883    void SetTimerResolution(uint hertz)
884    {
885       // timerDelay = hertz ? (1000000 / hertz) : MAXINT;
886    }
887
888    bool SetIcon(Window window, BitmapResource resource)
889    {
890       if(resource)
891       {
892          /*Bitmap bitmap { };
893          if(bitmap.Load(resource.fileName, null, null))
894          {
895          }
896          delete bitmap;*/
897       }
898       return true;
899    }
900 }
901
902 struct SavedState
903 {
904     float angle;
905     int x;
906     int y;
907 };
908
909 static AndroidActivity androidActivity;
910
911 default const char * AndroidInterface_GetLibLocation(Application a)
912 {
913    static char loc[MAX_LOCATION];
914    // sprintf(loc, "/data/data/com.ecere.%s/lib/lib", a.argv[0]);
915    sprintf(loc, "/data/app/com.ecere.%s-1/lib/arm64/lib", a.argv[0]);
916    return loc;
917 }
918
919 static bool gotInit;
920
921 default float AMotionEvent_getAxisValue(const AInputEvent* motion_event,
922         int32_t axis, size_t pointer_index);
923
924
925 static define AMETA_META_ON       = 0x00010000;
926 static define AMETA_META_LEFT_ON  = 0x00020000;
927 static define AMETA_META_RIGHT_ON = 0x00040000;
928
929 static Key keyCodeTable[] =
930 {
931     0, //AKEYCODE_UNKNOWN         = 0,
932     0, //AKEYCODE_SOFT_LEFT       = 1,
933     0, //AKEYCODE_SOFT_RIGHT      = 2,
934     0, //AKEYCODE_HOME            = 3,
935     0, //AKEYCODE_BACK            = 4,
936     0, //AKEYCODE_CALL            = 5,
937     0, //AKEYCODE_ENDCALL         = 6,
938     k0, //AKEYCODE_0               = 7,
939     k1, //AKEYCODE_1               = 8,
940     k2, //AKEYCODE_2               = 9,
941     k3, //AKEYCODE_3               = 10,
942     k4, //AKEYCODE_4               = 11,
943     k5, //AKEYCODE_5               = 12,
944     k6, //AKEYCODE_6               = 13,
945     k7, //AKEYCODE_7               = 14,
946     k8, //AKEYCODE_8               = 15,
947     k9, //AKEYCODE_9               = 16,
948     keyPadStar, //AKEYCODE_STAR            = 17,
949     Key { k3, shift = true }, //AKEYCODE_POUND           = 18,
950     up, //AKEYCODE_DPAD_UP         = 19,
951     down, //AKEYCODE_DPAD_DOWN       = 20,
952     left, //AKEYCODE_DPAD_LEFT       = 21,
953     right, //AKEYCODE_DPAD_RIGHT      = 22,
954     keyPad5, //AKEYCODE_DPAD_CENTER     = 23,
955     0, //AKEYCODE_VOLUME_UP       = 24,
956     0, //AKEYCODE_VOLUME_DOWN     = 25,
957     0, //AKEYCODE_POWER           = 26,
958     0, //AKEYCODE_CAMERA          = 27,
959     0, //AKEYCODE_CLEAR           = 28,
960     a, //AKEYCODE_A               = 29,
961     b, //AKEYCODE_B               = 30,
962     c, //AKEYCODE_C               = 31,
963     d, //AKEYCODE_D               = 32,
964     e, //AKEYCODE_E               = 33,
965     f, //AKEYCODE_F               = 34,
966     g, //AKEYCODE_G               = 35,
967     h, //AKEYCODE_H               = 36,
968     i, //AKEYCODE_I               = 37,
969     j, //AKEYCODE_J               = 38,
970     k, //AKEYCODE_K               = 39,
971     l, //AKEYCODE_L               = 40,
972     m, //AKEYCODE_M               = 41,
973     n, //AKEYCODE_N               = 42,
974     o, //AKEYCODE_O               = 43,
975     p, //AKEYCODE_P               = 44,
976     q, //AKEYCODE_Q               = 45,
977     r, //AKEYCODE_R               = 46,
978     s, //AKEYCODE_S               = 47,
979     t, //AKEYCODE_T               = 48,
980     u, //AKEYCODE_U               = 49,
981     v, //AKEYCODE_V               = 50,
982     w, //AKEYCODE_W               = 51,
983     x, //AKEYCODE_X               = 52,
984     y, //AKEYCODE_Y               = 53,
985     z, //AKEYCODE_Z               = 54,
986     comma, //AKEYCODE_COMMA           = 55,
987     period, //AKEYCODE_PERIOD          = 56,
988     leftAlt, //AKEYCODE_ALT_LEFT        = 57,
989     rightAlt, //AKEYCODE_ALT_RIGHT       = 58,
990     leftShift, //AKEYCODE_SHIFT_LEFT      = 59,
991     rightShift, //AKEYCODE_SHIFT_RIGHT     = 60,
992     tab, //AKEYCODE_TAB             = 61,
993     space, //AKEYCODE_SPACE           = 62,
994     0, //AKEYCODE_SYM             = 63,
995     0, //AKEYCODE_EXPLORER        = 64,
996     0, //AKEYCODE_ENVELOPE        = 65,
997     enter, //AKEYCODE_ENTER           = 66,
998     backSpace, //AKEYCODE_DEL             = 67,
999     backQuote, //AKEYCODE_GRAVE           = 68,
1000     minus, //AKEYCODE_MINUS           = 69,
1001     plus, //AKEYCODE_EQUALS          = 70,
1002     leftBracket, //AKEYCODE_LEFT_BRACKET    = 71,
1003     rightBracket, //AKEYCODE_RIGHT_BRACKET   = 72,
1004     backSlash, //AKEYCODE_BACKSLASH       = 73,
1005     semicolon, //AKEYCODE_SEMICOLON       = 74,
1006     quote, //AKEYCODE_APOSTROPHE      = 75,
1007     slash, //AKEYCODE_SLASH           = 76,
1008     Key { k2, shift = true }, //AKEYCODE_AT              = 77,
1009     0, //AKEYCODE_NUM             = 78,      // Interpreted as an Alt
1010     0, //AKEYCODE_HEADSETHOOK     = 79,
1011     0, //AKEYCODE_FOCUS           = 80,   // *Camera* focus
1012     keyPadPlus, //AKEYCODE_PLUS            = 81,
1013     0, //AKEYCODE_MENU            = 82,
1014     0, //AKEYCODE_NOTIFICATION    = 83,
1015     0, //AKEYCODE_SEARCH          = 84,
1016     0, //AKEYCODE_MEDIA_PLAY_PAUSE= 85,
1017     0, //AKEYCODE_MEDIA_STOP      = 86,
1018     0, //AKEYCODE_MEDIA_NEXT      = 87,
1019     0, //AKEYCODE_MEDIA_PREVIOUS  = 88,
1020     0, //AKEYCODE_MEDIA_REWIND    = 89,
1021     0, //AKEYCODE_MEDIA_FAST_FORWARD = 90,
1022     0, //AKEYCODE_MUTE            = 91,
1023     0, //AKEYCODE_PAGE_UP         = 92,
1024     0, //AKEYCODE_PAGE_DOWN       = 93,
1025     0, //AKEYCODE_PICTSYMBOLS     = 94,
1026     0, //AKEYCODE_SWITCH_CHARSET  = 95,
1027     0, //AKEYCODE_BUTTON_A        = 96,
1028     0, //AKEYCODE_BUTTON_B        = 97,
1029     0, //AKEYCODE_BUTTON_C        = 98,
1030     0, //AKEYCODE_BUTTON_X        = 99,
1031     0, //AKEYCODE_BUTTON_Y        = 100,
1032     0, //AKEYCODE_BUTTON_Z        = 101,
1033     0, //AKEYCODE_BUTTON_L1       = 102,
1034     0, //AKEYCODE_BUTTON_R1       = 103,
1035     0, //AKEYCODE_BUTTON_L2       = 104,
1036     0, //AKEYCODE_BUTTON_R2       = 105,
1037     0, //AKEYCODE_BUTTON_THUMBL   = 106,
1038     0, //AKEYCODE_BUTTON_THUMBR   = 107,
1039     0, //AKEYCODE_BUTTON_START    = 108,
1040     0, //AKEYCODE_BUTTON_SELECT   = 109,
1041     0, //AKEYCODE_BUTTON_MODE     = 110,
1042     escape, //AKEYCODE_BUTTON_ESCAPE = 111,
1043     del, //AKEYCODE_BUTTON_ESCAPE    = 112,
1044     leftControl, // = 113
1045     rightControl, // = 114
1046     capsLock, // = 115
1047     scrollLock, // = 116
1048     0, // = 117      KEYCODE_META_LEFT
1049     0, // = 118      KEYCODE_META_RIGHT
1050     0, // = 119      KEYCODE_FUNCTION
1051     printScreen, // = 120      KEYCODE_SYSRQ
1052     pauseBreak, // = 121
1053     home, // = 122
1054     end, // = 123
1055     insert // = 124
1056 };
1057
1058 // Why don't we have this in the NDK :(
1059 // default int32_t AKeyEvent_getUnichar(const AInputEvent* key_event);
1060
1061 class AndroidActivity : AndroidAppGlue
1062 {
1063    AndroidPollSource source;
1064    int events;
1065    LooperID ident;
1066    /*
1067    ASensorManager* sensorManager;
1068    const ASensor* accelerometerSensor;
1069    ASensorEventQueue* sensorEventQueue;
1070    */
1071    SavedState state;
1072
1073    int onInputEvent(AInputEvent* event)
1074    {
1075       Window window = guiApp.desktop;
1076       uint type = AInputEvent_getType(event);
1077       if(type == AINPUT_EVENT_TYPE_MOTION)
1078       {
1079          uint actionAndIndex = AMotionEvent_getAction(event);
1080          //uint source = AInputEvent_getSource(event);
1081          uint action = actionAndIndex & AMOTION_EVENT_ACTION_MASK;
1082          //uint index  = (actionAndIndex & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
1083          //uint flags = AMotionEvent_getFlags(event);
1084          uint meta = AMotionEvent_getMetaState(event);
1085          //uint edge = AMotionEvent_getEdgeFlags(event);
1086          //int64 downTime = AMotionEvent_getDownTime(event);     // nanotime
1087          //int64 eventTime = AMotionEvent_getDownTime(event);
1088          //float axis;
1089          Modifiers keyFlags = 0;
1090          int x = (int)AMotionEvent_getX(event, 0);
1091          int y = (int)AMotionEvent_getY(event, 0);
1092          bool shift = (meta & AMETA_SHIFT_ON) ? true : false;
1093          bool alt = (meta & AMETA_ALT_ON) ? true : false;
1094          //bool sym = (meta & AMETA_SYM_ON) ? true : false;
1095
1096          keyFlags.shift = shift;
1097          keyFlags.alt = alt;
1098
1099          //PrintLn("Got a motion input event: ", action);
1100          /*
1101          if(action == 8) //AMOTION_EVENT_ACTION_SCROLL)
1102             axis = AMotionEvent_getAxisValue(event, 9, index); //AMOTION_EVENT_AXIS_VSCROLL);
1103          */
1104
1105          AInputQueue_finishEvent(inputQueue, event, 1);
1106          switch(action)
1107          {
1108             /*
1109             case 8: //AMOTION_EVENT_ACTION_SCROLL:
1110                window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, (axis < 0) ? wheelUp : wheelDown, 0);
1111                break;
1112                */
1113             case AMOTION_EVENT_ACTION_DOWN:
1114                mouseX = x, mouseY = y;
1115                window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, x, y, &keyFlags, false, true);
1116                break;
1117             case AMOTION_EVENT_ACTION_UP:
1118                mouseX = x, mouseY = y;
1119                window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp, x, y, &keyFlags, false, true);
1120                break;
1121             case AMOTION_EVENT_ACTION_MOVE:
1122                mouseX = x, mouseY = y;
1123                window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove, x, y, &keyFlags, false, true);
1124                break;
1125             case AMOTION_EVENT_ACTION_CANCEL: break;
1126             case AMOTION_EVENT_ACTION_OUTSIDE: break;
1127             case AMOTION_EVENT_ACTION_POINTER_DOWN: break;
1128             case AMOTION_EVENT_ACTION_POINTER_UP: break;
1129          }
1130          return 1;
1131       }
1132       else if(type == AINPUT_EVENT_TYPE_KEY)
1133       {
1134          uint action = AKeyEvent_getAction(event);
1135          //uint flags = AKeyEvent_getFlags(event);
1136          uint keyCode = AKeyEvent_getKeyCode(event);
1137          uint meta = AKeyEvent_getMetaState(event);
1138          Key key = keyCodeTable[keyCode];
1139          bool shift = (meta & AMETA_SHIFT_ON) ? true : false;
1140          bool alt = (meta & AMETA_ALT_ON || meta & AMETA_ALT_LEFT_ON || meta & AMETA_ALT_RIGHT_ON) ? true : false;
1141          //bool metaMeta = (meta & AMETA_META_ON || meta & AMETA_META_LEFT_ON || meta & AMETA_META_RIGHT_ON) ? true : false;
1142          //bool sym = (meta & AMETA_SYM_ON) ? true : false;
1143          //unichar ch = AKeyEvent_getUnichar(event);
1144          unichar ch = 0;
1145
1146          key.shift = shift;
1147          key.alt = alt;
1148
1149          AInputQueue_finishEvent(inputQueue, event, 1);
1150
1151          // PrintLn("Got a key: action = ", action, ", flags = ", flags, ", keyCode = ", keyCode, ", meta = ", meta, ": key = ", (int)key);
1152
1153          if(key)
1154          {
1155             if(action == AKEY_EVENT_ACTION_DOWN || action == AKEY_EVENT_ACTION_MULTIPLE)
1156             {
1157                /*if(key == wheelDown || key == wheelUp)
1158                   window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, key, ch);
1159                else*/
1160                {
1161                   char c = Interface::TranslateKey(key.code, shift);
1162                   if(c > 0) ch = c;
1163                   window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, key, ch);
1164                }
1165             }
1166             else if(action == AKEY_EVENT_ACTION_UP)
1167                window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, key, ch);
1168          }
1169          return 1;
1170       }
1171       else
1172          AInputQueue_finishEvent(inputQueue, event, 0);
1173       return 0;
1174    }
1175
1176    void onAppCmd(AppCommand cmd)
1177    {
1178       switch(cmd)
1179       {
1180          case saveState:
1181             setSavedState(&state, sizeof(state));
1182             break;
1183          case initWindow:
1184             if(window)
1185             {
1186                int w, h;
1187                gotInit = true;
1188                ANativeWindow_setBuffersGeometry(window, 0, 0, 0); //format);
1189                w = ANativeWindow_getWidth(window);
1190                h = ANativeWindow_getHeight(window);
1191                guiApp.Initialize(false);
1192                guiApp.desktop.windowHandle = window;
1193                guiApp.interfaceDriver = null;
1194                guiApp.SwitchMode(true, null, 0, 0, 0, null, false);
1195
1196                if(desktopW != w || desktopH != h)
1197                {
1198                   guiApp.SetDesktopPosition(0, 0, w, h, true);
1199                   desktopW = w;
1200                   desktopH = h;
1201                }
1202                guiApp.desktop.Update(null);
1203             }
1204             break;
1205          case termWindow:
1206             guiApp.desktop.UnloadGraphics(false);
1207             break;
1208          case gainedFocus:
1209             guiApp.desktop.Update(null);
1210             guiApp.SetAppFocus(true);
1211             /*
1212             if(accelerometerSensor)
1213             {
1214                ASensorEventQueue_enableSensor(sensorEventQueue, accelerometerSensor);
1215                ASensorEventQueue_setEventRate(sensorEventQueue, accelerometerSensor, (1000L/60)*1000);
1216             }
1217             */
1218             break;
1219          case lostFocus:
1220             /*
1221             if(accelerometerSensor)
1222                ASensorEventQueue_disableSensor(sensorEventQueue, accelerometerSensor);
1223             */
1224             guiApp.SetAppFocus(false);
1225             guiApp.desktop.Update(null);
1226             break;
1227          case configChanged:
1228             if(window)
1229                guiApp.desktop.UpdateDisplay();
1230             break;
1231       }
1232    }
1233
1234    void main()
1235    {
1236       androidActivity = this;
1237       /* Let's have fun with sensors when we have an actual device to play with
1238       sensorManager = ASensorManager_getInstance();
1239       accelerometerSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER);
1240       sensorEventQueue = ASensorManager_createEventQueue(sensorManager, looper, LooperID::user, null, null);
1241       */
1242
1243       if(savedState)
1244          state = *(SavedState*)savedState;
1245
1246       {
1247          Module app;
1248
1249          // Evolve the Application class into a GuiApplication
1250          eInstance_Evolve((Instance *)&__androidCurrentModule, class(GuiApplication));
1251
1252          // Wait for the initWindow command:
1253          guiApp.interfaceDriver = class(AndroidInterface);
1254          while(!gotInit)
1255          {
1256             // Can't call the GuiApplication here, because GuiApplication::Initialize() has not been called yet
1257             guiApp.interfaceDriver.Wait();
1258             guiApp.interfaceDriver.ProcessInput(true);
1259          }
1260
1261          // Invoke __ecereDll_Load() in lib[our package name].so
1262          app = eModule_Load(__androidCurrentModule, moduleName, publicAccess);
1263          if(app)
1264          {
1265             Class c;
1266             // Find out if any GuiApplication class was defined in our module
1267             for(c = app.classes.first; c && !eClass_IsDerived(c, class(GuiApplication)); c = c.next);
1268             if(!c) c = class(GuiApplication);
1269
1270             guiApp.lockMutex.Release();   // TOCHECK: Seems the evolve is losing our mutex lock here ?
1271
1272             // Evolve the Application into it
1273             eInstance_Evolve((Instance *)&__androidCurrentModule, c);
1274             guiApp = (GuiApplication)__androidCurrentModule;
1275
1276             {
1277                const String skin = guiApp.skin;
1278                *&guiApp.currentSkin = null;
1279                guiApp.SelectSkin(skin);
1280             }
1281
1282             guiApp.lockMutex.Wait();
1283
1284             // Call Main()
1285             ((void (*)(void *))(void *)__androidCurrentModule._vTbl[12])(__androidCurrentModule);
1286          }
1287
1288          if(!destroyRequested)
1289             ANativeActivity_finish(activity);
1290          while(!destroyRequested)
1291          {
1292             guiApp.interfaceDriver.Wait();
1293             guiApp.interfaceDriver.ProcessInput(true);
1294          }
1295       }
1296    }
1297 }