summary refs log tree commit diff
path: root/src/LBackend_Android.c
diff options
context:
space:
mode:
authorWlodekM <[email protected]>2024-06-16 10:35:45 +0300
committerWlodekM <[email protected]>2024-06-16 10:35:45 +0300
commitabef6da56913f1c55528103e60a50451a39628b1 (patch)
treeb3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/LBackend_Android.c
initial commit
Diffstat (limited to 'src/LBackend_Android.c')
-rw-r--r--src/LBackend_Android.c614
1 files changed, 614 insertions, 0 deletions
diff --git a/src/LBackend_Android.c b/src/LBackend_Android.c
new file mode 100644
index 0000000..3ec21f3
--- /dev/null
+++ b/src/LBackend_Android.c
@@ -0,0 +1,614 @@
+#include "LBackend.h"
+#if defined CC_BUILD_ANDROID11111111111
+#include "Launcher.h"
+#include "Drawer2D.h"
+#include "Window.h"
+#include "LWidgets.h"
+#include "String.h"
+#include "Gui.h"
+#include "Drawer2D.h"
+#include "Launcher.h"
+#include "ExtMath.h"
+#include "Window.h"
+#include "Funcs.h"
+#include "LWeb.h"
+#include "Platform.h"
+#include "LScreens.h"
+#include "Input.h"
+#include "Utils.h"
+#include "Event.h"
+#include <android/native_window.h>
+#include <android/native_window_jni.h>
+#include <android/keycodes.h>
+#include <android/bitmap.h>
+
+struct FontDesc logoFont;
+
+static void HookEvents(void);
+static void LBackend_InitHooks(void);
+void LBackend_Init(void) {
+    HookEvents();
+    LBackend_InitHooks();
+}
+
+void LBackend_Free(void) {
+    Font_Free(&logoFont);
+}
+
+void LBackend_UpdateLogoFont(void) {
+    Font_Free(&logoFont);
+    Launcher_MakeLogoFont(&logoFont);
+}
+void LBackend_DrawLogo(struct Context2D* ctx, const char* title) {
+    Launcher_DrawLogo(&logoFont, title, ctx);
+}
+
+static void LBackend_LayoutDimensions(struct LWidget* w) {
+    const struct LLayout* l = w->layouts + 2;
+    while (l->type)
+    {
+        switch (l->type)
+        {
+            case LLAYOUT_WIDTH:
+                w->width  = WindowInfo.Width  - w->x - Display_ScaleX(l->offset);
+                w->width  = max(1, w->width);
+                break;
+            case LLAYOUT_HEIGHT:
+                w->height = WindowInfo.Height - w->y - Display_ScaleY(l->offset);
+                w->height = max(1, w->height);
+                break;
+        }
+        l++;
+    }
+}
+
+static void LBackend_GetLayoutArgs(void* widget, jvalue* args) {
+    struct LWidget* w = (struct LWidget*)widget;
+    const struct LLayout* l = w->layouts;
+
+    args[0].i = l[0].type & 0xFF;
+    args[1].i = Display_ScaleX(l[0].offset);
+    args[2].i = l[1].type & 0xFF;
+    args[3].i = Display_ScaleY(l[1].offset);
+}
+
+void LBackend_LayoutWidget(struct LWidget* w) {
+    const struct LLayout* l = w->layouts;
+    // TODO remove this? once Table is done
+
+    /* e.g. Table widget needs adjusts width/height based on window */
+    if (l[1].type & LLAYOUT_EXTRA)
+        LBackend_LayoutDimensions(w);
+}
+
+void LBackend_MarkDirty(void* widget) { }
+void LBackend_InitFramebuffer(void) { }
+void LBackend_FreeFramebuffer(void) { }
+
+static void JNICALL java_drawBackground(JNIEnv* env, jobject o, jobject bmp) {
+    Platform_LogConst("---$$$--");
+    AndroidBitmapInfo info;
+    void* addr = NULL;
+
+    AndroidBitmap_getInfo(env, bmp, &info);
+    AndroidBitmap_lockPixels(env, bmp, &addr);
+
+    // TODO refactor this
+    struct Context2D ctx;
+    struct Bitmap pixels;
+    pixels.scan0  = addr;
+    pixels.width  = info.width;
+    pixels.height = info.height;
+    Context2D_Wrap(&ctx, &pixels);
+
+    struct LScreen* s = Launcher_Active;
+    if (s) s->DrawBackground(s, &ctx);
+
+    AndroidBitmap_unlockPixels(env, bmp);
+}
+
+void LBackend_Redraw(void) {
+    JNIEnv* env;
+    JavaGetCurrentEnv(env);
+    JavaCallVoid(env, "redrawBackground", "()V", NULL);
+}
+
+static void LBackend_ButtonUpdateBackground(struct LButton* btn);
+void LBackend_ThemeChanged(void) {
+    struct LScreen* s = Launcher_Active;
+    LBackend_Redraw();
+
+    for (int i = 0; i < s->numWidgets; i++)
+    {
+        struct LWidget* w = s->widgets[i];
+        if (w->type != LWIDGET_BUTTON) continue;
+        LBackend_ButtonUpdateBackground((struct LButton*)w);
+    }
+}
+
+void LBackend_Tick(void) { }
+
+static struct LWidget* FindWidgetForView(int id) {
+    struct LScreen* s = Launcher_Active;
+    for (int i = 0; i < s->numWidgets; i++)
+    {
+        void* meta = s->widgets[i]->meta;
+        if (meta != id) continue;
+
+        return s->widgets[i];
+    }
+    return NULL;
+}
+
+static int ToAndroidColor(BitmapCol color) {
+    int R = BitmapCol_R(color);
+    int G = BitmapCol_G(color);
+    int B = BitmapCol_B(color);
+    int A = BitmapCol_A(color);
+    return (A << 24) | (R << 16) | (G << 8) | B;
+}
+
+
+static jstring JNICALL java_nextTextPart(JNIEnv* env, jobject o, jstring total, jintArray state) {
+    char buffer[NATIVE_STR_LEN];
+    cc_string text = JavaGetString(env, total, buffer);
+
+    jint* state_ = (*env)->GetIntArrayElements(env, state, NULL);
+    text.buffer += state_[0];
+    text.length -= state_[0];
+
+    cc_string left = text, part;
+    char colorCode = 'f';
+
+    Drawer2D_UNSAFE_NextPart(&text, &part, &colorCode);
+    BitmapCol color = Drawer2D_GetColor(colorCode);
+
+    state_[0] += left.length - text.length;
+    state_[1] = ToAndroidColor(color);
+
+    (*env)->ReleaseIntArrayElements(env, state, state_, 0);
+    return JavaMakeString(env, &part);
+}
+
+
+/*########################################################################################################################*
+*-----------------------------------------------------Event handling------------------------------------------------------*
+*#########################################################################################################################*/
+static void RequestRedraw(void* obj) { LBackend_Redraw(); }
+
+static void OnPointerUp(void* obj, int idx) {
+    Launcher_Active->MouseUp(Launcher_Active, idx);
+}
+
+static void OnWindowCreated(void* obj) {
+    // e.g. after pause and resume
+    // TODO should pause/resume not trigger launcher screen recreation?
+    if (Launcher_Active) Launcher_SetScreen(Launcher_Active);
+}
+
+static void HookEvents(void) {
+    Event_Register_(&WindowEvents.RedrawNeeded, NULL, RequestRedraw);
+    Event_Register_(&PointerEvents.Up,          NULL, OnPointerUp);
+    Event_Register_(&WindowEvents.Created,      NULL, OnWindowCreated);
+}
+
+
+/*########################################################################################################################*
+*------------------------------------------------------ButtonWidget-------------------------------------------------------*
+*#########################################################################################################################*/
+void LBackend_ButtonInit(struct LButton* w, int width, int height) {
+    w->_textWidth  = Display_ScaleX(width);
+    w->_textHeight = Display_ScaleY(height);
+}
+
+static void LBackend_ButtonShow(struct LButton* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[6];
+
+    LBackend_GetLayoutArgs(w, args);
+    args[4].i = w->_textWidth;
+    args[5].i = w->_textHeight;
+
+    jmethodID method = JavaGetIMethod(env, "buttonAdd", "(IIIIII)I");
+    w->meta = (void*)JavaICall_Int(env, method, args);
+}
+
+void LBackend_ButtonUpdate(struct LButton* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[2];
+    if (!w->meta) return;
+
+    args[0].i = (int)w->meta;
+    args[1].l = JavaMakeString(env, &w->text);
+
+    // TODO share logic with LabelUpdate/ButtonUpdate
+    jmethodID method = JavaGetIMethod(env, "buttonUpdate", "(ILjava/lang/String;)V");
+    JavaICall_Void(env, method, args);
+    (*env)->DeleteLocalRef(env, args[1].l);
+}
+void LBackend_ButtonDraw(struct LButton* w) { }
+
+static void JNICALL java_makeButtonActive(JNIEnv* env, jobject o, jobject bmp) {
+    Platform_LogConst("---&&&--");
+    AndroidBitmapInfo info;
+    void* addr = NULL;
+
+    // TODO share code with drawBackground
+    AndroidBitmap_getInfo(env, bmp, &info);
+    AndroidBitmap_lockPixels(env, bmp, &addr);
+
+    // TODO refactor this
+    struct Context2D ctx;
+    struct Bitmap pixels;
+    pixels.scan0  = addr;
+    pixels.width  = info.width;
+    pixels.height = info.height;
+    Context2D_Wrap(&ctx, &pixels);
+
+    LButton_DrawBackground(&ctx, 0, 0, info.width, info.height, true);
+    AndroidBitmap_unlockPixels(env, bmp);
+}
+
+static void JNICALL java_makeButtonDefault(JNIEnv* env, jobject o, jobject bmp) {
+    Platform_LogConst("---####--");
+    AndroidBitmapInfo info;
+    void* addr = NULL;
+
+    // TODO share code with drawBackground
+    AndroidBitmap_getInfo(env, bmp, &info);
+    AndroidBitmap_lockPixels(env, bmp, &addr);
+
+    // TODO refactor this
+    struct Context2D ctx;
+    struct Bitmap pixels;
+    pixels.scan0  = addr;
+    pixels.width  = info.width;
+    pixels.height = info.height;
+    Context2D_Wrap(&ctx, &pixels);
+
+    LButton_DrawBackground(&ctx, 0, 0, info.width, info.height, false);
+    AndroidBitmap_unlockPixels(env, bmp);
+}
+
+static void LBackend_ButtonUpdateBackground(struct LButton* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[1];
+    if (!w->meta) return;
+
+    args[0].i = (int)w->meta;
+    jmethodID method = JavaGetIMethod(env, "buttonUpdateBackground", "(I)V");
+    JavaICall_Void(env, method, args);
+}
+
+
+/*########################################################################################################################*
+*-----------------------------------------------------CheckboxWidget------------------------------------------------------*
+*#########################################################################################################################*/
+void LBackend_CheckboxInit(struct LCheckbox* w) { }
+
+static void LBackend_CheckboxShow(struct LCheckbox* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[6];
+
+    LBackend_GetLayoutArgs(w, args);
+    args[4].l = JavaMakeString(env, &w->text);
+    args[5].z = w->value;
+
+    jmethodID method = JavaGetIMethod(env, "checkboxAdd", "(IIIILjava/lang/String;Z)I");
+    w->meta = (void*)JavaICall_Int(env, method, args);
+    (*env)->DeleteLocalRef(env, args[4].l);
+}
+void LBackend_CheckboxDraw(struct LCheckbox* w) { }
+
+void LBackend_CheckboxUpdate(struct LCheckbox* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[2];
+    if (!w->meta) return;
+
+    args[0].i = (int)w->meta;
+    args[1].z = w->value;
+
+    // TODO share logic with LabelUpdate/ButtonUpdate
+    jmethodID method = JavaGetIMethod(env, "checkboxUpdate", "(IZ)V");
+    JavaICall_Void(env, method, args);
+}
+
+
+/*########################################################################################################################*
+*------------------------------------------------------InputWidget--------------------------------------------------------*
+*#########################################################################################################################*/
+void LBackend_InputInit(struct LInput* w, int width) {
+    w->_textHeight = Display_ScaleX(width);
+}
+
+static void LBackend_InputShow(struct LInput* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[8];
+
+    LBackend_GetLayoutArgs(w, args);
+    args[4].i = w->_textHeight;
+    args[5].i = Display_ScaleY(LINPUT_HEIGHT);
+    args[6].i = w->inputType;
+    args[7].l = JavaMakeConst(env, w->hintText);
+
+    jmethodID method = JavaGetIMethod(env, "inputAdd", "(IIIIIIILjava/lang/String;)I");
+    w->meta = (void*)JavaICall_Int(env, method, args);
+    (*env)->DeleteLocalRef(env, args[7].l);
+}
+
+void LBackend_InputUpdate(struct LInput* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[2];
+    if (!w->meta) return;
+
+    args[0].i = (int)w->meta;
+    args[1].l = JavaMakeString(env, &w->text);
+
+    // TODO share logic with LabelUpdate/ButtonUpdate
+    jmethodID method = JavaGetIMethod(env, "inputUpdate", "(ILjava/lang/String;)V");
+    JavaICall_Void(env, method, args);
+    (*env)->DeleteLocalRef(env, args[1].l);
+}
+
+void LBackend_InputTick(struct LInput* w) { }
+void LBackend_InputSelect(struct LInput* w, int idx, cc_bool wasSelected) { }
+void LBackend_InputUnselect(struct LInput* w) { }
+void LBackend_InputDraw(struct LInput* w) { }
+
+
+/*########################################################################################################################*
+*------------------------------------------------------LabelWidget--------------------------------------------------------*
+*#########################################################################################################################*/
+void LBackend_LabelInit(struct LLabel* w) { }
+
+static void LBackend_LabelShow(struct LLabel* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[4];
+    LBackend_GetLayoutArgs(w, args);
+
+    jmethodID method = JavaGetIMethod(env, "labelAdd", "(IIII)I");
+    w->meta = (void*)JavaICall_Int(env, method, args);
+}
+
+void LBackend_LabelUpdate(struct LLabel* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[2];
+    if (!w->meta) return;
+
+    args[0].i = (int)w->meta;
+    args[1].l = JavaMakeString(env, &w->text);
+
+    // TODO share logic with LabelUpdate/ButtonUpdate
+    jmethodID method = JavaGetIMethod(env, "labelUpdate", "(ILjava/lang/String;)V");
+    JavaICall_Void(env, method, args);
+    (*env)->DeleteLocalRef(env, args[1].l);
+}
+void LBackend_LabelDraw(struct LLabel* w) { }
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------LineWidget--------------------------------------------------------*
+*#########################################################################################################################*/
+void LBackend_LineInit(struct LLine* w, int width) {
+    w->_width = Display_ScaleX(width);
+}
+
+static void LBackend_LineShow(struct LLine* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[7];
+
+    LBackend_GetLayoutArgs(w, args);
+    args[4].i = w->_width;
+    args[5].i = Display_ScaleY(LLINE_HEIGHT);
+    args[6].i = ToAndroidColor(LLine_GetColor());
+
+    jmethodID method = JavaGetIMethod(env, "lineAdd", "(IIIIIII)I");
+    w->meta = (void*)JavaICall_Int(env, method, args);
+}
+void LBackend_LineDraw(struct LLine* w) { }
+
+
+/*########################################################################################################################*
+*------------------------------------------------------SliderWidget-------------------------------------------------------*
+*#########################################################################################################################*/
+void LBackend_SliderInit(struct LSlider* w, int width, int height) {
+    w->_width  = Display_ScaleX(width);
+    w->_height = Display_ScaleY(height);
+}
+
+static void LBackend_SliderShow(struct LSlider* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[7];
+
+    LBackend_GetLayoutArgs(w, args);
+    args[4].i = w->_width;
+    args[5].i = w->_height;
+    args[6].i = ToAndroidColor(w->color);
+
+    jmethodID method = JavaGetIMethod(env, "sliderAdd", "(IIIIIII)I");
+    w->meta = (void*)JavaICall_Int(env, method, args);
+}
+
+void LBackend_SliderUpdate(struct LSlider* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[2];
+    if (!w->meta) return;
+
+    args[0].i = (int)w->meta;
+    args[1].i = w->value;
+
+    jmethodID method = JavaGetIMethod(env, "sliderUpdate", "(II)V");
+    JavaICall_Void(env, method, args);
+}
+void LBackend_SliderDraw(struct LSlider* w) {}
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------TableWidget-------------------------------------------------------*
+*#########################################################################################################################*/
+void LBackend_TableInit(struct LTable* w) {
+
+}
+
+static void LBackend_TableShow(struct LTable* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[5];
+
+    LBackend_GetLayoutArgs(w, args);
+    args[4].i = ToAndroidColor(LTable_RowColor(1, false, false));
+
+    jmethodID method = JavaGetIMethod(env, "tableAdd", "(IIIII)I");
+    w->meta = (void*)JavaICall_Int(env, method, args);
+    LBackend_TableUpdate(w);
+}
+
+static jstring GetTableDetails(JNIEnv* env, struct ServerInfo* server) {
+    char buffer[NATIVE_STR_LEN];
+    cc_string text = String_FromArray(buffer);
+
+    String_Format2(&text, "%i/%i players, up for ", &server->players, &server->maxPlayers);
+    LTable_FormatUptime(&text, server->uptime);
+    if (server->software.length) String_Format1(&text, " | %s", &server->software);
+
+    return JavaMakeString(env, &text);
+}
+
+void LBackend_TableUpdate(struct LTable* w) {
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jvalue args[3];
+    jmethodID method;
+
+    method = JavaGetIMethod(env, "tableStartUpdate", "()V");
+    JavaICall_Void(env, method, args);
+    method = JavaGetIMethod(env, "tableAddEntry", "(Ljava/lang/String;Ljava/lang/String;Z)V");
+
+    for (int i = 0; i < w->rowsCount; i++)
+    {
+        struct ServerInfo* info = LTable_Get(i);
+        args[0].l = JavaMakeString(env, &info->name);
+        args[1].l = GetTableDetails(env, info);
+        args[2].z = info->featured;
+        JavaICall_Void(env, method, args);
+
+        (*env)->DeleteLocalRef(env, args[0].l);
+        (*env)->DeleteLocalRef(env, args[1].l);
+    }
+
+    method    = JavaGetIMethod(env, "tableFinishUpdate", "(I)V");
+    args[0].i = (int)w->meta;
+    JavaICall_Void(env, method, args);
+}
+
+void LBackend_TableReposition(struct LTable* w) {
+
+}
+
+void LBackend_TableFlagAdded(struct LTable* w) {
+}
+
+void LBackend_TableDraw(struct LTable* w) { }
+void LBackend_TableMouseDown(struct LTable* w, int idx) { }
+void LBackend_TableMouseMove(struct LTable* w, int idx) { }
+void LBackend_TableMouseUp(struct LTable* w, int idx) { }
+
+static jint JNICALL java_tableGetColor(JNIEnv* env, jobject o, jint row, jboolean selected, jboolean featured) {
+    return ToAndroidColor(LTable_RowColor(row, selected, featured));
+}
+
+
+/*########################################################################################################################*
+*--------------------------------------------------------UIBackend--------------------------------------------------------*
+*#########################################################################################################################*/
+#define UI_EVENT_CLICKED 1
+#define UI_EVENT_CHANGED 2
+
+static void JNICALL java_UIClicked(JNIEnv* env, jobject o, jint id) {
+    struct LWidget* w = FindWidgetForView(id);
+    if (!w) return;
+
+    if (w->OnClick) w->OnClick(w);
+}
+
+static void JNICALL java_UIChanged(JNIEnv* env, jobject o, jint id, jint val) {
+    struct LCheckbox* cb = (struct LCheckbox*)FindWidgetForView(id);
+    if (!cb) return;
+
+    cb->value = val;
+    if (cb->ValueChanged) cb->ValueChanged(cb);
+}
+
+static void JNICALL java_UIString(JNIEnv* env, jobject o, jint id, jstring str) {
+    struct LInput* ipt = (struct LInput*)FindWidgetForView(id);
+    if (!ipt) return;
+
+    char buffer[NATIVE_STR_LEN];
+    cc_string text = JavaGetString(env, str, buffer);
+    String_Copy(&ipt->text, &text);
+    if (ipt->TextChanged) ipt->TextChanged(ipt);
+}
+
+static void ShowWidget(struct LWidget* w) {
+    switch (w->type) {
+        case LWIDGET_BUTTON:
+            LBackend_ButtonShow((struct LButton*)w);
+            LBackend_ButtonUpdate((struct LButton*)w);
+            break;
+        case LWIDGET_CHECKBOX:
+            LBackend_CheckboxShow((struct LCheckbox*)w);
+            break;
+        case LWIDGET_INPUT:
+            LBackend_InputShow((struct LInput*)w);
+            LBackend_InputUpdate((struct LInput*)w);
+            break;
+        case LWIDGET_LABEL:
+            LBackend_LabelShow((struct LLabel*)w);
+            LBackend_LabelUpdate((struct LLabel*)w);
+            break;
+        case LWIDGET_LINE:
+            LBackend_LineShow((struct LLine*)w);
+            break;
+        case LWIDGET_SLIDER:
+            LBackend_SliderShow((struct LSlider*)w);
+            break;
+        case LWIDGET_TABLE:
+            LBackend_TableShow((struct LTable*)w);
+            break;
+    }
+}
+
+void LBackend_SetScreen(struct LScreen* s) {
+    for (int i = 0; i < s->numWidgets; i++)
+    {
+        ShowWidget(s->widgets[i]);
+    }
+}
+
+void LBackend_CloseScreen(struct LScreen* s) {
+    // stop referencing widgets
+    for (int i = 0; i < s->numWidgets; i++)
+    {
+        s->widgets[i]->meta = NULL;
+    }
+
+    JNIEnv* env; JavaGetCurrentEnv(env);
+    jmethodID method = JavaGetIMethod(env, "clearWidgetsAsync", "()V");
+    JavaICall_Void(env, method, NULL);
+}
+
+static const JNINativeMethod methods[] = {
+        { "nextTextPart",       "(Ljava/lang/String;[I)Ljava/lang/String;", java_nextTextPart },
+        { "drawBackground",     "(Landroid/graphics/Bitmap;)V", java_drawBackground },
+        { "makeButtonActive",   "(Landroid/graphics/Bitmap;)V", java_makeButtonActive },
+        { "makeButtonDefault",  "(Landroid/graphics/Bitmap;)V", java_makeButtonDefault },
+        { "processOnUIClicked", "(I)V", java_UIClicked },
+        { "processOnUIChanged", "(II)V", java_UIChanged },
+        { "processOnUIString",  "(ILjava/lang/String;)V", java_UIString },
+        { "tableGetColor",      "(IZZ)I", java_tableGetColor },
+};
+
+static void LBackend_InitHooks(void) {
+    JNIEnv* env;
+    JavaGetCurrentEnv(env);
+    JavaRegisterNatives(env, methods);
+}
+#endif
\ No newline at end of file