Android: WIP on pre 2.3 support

This commit is contained in:
UnknownShadow200 2025-11-07 19:55:45 +11:00
parent 500723614b
commit 30a575dc28
3 changed files with 214 additions and 202 deletions

View File

@ -229,7 +229,6 @@ add_library(classicube SHARED
) )
target_link_libraries(classicube target_link_libraries(classicube
android
EGL EGL
GLESv2 GLESv2
log) log)

View File

@ -6,8 +6,6 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
@ -25,6 +23,8 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.graphics.Rect; import android.graphics.Rect;
import android.net.Uri; import android.net.Uri;
@ -913,118 +913,6 @@ public class MainActivity extends Activity
} }
// ======================================================================
// -------------------------------- HTTP --------------------------------
// ======================================================================
// Implements java Android side of the Android HTTP backend (See Http.c)
static HttpURLConnection conn;
static InputStream src;
static byte[] readCache = new byte[8192];
public static int httpInit(String url, String method) {
// newer android versions block cleartext traffic by default
if (android.os.Build.VERSION.SDK_INT >= 26) {
url = url.replace("http://", "https://");
}
try {
conn = (HttpURLConnection)new URL(url).openConnection();
conn.setDoInput(true);
conn.setRequestMethod(method);
conn.setInstanceFollowRedirects(true);
httpAddMethodHeaders(method);
return 0;
} catch (Exception ex) {
return httpOnError(ex);
}
}
static void httpAddMethodHeaders(String method) {
if (!method.equals("HEAD")) return;
// Ever since dropbox switched to to chunked transfer encoding,
// sending a HEAD request to dropbox always seems to result in the
// next GET request failing with 'Unexpected status line' ProtocolException
// Seems to be a known issue: https://github.com/square/okhttp/issues/3689
// Simplest workaround is to ask for connection to be closed after HEAD request
httpSetHeader("connection", "close");
}
public static void httpSetHeader(String name, String value) {
conn.setRequestProperty(name, value);
}
public static int httpSetData(byte[] data) {
try {
conn.setDoOutput(true);
conn.getOutputStream().write(data);
conn.getOutputStream().flush();
return 0;
} catch (Exception ex) {
return httpOnError(ex);
}
}
public static int httpPerform() {
int len;
try {
conn.connect();
// Some implementations also provide this as getHeaderField(0), but some don't
httpParseHeader("HTTP/1.1 " + conn.getResponseCode() + " MSG");
// Legitimate webservers aren't going to reply with over 200 headers
for (int i = 0; i < 200; i++) {
String key = conn.getHeaderFieldKey(i);
String val = conn.getHeaderField(i);
if (key == null && val == null) break;
if (key == null) {
httpParseHeader(val);
} else {
httpParseHeader(key + ":" + val);
}
}
src = conn.getInputStream();
while ((len = src.read(readCache)) > 0) {
httpAppendData(readCache, len);
}
httpFinish();
return 0;
} catch (Exception ex) {
return httpOnError(ex);
}
}
static void httpFinish() {
conn = null;
try {
src.close();
} catch (Exception ex) { }
src = null;
}
// TODO: Should we prune this list?
static List<String> errors = new ArrayList<String>();
static int httpOnError(Exception ex) {
ex.printStackTrace();
httpFinish();
errors.add(ex.getMessage());
return -errors.size(); // don't want 0 as an error code
}
public static String httpDescribeError(int res) {
res = -res - 1;
return res >= 0 && res < errors.size() ? errors.get(res) : null;
}
native static void httpParseHeader(String header);
native static void httpAppendData(byte[] data, int len);
// ====================================================================== // ======================================================================
// -------------------------------- SSL --------------------------------- // -------------------------------- SSL ---------------------------------
// ====================================================================== // ======================================================================
@ -1087,4 +975,39 @@ public class MainActivity extends Activity
chain.clear(); chain.clear();
return result; return result;
} }
// ======================================================================
// --------------------------- Legacy window ----------------------------
// ======================================================================
Canvas cur_canvas;
public int[] legacy_lockCanvas(int l, int t, int r, int b) {
CCView surface = (CCView)curView;
Rect rect = new Rect(l, t, r, b);
cur_canvas = surface.getHolder().lockCanvas(rect);
int w = (r - l);
int h = (b - t);
return new int[w * h];
}
public void legacy_unlockCanvas(int l, int t, int r, int b, int[] buf) {
CCView surface = (CCView)curView;
int w = (r - l);
int h = (b - t);
// TODO avoid need to swap R/B
for (int i = 0; i < buf.length; i++)
{
int color = buf[i];
int R = color & 0x00FF0000;
int B = color & 0x000000FF;
buf[i] = (color & 0xFF00FF00) | (B << 16) | (R >> 16);
}
cur_canvas.drawBitmap(buf, 0, w, l, t, w, h, false, null);
surface.getHolder().unlockCanvasAndPost(cur_canvas);
cur_canvas = null;
}
} }

View File

@ -14,6 +14,7 @@
static ANativeWindow* win_handle; static ANativeWindow* win_handle;
static cc_bool winCreated; static cc_bool winCreated;
static jmethodID JAVA_openKeyboard, JAVA_setKeyboardText, JAVA_closeKeyboard; static jmethodID JAVA_openKeyboard, JAVA_setKeyboardText, JAVA_closeKeyboard;
static jmethodID JAVA_getWindowState, JAVA_enterFullscreen, JAVA_exitFullscreen; static jmethodID JAVA_getWindowState, JAVA_enterFullscreen, JAVA_exitFullscreen;
static jmethodID JAVA_showAlert, JAVA_setRequestedOri; static jmethodID JAVA_showAlert, JAVA_setRequestedOri;
@ -22,6 +23,12 @@ static jmethodID JAVA_processedSurfaceDestroyed, JAVA_processEvents;
static jmethodID JAVA_getDpiX, JAVA_getDpiY, JAVA_setupForGame; static jmethodID JAVA_getDpiX, JAVA_getDpiY, JAVA_setupForGame;
static jmethodID JAVA_getClipboardText, JAVA_setClipboardText; static jmethodID JAVA_getClipboardText, JAVA_setClipboardText;
static int32_t (*_ANativeWindow_lock)(ANativeWindow* window, ANativeWindow_Buffer* oBuffer, ARect* ioDirtyBounds);
static int32_t (*_ANativeWindow_unlockAndPost)(ANativeWindow* window);
static ANativeWindow* (*_ANativeWindow_fromSurface)(JNIEnv* env, jobject surface);
static void (*_ANativeWindow_release)(ANativeWindow* window);
static void SetWindowBounds(int width, int height) { static void SetWindowBounds(int width, int height) {
Window_Main.Width = width; Window_Main.Width = width;
Window_Main.Height = height; Window_Main.Height = height;
@ -176,7 +183,7 @@ static void JNICALL java_processJoystickR(JNIEnv* env, jobject o, jint x, jint y
static void JNICALL java_processSurfaceCreated(JNIEnv* env, jobject o, jobject surface, jint w, jint h) { static void JNICALL java_processSurfaceCreated(JNIEnv* env, jobject o, jobject surface, jint w, jint h) {
Platform_LogConst("WIN - CREATED"); Platform_LogConst("WIN - CREATED");
win_handle = ANativeWindow_fromSurface(env, surface); win_handle = _ANativeWindow_fromSurface(env, surface);
winCreated = true; winCreated = true;
Window_Main.Handle.ptr = win_handle; Window_Main.Handle.ptr = win_handle;
@ -187,7 +194,7 @@ static void JNICALL java_processSurfaceCreated(JNIEnv* env, jobject o, jobject s
static void JNICALL java_processSurfaceDestroyed(JNIEnv* env, jobject o) { static void JNICALL java_processSurfaceDestroyed(JNIEnv* env, jobject o) {
Platform_LogConst("WIN - DESTROYED"); Platform_LogConst("WIN - DESTROYED");
if (win_handle) ANativeWindow_release(win_handle); if (win_handle) _ANativeWindow_release(win_handle);
win_handle = NULL; win_handle = NULL;
Window_Main.Handle.ptr = NULL; Window_Main.Handle.ptr = NULL;
@ -254,89 +261,6 @@ static void JNICALL java_onLowMemory(JNIEnv* env, jobject o) {
static void JNICALL java_processOFDResult(JNIEnv* env, jobject o, jstring str); static void JNICALL java_processOFDResult(JNIEnv* env, jobject o, jstring str);
static const JNINativeMethod methods[] = {
{ "processKeyDown", "(I)V", java_processKeyDown },
{ "processKeyUp", "(I)V", java_processKeyUp },
{ "processKeyChar", "(I)V", java_processKeyChar },
{ "processKeyText", "(Ljava/lang/String;)V", java_processKeyText },
{ "processPointerDown", "(IIII)V", java_processPointerDown },
{ "processPointerUp", "(IIII)V", java_processPointerUp },
{ "processPointerMove", "(IIII)V", java_processPointerMove },
{ "processJoystickL", "(II)V", java_processJoystickL },
{ "processJoystickR", "(II)V", java_processJoystickR },
{ "processSurfaceCreated", "(Landroid/view/Surface;II)V", java_processSurfaceCreated },
{ "processSurfaceDestroyed", "()V", java_processSurfaceDestroyed },
{ "processSurfaceResized", "(II)V", java_processSurfaceResized },
{ "processSurfaceRedrawNeeded", "()V", java_processSurfaceRedrawNeeded },
{ "processOnStart", "()V", java_onStart },
{ "processOnStop", "()V", java_onStop },
{ "processOnResume", "()V", java_onResume },
{ "processOnPause", "()V", java_onPause },
{ "processOnDestroy", "()V", java_onDestroy },
{ "processOnGotFocus", "()V", java_onGotFocus },
{ "processOnLostFocus", "()V", java_onLostFocus },
{ "processOnLowMemory", "()V", java_onLowMemory },
{ "processOFDResult", "(Ljava/lang/String;)V", java_processOFDResult },
};
static void CacheJavaMethodRefs(JNIEnv* env) {
JAVA_openKeyboard = Java_GetIMethod(env, "openKeyboard", "(Ljava/lang/String;I)V");
JAVA_setKeyboardText = Java_GetIMethod(env, "setKeyboardText", "(Ljava/lang/String;)V");
JAVA_closeKeyboard = Java_GetIMethod(env, "closeKeyboard", "()V");
JAVA_getWindowState = Java_GetIMethod(env, "getWindowState", "()I");
JAVA_enterFullscreen = Java_GetIMethod(env, "enterFullscreen", "()V");
JAVA_exitFullscreen = Java_GetIMethod(env, "exitFullscreen", "()V");
JAVA_getDpiX = Java_GetIMethod(env, "getDpiX", "()F");
JAVA_getDpiY = Java_GetIMethod(env, "getDpiY", "()F");
JAVA_setupForGame = Java_GetIMethod(env, "setupForGame", "()V");
JAVA_processedSurfaceDestroyed = Java_GetIMethod(env, "processedSurfaceDestroyed", "()V");
JAVA_processEvents = Java_GetIMethod(env, "processEvents", "()V");
JAVA_showAlert = Java_GetIMethod(env, "showAlert", "(Ljava/lang/String;Ljava/lang/String;)V");
JAVA_setRequestedOri = Java_GetIMethod(env, "setRequestedOrientation", "(I)V");
JAVA_openFileDialog = Java_GetIMethod(env, "openFileDialog", "(Ljava/lang/String;)I");
JAVA_saveFileDialog = Java_GetIMethod(env, "saveFileDialog", "(Ljava/lang/String;Ljava/lang/String;)I");
JAVA_getClipboardText = Java_GetIMethod(env, "getClipboardText", "()Ljava/lang/String;");
JAVA_setClipboardText = Java_GetIMethod(env, "setClipboardText", "(Ljava/lang/String;)V");
}
void Window_PreInit(void) {
JNIEnv* env;
Java_GetCurrentEnv(env);
Java_RegisterNatives(env, methods);
CacheJavaMethodRefs(env);
DisplayInfo.CursorVisible = true;
}
// TODO move to bottom of file?
void Window_Init(void) {
JNIEnv* env;
/* TODO: ANativeActivity_setWindowFlags(app->activity, AWINDOW_FLAG_FULLSCREEN, 0); */
Java_GetCurrentEnv(env);
Window_Main.SoftKeyboard = SOFT_KEYBOARD_RESIZE;
Input_SetTouchMode(true);
Gui_SetTouchUI(true);
Input.Sources = INPUT_SOURCE_NORMAL;
DisplayInfo.Depth = 32;
DisplayInfo.ScaleX = Java_ICall_Float(env, JAVA_getDpiX, NULL);
DisplayInfo.ScaleY = Java_ICall_Float(env, JAVA_getDpiY, NULL);
}
void Window_Free(void) { }
static void RemakeWindowSurface(void) { static void RemakeWindowSurface(void) {
JNIEnv* env; JNIEnv* env;
Java_GetCurrentEnv(env); Java_GetCurrentEnv(env);
@ -547,7 +471,7 @@ void Window_DrawFramebuffer(Rect2D r, struct Bitmap* bmp) {
b.top = r.y; b.bottom = r.y + r.height; b.top = r.y; b.bottom = r.y + r.height;
/* Platform_Log4("DIRTY: %i,%i - %i,%i", &b.left, &b.top, &b.right, &b.bottom); */ /* Platform_Log4("DIRTY: %i,%i - %i,%i", &b.left, &b.top, &b.right, &b.bottom); */
res = ANativeWindow_lock(win_handle, &buffer, &b); res = _ANativeWindow_lock(win_handle, &buffer, &b);
if (res) Process_Abort2(res, "Locking window pixels"); if (res) Process_Abort2(res, "Locking window pixels");
/* Platform_Log4("ADJUS: %i,%i - %i,%i", &b.left, &b.top, &b.right, &b.bottom); */ /* Platform_Log4("ADJUS: %i,%i - %i,%i", &b.left, &b.top, &b.right, &b.bottom); */
@ -565,7 +489,7 @@ void Window_DrawFramebuffer(Rect2D r, struct Bitmap* bmp) {
{ {
Mem_Copy(dst + y * buffer.stride, src + y * bmp->width, size); Mem_Copy(dst + y * buffer.stride, src + y * bmp->width, size);
} }
res = ANativeWindow_unlockAndPost(win_handle); res = _ANativeWindow_unlockAndPost(win_handle);
if (res) Process_Abort2(res, "Unlocking window pixels"); if (res) Process_Abort2(res, "Unlocking window pixels");
} }
@ -617,3 +541,169 @@ void Window_LockLandscapeOrientation(cc_bool lock) {
void Window_EnableRawMouse(void) { DefaultEnableRawMouse(); } void Window_EnableRawMouse(void) { DefaultEnableRawMouse(); }
void Window_UpdateRawMouse(void) { } void Window_UpdateRawMouse(void) { }
void Window_DisableRawMouse(void) { DefaultDisableRawMouse(); } void Window_DisableRawMouse(void) { DefaultDisableRawMouse(); }
/*########################################################################################################################*
*------------------------------------------------------libandroid---------------------------------------------------------*
*#########################################################################################################################*/
static const cc_string droidLib = String_FromConst("libandroid.so");
static cc_bool LoadDroidFuncs(void) {
static const struct DynamicLibSym funcs[] = {
DynamicLib_ReqSym(ANativeWindow_lock),
DynamicLib_ReqSym(ANativeWindow_unlockAndPost),
DynamicLib_ReqSym(ANativeWindow_fromSurface),
DynamicLib_ReqSym(ANativeWindow_release),
};
void* lib;
return DynamicLib_LoadAll(&droidLib, funcs, Array_Elems(funcs), &lib);
}
static ANativeWindow* fallback_fromSurface(JNIEnv* env, jobject surface) {
return (void*)1;
}
static void fallback_release(ANativeWindow* window) {
}
static jobject canvas_arr;
static uint32_t* canvas_ptr;
static ARect canvas_rect;
static int32_t fallback_lock(ANativeWindow* window, ANativeWindow_Buffer* oBuffer, ARect* ioDirtyBounds) {
JNIEnv* env;
jvalue args[4];
Java_GetCurrentEnv(env);
args[0].i = ioDirtyBounds->left;
args[1].i = ioDirtyBounds->top;
args[2].i = ioDirtyBounds->right;
args[3].i = ioDirtyBounds->bottom;
jmethodID method = Java_GetIMethod(env, "legacy_lockCanvas", "(IIII)[I");
canvas_arr = Java_ICall_Obj(env, method, args);
canvas_ptr = (*env)->GetIntArrayElements(env, canvas_arr, NULL);
oBuffer->stride = ioDirtyBounds->right - ioDirtyBounds->left;
// Counteract pointer adjustment done in Window_DrawFramebuffer
oBuffer->bits = canvas_ptr - (ioDirtyBounds->top * oBuffer->stride) - ioDirtyBounds->left;
canvas_rect = *ioDirtyBounds;
if (!canvas_ptr) Process_Abort("Out of memory for canvas");
return 0;
}
static int32_t fallback_unlockAndPost(ANativeWindow* window) {
JNIEnv* env;
jvalue args[5];
Java_GetCurrentEnv(env);
(*env)->ReleaseIntArrayElements(env, canvas_arr, canvas_ptr, 0);
args[0].i = canvas_rect.left;
args[1].i = canvas_rect.top;
args[2].i = canvas_rect.right;
args[3].i = canvas_rect.bottom;
args[4].l = canvas_arr;
jmethodID method = Java_GetIMethod(env, "legacy_unlockCanvas", "(IIII[I)V");
Java_ICall_Void(env, method, args);
(*env)->DeleteLocalRef(env, canvas_arr);
canvas_arr = NULL;
return 0;
}
static void UseFallbackDroidFuncs(void) {
_ANativeWindow_fromSurface = fallback_fromSurface;
_ANativeWindow_release = fallback_release;
_ANativeWindow_lock = fallback_lock;
_ANativeWindow_unlockAndPost = fallback_unlockAndPost;
}
/*########################################################################################################################*
*----------------------------------------------------Initialisation-------------------------------------------------------*
*#########################################################################################################################*/
static const JNINativeMethod methods[] = {
{ "processKeyDown", "(I)V", java_processKeyDown },
{ "processKeyUp", "(I)V", java_processKeyUp },
{ "processKeyChar", "(I)V", java_processKeyChar },
{ "processKeyText", "(Ljava/lang/String;)V", java_processKeyText },
{ "processPointerDown", "(IIII)V", java_processPointerDown },
{ "processPointerUp", "(IIII)V", java_processPointerUp },
{ "processPointerMove", "(IIII)V", java_processPointerMove },
{ "processJoystickL", "(II)V", java_processJoystickL },
{ "processJoystickR", "(II)V", java_processJoystickR },
{ "processSurfaceCreated", "(Landroid/view/Surface;II)V", java_processSurfaceCreated },
{ "processSurfaceDestroyed", "()V", java_processSurfaceDestroyed },
{ "processSurfaceResized", "(II)V", java_processSurfaceResized },
{ "processSurfaceRedrawNeeded", "()V", java_processSurfaceRedrawNeeded },
{ "processOnStart", "()V", java_onStart },
{ "processOnStop", "()V", java_onStop },
{ "processOnResume", "()V", java_onResume },
{ "processOnPause", "()V", java_onPause },
{ "processOnDestroy", "()V", java_onDestroy },
{ "processOnGotFocus", "()V", java_onGotFocus },
{ "processOnLostFocus", "()V", java_onLostFocus },
{ "processOnLowMemory", "()V", java_onLowMemory },
{ "processOFDResult", "(Ljava/lang/String;)V", java_processOFDResult },
};
static void CacheJavaMethodRefs(JNIEnv* env) {
JAVA_openKeyboard = Java_GetIMethod(env, "openKeyboard", "(Ljava/lang/String;I)V");
JAVA_setKeyboardText = Java_GetIMethod(env, "setKeyboardText", "(Ljava/lang/String;)V");
JAVA_closeKeyboard = Java_GetIMethod(env, "closeKeyboard", "()V");
JAVA_getWindowState = Java_GetIMethod(env, "getWindowState", "()I");
JAVA_enterFullscreen = Java_GetIMethod(env, "enterFullscreen", "()V");
JAVA_exitFullscreen = Java_GetIMethod(env, "exitFullscreen", "()V");
JAVA_getDpiX = Java_GetIMethod(env, "getDpiX", "()F");
JAVA_getDpiY = Java_GetIMethod(env, "getDpiY", "()F");
JAVA_setupForGame = Java_GetIMethod(env, "setupForGame", "()V");
JAVA_processedSurfaceDestroyed = Java_GetIMethod(env, "processedSurfaceDestroyed", "()V");
JAVA_processEvents = Java_GetIMethod(env, "processEvents", "()V");
JAVA_showAlert = Java_GetIMethod(env, "showAlert", "(Ljava/lang/String;Ljava/lang/String;)V");
JAVA_setRequestedOri = Java_GetIMethod(env, "setRequestedOrientation", "(I)V");
JAVA_openFileDialog = Java_GetIMethod(env, "openFileDialog", "(Ljava/lang/String;)I");
JAVA_saveFileDialog = Java_GetIMethod(env, "saveFileDialog", "(Ljava/lang/String;Ljava/lang/String;)I");
JAVA_getClipboardText = Java_GetIMethod(env, "getClipboardText", "()Ljava/lang/String;");
JAVA_setClipboardText = Java_GetIMethod(env, "setClipboardText", "(Ljava/lang/String;)V");
}
void Window_PreInit(void) {
JNIEnv* env;
Java_GetCurrentEnv(env);
Java_RegisterNatives(env, methods);
CacheJavaMethodRefs(env);
DisplayInfo.CursorVisible = true;
if (!LoadDroidFuncs()) UseFallbackDroidFuncs();
}
void Window_Init(void) {
JNIEnv* env;
/* TODO: ANativeActivity_setWindowFlags(app->activity, AWINDOW_FLAG_FULLSCREEN, 0); */
Java_GetCurrentEnv(env);
Window_Main.SoftKeyboard = SOFT_KEYBOARD_RESIZE;
Input_SetTouchMode(true);
Gui_SetTouchUI(true);
Input.Sources = INPUT_SOURCE_NORMAL;
DisplayInfo.Depth = 32;
DisplayInfo.ScaleX = Java_ICall_Float(env, JAVA_getDpiX, NULL);
DisplayInfo.ScaleY = Java_ICall_Float(env, JAVA_getDpiY, NULL);
}
void Window_Free(void) { }