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