From fbb91e547d25539ddeed20aee0c193dc6a751c1d Mon Sep 17 00:00:00 2001 From: mittorn Date: Tue, 24 Oct 2023 02:36:45 +0300 Subject: [PATCH 01/43] Revert "engine: partially remove legacy Android port, in preparation of new port merge" This reverts commit ef663a87909e5f6b72902c034ef9ed8a8e149a1f. --- common/backends.h | 7 + common/defaults.h | 22 +- engine/platform/android/android.c | 2 +- engine/platform/android/android_nosdl.c | 997 ++++++++++++++++++++++++ engine/platform/android/android_priv.h | 46 ++ engine/platform/android/snd_opensles.c | 277 +++++++ engine/platform/android/vid_android.c | 667 ++++++++++++++++ 7 files changed, 2015 insertions(+), 3 deletions(-) create mode 100644 engine/platform/android/android_nosdl.c create mode 100644 engine/platform/android/android_priv.h create mode 100644 engine/platform/android/snd_opensles.c create mode 100644 engine/platform/android/vid_android.c diff --git a/common/backends.h b/common/backends.h index 6fd51f19f6..77bcced827 100644 --- a/common/backends.h +++ b/common/backends.h @@ -18,17 +18,21 @@ GNU General Public License for more details. // video backends (XASH_VIDEO) #define VIDEO_NULL 0 #define VIDEO_SDL 1 +#define VIDEO_ANDROID 2 #define VIDEO_FBDEV 3 #define VIDEO_DOS 4 + // audio backends (XASH_SOUND) #define SOUND_NULL 0 #define SOUND_SDL 1 +#define SOUND_OPENSLES 2 #define SOUND_ALSA 3 // input (XASH_INPUT) #define INPUT_NULL 0 #define INPUT_SDL 1 +#define INPUT_ANDROID 2 #define INPUT_EVDEV 3 // timer (XASH_TIMER) @@ -41,13 +45,16 @@ GNU General Public License for more details. // messageboxes (XASH_MESSAGEBOX) #define MSGBOX_STDERR 0 #define MSGBOX_SDL 1 +#define MSGBOX_ANDROID 2 #define MSGBOX_WIN32 3 #define MSGBOX_NSWITCH 4 + // library loading (XASH_LIB) #define LIB_NULL 0 #define LIB_POSIX 1 #define LIB_WIN32 2 #define LIB_STATIC 3 + #endif /* BACKENDS_H */ diff --git a/common/defaults.h b/common/defaults.h index cbcbd13e88..f870e38966 100644 --- a/common/defaults.h +++ b/common/defaults.h @@ -52,6 +52,26 @@ SETUP BACKENDS DEFINITIONS #endif #endif // XASH_MESSAGEBOX #endif + #elif XASH_ANDROID + // we are building for Android platform, use Android APIs + #ifndef XASH_VIDEO + #define XASH_VIDEO VIDEO_ANDROID + #endif // XASH_VIDEO + + #ifndef XASH_INPUT + #define XASH_INPUT INPUT_ANDROID + #endif // XASH_INPUT + + #ifndef XASH_SOUND + #define XASH_SOUND SOUND_OPENSLES + #endif // XASH_SOUND + + #ifndef XASH_MESSAGEBOX + #define XASH_MESSAGEBOX MSGBOX_ANDROID + #endif // XASH_MESSAGEBOX + + #define XASH_USE_EVDEV 1 + #define XASH_DYNAMIC_DLADDR #elif XASH_LINUX // we are building for Linux without SDL2, can draw only to framebuffer yet #ifndef XASH_VIDEO @@ -151,8 +171,6 @@ Default build-depended cvar and constant values #define DEFAULT_MODE_WIDTH 960 #define DEFAULT_MODE_HEIGHT 544 #define DEFAULT_ALLOWCONSOLE 1 -#elif XASH_ANDROID - #define DEFAULT_TOUCH_ENABLE "1" #elif XASH_MOBILE_PLATFORM #define DEFAULT_TOUCH_ENABLE "1" #define DEFAULT_M_IGNORE "1" diff --git a/engine/platform/android/android.c b/engine/platform/android/android.c index 7bba5ca582..de63238211 100644 --- a/engine/platform/android/android.c +++ b/engine/platform/android/android.c @@ -14,7 +14,7 @@ GNU General Public License for more details. */ #include "platform/platform.h" -#if !defined(XASH_DEDICATED) +#if !defined(XASH_DEDICATED) && XASH_SDL #include "input.h" #include "client.h" diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c new file mode 100644 index 0000000000..6d32b62f3b --- /dev/null +++ b/engine/platform/android/android_nosdl.c @@ -0,0 +1,997 @@ +/* +android_nosdl.c - android backend +Copyright (C) 2016-2019 mittorn + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#include "platform/platform.h" +#if !defined(XASH_DEDICATED) && !XASH_SDL +#include "input.h" +#include "client.h" +#include "sound.h" +#include "platform/android/android_priv.h" +#include "errno.h" +#include +#include + +#ifndef JNICALL +#define JNICALL // a1ba: workaround for my IDE, where Java files are not included +#define JNIEXPORT +#endif + +convar_t *android_sleep; + +static const int s_android_scantokey[] = +{ + 0, K_LEFTARROW, K_RIGHTARROW, K_AUX26, K_ESCAPE, // 0 + K_AUX26, K_AUX25, '0', '1', '2', // 5 + '3', '4', '5', '6', '7', // 10 + '8', '9', '*', '#', K_UPARROW, // 15 + K_DOWNARROW, K_LEFTARROW, K_RIGHTARROW, K_ENTER, K_AUX32, // 20 + K_AUX31, K_AUX29, K_AUX28, K_AUX27, 'a', // 25 + 'b', 'c', 'd', 'e', 'f', // 30 + 'g', 'h', 'i', 'j', 'k', // 35 + 'l', 'm', 'n', 'o', 'p', // 40 + 'q', 'r', 's', 't', 'u', // 45 + 'v', 'w', 'x', 'y', 'z', // 50 + ',', '.', K_ALT, K_ALT, K_SHIFT, // 55 + K_SHIFT, K_TAB, K_SPACE, 0, 0, // 60 + 0, K_ENTER, K_BACKSPACE, '`', '-', // 65 + '=', '[', ']', '\\', ';', // 70 + '\'', '/', '@', K_KP_NUMLOCK, 0, // 75 + 0, '+', '`', 0, 0, // 80 + 0, 0, 0, 0, 0, // 85 + 0, 0, K_PGUP, K_PGDN, 0, // 90 + 0, K_AUX1, K_AUX2, K_AUX14, K_AUX3, // 95 + K_AUX4, K_AUX15, K_AUX6, K_AUX7, K_JOY1, // 100 + K_JOY2, K_AUX10, K_AUX11, K_ESCAPE, K_ESCAPE, // 105 + 0, K_ESCAPE, K_DEL, K_CTRL, K_CTRL, // 110 + K_CAPSLOCK, 0, 0, 0, 0, // 115 + 0, K_PAUSE, K_HOME, K_END, K_INS, // 120 + 0, 0, 0, 0, 0, // 125 + 0, K_F1, K_F2, K_F3, K_F4, // 130 + K_F5, K_F6, K_F7, K_F8, K_F9, // 135 + K_F10, K_F11, K_F12, K_KP_NUMLOCK, K_KP_INS, // 140 + K_KP_END, K_KP_DOWNARROW, K_KP_PGDN, K_KP_LEFTARROW, K_KP_5, // 145 + K_KP_RIGHTARROW,K_KP_HOME, K_KP_UPARROW, K_KP_PGUP, K_KP_SLASH, // 150 + 0, K_KP_MINUS, K_KP_PLUS, K_KP_DEL, ',', // 155 + K_KP_ENTER, '=', '(', ')' +}; + +#define ANDROID_MAX_EVENTS 64 +#define MAX_FINGERS 10 + +typedef enum event_type +{ + event_touch_down = 0, + event_touch_up, + event_touch_move, // compatible with touchEventType + event_key_down, + event_key_up, + event_set_pause, + event_resize, + event_joyhat, + event_joyball, + event_joybutton, + event_joyaxis, + event_joyadd, + event_joyremove, + event_onpause, + event_ondestroy, + event_onresume, + event_onfocuschange, +} eventtype_t; + +typedef struct touchevent_s +{ + float x; + float y; + float dx; + float dy; +} touchevent_t; + +typedef struct joyball_s +{ + short xrel; + short yrel; + byte ball; +} joyball_t; + +typedef struct joyhat_s +{ + byte hat; + byte key; +} joyhat_t; + +typedef struct joyaxis_s +{ + short val; + byte axis; +} joyaxis_t; + +typedef struct joybutton_s +{ + int down; + byte button; +} joybutton_t; + +typedef struct keyevent_s +{ + int code; +} keyevent_t; + +typedef struct event_s +{ + eventtype_t type; + int arg; + union + { + touchevent_t touch; + joyhat_t hat; + joyball_t ball; + joyaxis_t axis; + joybutton_t button; + keyevent_t key; + }; +} event_t; + +typedef struct finger_s +{ + float x, y; + qboolean down; +} finger_t; + +static struct { + pthread_mutex_t mutex; // this mutex is locked while not running frame, used for events synchronization + pthread_mutex_t framemutex; // this mutex is locked while engine is running and unlocked while it reading events, used for pause in background. + event_t queue[ANDROID_MAX_EVENTS]; + volatile int count; + finger_t fingers[MAX_FINGERS]; + char inputtext[256]; + float mousex, mousey; +} events = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }; + +struct jnimethods_s jni; +struct jnimouse_s jnimouse; + +#define Android_Lock() pthread_mutex_lock(&events.mutex); +#define Android_Unlock() pthread_mutex_unlock(&events.mutex); +#define Android_PushEvent() Android_Unlock() + +typedef void (*pfnChangeGame)( const char *progname ); +int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGame, pfnChangeGame func ); + +/* +======================== +Android_AllocEvent + +Lock event queue and return pointer to next event. +Caller must do Android_PushEvent() to unlock queue after setting parameters. +======================== +*/ +event_t *Android_AllocEvent( void ) +{ + Android_Lock(); + if( events.count == ANDROID_MAX_EVENTS ) + { + events.count--; //override last event + __android_log_print( ANDROID_LOG_ERROR, "Xash", "Too many events!!!" ); + } + return &events.queue[ events.count++ ]; +} + + +/* +===================================================== +JNI callbacks + +On application start, setenv and onNativeResize called from +ui thread to set up engine configuration +nativeInit called directly from engine thread and will not return until exit. +These functions may be called from other threads at any time: +nativeKey +nativeTouch +onNativeResize +nativeString +nativeSetPause +===================================================== +*/ +#define VA_ARGS(...) , ##__VA_ARGS__ // GCC extension +#define DECLARE_JNI_INTERFACE( ret, name, ... ) \ + JNIEXPORT ret JNICALL Java_su_xash_engine_XashActivity_##name( JNIEnv *env, jclass clazz VA_ARGS(__VA_ARGS__) ) + +DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) +{ + int i; + int argc; + int status; + /* Prepare the arguments. */ + + int len = (*env)->GetArrayLength(env, array); + char** argv = calloc( 1 + len + 1, sizeof( char ** )); + argc = 0; + argv[argc++] = strdup("app_process"); + for (i = 0; i < len; ++i) { + const char* utf; + char* arg = NULL; + jstring string = (*env)->GetObjectArrayElement(env, array, i); + if (string) { + utf = (*env)->GetStringUTFChars(env, string, 0); + if (utf) { + arg = strdup(utf); + (*env)->ReleaseStringUTFChars(env, string, utf); + } + (*env)->DeleteLocalRef(env, string); + } + if (!arg) { + arg = strdup(""); + } + argv[argc++] = arg; + } + argv[argc] = NULL; + prctl(PR_SET_DUMPABLE, 1); + + /* Init callbacks. */ + + jni.env = env; + jni.actcls = (*env)->FindClass(env, "su/xash/engine/XashActivity"); + jni.enableTextInput = (*env)->GetStaticMethodID(env, jni.actcls, "showKeyboard", "(I)V"); + jni.vibrate = (*env)->GetStaticMethodID(env, jni.actcls, "vibrate", "(I)V" ); + jni.messageBox = (*env)->GetStaticMethodID(env, jni.actcls, "messageBox", "(Ljava/lang/String;Ljava/lang/String;)V"); + jni.notify = (*env)->GetStaticMethodID(env, jni.actcls, "engineThreadNotify", "()V"); + jni.setTitle = (*env)->GetStaticMethodID(env, jni.actcls, "setTitle", "(Ljava/lang/String;)V"); + jni.setIcon = (*env)->GetStaticMethodID(env, jni.actcls, "setIcon", "(Ljava/lang/String;)V"); + jni.getAndroidId = (*env)->GetStaticMethodID(env, jni.actcls, "getAndroidID", "()Ljava/lang/String;"); + jni.saveID = (*env)->GetStaticMethodID(env, jni.actcls, "saveID", "(Ljava/lang/String;)V"); + jni.loadID = (*env)->GetStaticMethodID(env, jni.actcls, "loadID", "()Ljava/lang/String;"); + jni.showMouse = (*env)->GetStaticMethodID(env, jni.actcls, "showMouse", "(I)V"); + jni.shellExecute = (*env)->GetStaticMethodID(env, jni.actcls, "shellExecute", "(Ljava/lang/String;)V"); + + jni.swapBuffers = (*env)->GetStaticMethodID(env, jni.actcls, "swapBuffers", "()V"); + jni.toggleEGL = (*env)->GetStaticMethodID(env, jni.actcls, "toggleEGL", "(I)V"); + jni.createGLContext = (*env)->GetStaticMethodID(env, jni.actcls, "createGLContext", "([I[I)Z"); + jni.getGLAttribute = (*env)->GetStaticMethodID(env, jni.actcls, "getGLAttribute", "(I)I"); + jni.deleteGLContext = (*env)->GetStaticMethodID(env, jni.actcls, "deleteGLContext", "()Z"); + jni.getSelectedPixelFormat = (*env)->GetStaticMethodID(env, jni.actcls, "getSelectedPixelFormat", "()I"); + jni.getSurface = (*env)->GetStaticMethodID(env, jni.actcls, "getNativeSurface", "()Landroid/view/Surface;"); + + /* Run the application. */ + + status = Host_Main( argc, argv, getenv("XASH3D_GAMEDIR"), false, NULL ); + + /* Release the arguments. */ + + for (i = 0; i < argc; ++i) + free(argv[i]); + free(argv); + + return status; +} + +DECLARE_JNI_INTERFACE( void, onNativeResize, jint width, jint height ) +{ + event_t *event; + + if( !width || !height ) + return; + + jni.width=width, jni.height=height; + + // alloc update event to change screen size + event = Android_AllocEvent(); + event->type = event_resize; + Android_PushEvent(); +} + +DECLARE_JNI_INTERFACE( void, nativeQuit ) +{ +} + +DECLARE_JNI_INTERFACE( void, nativeSetPause, jint pause ) +{ + event_t *event = Android_AllocEvent(); + event->type = event_set_pause; + event->arg = pause; + Android_PushEvent(); + + // if pause enabled, hold engine by locking frame mutex. + // Engine will stop after event reading and will not continue untill unlock + if( android_sleep && android_sleep->value ) + { + if( pause ) + pthread_mutex_lock( &events.framemutex ); + else + pthread_mutex_unlock( &events.framemutex ); + } +} + +DECLARE_JNI_INTERFACE( void, nativeUnPause ) +{ + // UnPause engine before sending critical events + if( android_sleep && android_sleep->value ) + pthread_mutex_unlock( &events.framemutex ); +} + +DECLARE_JNI_INTERFACE( void, nativeKey, jint down, jint code ) +{ + event_t *event; + + if( code < 0 ) + { + event = Android_AllocEvent(); + event->arg = (-code) & 255; + event->type = down?event_key_down:event_key_up; + Android_PushEvent(); + } + else + { + if( code >= ( sizeof( s_android_scantokey ) / sizeof( s_android_scantokey[0] ) ) ) + { + Con_DPrintf( "nativeKey: unknown Android key %d\n", code ); + return; + } + + if( !s_android_scantokey[code] ) + { + Con_DPrintf( "nativeKey: unmapped Android key %d\n", code ); + return; + } + + event = Android_AllocEvent(); + event->type = down?event_key_down:event_key_up; + event->arg = s_android_scantokey[code]; + Android_PushEvent(); + } +} + +DECLARE_JNI_INTERFACE( void, nativeString, jobject string ) +{ + char* str = (char *) (*env)->GetStringUTFChars(env, string, NULL); + + Android_Lock(); + strncat( events.inputtext, str, 256 ); + Android_Unlock(); + + (*env)->ReleaseStringUTFChars(env, string, str); +} + +#ifdef SOFTFP_LINK +DECLARE_JNI_INTERFACE( void, nativeTouch, jint finger, jint action, jfloat x, jfloat y ) __attribute__((pcs("aapcs"))); +#endif +DECLARE_JNI_INTERFACE( void, nativeTouch, jint finger, jint action, jfloat x, jfloat y ) +{ + float dx, dy; + event_t *event; + + // if something wrong with android event + if( finger > MAX_FINGERS ) + return; + + // not touch action? + if( !( action >=0 && action <= 2 ) ) + return; + + // 0.0f .. 1.0f + x /= jni.width; + y /= jni.height; + + if( action ) + dx = x - events.fingers[finger].x, dy = y - events.fingers[finger].y; + else + dx = dy = 0.0f; + events.fingers[finger].x = x, events.fingers[finger].y = y; + + // check if we should skip some events + if( ( action == 2 ) && ( !dx && !dy ) ) + return; + + if( ( action == 0 ) && events.fingers[finger].down ) + return; + + if( ( action == 1 ) && !events.fingers[finger].down ) + return; + + if( action == 2 && !events.fingers[finger].down ) + action = 0; + + if( action == 0 ) + events.fingers[finger].down = true; + else if( action == 1 ) + events.fingers[finger].down = false; + + event = Android_AllocEvent(); + event->arg = finger; + event->type = action; + event->touch.x = x; + event->touch.y = y; + event->touch.dx = dx; + event->touch.dy = dy; + Android_PushEvent(); +} + +DECLARE_JNI_INTERFACE( void, nativeBall, jint id, jbyte ball, jshort xrel, jshort yrel ) +{ + event_t *event = Android_AllocEvent(); + + event->type = event_joyball; + event->arg = id; + event->ball.ball = ball; + event->ball.xrel = xrel; + event->ball.yrel = yrel; + Android_PushEvent(); +} + +DECLARE_JNI_INTERFACE( void, nativeHat, jint id, jbyte hat, jbyte key, jboolean down ) +{ + event_t *event = Android_AllocEvent(); + static byte engineKeys; + + if( !key ) + engineKeys = 0; // centered; + + if( down ) + engineKeys |= key; + else + engineKeys &= ~key; + + event->type = event_joyhat; + event->arg = id; + event->hat.hat = hat; + event->hat.key = engineKeys; + Android_PushEvent(); +} + +DECLARE_JNI_INTERFACE( void, nativeAxis, jint id, jbyte axis, jshort val ) +{ + event_t *event = Android_AllocEvent(); + event->type = event_joyaxis; + event->arg = id; + event->axis.axis = axis; + event->axis.val = val; + + __android_log_print(ANDROID_LOG_VERBOSE, "Xash", "axis %i %i", axis, val ); + Android_PushEvent(); +} + +DECLARE_JNI_INTERFACE( void, nativeJoyButton, jint id, jbyte button, jboolean down ) +{ + event_t *event = Android_AllocEvent(); + event->type = event_joybutton; + event->arg = id; + event->button.button = button; + event->button.down = down; + __android_log_print(ANDROID_LOG_VERBOSE, "Xash", "button %i", button ); + Android_PushEvent(); +} + +DECLARE_JNI_INTERFACE( void, nativeJoyAdd, jint id ) +{ + event_t *event = Android_AllocEvent(); + event->type = event_joyadd; + event->arg = id; + Android_PushEvent(); +} + +DECLARE_JNI_INTERFACE( void, nativeJoyDel, jint id ) +{ + event_t *event = Android_AllocEvent(); + event->type = event_joyremove; + event->arg = id; + Android_PushEvent(); +} + +DECLARE_JNI_INTERFACE( void, nativeOnResume ) +{ + event_t *event = Android_AllocEvent(); + event->type = event_onresume; + Android_PushEvent(); +} + +DECLARE_JNI_INTERFACE( void, nativeOnFocusChange ) +{ + event_t *event = Android_AllocEvent(); + event->type = event_onfocuschange; + Android_PushEvent(); +} + +DECLARE_JNI_INTERFACE( void, nativeOnPause ) +{ + event_t *event = Android_AllocEvent(); + event->type = event_onpause; + Android_PushEvent(); +} + +DECLARE_JNI_INTERFACE( void, nativeOnDestroy ) +{ + event_t *event = Android_AllocEvent(); + event->type = event_ondestroy; + Android_PushEvent(); +} + +DECLARE_JNI_INTERFACE( int, setenv, jstring key, jstring value, jboolean overwrite ) +{ + char* k = (char *) (*env)->GetStringUTFChars(env, key, NULL); + char* v = (char *) (*env)->GetStringUTFChars(env, value, NULL); + int err = setenv(k, v, overwrite); + (*env)->ReleaseStringUTFChars(env, key, k); + (*env)->ReleaseStringUTFChars(env, value, v); + return err; +} + + +DECLARE_JNI_INTERFACE( void, nativeMouseMove, jfloat x, jfloat y ) +{ + Android_Lock(); + events.mousex += x; + events.mousey += y; + Android_Unlock(); +} + +DECLARE_JNI_INTERFACE( int, nativeTestWritePermission, jstring jPath ) +{ + char *path = (char *)(*env)->GetStringUTFChars(env, jPath, NULL); + FILE *fd; + char testFile[PATH_MAX]; + int ret = 0; + + // maybe generate new file everytime? + Q_snprintf( testFile, PATH_MAX, "%s/.testfile", path ); + + __android_log_print( ANDROID_LOG_VERBOSE, "Xash", "nativeTestWritePermission: file=%s", testFile ); + + fd = fopen( testFile, "w+" ); + + if( fd ) + { + __android_log_print( ANDROID_LOG_VERBOSE, "Xash", "nativeTestWritePermission: passed" ); + ret = 1; + fclose( fd ); + + remove( testFile ); + } + else + { + __android_log_print( ANDROID_LOG_VERBOSE, "Xash", "nativeTestWritePermission: error=%s", strerror( errno ) ); + } + + (*env)->ReleaseStringUTFChars( env, jPath, path ); + + return ret; +} + +JNIEXPORT jint JNICALL JNI_OnLoad( JavaVM *vm, void *reserved ) +{ + return JNI_VERSION_1_6; +} + +/* +======================== +Platform_Init + +Initialize android-related cvars +======================== +*/ +void Platform_Init( void ) +{ + android_sleep = Cvar_Get( "android_sleep", "1", FCVAR_ARCHIVE, "Enable sleep in background" ); +} + +void Platform_Shutdown( void ) +{ + +} + +/* +======================== +Android_EnableTextInput + +Show virtual keyboard +======================== +*/ +void Platform_EnableTextInput( qboolean enable ) +{ + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.enableTextInput, enable ); +} + +/* +======================== +Android_Vibrate +======================== +*/ +void Platform_Vibrate( float life, char flags ) +{ + if( life ) + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.vibrate, (int)life ); +} + +/* +======================== +Android_GetNativeObject +======================== +*/ +void *Platform_GetNativeObject( const char *objName ) +{ + static const char *availObjects[] = { "JNIEnv", "ActivityClass", NULL }; + void *object = NULL; + + if( !objName ) + { + object = (void*)availObjects; + } + else if( !strcasecmp( objName, "JNIEnv" ) ) + { + object = (void*)jni.env; + } + else if( !strcasecmp( objName, "ActivityClass" ) ) + { + object = (void*)jni.actcls; + } + + return object; +} + +/* +======================== +Android_MessageBox + +Show messagebox and wait for OK button press +======================== +*/ +#if XASH_MESSAGEBOX == MSGBOX_ANDROID +void Platform_MessageBox( const char *title, const char *text, qboolean parentMainWindow ) +{ + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.messageBox, (*jni.env)->NewStringUTF( jni.env, title ), (*jni.env)->NewStringUTF( jni.env ,text ) ); +} +#endif // XASH_MESSAGEBOX == MSGBOX_ANDROID + +/* +======================== +Android_GetAndroidID +======================== +*/ +const char *Android_GetAndroidID( void ) +{ + static char id[65]; + const char *resultCStr; + jstring resultJNIStr; + + if( id[0] ) + return id; + + resultJNIStr = (jstring)(*jni.env)->CallStaticObjectMethod( jni.env, jni.actcls, jni.getAndroidId ); + resultCStr = (*jni.env)->GetStringUTFChars( jni.env, resultJNIStr, NULL ); + Q_strncpy( id, resultCStr, 64 ); + (*jni.env)->ReleaseStringUTFChars( jni.env, resultJNIStr, resultCStr ); + + if( !id[0] ) + return NULL; + + return id; +} + +/* +======================== +Android_LoadID +======================== +*/ +const char *Android_LoadID( void ) +{ + static char id[65]; + jstring resultJNIStr = (jstring)(*jni.env)->CallStaticObjectMethod( jni.env, jni.actcls, jni.loadID ); + const char *resultCStr = (*jni.env)->GetStringUTFChars( jni.env, resultJNIStr, NULL ); + Q_strncpy( id, resultCStr, 64 ); + (*jni.env)->ReleaseStringUTFChars( jni.env, resultJNIStr, resultCStr ); + return id; +} + +/* +======================== +Android_SaveID +======================== +*/ +void Android_SaveID( const char *id ) +{ + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.saveID, (*jni.env)->NewStringUTF( jni.env, id ) ); +} + +/* +======================== +Android_MouseMove +======================== +*/ +void Platform_MouseMove( float *x, float *y ) +{ + *x = jnimouse.x; + *y = jnimouse.y; + jnimouse.x = 0; + jnimouse.y = 0; + // Con_Reportf( "Android_MouseMove: %f %f\n", *x, *y ); +} + +/* +======================== +Android_AddMove +======================== +*/ +void Android_AddMove( float x, float y ) +{ + jnimouse.x += x; + jnimouse.y += y; +} + +void GAME_EXPORT Platform_GetMousePos( int *x, int *y ) +{ + // stub +} + +void GAME_EXPORT Platform_SetMousePos( int x, int y ) +{ + // stub +} + +int Platform_JoyInit( int numjoy ) +{ + // stub + return 0; +} + +/* +======================== +Android_ShowMouse +======================== +*/ +void Android_ShowMouse( qboolean show ) +{ + if( m_ignore->value ) + show = true; + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.showMouse, show ); +} + +/* +======================== +Android_ShellExecute +======================== +*/ +void Platform_ShellExecute( const char *path, const char *parms ) +{ + jstring jstr; + + if( !path ) + return; // useless + + // get java.lang.String + jstr = (*jni.env)->NewStringUTF( jni.env, path ); + + // open browser + (*jni.env)->CallStaticVoidMethod(jni.env, jni.actcls, jni.shellExecute, jstr); + + // no need to free jstr +} + +int Platform_GetClipboardText( char *buffer, size_t size ) +{ + // stub + if( size ) buffer[0] = 0; + return 0; +} + +void Platform_SetClipboardText( const char *buffer ) +{ + // stub +} + +void Platform_SetCursorType( VGUI_DefaultCursor cursor ) +{ + if( cursor == dc_arrow ) + Android_ShowMouse( true ); + else + Android_ShowMouse( false ); + +} + +key_modifier_t Platform_GetKeyModifiers( void ) +{ + // stub + return KeyModifier_None; +} + +void Platform_PreCreateMove( void ) +{ + // stub +} + +/* +======================== +Android_RunEvents + +Execute all events from queue +======================== +*/ +void Platform_RunEvents( void ) +{ + int i; + + // enter events read + Android_Lock(); + pthread_mutex_unlock( &events.framemutex ); + + for( i = 0; i < events.count; i++ ) + { + switch( events.queue[i].type ) + { + case event_touch_down: + case event_touch_up: + case event_touch_move: + IN_TouchEvent( (touchEventType)events.queue[i].type, events.queue[i].arg, + events.queue[i].touch.x, events.queue[i].touch.y, + events.queue[i].touch.dx, events.queue[i].touch.dy ); + break; + + case event_key_down: + Key_Event( events.queue[i].arg, true ); + + if( events.queue[i].arg == K_AUX31 || events.queue[i].arg == K_AUX29 ) + { + host.force_draw_version = true; + host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; + } + break; + case event_key_up: + Key_Event( events.queue[i].arg, false ); + + if( events.queue[i].arg == K_AUX31 || events.queue[i].arg == K_AUX29 ) + { + host.force_draw_version = true; + host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; + } + break; + + case event_set_pause: + // destroy EGL surface when hiding application + if( !events.queue[i].arg ) + { + SNDDMA_Activate( true ); +// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); + Android_UpdateSurface( true ); + host.status = HOST_FRAME; + SetBits( gl_vsync->flags, FCVAR_CHANGED ); // set swap interval + host.force_draw_version = true; + host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; + } + + if( events.queue[i].arg ) + { + SNDDMA_Activate( false ); + Android_UpdateSurface( false ); + host.status = HOST_NOFOCUS; +// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); + } + break; + + case event_resize: + // reinitialize EGL and change engine screen size + if( host.status == HOST_FRAME &&( refState.width != jni.width || refState.height != jni.height ) ) + { +// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); +// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); + Android_UpdateSurface( true ); + SetBits( gl_vsync->flags, FCVAR_CHANGED ); // set swap interval + VID_SetMode(); + } + break; + case event_joyadd: + Joy_AddEvent(); + break; + case event_joyremove: + Joy_RemoveEvent(); + break; + case event_joyball: + if( !Joy_IsActive() ) + Joy_AddEvent(); + Joy_BallMotionEvent( events.queue[i].ball.ball, + events.queue[i].ball.xrel, events.queue[i].ball.yrel ); + break; + case event_joyhat: + if( !Joy_IsActive() ) + Joy_AddEvent(); + Joy_HatMotionEvent( events.queue[i].hat.hat, events.queue[i].hat.key ); + break; + case event_joyaxis: + if( !Joy_IsActive() ) + Joy_AddEvent(); + Joy_AxisMotionEvent( events.queue[i].axis.axis, events.queue[i].axis.val ); + break; + case event_joybutton: + if( !Joy_IsActive() ) + Joy_AddEvent(); + Joy_ButtonEvent( events.queue[i].button.button, (byte)events.queue[i].button.down ); + break; + case event_ondestroy: + //host.skip_configs = true; // skip config save, because engine may be killed during config save + Sys_Quit(); + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify ); + break; + case event_onpause: +#ifdef PARANOID_CONFIG_SAVE + switch( host.status ) + { + case HOST_INIT: + case HOST_CRASHED: + case HOST_ERR_FATAL: + Con_Reportf( S_WARN "Abnormal host state during onPause (%d), skipping config save!\n", host.status ); + break; + default: + // restore all latched cheat cvars + Cvar_SetCheatState( true ); + Host_WriteConfig(); + } +#endif + // disable sound during call/screen-off + SNDDMA_Activate( false ); +// host.status = HOST_NOFOCUS; + // stop blocking UI thread + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify ); + + break; + case event_onresume: + // re-enable sound after onPause +// host.status = HOST_FRAME; + SNDDMA_Activate( true ); + host.force_draw_version = true; + host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; + break; + case event_onfocuschange: + host.force_draw_version = true; + host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; + break; + } + } + + events.count = 0; // no more events + + // text input handled separately to allow unicode symbols + for( i = 0; events.inputtext[i]; i++ ) + { + int ch; + + // if engine does not use utf-8, we need to convert it to preferred encoding + if( !Q_stricmp( cl_charset->string, "utf-8" ) ) + ch = (unsigned char)events.inputtext[i]; + else + ch = Con_UtfProcessCharForce( (unsigned char)events.inputtext[i] ); + + if( !ch ) // utf-8 + continue; + + // some keyboards may send enter as text + if( ch == '\n' ) + { + Key_Event( K_ENTER, true ); + Key_Event( K_ENTER, false ); + continue; + } + + // otherwise just push it by char, text render will decode unicode strings + CL_CharEvent( ch ); + } + events.inputtext[0] = 0; // no more text + + jnimouse.x += events.mousex; + events.mousex = 0; + jnimouse.y += events.mousey; + events.mousey = 0; + + //end events read + Android_Unlock(); + pthread_mutex_lock( &events.framemutex ); +} + +#endif // XASH_DEDICATED diff --git a/engine/platform/android/android_priv.h b/engine/platform/android/android_priv.h new file mode 100644 index 0000000000..3af2719735 --- /dev/null +++ b/engine/platform/android/android_priv.h @@ -0,0 +1,46 @@ +#pragma once +#ifndef ANDROID_PRIV_H +#define ANDROID_PRIV_H + +#include +#include +#include + +extern struct jnimethods_s +{ + jclass actcls; + JavaVM *vm; + JNIEnv *env; + jmethodID enableTextInput; + jmethodID vibrate; + jmethodID messageBox; + jmethodID notify; + jmethodID setTitle; + jmethodID setIcon; + jmethodID getAndroidId; + jmethodID saveID; + jmethodID loadID; + jmethodID showMouse; + jmethodID shellExecute; + jmethodID swapBuffers; + jmethodID toggleEGL; + jmethodID createGLContext; + jmethodID getGLAttribute; + jmethodID deleteGLContext; + jmethodID getSelectedPixelFormat; + jmethodID getSurface; + int width, height; +} jni; + + +extern struct jnimouse_s +{ + float x, y; +} jnimouse; + +// +// vid_android.c +// +void Android_UpdateSurface( qboolean active ); + +#endif // ANDROID_PRIV_H diff --git a/engine/platform/android/snd_opensles.c b/engine/platform/android/snd_opensles.c new file mode 100644 index 0000000000..4cf5bbe061 --- /dev/null +++ b/engine/platform/android/snd_opensles.c @@ -0,0 +1,277 @@ +/* +Copyright (C) 2015 SiPlus, Chasseur de bots + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "common.h" +#include "platform/platform.h" +#if XASH_SOUND == SOUND_OPENSLES +#include +#include "pthread.h" +#include "sound.h" + +extern dma_t dma; + +static SLObjectItf snddma_android_engine = NULL; +static SLObjectItf snddma_android_outputMix = NULL; +static SLObjectItf snddma_android_player = NULL; +static SLBufferQueueItf snddma_android_bufferQueue; +static SLPlayItf snddma_android_play; + +static pthread_mutex_t snddma_android_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int snddma_android_size; + +static const SLInterfaceID *pSL_IID_ENGINE; +static const SLInterfaceID *pSL_IID_BUFFERQUEUE; +static const SLInterfaceID *pSL_IID_PLAY; +static SLresult SLAPIENTRY (*pslCreateEngine)( + SLObjectItf *pEngine, + SLuint32 numOptions, + const SLEngineOption *pEngineOptions, + SLuint32 numInterfaces, + const SLInterfaceID *pInterfaceIds, + const SLboolean * pInterfaceRequired +); + +void SNDDMA_Activate( qboolean active ) +{ + if( !dma.initialized ) + return; + + if( active ) + { + memset( dma.buffer, 0, snddma_android_size * 2 ); + (*snddma_android_bufferQueue)->Enqueue( snddma_android_bufferQueue, dma.buffer, snddma_android_size ); + (*snddma_android_play)->SetPlayState( snddma_android_play, SL_PLAYSTATE_PLAYING ); + } + else + { + (*snddma_android_play)->SetPlayState( snddma_android_play, SL_PLAYSTATE_STOPPED ); + (*snddma_android_bufferQueue)->Clear( snddma_android_bufferQueue ); + } +} + +static void SNDDMA_Android_Callback( SLBufferQueueItf bq, void *context ) +{ + uint8_t *buffer2; + + pthread_mutex_lock( &snddma_android_mutex ); + + buffer2 = ( uint8_t * )dma.buffer + snddma_android_size; + (*bq)->Enqueue( bq, buffer2, snddma_android_size ); + memcpy( buffer2, dma.buffer, snddma_android_size ); + memset( dma.buffer, 0, snddma_android_size ); + dma.samplepos += dma.samples; + + pthread_mutex_unlock( &snddma_android_mutex ); +} + +static const char *SNDDMA_Android_Init( void ) +{ + SLresult result; + + SLEngineItf engine; + + int freq; + + SLDataLocator_BufferQueue sourceLocator; + SLDataFormat_PCM sourceFormat; + SLDataSource source; + + SLDataLocator_OutputMix sinkLocator; + SLDataSink sink; + + SLInterfaceID interfaceID; + SLboolean interfaceRequired; + + int samples; + void *handle = dlopen( "libOpenSLES.so", RTLD_LAZY ); + + if( !handle ) + return "dlopen for libOpenSLES.so"; + + pslCreateEngine = dlsym( handle, "slCreateEngine" ); + + if( !pslCreateEngine ) + return "resolve slCreateEngine"; + + pSL_IID_ENGINE = dlsym( handle, "SL_IID_ENGINE" ); + + if( !pSL_IID_ENGINE ) + return "resolve SL_IID_ENGINE"; + + pSL_IID_PLAY = dlsym( handle, "SL_IID_PLAY" ); + + if( !pSL_IID_PLAY ) + return "resolve SL_IID_PLAY"; + + pSL_IID_BUFFERQUEUE = dlsym( handle, "SL_IID_BUFFERQUEUE" ); + + if( !pSL_IID_BUFFERQUEUE ) + return "resolve SL_IID_BUFFERQUEUE"; + + + result = pslCreateEngine( &snddma_android_engine, 0, NULL, 0, NULL, NULL ); + if( result != SL_RESULT_SUCCESS ) return "slCreateEngine"; + result = (*snddma_android_engine)->Realize( snddma_android_engine, SL_BOOLEAN_FALSE ); + if( result != SL_RESULT_SUCCESS ) return "engine->Realize"; + result = (*snddma_android_engine)->GetInterface( snddma_android_engine, *pSL_IID_ENGINE, &engine ); + if( result != SL_RESULT_SUCCESS ) return "engine->GetInterface(ENGINE)"; + + result = (*engine)->CreateOutputMix( engine, &snddma_android_outputMix, 0, NULL, NULL ); + if( result != SL_RESULT_SUCCESS ) return "engine->CreateOutputMix"; + result = (*snddma_android_outputMix)->Realize( snddma_android_outputMix, SL_BOOLEAN_FALSE ); + if( result != SL_RESULT_SUCCESS ) return "outputMix->Realize"; + + freq = SOUND_DMA_SPEED; + sourceLocator.locatorType = SL_DATALOCATOR_BUFFERQUEUE; + sourceLocator.numBuffers = 2; + sourceFormat.formatType = SL_DATAFORMAT_PCM; + sourceFormat.numChannels = 2; // always stereo, because engine supports only stereo + sourceFormat.samplesPerSec = freq * 1000; + sourceFormat.bitsPerSample = 16; // always 16 bit audio + sourceFormat.containerSize = sourceFormat.bitsPerSample; + sourceFormat.channelMask = SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT; + sourceFormat.endianness = SL_BYTEORDER_LITTLEENDIAN; + source.pLocator = &sourceLocator; + source.pFormat = &sourceFormat; + + sinkLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; + sinkLocator.outputMix = snddma_android_outputMix; + sink.pLocator = &sinkLocator; + sink.pFormat = NULL; + + interfaceID = *pSL_IID_BUFFERQUEUE; + interfaceRequired = SL_BOOLEAN_TRUE; + + result = (*engine)->CreateAudioPlayer( engine, &snddma_android_player, &source, &sink, 1, &interfaceID, &interfaceRequired ); + if( result != SL_RESULT_SUCCESS ) return "engine->CreateAudioPlayer"; + result = (*snddma_android_player)->Realize( snddma_android_player, SL_BOOLEAN_FALSE ); + if( result != SL_RESULT_SUCCESS ) return "player->Realize"; + result = (*snddma_android_player)->GetInterface( snddma_android_player, *pSL_IID_BUFFERQUEUE, &snddma_android_bufferQueue ); + if( result != SL_RESULT_SUCCESS ) return "player->GetInterface(BUFFERQUEUE)"; + result = (*snddma_android_player)->GetInterface( snddma_android_player, *pSL_IID_PLAY, &snddma_android_play ); + if( result != SL_RESULT_SUCCESS ) return "player->GetInterface(PLAY)"; + result = (*snddma_android_bufferQueue)->RegisterCallback( snddma_android_bufferQueue, SNDDMA_Android_Callback, NULL ); + if( result != SL_RESULT_SUCCESS ) return "bufferQueue->RegisterCallback"; + + samples = s_samplecount.value; + if( !samples ) + samples = 4096; + + dma.format.channels = sourceFormat.numChannels; + dma.samples = samples * sourceFormat.numChannels; + dma.format.speed = freq; + snddma_android_size = dma.samples * ( sourceFormat.bitsPerSample >> 3 ); + dma.buffer = Z_Malloc( snddma_android_size * 2 ); + dma.samplepos = 0; + // dma.sampleframes = dma.samples / dma.format.channels; + dma.format.width = 2; + if( !dma.buffer ) return "malloc"; + + //snddma_android_mutex = trap_Mutex_Create(); + + dma.initialized = true; + + SNDDMA_Activate( true ); + + return NULL; +} + +qboolean SNDDMA_Init( void ) +{ + const char *initError; + + Msg( "OpenSL ES audio device initializing...\n" ); + + initError = SNDDMA_Android_Init(); + if( initError ) + { + Msg( S_ERROR "SNDDMA_Init: %s failed.\n", initError ); + SNDDMA_Shutdown(); + return false; + } + + Msg( "OpenSL ES audio initialized.\n" ); + dma.backendName = "OpenSL ES"; + return true; +} + +void SNDDMA_Shutdown( void ) +{ + Msg( "Closing OpenSL ES audio device...\n" ); + + if( snddma_android_player ) + { + (*snddma_android_player)->Destroy( snddma_android_player ); + snddma_android_player = NULL; + } + if( snddma_android_outputMix ) + { + (*snddma_android_outputMix)->Destroy( snddma_android_outputMix ); + snddma_android_outputMix = NULL; + } + if( snddma_android_engine ) + { + (*snddma_android_engine)->Destroy( snddma_android_engine ); + snddma_android_engine = NULL; + } + + if( dma.buffer ) + { + Z_Free( dma.buffer ); + dma.buffer = NULL; + } + + //if( snddma_android_mutex ) + //trap_Mutex_Destroy( &snddma_android_mutex ); + + Msg( "OpenSL ES audio device shut down.\n" ); +} + +void SNDDMA_Submit( void ) +{ + pthread_mutex_unlock( &snddma_android_mutex ); +} + +void SNDDMA_BeginPainting( void ) +{ + pthread_mutex_lock( &snddma_android_mutex ); +} + +qboolean VoiceCapture_Init( void ) +{ + return false; +} + +qboolean VoiceCapture_Activate( qboolean activate ) +{ + return false; +} + +qboolean VoiceCapture_Lock( qboolean lock ) +{ + return false; +} + +void VoiceCapture_Shutdown( void ) +{ + +} +#endif diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c new file mode 100644 index 0000000000..8519e0242e --- /dev/null +++ b/engine/platform/android/vid_android.c @@ -0,0 +1,667 @@ +#include "platform/platform.h" +#in !XASH_SDL +#include "input.h" +#include "client.h" +#include "filesystem.h" +#include "platform/android/android_priv.h" +#include "vid_common.h" +#include +#include +#include +#include + +static struct vid_android_s +{ + int gl_attribs[REF_GL_ATTRIBUTES_COUNT]; + qboolean gl_attribs_set[REF_GL_ATTRIBUTES_COUNT]; + EGLint gl_api; + qboolean gles1; + void *libgles1, *libgles2; + qboolean has_context; + ANativeWindow* window; +} vid_android; + +static struct nw_s +{ + void (*release)(ANativeWindow* window); + int32_t (*getWidth)(ANativeWindow* window); + int32_t (*getHeight)(ANativeWindow* window); + int32_t (*getFormat)(ANativeWindow* window); + int32_t (*setBuffersGeometry)(ANativeWindow* window, int32_t width, int32_t height, int32_t format); + int32_t (*lock)(ANativeWindow* window, ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds); + int32_t (*unlockAndPost)(ANativeWindow* window); + ANativeWindow* (*fromSurface)(JNIEnv* env, jobject surface); +} nw; + +#define NW_FF(x) {"ANativeWindow_"#x, (void*)&nw.x} + + +static dllfunc_t android_funcs[] = +{ + NW_FF(release), + NW_FF(getWidth), + NW_FF(getHeight), + NW_FF(getFormat), + NW_FF(setBuffersGeometry), + NW_FF(lock), + NW_FF(unlockAndPost), + NW_FF(fromSurface), + { NULL, NULL } +}; +#undef NW_FF +dll_info_t android_info = { "libandroid.so", android_funcs, false }; + +static struct egl_s +{ + EGLSurface (*GetCurrentSurface)(EGLint readdraw); + EGLDisplay (*GetCurrentDisplay)(void); + EGLint (*GetError)(void); + EGLBoolean (*SwapBuffers)(EGLDisplay dpy, EGLSurface surface); + EGLBoolean (*SwapInterval)(EGLDisplay dpy, EGLint interval); + void *(*GetProcAddress)(const char *procname); +} egl; +#undef GetProcAddress +#define EGL_FF(x) {"egl"#x, (void*)&egl.x} +static dllfunc_t egl_funcs[] = +{ + EGL_FF(SwapInterval), + EGL_FF(SwapBuffers), + EGL_FF(GetError), + EGL_FF(GetCurrentDisplay), + EGL_FF(GetCurrentSurface), + EGL_FF(GetProcAddress), + { NULL, NULL } +}; +#undef EGL_FF +dll_info_t egl_info = { "libEGL.so", egl_funcs, false }; + +static struct nativeegl_s +{ + qboolean valid; + void *window; + EGLDisplay dpy; + EGLSurface surface; + EGLContext context; + EGLConfig cfg; + EGLint numCfg; + + const char *extensions; +} negl; + +/* +======================== +Android_SwapInterval +======================== +*/ +static void Android_SwapInterval( int interval ) +{ + if( negl.valid ) + egl.SwapInterval( negl.dpy, interval ); +} + +/* +======================== +Android_SetTitle +======================== +*/ +static void Android_SetTitle( const char *title ) +{ + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.setTitle, (*jni.env)->NewStringUTF( jni.env, title ) ); +} + +/* +======================== +Android_SetIcon +======================== +*/ +static void Android_SetIcon( const char *path ) +{ + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.setIcon, (*jni.env)->NewStringUTF( jni.env, path ) ); +} + +/* +======================== +Android_GetScreenRes + +Resolution got from last resize event +======================== +*/ +static void Android_GetScreenRes( int *width, int *height ) +{ + *width=jni.width, *height=jni.height; +} + +/* +======================== +Android_SwapBuffers + +Update screen. Use native EGL if possible +======================== +*/ +void GL_SwapBuffers( void ) +{ + if( negl.valid ) + { + egl.SwapBuffers( negl.dpy, negl.surface ); + } + else + { + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.swapBuffers ); + } +} + +/* +======================== +Android_UpdateSurface + +Check if we may use native EGL without jni calls +======================== +*/ +void Android_UpdateSurface( qboolean active ) +{ + negl.valid = false; + + if( nw.release ) + { + if( vid_android.window && !active ) + { + nw.release( vid_android.window ); + vid_android.window = NULL; + } + + if( active ) + { + jobject surf; + if( vid_android.window ) + nw.release( vid_android.window ); + surf = (*jni.env)->CallStaticObjectMethod(jni.env, jni.actcls, jni.getSurface); + Con_Printf("s %p\n", surf); + vid_android.window = nw.fromSurface(jni.env, surf); + Con_Printf("w %p\n", vid_android.window); + nw.setBuffersGeometry(vid_android.window, 0, 0, WINDOW_FORMAT_RGB_565 ); + (*jni.env)->DeleteLocalRef( jni.env, surf ); + } + return; + } + + if( !vid_android.has_context ) + return; + + if( ( active && host.status == HOST_FRAME ) || !active ) + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); + + if( active ) + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); + + if( !Sys_CheckParm("-nativeegl") || !active ) + return; // enabled by user + + if( !egl.GetCurrentDisplay ) + return; + + negl.dpy = egl.GetCurrentDisplay(); + + if( negl.dpy == EGL_NO_DISPLAY ) + return; + + negl.surface = egl.GetCurrentSurface(EGL_DRAW); + + if( negl.surface == EGL_NO_SURFACE ) + return; + + // now check if swapBuffers does not give error + if( egl.SwapBuffers( negl.dpy, negl.surface ) == EGL_FALSE ) + return; + + // double check + if( egl.GetError() != EGL_SUCCESS ) + return; + + __android_log_print( ANDROID_LOG_VERBOSE, "Xash", "native EGL enabled" ); + + negl.valid = true; +} + +/* +======================== +Android_GetGLAttribute +======================== +*/ +static int Android_GetGLAttribute( int eglAttr ) +{ + int ret = (*jni.env)->CallStaticIntMethod( jni.env, jni.actcls, jni.getGLAttribute, eglAttr ); + // Con_Reportf( "Android_GetGLAttribute( %i ) => %i\n", eglAttr, ret ); + return ret; +} + +int Android_GetSelectedPixelFormat( void ) +{ + return (*jni.env)->CallStaticIntMethod( jni.env, jni.actcls, jni.getSelectedPixelFormat ); +} + +qboolean R_Init_Video( const int type ) +{ + char buf[MAX_VA_STRING]; + qboolean retval; + + switch( Android_GetSelectedPixelFormat() ) + { + case 1: + refState.desktopBitsPixel = 16; + break; + case 2: + refState.desktopBitsPixel = 8; + break; + default: + refState.desktopBitsPixel = 32; + break; + } + + if( FS_FileExists( GI->iconpath, true ) ) + { + Q_snprintf( buf, sizeof( buf ), "%s/%s/%s", COM_CheckStringEmpty( host.rodir ) ? host.rodir : host.rootdir, GI->gamefolder, GI->iconpath ); + Android_SetIcon( buf ); + } + + Android_SetTitle( GI->title ); + + VID_StartupGamma(); + + switch( type ) + { + case REF_SOFTWARE: + glw_state.software = true; + break; + case REF_GL: + glw_state.software = false; + Sys_LoadLibrary( &egl_info ); + + if( !glw_state.safe && Sys_GetParmFromCmdLine( "-safegl", buf ) ) + glw_state.safe = bound( SAFE_NO, Q_atoi( buf ), SAFE_DONTCARE ); + + break; + default: + Host_Error( "Can't initialize unknown context type %d!\n", type ); + break; + } + + if( glw_state.software ) + { + uint arg; +// Con_Reportf( S_ERROR "Native software mode isn't supported on Android yet! :(\n" ); +// return false; + Sys_LoadLibrary( &android_info ); + Android_UpdateSurface( true ); + if( !SW_CreateBuffer( jni.width, jni.height, &arg, &arg, &arg, &arg, &arg ) ) + return false; + } + + while( !(retval = VID_SetMode()) ) + { + glw_state.safe++; + if( glw_state.safe > SAFE_LAST ) + return false; + } + + switch( type ) + { + case REF_GL: + // refdll also can check extensions + ref.dllFuncs.GL_InitExtensions(); + break; + case REF_SOFTWARE: + default: + break; + } + + host.renderinfo_changed = false; + + return true; +} + +void R_Free_Video( void ) +{ + // (*jni.env)->CallStaticBooleanMethod( jni.env, jni.actcls, jni.deleteGLContext ); + + // VID_DestroyWindow (); + + // R_FreeVideoModes(); + Sys_FreeLibrary( &android_info ); + Sys_FreeLibrary( &egl_info ); + vid_android.has_context = false; + ref.dllFuncs.GL_ClearExtensions(); +} + +#define COPY_ATTR_IF_SET( refattr, attr ) \ + if( vid_android.gl_attribs_set[refattr] ) \ + { \ + attribs[i++] = attr; \ + attribs[i++] = vid_android.gl_attribs[refattr]; \ + } + +static size_t VID_GenerateConfig( EGLint *attribs, size_t size ) +{ + size_t i = 0; + + memset( attribs, 0, size * sizeof( EGLint ) ); + vid_android.gles1 = false; + memset( vid_android.gl_attribs, 0, sizeof( vid_android.gl_attribs )); + memset( vid_android.gl_attribs_set, 0, sizeof( vid_android.gl_attribs_set )); + + // refdll can request some attributes + ref.dllFuncs.GL_SetupAttributes( glw_state.safe ); + + COPY_ATTR_IF_SET( REF_GL_RED_SIZE, EGL_RED_SIZE ); + COPY_ATTR_IF_SET( REF_GL_GREEN_SIZE, EGL_GREEN_SIZE ); + COPY_ATTR_IF_SET( REF_GL_BLUE_SIZE, EGL_BLUE_SIZE ); + COPY_ATTR_IF_SET( REF_GL_ALPHA_SIZE, EGL_ALPHA_SIZE ); + COPY_ATTR_IF_SET( REF_GL_DEPTH_SIZE, EGL_DEPTH_SIZE ); + COPY_ATTR_IF_SET( REF_GL_STENCIL_SIZE, EGL_STENCIL_SIZE ); + COPY_ATTR_IF_SET( REF_GL_MULTISAMPLEBUFFERS, EGL_SAMPLE_BUFFERS ); + COPY_ATTR_IF_SET( REF_GL_MULTISAMPLESAMPLES, EGL_SAMPLES ); + + if( vid_android.gl_attribs_set[REF_GL_ACCELERATED_VISUAL] ) + { + attribs[i++] = EGL_CONFIG_CAVEAT; + attribs[i++] = vid_android.gl_attribs[REF_GL_ACCELERATED_VISUAL] ? EGL_NONE : EGL_DONT_CARE; + } + + // BigGL support + attribs[i++] = EGL_RENDERABLE_TYPE; + vid_android.gl_api = EGL_OPENGL_ES_API; + + if( vid_android.gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] && + !( vid_android.gl_attribs[REF_GL_CONTEXT_PROFILE_MASK] & REF_GL_CONTEXT_PROFILE_ES )) + { + attribs[i++] = EGL_OPENGL_BIT; + vid_android.gl_api = EGL_OPENGL_API; + } + else if( vid_android.gl_attribs_set[REF_GL_CONTEXT_MAJOR_VERSION] && + vid_android.gl_attribs[REF_GL_CONTEXT_MAJOR_VERSION] >= 2 ) + { + attribs[i++] = EGL_OPENGL_ES2_BIT; + } + else + { + i--; // erase EGL_RENDERABLE_TYPE + vid_android.gles1 = true; + } + + attribs[i++] = EGL_NONE; + + return i; +} + +static size_t VID_GenerateContextConfig( EGLint *attribs, size_t size ) +{ + size_t i = 0; + + memset( attribs, 0, size * sizeof( EGLint )); + + /*if( Q_strcmp( negl.extensions, " EGL_KHR_create_context ") ) + { + if( vid_android.gl_attribs_set[REF_GL_CONTEXT_FLAGS] ) + { + attribs[i++] = 0x30FC; // EGL_CONTEXT_FLAGS_KHR + attribs[i++] = vid_android.gl_attribs[REF_GL_CONTEXT_FLAGS] & ((REF_GL_CONTEXT_ROBUST_ACCESS_FLAG << 1) - 1); + } + + if( vid_android.gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] ) + { + int val = vid_android.gl_attribs[REF_GL_CONTEXT_PROFILE_MASK]; + + if( val & ( (REF_GL_CONTEXT_PROFILE_COMPATIBILITY << 1) - 1 ) ) + { + attribs[i++] = 0x30FD; // EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; + attribs[i++] = val; + } + } + + COPY_ATTR_IF_SET( REF_GL_CONTEXT_MAJOR_VERSION, EGL_CONTEXT_CLIENT_VERSION ); + COPY_ATTR_IF_SET( REF_GL_CONTEXT_MINOR_VERSION, 0x30FB ); + } + else*/ + { + // without extension we can set only major version + COPY_ATTR_IF_SET( REF_GL_CONTEXT_MAJOR_VERSION, EGL_CONTEXT_CLIENT_VERSION ); + } + + attribs[i++] = EGL_NONE; + + return i; +} + +qboolean VID_SetMode( void ) +{ + EGLint format; + jintArray attribs, contextAttribs; + static EGLint nAttribs[32+1], nContextAttribs[32+1]; + const size_t attribsSize = ARRAYSIZE( nAttribs ); + size_t s1, s2; + + if( vid_android.has_context ) + { + R_ChangeDisplaySettings( 0, 0, false ); // width and height are ignored anyway + return true; + } + + s1 = VID_GenerateConfig(nAttribs, attribsSize); + s2 = VID_GenerateContextConfig(nContextAttribs, attribsSize); + + attribs = (*jni.env)->NewIntArray( jni.env, s1 ); + contextAttribs = (*jni.env)->NewIntArray( jni.env, s2 ); + + (*jni.env)->SetIntArrayRegion( jni.env, attribs, 0, s1, nAttribs ); + (*jni.env)->SetIntArrayRegion( jni.env, contextAttribs, 0, s2, nContextAttribs ); + + R_ChangeDisplaySettings( 0, 0, false ); // width and height are ignored anyway + + if( glw_state.software ) + return true; + + if( (*jni.env)->CallStaticBooleanMethod( jni.env, jni.actcls, jni.createGLContext, attribs, contextAttribs ) ) + { + vid_android.has_context = true; + return true; + } + + return false; +} + +rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ) +{ + int render_w, render_h; + uint rotate = vid_rotate->value; + + Android_GetScreenRes(&width, &height); + + render_w = width; + render_h = height; + + Con_Reportf( "R_ChangeDisplaySettings: forced resolution to %dx%d)\n", width, height); + + if( ref.dllFuncs.R_SetDisplayTransform( rotate, 0, 0, vid_scale->value, vid_scale->value ) ) + { + if( rotate & 1 ) + { + int swap = render_w; + + render_w = render_h; + render_h = swap; + } + + render_h /= vid_scale->value; + render_w /= vid_scale->value; + } + else + { + Con_Printf( S_WARN "failed to setup screen transform\n" ); + } + + R_SaveVideoMode( width, height, render_w, render_h ); + + refState.wideScreen = true; // V_AdjustFov will check for widescreen + + return rserr_ok; +} + +int GL_SetAttribute( int attr, int val ) +{ + if( attr < 0 || attr >= REF_GL_ATTRIBUTES_COUNT ) + return -1; + + vid_android.gl_attribs[attr] = val; + vid_android.gl_attribs_set[attr] = true; + return 0; +} + +int GL_GetAttribute( int attr, int *val ) +{ + EGLBoolean ret; + + if( attr < 0 || attr >= REF_GL_ATTRIBUTES_COUNT ) + return -1; + + if( !val ) + return -1; + + switch( attr ) + { + case REF_GL_RED_SIZE: + *val = Android_GetGLAttribute( EGL_RED_SIZE ); + return 0; + case REF_GL_GREEN_SIZE: + *val = Android_GetGLAttribute( EGL_GREEN_SIZE ); + return 0; + case REF_GL_BLUE_SIZE: + *val = Android_GetGLAttribute( EGL_BLUE_SIZE ); + return 0; + case REF_GL_ALPHA_SIZE: + *val = Android_GetGLAttribute( EGL_ALPHA_SIZE ); + return 0; + case REF_GL_DEPTH_SIZE: + *val = Android_GetGLAttribute( EGL_DEPTH_SIZE ); + return 0; + case REF_GL_STENCIL_SIZE: + *val = Android_GetGLAttribute( EGL_STENCIL_SIZE ); + return 0; + case REF_GL_MULTISAMPLESAMPLES: + *val = Android_GetGLAttribute( EGL_SAMPLES ); + return 0; + } + + return -1; +} + +int R_MaxVideoModes( void ) +{ + return 0; +} + +vidmode_t* R_GetVideoMode( int num ) +{ + return NULL; +} + +void* GL_GetProcAddress( const char *name ) // RenderAPI requirement +{ + void *gles; + void *addr; + + if( vid_android.gles1 ) + { + if( !vid_android.libgles1 ) + vid_android.libgles1 = dlopen("libGLESv1_CM.so", RTLD_NOW); + gles = vid_android.libgles1; + } + else + { + if( !vid_android.libgles2 ) + vid_android.libgles2 = dlopen("libGLESv2.so", RTLD_NOW); + gles = vid_android.libgles2; + } + + if( gles && ( addr = dlsym(gles, name ) ) ) + return addr; + + if( !egl.GetProcAddress ) + return NULL; + + return egl.GetProcAddress( name ); +} + +void GL_UpdateSwapInterval( void ) +{ + // disable VSync while level is loading + if( cls.state < ca_active ) + { + Android_SwapInterval( 0 ); + SetBits( gl_vsync->flags, FCVAR_CHANGED ); + } + else if( FBitSet( gl_vsync->flags, FCVAR_CHANGED )) + { + ClearBits( gl_vsync->flags, FCVAR_CHANGED ); + Android_SwapInterval( gl_vsync->value ); + } +} + +void *SW_LockBuffer( void ) +{ + ANativeWindow_Buffer buffer; + if( !nw.lock || !vid_android.window ) + return NULL; + if( nw.lock( vid_android.window, &buffer, NULL ) ) + return NULL; + if( buffer.width < refState.width || buffer.height < refState.height ) + return NULL; + return buffer.bits; +} + +void SW_UnlockBuffer( void ) +{ + if( nw.unlockAndPost ) + nw.unlockAndPost( vid_android.window ); +} + +qboolean SW_CreateBuffer( int width, int height, uint *stride, uint *bpp, uint *r, uint *g, uint *b ) +{ + ANativeWindow_Buffer buffer; + int lock; + if( !nw.lock || !vid_android.window ) + return false; + nw.unlockAndPost( vid_android.window ); + + if( ( lock = nw.lock( vid_android.window, &buffer, NULL ) ) ) + { + Con_Printf( "SW_CreateBuffer: lock %d\n", lock ); + return false; + } + nw.unlockAndPost( vid_android.window ); + Con_Printf( "SW_CreateBuffer: buffer %d %d %x %d %p\n", buffer.width, buffer.height, buffer.format, buffer.stride, buffer.bits ); + if( width > buffer.width || height > buffer.height ) + { + Con_Printf( "SW_CreateBuffer: buffer too small %d %d\n", width, height ); + // resize event missed? + if( jni.width < buffer.width ) + jni.width = buffer.width; + if( jni.height < buffer.height ) + jni.width = buffer.height; + VID_SetMode(); + Android_UpdateSurface( 1 ); + return false; + } + if( buffer.format != WINDOW_FORMAT_RGB_565 ) + { + Con_Printf( "SW_CreateBuffer: wrong format %d\n", buffer.format ); + return false; + } + Con_Printf( "SW_CreateBuffer: ok\n" ); + *stride = buffer.stride; + + *bpp = 2; + *r = (((1 << 5) - 1) << (5+6)); + *g = (((1 << 6) - 1) << (5)); + *b = (((1 << 5) - 1) << (0)); + return true; +} +#endif \ No newline at end of file From eed13cf9f24e50747b3a3ef86fc9c593654cf54e Mon Sep 17 00:00:00 2001 From: mittorn Date: Tue, 24 Oct 2023 04:59:19 +0300 Subject: [PATCH 02/43] waifulib/xcompile: host clang experiments (ndk ld) --- scripts/waifulib/xcompile.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index 18f190a001..0d57bc61c0 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -214,7 +214,7 @@ def cc(self): if 'CC' in environ: s = environ['CC'] - return '%s --target=%s%d' % (s, self.ndk_triplet(), self.api) + return '%s --target=%s' % (s, self.ndk_triplet()) #%s%d' % (s, self.ndk_triplet(), self.api) return self.gen_toolchain_path() + ('clang' if self.is_clang() else 'gcc') def cxx(self): @@ -225,7 +225,7 @@ def cxx(self): if 'CXX' in environ: s = environ['CXX'] - return '%s --target=%s%d' % (s, self.ndk_triplet(), self.api) + return '%s --target=%s' % (s, self.ndk_triplet()) #%s%d' % (s, self.ndk_triplet(), self.api) return self.gen_toolchain_path() + ('clang++' if self.is_clang() else 'g++') def strip(self): @@ -242,7 +242,8 @@ def strip(self): def system_stl(self): # TODO: proper STL support - return os.path.abspath(os.path.join(self.ndk_home, 'sources', 'cxx-stl', 'system', 'include')) + #return os.path.abspath(os.path.join(self.ndk_home, 'sources', 'cxx-stl', 'system', 'include')) broken with clang-15? TODO: check different ndk versions + return os.path.abspath(os.path.join(self.ndk_home, 'sources', 'cxx-stl', 'stlport', 'stlport')) def libsysroot(self): arch = self.arch @@ -316,15 +317,18 @@ def linkflags(self): linkflags = [] if self.is_host(): linkflags += ['--gcc-toolchain=%s' % self.gen_gcc_toolchain_path()] + linkflags += ['--gcc-install-dir=%s/lib/gcc/%s/4.9/' % (self.gen_gcc_toolchain_path(), self.ndk_triplet())] + linkflags += ['-fuse-ld=%s/bin/aarch64-linux-android-ld.bfd' % self.gen_gcc_toolchain_path()] if self.ndk_rev <= ANDROID_NDK_SYSROOT_FLAG_MAX: linkflags += ['--sysroot=%s' % (self.sysroot())] elif self.is_host(): linkflags += ['--sysroot=%s/sysroot' % (self.gen_gcc_toolchain_path())] - if self.is_clang() or self.is_host(): - linkflags += ['-fuse-ld=lld'] - else: linkflags += ['-no-canonical-prefixes'] + #if self.is_clang() or self.is_host(): + # linkflags += ['-fuse-ld=lld'] + #else: + linkflags += ['-no-canonical-prefixes'] linkflags += ['-Wl,--hash-style=sysv', '-Wl,--no-undefined'] return linkflags From bb9775048b10bd624f75f0b02a89611fdb4512dd Mon Sep 17 00:00:00 2001 From: mittorn Date: Tue, 24 Oct 2023 05:06:33 +0300 Subject: [PATCH 03/43] platform/android_nosdl: fix build (ndk10, host clang15) --- engine/platform/android/android_nosdl.c | 19 ++++++--------- engine/platform/android/vid_android.c | 31 ++++++++++++++----------- engine/wscript | 5 ++++ 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index 6d32b62f3b..de26603204 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -579,12 +579,12 @@ Platform_Init Initialize android-related cvars ======================== */ -void Platform_Init( void ) +void Android_Init( void ) { android_sleep = Cvar_Get( "android_sleep", "1", FCVAR_ARCHIVE, "Enable sleep in background" ); } -void Platform_Shutdown( void ) +void Android_Shutdown( void ) { } @@ -617,7 +617,7 @@ void Platform_Vibrate( float life, char flags ) Android_GetNativeObject ======================== */ -void *Platform_GetNativeObject( const char *objName ) +void *Android_GetNativeObject( const char *objName ) { static const char *availObjects[] = { "JNIEnv", "ActivityClass", NULL }; void *object = NULL; @@ -750,7 +750,7 @@ Android_ShowMouse */ void Android_ShowMouse( qboolean show ) { - if( m_ignore->value ) + if( m_ignore.value ) show = true; (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.showMouse, show ); } @@ -840,7 +840,6 @@ void Platform_RunEvents( void ) if( events.queue[i].arg == K_AUX31 || events.queue[i].arg == K_AUX29 ) { - host.force_draw_version = true; host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; } break; @@ -849,7 +848,6 @@ void Platform_RunEvents( void ) if( events.queue[i].arg == K_AUX31 || events.queue[i].arg == K_AUX29 ) { - host.force_draw_version = true; host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; } break; @@ -862,8 +860,7 @@ void Platform_RunEvents( void ) // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); Android_UpdateSurface( true ); host.status = HOST_FRAME; - SetBits( gl_vsync->flags, FCVAR_CHANGED ); // set swap interval - host.force_draw_version = true; + SetBits( gl_vsync.flags, FCVAR_CHANGED ); // set swap interval host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; } @@ -883,7 +880,7 @@ void Platform_RunEvents( void ) // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); Android_UpdateSurface( true ); - SetBits( gl_vsync->flags, FCVAR_CHANGED ); // set swap interval + SetBits( gl_vsync.flags, FCVAR_CHANGED ); // set swap interval VID_SetMode(); } break; @@ -945,11 +942,9 @@ void Platform_RunEvents( void ) // re-enable sound after onPause // host.status = HOST_FRAME; SNDDMA_Activate( true ); - host.force_draw_version = true; host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; break; case event_onfocuschange: - host.force_draw_version = true; host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; break; } @@ -963,7 +958,7 @@ void Platform_RunEvents( void ) int ch; // if engine does not use utf-8, we need to convert it to preferred encoding - if( !Q_stricmp( cl_charset->string, "utf-8" ) ) + if( !Q_stricmp( cl_charset.string, "utf-8" ) ) ch = (unsigned char)events.inputtext[i]; else ch = Con_UtfProcessCharForce( (unsigned char)events.inputtext[i] ); diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index 8519e0242e..6bc907e86e 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -1,5 +1,5 @@ #include "platform/platform.h" -#in !XASH_SDL +#if !XASH_SDL #include "input.h" #include "client.h" #include "filesystem.h" @@ -75,6 +75,7 @@ static dllfunc_t egl_funcs[] = #undef EGL_FF dll_info_t egl_info = { "libEGL.so", egl_funcs, false }; + static struct nativeegl_s { qboolean valid; @@ -88,6 +89,8 @@ static struct nativeegl_s const char *extensions; } negl; +convar_t *cv_vid_scale; +convar_t *cv_vid_rotate; /* ======================== Android_SwapInterval @@ -243,6 +246,8 @@ qboolean R_Init_Video( const int type ) { char buf[MAX_VA_STRING]; qboolean retval; + cv_vid_scale = Cvar_FindVar( "vid_scale" ); + cv_vid_rotate = Cvar_FindVar( "vid_rotate" ); switch( Android_GetSelectedPixelFormat() ) { @@ -441,7 +446,7 @@ qboolean VID_SetMode( void ) if( vid_android.has_context ) { - R_ChangeDisplaySettings( 0, 0, false ); // width and height are ignored anyway + R_ChangeDisplaySettings( 0, 0, WINDOW_MODE_WINDOWED ); // width and height are ignored anyway return true; } @@ -454,7 +459,7 @@ qboolean VID_SetMode( void ) (*jni.env)->SetIntArrayRegion( jni.env, attribs, 0, s1, nAttribs ); (*jni.env)->SetIntArrayRegion( jni.env, contextAttribs, 0, s2, nContextAttribs ); - R_ChangeDisplaySettings( 0, 0, false ); // width and height are ignored anyway + R_ChangeDisplaySettings( 0, 0, WINDOW_MODE_WINDOWED ); // width and height are ignored anyway if( glw_state.software ) return true; @@ -468,10 +473,10 @@ qboolean VID_SetMode( void ) return false; } -rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ) +rserr_t R_ChangeDisplaySettings( int width, int height, window_mode_t window_mode ) { int render_w, render_h; - uint rotate = vid_rotate->value; + uint rotate = cv_vid_rotate->value; Android_GetScreenRes(&width, &height); @@ -480,7 +485,7 @@ rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ) Con_Reportf( "R_ChangeDisplaySettings: forced resolution to %dx%d)\n", width, height); - if( ref.dllFuncs.R_SetDisplayTransform( rotate, 0, 0, vid_scale->value, vid_scale->value ) ) + if( ref.dllFuncs.R_SetDisplayTransform( rotate, 0, 0, cv_vid_scale->value, cv_vid_scale->value ) ) { if( rotate & 1 ) { @@ -490,15 +495,15 @@ rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ) render_h = swap; } - render_h /= vid_scale->value; - render_w /= vid_scale->value; + render_h /= cv_vid_scale->value; + render_w /= cv_vid_scale->value; } else { Con_Printf( S_WARN "failed to setup screen transform\n" ); } - R_SaveVideoMode( width, height, render_w, render_h ); + R_SaveVideoMode( width, height, render_w, render_h, true ); refState.wideScreen = true; // V_AdjustFov will check for widescreen @@ -596,12 +601,12 @@ void GL_UpdateSwapInterval( void ) if( cls.state < ca_active ) { Android_SwapInterval( 0 ); - SetBits( gl_vsync->flags, FCVAR_CHANGED ); + SetBits( gl_vsync.flags, FCVAR_CHANGED ); } - else if( FBitSet( gl_vsync->flags, FCVAR_CHANGED )) + else if( FBitSet( gl_vsync.flags, FCVAR_CHANGED )) { - ClearBits( gl_vsync->flags, FCVAR_CHANGED ); - Android_SwapInterval( gl_vsync->value ); + ClearBits( gl_vsync.flags, FCVAR_CHANGED ); + Android_SwapInterval( gl_vsync.value ); } } diff --git a/engine/wscript b/engine/wscript index c9fcb9dd72..9a7983927d 100644 --- a/engine/wscript +++ b/engine/wscript @@ -32,6 +32,9 @@ def options(opt): grp.add_option('--enable-engine-fuzz', action = 'store_true', dest = 'ENGINE_FUZZ', default = False, help = 'add LLVM libFuzzer [default: %default]' ) + grp.add_option('--enable-android-legacy', action = 'store_true', dest = 'ANDROID_LEGACY', default = False, + help = 'allow build legacy android port without SDL (deprecated, need for renderers debug on ancient devices)') + opt.load('sdl2') def configure(conf): @@ -75,6 +78,8 @@ def configure(conf): conf.define('XASH_SDL', 12) conf.check_cfg(package='sdl', args='--cflags --libs', uselib_store='SDL2' ) conf.env.HAVE_SDL2 = True + elif conf.options.ANDROID_LEGACY: + pass #just test if it can build else: conf.load('sdl2') if not conf.env.HAVE_SDL2: From 30c69d3ac8573933df6e19005e0c7c21e52586f0 Mon Sep 17 00:00:00 2001 From: mittorn Date: Tue, 24 Oct 2023 07:44:05 +0300 Subject: [PATCH 04/43] wscript: add external projects support (updated waf required) --- wscript | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/wscript b/wscript index 4237c466be..9b6e46af89 100644 --- a/wscript +++ b/wscript @@ -2,7 +2,7 @@ # encoding: utf-8 # a1batross, mittorn, 2018 -from waflib import Build, Context, Logs +from waflib import Build, Context, Logs, Options, Configure from waflib.Tools import waf_unit_test import sys import os @@ -104,6 +104,20 @@ REFDLLS = [ RefDll('null', False), ] +def process_extra_projects(ctx): + projs = '' + if isinstance(ctx,Options.OptionsContext): + options, commands, envvars = ctx.parse_cmd_args() + projs = options.EXTRA_PROJECTS + elif isinstance(ctx,Configure.ConfigurationContext): + projs = ctx.options.EXTRA_PROJECTS + ctx.env.EXTRA_PROJECTS = projs + else: + projs = ctx.env.EXTRA_PROJECTS + for proj in projs.split(','): + ctx.add_subproject(proj) + + def options(opt): opt.load('reconfigure compiler_optimizations xshlib xcompile compiler_cxx compiler_c sdl2 clang_compilation_database strip_on_install waf_unit_test msdev msvs msvc subproject cmake') @@ -154,12 +168,15 @@ def options(opt): grp.add_option('--enable-fuzzer', action = 'store_true', dest = 'ENABLE_FUZZER', default = False, help = 'enable building libFuzzer runner [default: %default]' ) + grp.add_option('--extra-projects', action = 'store', dest = 'EXTRA_PROJECTS', default = '', type = 'string', + help = 'add extra projects' ) for i in SUBDIRS: if not i.is_exists(opt): continue opt.add_subproject(i.name) + process_extra_projects(opt) def configure(conf): conf.load('fwgslib reconfigure compiler_optimizations') @@ -493,6 +510,7 @@ int main(void) { return !opus_custom_encoder_init((OpusCustomEncoder *)1, (const continue conf.add_subproject(i.name) + process_extra_projects(conf) def build(bld): # guard rails to not let install to root @@ -515,3 +533,4 @@ def build(bld): if bld.env.TESTS: bld.add_post_fun(waf_unit_test.summary) bld.add_post_fun(waf_unit_test.set_exit_code) + process_extra_projects(bld) From 34aab39f9b00f9cb1f8d349727a01db01b8b43e0 Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 25 Oct 2023 22:20:32 +0300 Subject: [PATCH 05/43] waifulib/xcompile: fix host clang16 --- scripts/waifulib/xcompile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index 0d57bc61c0..ef6dd402de 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -319,6 +319,8 @@ def linkflags(self): linkflags += ['--gcc-toolchain=%s' % self.gen_gcc_toolchain_path()] linkflags += ['--gcc-install-dir=%s/lib/gcc/%s/4.9/' % (self.gen_gcc_toolchain_path(), self.ndk_triplet())] linkflags += ['-fuse-ld=%s/bin/aarch64-linux-android-ld.bfd' % self.gen_gcc_toolchain_path()] + linkflags += ['--unwindlib=none'] + linkflags += ['--rtlib=libgcc'] if self.ndk_rev <= ANDROID_NDK_SYSROOT_FLAG_MAX: linkflags += ['--sysroot=%s' % (self.sysroot())] From c2e14eb3a334d28b59a25f83fa5ef3789fd10e06 Mon Sep 17 00:00:00 2001 From: mittorn Date: Thu, 26 Oct 2023 04:52:41 +0300 Subject: [PATCH 06/43] platform/android: rewrite egl part to separate not android-specific file --- engine/platform/android/android_nosdl.c | 1 + engine/platform/android/eglutil.c | 421 ++++++++++++++++++++++++ engine/platform/android/eglutil.h | 58 ++++ engine/platform/android/vid_android.c | 265 ++++----------- 4 files changed, 542 insertions(+), 203 deletions(-) create mode 100644 engine/platform/android/eglutil.c create mode 100644 engine/platform/android/eglutil.h diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index de26603204..95f15bdb8d 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -879,6 +879,7 @@ void Platform_RunEvents( void ) { // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); + Android_UpdateSurface( false ); Android_UpdateSurface( true ); SetBits( gl_vsync.flags, FCVAR_CHANGED ); // set swap interval VID_SetMode(); diff --git a/engine/platform/android/eglutil.c b/engine/platform/android/eglutil.c new file mode 100644 index 0000000000..c14886ab87 --- /dev/null +++ b/engine/platform/android/eglutil.c @@ -0,0 +1,421 @@ +/* +eglutil.c - EGL context utility +Copyright (C) 2023 mittorn + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "platform/platform.h" +#if !defined(XASH_DEDICATED) && !XASH_SDL +#include "eglutil.h" +#include "client.h" +#include "vid_common.h" +#include +#include + +struct eglapi_s egl; + +#undef GetProcAddress // windows.h? +#define EGL_FF(x) {"egl"#x, (void*)&egl.x} +static dllfunc_t egl_funcs[] = +{ + EGL_FF(SwapInterval), + EGL_FF(SwapBuffers), + EGL_FF(GetError), + EGL_FF(GetCurrentDisplay), + EGL_FF(GetCurrentSurface), + EGL_FF(GetProcAddress), + EGL_FF(GetConfigAttrib), + EGL_FF(GetDisplay), + EGL_FF(Initialize), + EGL_FF(Terminate), + EGL_FF(QueryString), + EGL_FF(ChooseConfig), + EGL_FF(CreateWindowSurface), + EGL_FF(CreateContext), + EGL_FF(DestroyContext), + EGL_FF(MakeCurrent), + EGL_FF(BindAPI), + EGL_FF(DestroySurface), + { NULL, NULL } +}; +#undef EGL_FF +dll_info_t egl_info = { "libEGL.so", egl_funcs, false }; + +struct eglstate_s eglstate; + + + +/* +=================== + +refapi/egl wrappers + +=================== +*/ + +int EGL_SetAttribute( int attr, int val ) +{ + if( attr < 0 || attr >= REF_GL_ATTRIBUTES_COUNT ) + return -1; + + eglstate.gl_attribs[attr] = val; + eglstate.gl_attribs_set[attr] = true; + return 0; +} + +#define COPY_ATTR_IF_SET( refattr, attr ) \ + if( eglstate.gl_attribs_set[refattr] ) \ + { \ + attribs[i++] = attr; \ + attribs[i++] = eglstate.gl_attribs[refattr]; \ + } + +size_t EGL_GenerateConfig( EGLint *attribs, size_t size ) +{ + size_t i = 0; + + memset( attribs, 0, size * sizeof( EGLint ) ); + eglstate.gles1 = false; + memset( eglstate.gl_attribs, 0, sizeof( eglstate.gl_attribs )); + memset( eglstate.gl_attribs_set, 0, sizeof( eglstate.gl_attribs_set )); + + // refdll can request some attributes + ref.dllFuncs.GL_SetupAttributes( glw_state.safe ); + + COPY_ATTR_IF_SET( REF_GL_RED_SIZE, EGL_RED_SIZE ); + COPY_ATTR_IF_SET( REF_GL_GREEN_SIZE, EGL_GREEN_SIZE ); + COPY_ATTR_IF_SET( REF_GL_BLUE_SIZE, EGL_BLUE_SIZE ); + COPY_ATTR_IF_SET( REF_GL_ALPHA_SIZE, EGL_ALPHA_SIZE ); + COPY_ATTR_IF_SET( REF_GL_DEPTH_SIZE, EGL_DEPTH_SIZE ); + COPY_ATTR_IF_SET( REF_GL_STENCIL_SIZE, EGL_STENCIL_SIZE ); + COPY_ATTR_IF_SET( REF_GL_MULTISAMPLEBUFFERS, EGL_SAMPLE_BUFFERS ); + COPY_ATTR_IF_SET( REF_GL_MULTISAMPLESAMPLES, EGL_SAMPLES ); + + if( eglstate.gl_attribs_set[REF_GL_ACCELERATED_VISUAL] ) + { + attribs[i++] = EGL_CONFIG_CAVEAT; + attribs[i++] = eglstate.gl_attribs[REF_GL_ACCELERATED_VISUAL] ? EGL_NONE : EGL_DONT_CARE; + } + + // BigGL support + attribs[i++] = EGL_RENDERABLE_TYPE; + eglstate.gl_api = EGL_OPENGL_ES_API; + + if( eglstate.gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] && + !( eglstate.gl_attribs[REF_GL_CONTEXT_PROFILE_MASK] & REF_GL_CONTEXT_PROFILE_ES )) + { + attribs[i++] = EGL_OPENGL_BIT; + eglstate.gl_api = EGL_OPENGL_API; + } + else if( eglstate.gl_attribs_set[REF_GL_CONTEXT_MAJOR_VERSION] && + eglstate.gl_attribs[REF_GL_CONTEXT_MAJOR_VERSION] >= 2 ) + { + attribs[i++] = EGL_OPENGL_ES2_BIT; + } + else + { + i--; // erase EGL_RENDERABLE_TYPE + eglstate.gles1 = true; + } + + attribs[i++] = EGL_NONE; + + return i; +} + + +size_t EGL_GenerateContextConfig( EGLint *attribs, size_t size ) +{ + size_t i = 0; + + memset( attribs, 0, size * sizeof( EGLint )); + + if( Q_strstr( eglstate.extensions, "EGL_KHR_create_context") ) + { + Con_DPrintf( S_NOTE "EGL_KHR_create_context found, setting additional context flags\n"); + + if( eglstate.gl_attribs_set[REF_GL_CONTEXT_FLAGS] ) + { + attribs[i++] = 0x30FC; // EGL_CONTEXT_FLAGS_KHR + attribs[i++] = eglstate.gl_attribs[REF_GL_CONTEXT_FLAGS] & ((REF_GL_CONTEXT_ROBUST_ACCESS_FLAG << 1) - 1); + } + + if( eglstate.gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] ) + { + int val = eglstate.gl_attribs[REF_GL_CONTEXT_PROFILE_MASK]; + + if( val & ( (REF_GL_CONTEXT_PROFILE_COMPATIBILITY << 1) - 1 ) ) + { + attribs[i++] = 0x30FD; // EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; + attribs[i++] = val; + } + } + + COPY_ATTR_IF_SET( REF_GL_CONTEXT_MAJOR_VERSION, EGL_CONTEXT_CLIENT_VERSION ); + COPY_ATTR_IF_SET( REF_GL_CONTEXT_MINOR_VERSION, 0x30FB ); + } + else + { + // without extension we can set only major version + COPY_ATTR_IF_SET( REF_GL_CONTEXT_MAJOR_VERSION, EGL_CONTEXT_CLIENT_VERSION ); + if( eglstate.gl_attribs_set[REF_GL_CONTEXT_FLAGS] && (eglstate.gl_attribs[REF_GL_CONTEXT_FLAGS] & REF_GL_CONTEXT_DEBUG_FLAG )) + { + attribs[i++] = 0x31B0; // EGL_CONTEXT_OPENGL_DEBUG; + attribs[i++] = EGL_TRUE; + } + } + + + attribs[i++] = EGL_NONE; + + return i; +} + + + +/* +========================= +EGL_GetAttribute + +query +========================= +*/ +int EGL_GetAttribute( int attr, int *val ) +{ + EGLBoolean ret; + + if( attr < 0 || attr >= REF_GL_ATTRIBUTES_COUNT ) + return -1; + + if( !val ) + return -1; + + switch( attr ) + { + case REF_GL_RED_SIZE: + ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_RED_SIZE, val ); + return 0; + case REF_GL_GREEN_SIZE: + ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_GREEN_SIZE, val ); + return 0; + case REF_GL_BLUE_SIZE: + ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_BLUE_SIZE, val ); + return 0; + case REF_GL_ALPHA_SIZE: + ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_ALPHA_SIZE, val ); + return 0; + case REF_GL_DEPTH_SIZE: + ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_DEPTH_SIZE, val ); + return 0; + case REF_GL_STENCIL_SIZE: + ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_STENCIL_SIZE, val ); + return 0; + case REF_GL_MULTISAMPLESAMPLES: + ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_SAMPLES, val ); + return 0; + } + + return -1; +} + + +/* +========================= +EGL_UpdateSurface + +destroy old surface, recreate and make context current +must be called with valid context +========================= +*/ +qboolean EGL_UpdateSurface( void *window ) +{ + egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + + if( eglstate.surface ) + egl.DestroySurface( eglstate.dpy, eglstate.surface ); + + if( !window ) + { + Con_Reportf( S_NOTE "EGL_UpdateSurface: missing native window, detaching context\n" ); + } + + if(( eglstate.surface = egl.CreateWindowSurface( eglstate.dpy, eglstate.cfg, window, NULL )) == EGL_NO_SURFACE ) + { + Con_Reportf( S_ERROR "eglCreateWindowSurface returned error: 0x%x\n", egl.GetError() ); + return false; + } + + if( !egl.MakeCurrent( eglstate.dpy, eglstate.surface, eglstate.surface, eglstate.context )) + { + Con_Reportf( S_ERROR "eglMakeCurrent returned error: 0x%x\n", egl.GetError() ); + return false; + } + + return true; + +} + + +/* +========================= +EGL_CreateContext + +query attributes for ref and create context +========================= +*/ +qboolean EGL_CreateContext( void ) +{ + EGLint attribs[32+1], contextAttribs[32+1]; + const size_t attribsSize = ARRAYSIZE( attribs ); + size_t s1, s2; + + if( !eglstate.dpy && ( eglstate.dpy = egl.GetDisplay( EGL_DEFAULT_DISPLAY )) == EGL_NO_DISPLAY ) + { + Con_Reportf( S_ERROR "eglGetDisplay returned error: 0x%x\n", egl.GetError() ); + return false; + } + + eglstate.extensions = egl.QueryString( eglstate.dpy, EGL_EXTENSIONS ); + + s1 = EGL_GenerateConfig(attribs, attribsSize); + s2 = EGL_GenerateContextConfig(contextAttribs, attribsSize); + + + if( !egl.BindAPI( eglstate.gl_api )) + { + Con_Reportf( S_ERROR "eglBindAPI returned error: 0x%x\n", egl.GetError() ); + return false; + } + + if( !egl.Initialize( eglstate.dpy, NULL, NULL )) + { + Con_Reportf( S_ERROR "eglInitialize returned error: 0x%x\n", egl.GetError() ); + return false; + } + + if( !egl.ChooseConfig( eglstate.dpy, attribs, &eglstate.cfg, 1, &eglstate.numCfg )) + { + Con_Reportf( S_ERROR "eglChooseConfig returned error: 0x%x\n", egl.GetError() ); + return false; + } + + if(( eglstate.context = egl.CreateContext( eglstate.dpy, eglstate.cfg, NULL, contextAttribs )) == EGL_NO_CONTEXT ) + { + Con_Reportf( S_ERROR "eglCreateContext returned error: 0x%x\n", egl.GetError() ); + return false; + } + + eglstate.valid = true; + eglstate.imported = false; + + return true; +} + +/* +=========================== +EGL_ImportContext + +import current egl context to use EGL functions +=========================== +*/ +qboolean EGL_ImportContext( void ) +{ + if( !egl.GetCurrentDisplay ) + return false; + + eglstate.dpy = egl.GetCurrentDisplay(); + + if( eglstate.dpy == EGL_NO_DISPLAY ) + return false; + + eglstate.surface = egl.GetCurrentSurface( EGL_DRAW ); + + if( eglstate.surface == EGL_NO_SURFACE ) + return false; + + // now check if swapBuffers does not give error + if( egl.SwapBuffers( eglstate.dpy, eglstate.surface ) == EGL_FALSE ) + return false; + + // double check + if( egl.GetError() != EGL_SUCCESS ) + return false; + + eglstate.extensions = egl.QueryString( eglstate.dpy, EGL_EXTENSIONS ); + + eglstate.valid = eglstate.imported = true; + return true; +} + +/* +========================= +EGL_Terminate +========================= +*/ +void EGL_Terminate( void ) +{ + egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + + egl.DestroyContext( eglstate.dpy, eglstate.context ); + + egl.DestroySurface( eglstate.dpy, eglstate.surface ); + + egl.Terminate( eglstate.dpy ); + + Sys_FreeLibrary( &egl_info ); +} + +/* +========================= +EGL_GetProcAddress + +eglGetProcAddress/dlsym wrapper +========================= +*/ +void *EGL_GetProcAddress( const char *name ) +{ + void *gles; + void *addr; + + // TODO: cross-platform loading + if( eglstate.gles1 ) + { + if( !eglstate.libgles1 ) + eglstate.libgles1 = dlopen( "libGLESv1_CM.so", RTLD_NOW ); + gles = eglstate.libgles1; + } + else + { + if( !eglstate.libgles2 ) + eglstate.libgles2 = dlopen( "libGLESv2.so", RTLD_NOW ); + gles = eglstate.libgles2; + } + + if( gles && ( addr = dlsym( gles, name ))) + return addr; + + if( !egl.GetProcAddress ) + return NULL; + + return egl.GetProcAddress( name ); +} + +/* +========================= +EGL_LoadLibrary +========================= +*/ +void EGL_LoadLibrary( void ) +{ + Sys_LoadLibrary( &egl_info ); +} +#endif diff --git a/engine/platform/android/eglutil.h b/engine/platform/android/eglutil.h new file mode 100644 index 0000000000..35268d08f0 --- /dev/null +++ b/engine/platform/android/eglutil.h @@ -0,0 +1,58 @@ +#ifndef EGLUTIL_H +#define EGLUTIL_H +#include "platform/platform.h" +#include +#include + +extern struct eglstate_s +{ + qboolean valid, imported; + EGLDisplay dpy; + EGLSurface surface; + EGLContext context; + EGLConfig cfg; + EGLint numCfg; + + const char *extensions; + int gl_attribs[REF_GL_ATTRIBUTES_COUNT]; + qboolean gl_attribs_set[REF_GL_ATTRIBUTES_COUNT]; + EGLint gl_api; + qboolean gles1; + void *libgles1, *libgles2; +} eglstate; + +extern struct eglapi_s +{ + EGLSurface (*GetCurrentSurface)( EGLint readdraw ); + EGLDisplay (*GetCurrentDisplay)( void ); + EGLint (*GetError)( void ); + EGLBoolean (*SwapBuffers)( EGLDisplay dpy, EGLSurface surface ); + EGLBoolean (*SwapInterval)( EGLDisplay dpy, EGLint interval ); + void *(*GetProcAddress)( const char *procname ); + EGLBoolean (*GetConfigAttrib)( EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value ); + EGLDisplay (*GetDisplay)( NativeDisplayType display ); + EGLBoolean (*Initialize)( EGLDisplay dpy, EGLint *major, EGLint *minor ); + EGLBoolean (*Terminate)( EGLDisplay dpy ); + const char *(*QueryString)( EGLDisplay dpy, EGLint name ); + EGLBoolean (*ChooseConfig)( EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config ); + EGLSurface (*CreateWindowSurface)( EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint *attrib_list ); + EGLContext (*CreateContext)( EGLDisplay dpy, EGLConfig config, EGLContext share_list, const EGLint *attrib_list ); + EGLBoolean (*DestroyContext)( EGLDisplay dpy, EGLContext ctx ); + EGLBoolean (*MakeCurrent)( EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx ); + EGLBoolean (*BindAPI)( EGLenum api ); + EGLBoolean (*DestroySurface)( EGLDisplay dpy, EGLSurface surface ); + +} egl; + +void EGL_LoadLibrary( void ); +void * EGL_GetProcAddress( const char *name ); +void EGL_Terminate( void ); +qboolean EGL_ImportContext( void ); +qboolean EGL_CreateContext( void ); +qboolean EGL_UpdateSurface( void *window ); +int EGL_GetAttribute( int attr, int *val ); +size_t EGL_GenerateContextConfig( EGLint *attribs, size_t size ); +size_t EGL_GenerateConfig( EGLint *attribs, size_t size ); +int EGL_SetAttribute( int attr, int val ); + +#endif // EGLUTIL_H diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index 6bc907e86e..553884526f 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -7,18 +7,14 @@ #include "vid_common.h" #include #include -#include -#include +#include "eglutil.h" + static struct vid_android_s { - int gl_attribs[REF_GL_ATTRIBUTES_COUNT]; - qboolean gl_attribs_set[REF_GL_ATTRIBUTES_COUNT]; - EGLint gl_api; - qboolean gles1; - void *libgles1, *libgles2; qboolean has_context; ANativeWindow* window; + qboolean nativeegl; } vid_android; static struct nw_s @@ -51,43 +47,6 @@ static dllfunc_t android_funcs[] = #undef NW_FF dll_info_t android_info = { "libandroid.so", android_funcs, false }; -static struct egl_s -{ - EGLSurface (*GetCurrentSurface)(EGLint readdraw); - EGLDisplay (*GetCurrentDisplay)(void); - EGLint (*GetError)(void); - EGLBoolean (*SwapBuffers)(EGLDisplay dpy, EGLSurface surface); - EGLBoolean (*SwapInterval)(EGLDisplay dpy, EGLint interval); - void *(*GetProcAddress)(const char *procname); -} egl; -#undef GetProcAddress -#define EGL_FF(x) {"egl"#x, (void*)&egl.x} -static dllfunc_t egl_funcs[] = -{ - EGL_FF(SwapInterval), - EGL_FF(SwapBuffers), - EGL_FF(GetError), - EGL_FF(GetCurrentDisplay), - EGL_FF(GetCurrentSurface), - EGL_FF(GetProcAddress), - { NULL, NULL } -}; -#undef EGL_FF -dll_info_t egl_info = { "libEGL.so", egl_funcs, false }; - - -static struct nativeegl_s -{ - qboolean valid; - void *window; - EGLDisplay dpy; - EGLSurface surface; - EGLContext context; - EGLConfig cfg; - EGLint numCfg; - - const char *extensions; -} negl; convar_t *cv_vid_scale; convar_t *cv_vid_rotate; @@ -98,8 +57,8 @@ Android_SwapInterval */ static void Android_SwapInterval( int interval ) { - if( negl.valid ) - egl.SwapInterval( negl.dpy, interval ); + if( vid_android.nativeegl && eglstate.valid ) + egl.SwapInterval( eglstate.dpy, interval ); } /* @@ -143,9 +102,9 @@ Update screen. Use native EGL if possible */ void GL_SwapBuffers( void ) { - if( negl.valid ) + if( vid_android.nativeegl && eglstate.valid ) { - egl.SwapBuffers( negl.dpy, negl.surface ); + egl.SwapBuffers( eglstate.dpy, eglstate.surface ); } else { @@ -162,9 +121,9 @@ Check if we may use native EGL without jni calls */ void Android_UpdateSurface( qboolean active ) { - negl.valid = false; + vid_android.nativeegl = false; - if( nw.release ) + if( glw_state.software || eglstate.valid ) { if( vid_android.window && !active ) { @@ -174,16 +133,28 @@ void Android_UpdateSurface( qboolean active ) if( active ) { + EGLint format = WINDOW_FORMAT_RGB_565; jobject surf; if( vid_android.window ) nw.release( vid_android.window ); surf = (*jni.env)->CallStaticObjectMethod(jni.env, jni.actcls, jni.getSurface); - Con_Printf("s %p\n", surf); + Con_DPrintf("Surface handle %p\n", surf); vid_android.window = nw.fromSurface(jni.env, surf); - Con_Printf("w %p\n", vid_android.window); - nw.setBuffersGeometry(vid_android.window, 0, 0, WINDOW_FORMAT_RGB_565 ); + Con_DPrintf("NativeWindow %p\n", vid_android.window); + + if( eglstate.valid ) + egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_NATIVE_VISUAL_ID, &format ); + + nw.setBuffersGeometry(vid_android.window, 0, 0, format ); + (*jni.env)->DeleteLocalRef( jni.env, surf ); } + + if( Sys_CheckParm( "-egl" )) + { + EGL_UpdateSurface( vid_android.window ); + vid_android.nativeegl = true; + } return; } @@ -199,30 +170,7 @@ void Android_UpdateSurface( qboolean active ) if( !Sys_CheckParm("-nativeegl") || !active ) return; // enabled by user - if( !egl.GetCurrentDisplay ) - return; - - negl.dpy = egl.GetCurrentDisplay(); - - if( negl.dpy == EGL_NO_DISPLAY ) - return; - - negl.surface = egl.GetCurrentSurface(EGL_DRAW); - - if( negl.surface == EGL_NO_SURFACE ) - return; - - // now check if swapBuffers does not give error - if( egl.SwapBuffers( negl.dpy, negl.surface ) == EGL_FALSE ) - return; - - // double check - if( egl.GetError() != EGL_SUCCESS ) - return; - - __android_log_print( ANDROID_LOG_VERBOSE, "Xash", "native EGL enabled" ); - - negl.valid = true; + vid_android.nativeegl = EGL_ImportContext(); } /* @@ -272,6 +220,8 @@ qboolean R_Init_Video( const int type ) VID_StartupGamma(); + Sys_LoadLibrary( &android_info ); + switch( type ) { case REF_SOFTWARE: @@ -279,7 +229,7 @@ qboolean R_Init_Video( const int type ) break; case REF_GL: glw_state.software = false; - Sys_LoadLibrary( &egl_info ); + EGL_LoadLibrary(); if( !glw_state.safe && Sys_GetParmFromCmdLine( "-safegl", buf ) ) glw_state.safe = bound( SAFE_NO, Q_atoi( buf ), SAFE_DONTCARE ); @@ -295,7 +245,6 @@ qboolean R_Init_Video( const int type ) uint arg; // Con_Reportf( S_ERROR "Native software mode isn't supported on Android yet! :(\n" ); // return false; - Sys_LoadLibrary( &android_info ); Android_UpdateSurface( true ); if( !SW_CreateBuffer( jni.width, jni.height, &arg, &arg, &arg, &arg, &arg ) ) return false; @@ -332,110 +281,35 @@ void R_Free_Video( void ) // R_FreeVideoModes(); Sys_FreeLibrary( &android_info ); - Sys_FreeLibrary( &egl_info ); vid_android.has_context = false; ref.dllFuncs.GL_ClearExtensions(); } -#define COPY_ATTR_IF_SET( refattr, attr ) \ - if( vid_android.gl_attribs_set[refattr] ) \ - { \ - attribs[i++] = attr; \ - attribs[i++] = vid_android.gl_attribs[refattr]; \ - } - -static size_t VID_GenerateConfig( EGLint *attribs, size_t size ) +void *Android_GetWindow( void ) { - size_t i = 0; - - memset( attribs, 0, size * sizeof( EGLint ) ); - vid_android.gles1 = false; - memset( vid_android.gl_attribs, 0, sizeof( vid_android.gl_attribs )); - memset( vid_android.gl_attribs_set, 0, sizeof( vid_android.gl_attribs_set )); - - // refdll can request some attributes - ref.dllFuncs.GL_SetupAttributes( glw_state.safe ); - - COPY_ATTR_IF_SET( REF_GL_RED_SIZE, EGL_RED_SIZE ); - COPY_ATTR_IF_SET( REF_GL_GREEN_SIZE, EGL_GREEN_SIZE ); - COPY_ATTR_IF_SET( REF_GL_BLUE_SIZE, EGL_BLUE_SIZE ); - COPY_ATTR_IF_SET( REF_GL_ALPHA_SIZE, EGL_ALPHA_SIZE ); - COPY_ATTR_IF_SET( REF_GL_DEPTH_SIZE, EGL_DEPTH_SIZE ); - COPY_ATTR_IF_SET( REF_GL_STENCIL_SIZE, EGL_STENCIL_SIZE ); - COPY_ATTR_IF_SET( REF_GL_MULTISAMPLEBUFFERS, EGL_SAMPLE_BUFFERS ); - COPY_ATTR_IF_SET( REF_GL_MULTISAMPLESAMPLES, EGL_SAMPLES ); - - if( vid_android.gl_attribs_set[REF_GL_ACCELERATED_VISUAL] ) - { - attribs[i++] = EGL_CONFIG_CAVEAT; - attribs[i++] = vid_android.gl_attribs[REF_GL_ACCELERATED_VISUAL] ? EGL_NONE : EGL_DONT_CARE; - } - - // BigGL support - attribs[i++] = EGL_RENDERABLE_TYPE; - vid_android.gl_api = EGL_OPENGL_ES_API; + EGLint format; - if( vid_android.gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] && - !( vid_android.gl_attribs[REF_GL_CONTEXT_PROFILE_MASK] & REF_GL_CONTEXT_PROFILE_ES )) - { - attribs[i++] = EGL_OPENGL_BIT; - vid_android.gl_api = EGL_OPENGL_API; - } - else if( vid_android.gl_attribs_set[REF_GL_CONTEXT_MAJOR_VERSION] && - vid_android.gl_attribs[REF_GL_CONTEXT_MAJOR_VERSION] >= 2 ) + if( !vid_android.window ) { - attribs[i++] = EGL_OPENGL_ES2_BIT; - } - else - { - i--; // erase EGL_RENDERABLE_TYPE - vid_android.gles1 = true; + return NULL; } - attribs[i++] = EGL_NONE; - - return i; -} - -static size_t VID_GenerateContextConfig( EGLint *attribs, size_t size ) -{ - size_t i = 0; - - memset( attribs, 0, size * sizeof( EGLint )); - - /*if( Q_strcmp( negl.extensions, " EGL_KHR_create_context ") ) + if( !egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_NATIVE_VISUAL_ID, &format) ) { - if( vid_android.gl_attribs_set[REF_GL_CONTEXT_FLAGS] ) - { - attribs[i++] = 0x30FC; // EGL_CONTEXT_FLAGS_KHR - attribs[i++] = vid_android.gl_attribs[REF_GL_CONTEXT_FLAGS] & ((REF_GL_CONTEXT_ROBUST_ACCESS_FLAG << 1) - 1); - } - - if( vid_android.gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] ) - { - int val = vid_android.gl_attribs[REF_GL_CONTEXT_PROFILE_MASK]; - - if( val & ( (REF_GL_CONTEXT_PROFILE_COMPATIBILITY << 1) - 1 ) ) - { - attribs[i++] = 0x30FD; // EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; - attribs[i++] = val; - } - } - - COPY_ATTR_IF_SET( REF_GL_CONTEXT_MAJOR_VERSION, EGL_CONTEXT_CLIENT_VERSION ); - COPY_ATTR_IF_SET( REF_GL_CONTEXT_MINOR_VERSION, 0x30FB ); + Con_Reportf( S_ERROR "eglGetConfigAttrib(VISUAL_ID) returned error: 0x%x\n", egl.GetError() ); + return NULL; } - else*/ + + if( nw.setBuffersGeometry( vid_android.window, 0, 0, format ) ) { - // without extension we can set only major version - COPY_ATTR_IF_SET( REF_GL_CONTEXT_MAJOR_VERSION, EGL_CONTEXT_CLIENT_VERSION ); + Con_Reportf( S_ERROR "ANativeWindow_setBuffersGeometry returned error\n" ); + return NULL; } - attribs[i++] = EGL_NONE; - - return i; + return vid_android.window; } + qboolean VID_SetMode( void ) { EGLint format; @@ -444,14 +318,25 @@ qboolean VID_SetMode( void ) const size_t attribsSize = ARRAYSIZE( nAttribs ); size_t s1, s2; - if( vid_android.has_context ) + // create context on egl side by user request + if( !vid_android.has_context && Sys_CheckParm("-egl") ) + { + vid_android.has_context = vid_android.nativeegl = EGL_CreateContext(); + + if( vid_android.has_context ) + Android_UpdateSurface( true ); + else + return false; + } + + if( vid_android.has_context || glw_state.software ) { R_ChangeDisplaySettings( 0, 0, WINDOW_MODE_WINDOWED ); // width and height are ignored anyway return true; } - s1 = VID_GenerateConfig(nAttribs, attribsSize); - s2 = VID_GenerateContextConfig(nContextAttribs, attribsSize); + s1 = EGL_GenerateConfig(nAttribs, attribsSize); + s2 = EGL_GenerateContextConfig(nContextAttribs, attribsSize); attribs = (*jni.env)->NewIntArray( jni.env, s1 ); contextAttribs = (*jni.env)->NewIntArray( jni.env, s2 ); @@ -461,8 +346,6 @@ qboolean VID_SetMode( void ) R_ChangeDisplaySettings( 0, 0, WINDOW_MODE_WINDOWED ); // width and height are ignored anyway - if( glw_state.software ) - return true; if( (*jni.env)->CallStaticBooleanMethod( jni.env, jni.actcls, jni.createGLContext, attribs, contextAttribs ) ) { @@ -512,18 +395,16 @@ rserr_t R_ChangeDisplaySettings( int width, int height, window_mode_t window_m int GL_SetAttribute( int attr, int val ) { - if( attr < 0 || attr >= REF_GL_ATTRIBUTES_COUNT ) - return -1; - - vid_android.gl_attribs[attr] = val; - vid_android.gl_attribs_set[attr] = true; - return 0; + return EGL_SetAttribute( attr, val ); } int GL_GetAttribute( int attr, int *val ) { EGLBoolean ret; + if( eglstate.valid ) + return EGL_GetAttribute( attr, val ); + if( attr < 0 || attr >= REF_GL_ATTRIBUTES_COUNT ) return -1; @@ -570,29 +451,7 @@ vidmode_t* R_GetVideoMode( int num ) void* GL_GetProcAddress( const char *name ) // RenderAPI requirement { - void *gles; - void *addr; - - if( vid_android.gles1 ) - { - if( !vid_android.libgles1 ) - vid_android.libgles1 = dlopen("libGLESv1_CM.so", RTLD_NOW); - gles = vid_android.libgles1; - } - else - { - if( !vid_android.libgles2 ) - vid_android.libgles2 = dlopen("libGLESv2.so", RTLD_NOW); - gles = vid_android.libgles2; - } - - if( gles && ( addr = dlsym(gles, name ) ) ) - return addr; - - if( !egl.GetProcAddress ) - return NULL; - - return egl.GetProcAddress( name ); + return EGL_GetProcAddress( name ); } void GL_UpdateSwapInterval( void ) @@ -652,7 +511,7 @@ qboolean SW_CreateBuffer( int width, int height, uint *stride, uint *bpp, uint * if( jni.height < buffer.height ) jni.width = buffer.height; VID_SetMode(); - Android_UpdateSurface( 1 ); + Android_UpdateSurface( true ); return false; } if( buffer.format != WINDOW_FORMAT_RGB_565 ) @@ -669,4 +528,4 @@ qboolean SW_CreateBuffer( int width, int height, uint *stride, uint *bpp, uint * *b = (((1 << 5) - 1) << (0)); return true; } -#endif \ No newline at end of file +#endif From ea35863c269a8f0704a663a7b944892b9242deb5 Mon Sep 17 00:00:00 2001 From: mittorn Date: Thu, 26 Oct 2023 20:56:36 +0300 Subject: [PATCH 07/43] platform/android: disable swbuffer resize hack, wrong buffer size really is an error that should be fixed on Activity side, not here --- engine/platform/android/vid_android.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index 553884526f..0ddf6008bc 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -504,14 +504,16 @@ qboolean SW_CreateBuffer( int width, int height, uint *stride, uint *bpp, uint * Con_Printf( "SW_CreateBuffer: buffer %d %d %x %d %p\n", buffer.width, buffer.height, buffer.format, buffer.stride, buffer.bits ); if( width > buffer.width || height > buffer.height ) { - Con_Printf( "SW_CreateBuffer: buffer too small %d %d\n", width, height ); - // resize event missed? + // resize event missed? do not resize now, wait for REAL resize event or when java code will be fixed + Con_Printf( S_ERROR "SW_CreateBuffer: buffer too small, need %dx%d, got %dx%d, java part probably sucks\n", width, height, buffer.width, buffer.height ); +#if 0 if( jni.width < buffer.width ) jni.width = buffer.width; if( jni.height < buffer.height ) jni.width = buffer.height; VID_SetMode(); Android_UpdateSurface( true ); +#endif return false; } if( buffer.format != WINDOW_FORMAT_RGB_565 ) From 7b4fd3bcadebbf2236666ae9a94f718eb47ac0e8 Mon Sep 17 00:00:00 2001 From: mittorn Date: Fri, 27 Oct 2023 00:24:23 +0300 Subject: [PATCH 08/43] platform/android: debug surface enabling/disabling, add notification for event_set_pause, fix wrong host.status, prevent rendering while no surface --- engine/platform/android/android_nosdl.c | 15 ++++++++++----- engine/platform/android/vid_android.c | 25 ++++++++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index 95f15bdb8d..ba43047880 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -854,12 +854,12 @@ void Platform_RunEvents( void ) case event_set_pause: // destroy EGL surface when hiding application + Con_Printf( "pause event %d\n", events.queue[i].arg ); if( !events.queue[i].arg ) { SNDDMA_Activate( true ); // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); Android_UpdateSurface( true ); - host.status = HOST_FRAME; SetBits( gl_vsync.flags, FCVAR_CHANGED ); // set swap interval host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; } @@ -868,22 +868,27 @@ void Platform_RunEvents( void ) { SNDDMA_Activate( false ); Android_UpdateSurface( false ); - host.status = HOST_NOFOCUS; // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); } + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify ); break; case event_resize: // reinitialize EGL and change engine screen size - if( host.status == HOST_FRAME &&( refState.width != jni.width || refState.height != jni.height ) ) + if( ( host.status == HOST_FRAME || host.status == HOST_NOFOCUS ) &&( refState.width != jni.width || refState.height != jni.height )) { // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); + Con_DPrintf("resize event\n"); Android_UpdateSurface( false ); Android_UpdateSurface( true ); SetBits( gl_vsync.flags, FCVAR_CHANGED ); // set swap interval VID_SetMode(); } + else + { + Con_DPrintf("resize skip %d %d %d %d %d", jni.width, jni.height, refState.width, refState.height, host.status ); + } break; case event_joyadd: Joy_AddEvent(); @@ -934,14 +939,14 @@ void Platform_RunEvents( void ) #endif // disable sound during call/screen-off SNDDMA_Activate( false ); -// host.status = HOST_NOFOCUS; + host.status = HOST_SLEEP; // stop blocking UI thread (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify ); break; case event_onresume: // re-enable sound after onPause -// host.status = HOST_FRAME; + host.status = HOST_FRAME; SNDDMA_Activate( true ); host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; break; diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index 0ddf6008bc..374684a7c0 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -122,9 +122,10 @@ Check if we may use native EGL without jni calls void Android_UpdateSurface( qboolean active ) { vid_android.nativeegl = false; - - if( glw_state.software || eglstate.valid ) + Con_Printf("1\n"); + if( glw_state.software || ( eglstate.valid && !eglstate.imported ) ) { + Con_Printf("2\n"); if( vid_android.window && !active ) { nw.release( vid_android.window ); @@ -157,20 +158,33 @@ void Android_UpdateSurface( qboolean active ) } return; } - + Con_Printf("3\n"); if( !vid_android.has_context ) return; - if( ( active && host.status == HOST_FRAME ) || !active ) + //if( ( active && host.status == HOST_FRAME ) || !active ) + if( !active ) + { + Con_Printf("toggleEGL 0\n"); (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); + } + host.status = HOST_SLEEP; if( active ) + { + Con_Printf("toggleEGL 1\n"); (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); + host.status = HOST_FRAME; + } + + // todo: check opengl context here and set HOST_SLEEP if not if( !Sys_CheckParm("-nativeegl") || !active ) return; // enabled by user vid_android.nativeegl = EGL_ImportContext(); + if( vid_android.nativeegl ) + Con_DPrintf( "nativeEGL success\n"); } /* @@ -270,6 +284,7 @@ qboolean R_Init_Video( const int type ) host.renderinfo_changed = false; + host.status = HOST_FRAME; // where it should we done? We have broken host.status on all non-SDL platforms! return true; } @@ -323,7 +338,7 @@ qboolean VID_SetMode( void ) { vid_android.has_context = vid_android.nativeegl = EGL_CreateContext(); - if( vid_android.has_context ) + if( vid_android.has_context ) Android_UpdateSurface( true ); else return false; From 06c6371e69564231b9af59b8a3e743685c66f972 Mon Sep 17 00:00:00 2001 From: mittorn Date: Fri, 27 Oct 2023 00:25:12 +0300 Subject: [PATCH 09/43] platform/eglutil: prevent rendering while no surface --- engine/platform/android/eglutil.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/engine/platform/android/eglutil.c b/engine/platform/android/eglutil.c index c14886ab87..1f276af71e 100644 --- a/engine/platform/android/eglutil.c +++ b/engine/platform/android/eglutil.c @@ -239,6 +239,7 @@ must be called with valid context qboolean EGL_UpdateSurface( void *window ) { egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + host.status = HOST_SLEEP; if( eglstate.surface ) egl.DestroySurface( eglstate.dpy, eglstate.surface ); @@ -246,6 +247,7 @@ qboolean EGL_UpdateSurface( void *window ) if( !window ) { Con_Reportf( S_NOTE "EGL_UpdateSurface: missing native window, detaching context\n" ); + return false; } if(( eglstate.surface = egl.CreateWindowSurface( eglstate.dpy, eglstate.cfg, window, NULL )) == EGL_NO_SURFACE ) @@ -259,6 +261,8 @@ qboolean EGL_UpdateSurface( void *window ) Con_Reportf( S_ERROR "eglMakeCurrent returned error: 0x%x\n", egl.GetError() ); return false; } + Con_DPrintf( S_NOTE "restored current context\n" ); + host.status = HOST_FRAME; return true; From 44b1a8e863a98ee58ec5b25ff9988a4d273df16b Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 28 Oct 2023 23:26:11 +0300 Subject: [PATCH 10/43] platform/android: implement dumb dladdr fallback (only searches in server library) --- engine/platform/android/dlsym-weak.cpp | 77 ++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/engine/platform/android/dlsym-weak.cpp b/engine/platform/android/dlsym-weak.cpp index d585af7d1c..ae1af75b14 100644 --- a/engine/platform/android/dlsym-weak.cpp +++ b/engine/platform/android/dlsym-weak.cpp @@ -29,6 +29,7 @@ #if defined __ANDROID__ && !defined XASH_64BIT #include #include +#include #include "linker.h" static Elf_Sym* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name) { @@ -56,6 +57,33 @@ static Elf_Sym* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name) { return NULL; } +static Elf_Sym* soinfo_elf_lookup_reverse(soinfo* si, size_t addr) { + Elf_Sym* symtab = si->symtab; + + if( si->nbucket == 0 ) + return NULL; + + for(int j = 0; j < si->nbucket; j++) + for (unsigned n = si->bucket[j]; n != 0; n = si->chain[n]) { + Elf_Sym* s = symtab + n; + if (s->st_value != addr) continue; + + /* only concern ourselves with global and weak symbol definitions */ + switch (ELF_ST_BIND(s->st_info)) { + case STB_GLOBAL: + case STB_LOCAL: + case STB_WEAK: + if (s->st_shndx == SHN_UNDEF) { + continue; + } + return s; + } + } + + return NULL; +} + + static unsigned elfhash(const char* _name) { const unsigned char* name = (const unsigned char*) _name; unsigned h = 0, g; @@ -82,6 +110,55 @@ static Elf_Sym* dlsym_handle_lookup(soinfo* si, const char* name) { return soinfo_elf_lookup(si, elfhash(name), name); } +extern "C" void *ANDROID_LoadLibrary( const char *dllname ); +static int dladdr_fallback( const void *addr, Dl_info *info ) +{ + static soinfo *server_info; + Elf_Sym *sym; + + if( !server_info ) + server_info = (soinfo*)ANDROID_LoadLibrary( "server" ); + if( !server_info ) + return 0; + //__android_log_print( ANDROID_LOG_ERROR, "dladdr_fb", "%p %p\n", addr, server_info ); + + sym = soinfo_elf_lookup_reverse( server_info, ((char*)addr) - ((char*)server_info->base )); + //__android_log_print( ANDROID_LOG_ERROR, "dladdr_fb", "sym %p %p\n", addr, sym ); + if( sym ) + { + info->dli_sname = server_info->strtab + sym->st_name; + info->dli_fname = "server"; + info->dli_fbase = (void*)server_info->base; + info->dli_saddr = (void*)addr; + //__android_log_print( ANDROID_LOG_ERROR, "dladdr_fb", "name %p %s\n", addr, info->dli_sname ); + return 1; + } + + return 0; +} + + + +extern "C" int __attribute__((visibility("hidden"))) dladdr( const void *addr, Dl_info *info ) +{ + static int (*pDladdr)( const void *addr, Dl_info *info ); + // __android_log_print( ANDROID_LOG_ERROR, "dladdr", "dladdr %p %p %p\n", addr, pDladdr, &dladdr ); + + if(!pDladdr) + { + void *lib = dlopen( "libdl.so", RTLD_NOW ); + if( lib ) + *(void**)&pDladdr = dlsym( lib, "dladdr" ); + if( (void*)pDladdr == (void*)&dladdr ) + pDladdr = 0; + if( !pDladdr ) + pDladdr = dladdr_fallback; + } + // __android_log_print( ANDROID_LOG_ERROR, "dladdr", "dladdr %p\n", addr ); + return pDladdr( addr, info ); +} + + extern "C" void* dlsym_weak(void* handle, const char* symbol) { soinfo* found = NULL; From d423c437edfacbf4278a8e286f7d7960f4139572 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 28 Oct 2023 23:30:15 +0300 Subject: [PATCH 11/43] waf/xcompile: fix ld path for host clang --- scripts/waifulib/xcompile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index ef6dd402de..8b3eb495cc 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -318,7 +318,7 @@ def linkflags(self): if self.is_host(): linkflags += ['--gcc-toolchain=%s' % self.gen_gcc_toolchain_path()] linkflags += ['--gcc-install-dir=%s/lib/gcc/%s/4.9/' % (self.gen_gcc_toolchain_path(), self.ndk_triplet())] - linkflags += ['-fuse-ld=%s/bin/aarch64-linux-android-ld.bfd' % self.gen_gcc_toolchain_path()] + linkflags += ['-fuse-ld=%s/bin/%s-ld.bfd' % (self.gen_gcc_toolchain_path(), self.ndk_triplet())] linkflags += ['--unwindlib=none'] linkflags += ['--rtlib=libgcc'] From bb55de5963711a244b9565b2783a26a0902215a7 Mon Sep 17 00:00:00 2001 From: mittorn Date: Mon, 30 Oct 2023 03:10:44 +0300 Subject: [PATCH 12/43] platform/android: prevent rendering without context, fix android_sleep using surfaceless mode or dummy surface if possible --- engine/platform/android/android_nosdl.c | 32 +++++++---- engine/platform/android/android_priv.h | 10 +++- engine/platform/android/eglutil.c | 36 +++++++++++-- engine/platform/android/eglutil.h | 3 +- engine/platform/android/vid_android.c | 72 +++++++++++++------------ 5 files changed, 103 insertions(+), 50 deletions(-) diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index ba43047880..c226b45bca 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -261,7 +261,7 @@ DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) jni.getGLAttribute = (*env)->GetStaticMethodID(env, jni.actcls, "getGLAttribute", "(I)I"); jni.deleteGLContext = (*env)->GetStaticMethodID(env, jni.actcls, "deleteGLContext", "()Z"); jni.getSelectedPixelFormat = (*env)->GetStaticMethodID(env, jni.actcls, "getSelectedPixelFormat", "()I"); - jni.getSurface = (*env)->GetStaticMethodID(env, jni.actcls, "getNativeSurface", "()Landroid/view/Surface;"); + jni.getSurface = (*env)->GetStaticMethodID(env, jni.actcls, "getNativeSurface", "(I)Landroid/view/Surface;"); /* Run the application. */ @@ -810,12 +810,12 @@ void Platform_PreCreateMove( void ) /* ======================== -Android_RunEvents +Android_ProcessEvents Execute all events from queue ======================== */ -void Platform_RunEvents( void ) +static void Android_ProcessEvents( void ) { int i; @@ -859,7 +859,7 @@ void Platform_RunEvents( void ) { SNDDMA_Activate( true ); // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); - Android_UpdateSurface( true ); + Android_UpdateSurface( surface_active ); SetBits( gl_vsync.flags, FCVAR_CHANGED ); // set swap interval host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; } @@ -867,7 +867,7 @@ void Platform_RunEvents( void ) if( events.queue[i].arg ) { SNDDMA_Activate( false ); - Android_UpdateSurface( false ); + Android_UpdateSurface( !android_sleep->value ? surface_dummy : surface_pause ); // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); } (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify ); @@ -880,14 +880,14 @@ void Platform_RunEvents( void ) // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); // (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); Con_DPrintf("resize event\n"); - Android_UpdateSurface( false ); - Android_UpdateSurface( true ); + Android_UpdateSurface( surface_pause ); + Android_UpdateSurface( surface_active ); SetBits( gl_vsync.flags, FCVAR_CHANGED ); // set swap interval VID_SetMode(); } else { - Con_DPrintf("resize skip %d %d %d %d %d", jni.width, jni.height, refState.width, refState.height, host.status ); + Con_DPrintf("resize skip %d %d %d %d %d\n", jni.width, jni.height, refState.width, refState.height, host.status ); } break; case event_joyadd: @@ -939,14 +939,14 @@ void Platform_RunEvents( void ) #endif // disable sound during call/screen-off SNDDMA_Activate( false ); - host.status = HOST_SLEEP; + //host.status = HOST_SLEEP; // stop blocking UI thread (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify ); break; case event_onresume: // re-enable sound after onPause - host.status = HOST_FRAME; + //host.status = HOST_FRAME; SNDDMA_Activate( true ); host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; break; @@ -995,4 +995,16 @@ void Platform_RunEvents( void ) pthread_mutex_lock( &events.framemutex ); } +void Platform_RunEvents( void ) +{ + Android_ProcessEvents(); + + // do not allow running frames while android_sleep is 1, but surface/context not restored + while( android_sleep->value && host.status == HOST_SLEEP ) + { + usleep( 20000 ); + Android_ProcessEvents(); + } +} + #endif // XASH_DEDICATED diff --git a/engine/platform/android/android_priv.h b/engine/platform/android/android_priv.h index 3af2719735..e7776e4b3b 100644 --- a/engine/platform/android/android_priv.h +++ b/engine/platform/android/android_priv.h @@ -32,6 +32,14 @@ extern struct jnimethods_s int width, height; } jni; +typedef enum surfacestate_e +{ + surface_pause, + surface_active, + surface_dummy, + +} surfacestate_t; + extern struct jnimouse_s { @@ -41,6 +49,6 @@ extern struct jnimouse_s // // vid_android.c // -void Android_UpdateSurface( qboolean active ); +void Android_UpdateSurface( surfacestate_t state ); #endif // ANDROID_PRIV_H diff --git a/engine/platform/android/eglutil.c b/engine/platform/android/eglutil.c index 1f276af71e..17c4335186 100644 --- a/engine/platform/android/eglutil.c +++ b/engine/platform/android/eglutil.c @@ -236,18 +236,39 @@ destroy old surface, recreate and make context current must be called with valid context ========================= */ -qboolean EGL_UpdateSurface( void *window ) +qboolean EGL_UpdateSurface( void *window, qboolean dummy ) { + if( !eglstate.valid ) + return false; + egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); host.status = HOST_SLEEP; if( eglstate.surface ) + { egl.DestroySurface( eglstate.dpy, eglstate.surface ); + eglstate.surface = EGL_NO_SURFACE; + } if( !window ) { - Con_Reportf( S_NOTE "EGL_UpdateSurface: missing native window, detaching context\n" ); - return false; + + if( dummy && eglstate.support_surfaceless_context ) + { + if( egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglstate.context )) + { + Con_Reportf( S_NOTE "EGL_UpdateSurface: using surfaceless mode\n" ); + return true; + } + else + { + egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + eglstate.support_surfaceless_context = false; + } + Con_Reportf( S_NOTE "EGL_UpdateSurface: missing native window, detaching context\n" ); + } + + return false; // let platform fallback to dummy surface or pause engine } if(( eglstate.surface = egl.CreateWindowSurface( eglstate.dpy, eglstate.cfg, window, NULL )) == EGL_NO_SURFACE ) @@ -261,8 +282,10 @@ qboolean EGL_UpdateSurface( void *window ) Con_Reportf( S_ERROR "eglMakeCurrent returned error: 0x%x\n", egl.GetError() ); return false; } + Con_DPrintf( S_NOTE "restored current context\n" ); - host.status = HOST_FRAME; + if( !dummy) + host.status = HOST_FRAME; return true; @@ -321,6 +344,11 @@ qboolean EGL_CreateContext( void ) eglstate.valid = true; eglstate.imported = false; + // now check if it's safe to use surfaceless context here without surface fallback + if( eglstate.extensions && Q_strstr( eglstate.extensions, "EGL_KHR_surfaceless_context" )) + eglstate.support_surfaceless_context = true; + + return true; } diff --git a/engine/platform/android/eglutil.h b/engine/platform/android/eglutil.h index 35268d08f0..ce30bfa3ea 100644 --- a/engine/platform/android/eglutil.h +++ b/engine/platform/android/eglutil.h @@ -12,6 +12,7 @@ extern struct eglstate_s EGLContext context; EGLConfig cfg; EGLint numCfg; + qboolean support_surfaceless_context; const char *extensions; int gl_attribs[REF_GL_ATTRIBUTES_COUNT]; @@ -49,7 +50,7 @@ void * EGL_GetProcAddress( const char *name ); void EGL_Terminate( void ); qboolean EGL_ImportContext( void ); qboolean EGL_CreateContext( void ); -qboolean EGL_UpdateSurface( void *window ); +qboolean EGL_UpdateSurface( void *window, qboolean dummy ); int EGL_GetAttribute( int attr, int *val ); size_t EGL_GenerateContextConfig( EGLint *attribs, size_t size ); size_t EGL_GenerateConfig( EGLint *attribs, size_t size ); diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index 374684a7c0..21b885624c 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -119,63 +119,63 @@ Android_UpdateSurface Check if we may use native EGL without jni calls ======================== */ -void Android_UpdateSurface( qboolean active ) +void Android_UpdateSurface( surfacestate_t state ) { vid_android.nativeegl = false; - Con_Printf("1\n"); - if( glw_state.software || ( eglstate.valid && !eglstate.imported ) ) + qboolean active = state == surface_active; + + if( glw_state.software || ( eglstate.valid && !eglstate.imported )) { - Con_Printf("2\n"); - if( vid_android.window && !active ) + if( vid_android.window ) { + EGL_UpdateSurface( NULL, false ); nw.release( vid_android.window ); vid_android.window = NULL; } + if( state == surface_dummy && glw_state.software ) + return; + // first, ask EGL for surfaceless mode + if( state == surface_dummy && EGL_UpdateSurface( NULL, true )) + { + vid_android.nativeegl = true; + return; + } - if( active ) + if( state != surface_pause ) { EGLint format = WINDOW_FORMAT_RGB_565; jobject surf; if( vid_android.window ) nw.release( vid_android.window ); - surf = (*jni.env)->CallStaticObjectMethod(jni.env, jni.actcls, jni.getSurface); + surf = (*jni.env)->CallStaticObjectMethod( jni.env, jni.actcls, jni.getSurface, state ); Con_DPrintf("Surface handle %p\n", surf); - vid_android.window = nw.fromSurface(jni.env, surf); - Con_DPrintf("NativeWindow %p\n", vid_android.window); + if( surf ) + { + vid_android.window = nw.fromSurface(jni.env, surf); + Con_DPrintf("NativeWindow %p\n", vid_android.window); - if( eglstate.valid ) - egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_NATIVE_VISUAL_ID, &format ); + if( eglstate.valid ) + egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_NATIVE_VISUAL_ID, &format ); - nw.setBuffersGeometry(vid_android.window, 0, 0, format ); + nw.setBuffersGeometry(vid_android.window, 0, 0, format ); - (*jni.env)->DeleteLocalRef( jni.env, surf ); + (*jni.env)->DeleteLocalRef( jni.env, surf ); + } } - if( Sys_CheckParm( "-egl" )) + if( eglstate.valid && !eglstate.imported ) { - EGL_UpdateSurface( vid_android.window ); + EGL_UpdateSurface( vid_android.window, state == surface_dummy ); vid_android.nativeegl = true; } return; } - Con_Printf("3\n"); + if( !vid_android.has_context ) return; - //if( ( active && host.status == HOST_FRAME ) || !active ) - if( !active ) - { - Con_Printf("toggleEGL 0\n"); - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); - } - host.status = HOST_SLEEP; - - if( active ) - { - Con_Printf("toggleEGL 1\n"); - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); - host.status = HOST_FRAME; - } + (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, (int)state ); + host.status = HOST_FRAME; // active ? HOST_FRAME : HOST_SLEEP; // todo: check opengl context here and set HOST_SLEEP if not @@ -257,9 +257,13 @@ qboolean R_Init_Video( const int type ) if( glw_state.software ) { uint arg; -// Con_Reportf( S_ERROR "Native software mode isn't supported on Android yet! :(\n" ); -// return false; - Android_UpdateSurface( true ); + + if( !nw.release ) + { + Con_Reportf( S_ERROR "Native software mode unavailiable\n" ); + return false; + } + Android_UpdateSurface( surface_active ); if( !SW_CreateBuffer( jni.width, jni.height, &arg, &arg, &arg, &arg, &arg ) ) return false; } @@ -339,7 +343,7 @@ qboolean VID_SetMode( void ) vid_android.has_context = vid_android.nativeegl = EGL_CreateContext(); if( vid_android.has_context ) - Android_UpdateSurface( true ); + Android_UpdateSurface( surface_active ); else return false; } From 46c82e89e1ac6de6e634a94e90e7c6f03af81389 Mon Sep 17 00:00:00 2001 From: mittorn Date: Tue, 31 Oct 2023 05:07:40 +0300 Subject: [PATCH 13/43] waifu: set external waifulibs for extra-projects --- wscript | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/wscript b/wscript index 9b6e46af89..fffab40337 100644 --- a/wscript +++ b/wscript @@ -104,18 +104,35 @@ REFDLLS = [ RefDll('null', False), ] -def process_extra_projects(ctx): +def process_extra_projects_opts(ctx): projs = '' - if isinstance(ctx,Options.OptionsContext): - options, commands, envvars = ctx.parse_cmd_args() - projs = options.EXTRA_PROJECTS - elif isinstance(ctx,Configure.ConfigurationContext): - projs = ctx.options.EXTRA_PROJECTS - ctx.env.EXTRA_PROJECTS = projs - else: - projs = ctx.env.EXTRA_PROJECTS + options, commands, envvars = ctx.parse_cmd_args() + projs = options.EXTRA_PROJECTS + for proj in projs.split(','): + ctx.add_subproject(proj) +def process_extra_projects_conf(ctx): + projs = ctx.options.EXTRA_PROJECTS + ctx.env.EXTRA_PROJECTS = projs + for proj in projs.split(','): + tools_orig = ctx.tools.copy() ctx.add_subproject(proj) + waifulib_path = os.path.join(proj, 'scripts', 'waifulib') + if os.path.isdir(waifulib_path): + for tool in ctx.tools: + if not tool in tools_orig: + if os.path.isfile(os.path.join(waifulib_path, tool['tool'] + '.py')): + tool['tooldir'] = [waifulib_path] + Logs.info('External tooldir set: ' + str(tool)) + + +def process_extra_projects_bld(ctx): + projs = ctx.env.EXTRA_PROJECTS + for proj in projs.split(','): + ctx.add_subproject(proj) + + + def options(opt): @@ -176,7 +193,7 @@ def options(opt): continue opt.add_subproject(i.name) - process_extra_projects(opt) + process_extra_projects_opts(opt) def configure(conf): conf.load('fwgslib reconfigure compiler_optimizations') @@ -510,7 +527,7 @@ int main(void) { return !opus_custom_encoder_init((OpusCustomEncoder *)1, (const continue conf.add_subproject(i.name) - process_extra_projects(conf) + process_extra_projects_conf(conf) def build(bld): # guard rails to not let install to root @@ -533,4 +550,4 @@ def build(bld): if bld.env.TESTS: bld.add_post_fun(waf_unit_test.summary) bld.add_post_fun(waf_unit_test.set_exit_code) - process_extra_projects(bld) + process_extra_projects_bld(bld) From 16eeaef35c2e4c408f6b884a98bf8874616560c8 Mon Sep 17 00:00:00 2001 From: mittorn Date: Tue, 31 Oct 2023 21:09:36 +0300 Subject: [PATCH 14/43] wscript: check if extra-projects was set in configuration --- wscript | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/wscript b/wscript index fffab40337..f978f896e6 100644 --- a/wscript +++ b/wscript @@ -105,13 +105,16 @@ REFDLLS = [ ] def process_extra_projects_opts(ctx): - projs = '' options, commands, envvars = ctx.parse_cmd_args() projs = options.EXTRA_PROJECTS + if not projs: + return for proj in projs.split(','): ctx.add_subproject(proj) def process_extra_projects_conf(ctx): projs = ctx.options.EXTRA_PROJECTS + if not projs: + return ctx.env.EXTRA_PROJECTS = projs for proj in projs.split(','): @@ -128,6 +131,8 @@ def process_extra_projects_conf(ctx): def process_extra_projects_bld(ctx): projs = ctx.env.EXTRA_PROJECTS + if not projs: + return for proj in projs.split(','): ctx.add_subproject(proj) From 313f2a86b06b413bb1a43619e62392200ab6e6aa Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 1 Nov 2023 21:30:18 +0300 Subject: [PATCH 15/43] waifulib/xcompile: add android-from-none for new host-clang versions, support hardfloat abi in host-clang --- scripts/waifulib/xcompile.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index 8b3eb495cc..cfc7628366 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -263,8 +263,15 @@ def sysroot(self): def cflags(self, cxx = False): cflags = [] - - if self.ndk_rev <= ANDROID_NDK_SYSROOT_FLAG_MAX: + android_from_none = False + if self.is_host() and self.is_arm() and self.is_hardfp(): + # clang android target may change with ndk + # override target to none while compiling and + # add some android options manually + android_from_none = True + cflags += ['--target=arm-none-eabi'] + + if self.ndk_rev <= ANDROID_NDK_SYSROOT_FLAG_MAX and not android_from_none: cflags += ['--sysroot=%s' % (self.sysroot())] else: if self.is_host(): @@ -274,8 +281,10 @@ def cflags(self, cxx = False): ] cflags += ['-I%s' % (self.system_stl())] - if not self.is_clang(): - cflags += ['-DANDROID', '-D__ANDROID__'] + if not self.is_clang() or android_from_none: + cflags += ['-DANDROID', '-D__ANDROID__=1'] + if android_from_none: + cflags += [ '-D__linux__=1', '-fPIC'] # TODO: compare with linux target? if cxx and not self.is_clang() and self.toolchain not in ['4.8','4.9']: cflags += ['-fno-sized-deallocation'] @@ -292,7 +301,7 @@ def cflags(self, cxx = False): cflags += ['-mthumb', '-mfpu=neon', '-mcpu=cortex-a9'] if self.is_hardfp(): - cflags += ['-D_NDK_MATH_NO_SOFTFP=1', '-mfloat-abi=hard', '-DLOAD_HARDFP', '-DSOFTFP_LINK'] + cflags += ['-D_NDK_MATH_NO_SOFTFP=1', '-mfloat-abi=hard', '-DLOAD_HARDFP', '-DSOFTFP_LINK', '-DGLES_SOFTFLOAT'] if self.is_host(): # Clang builtin redefine w/ different calling convention bug From 55943ad1ce97d43951bf387431a4ddbb53ac113b Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 1 Nov 2023 23:34:34 +0300 Subject: [PATCH 16/43] waifulib/xcompile: disable stpcpy on host-clang --- scripts/waifulib/xcompile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index cfc7628366..1804cfb8f8 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -289,7 +289,7 @@ def cflags(self, cxx = False): if cxx and not self.is_clang() and self.toolchain not in ['4.8','4.9']: cflags += ['-fno-sized-deallocation'] - if self.is_clang(): + if self.is_clang() or self.is_host(): # stpcpy() isn't available in early Android versions # disable it here so Clang won't use it if self.api < ANDROID_STPCPY_API_MIN: From 1b9af8c51541b53b2e8f351be5a17b53f9df9eb0 Mon Sep 17 00:00:00 2001 From: mittorn Date: Thu, 2 Nov 2023 01:52:52 +0300 Subject: [PATCH 17/43] waifulib/xcompile: add objcopy tool to android configuration, allow switching to ndk binutils with host clang --- scripts/waifulib/xcompile.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index 1804cfb8f8..8f5a9e2880 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -44,6 +44,7 @@ class Android: ndk_rev = 0 is_hardfloat = False clang = False + ndk_binutils = False def __init__(self, ctx, arch, toolchain, api): self.ctx = ctx @@ -229,7 +230,7 @@ def cxx(self): return self.gen_toolchain_path() + ('clang++' if self.is_clang() else 'g++') def strip(self): - if self.is_host(): + if self.is_host() and not self.ndk_binutils: environ = getattr(self.ctx, 'environ', os.environ) if 'STRIP' in environ: @@ -240,6 +241,18 @@ def strip(self): return os.path.join(self.gen_binutils_path(), 'llvm-strip') return os.path.join(self.gen_binutils_path(), 'strip') + def objcopy(self): + if self.is_host() and not self.ndk_binutils: + environ = getattr(self.ctx, 'environ', os.environ) + + if 'OBJCOPY' in environ: + return environ['OBJCOPY'] + return 'llvm-objcopy' + + if self.ndk_rev >= 23: + return os.path.join(self.gen_binutils_path(), 'llvm-objcopy') + return os.path.join(self.gen_binutils_path(), 'objcopy') + def system_stl(self): # TODO: proper STL support #return os.path.abspath(os.path.join(self.ndk_home, 'sources', 'cxx-stl', 'system', 'include')) broken with clang-15? TODO: check different ndk versions @@ -327,7 +340,10 @@ def linkflags(self): if self.is_host(): linkflags += ['--gcc-toolchain=%s' % self.gen_gcc_toolchain_path()] linkflags += ['--gcc-install-dir=%s/lib/gcc/%s/4.9/' % (self.gen_gcc_toolchain_path(), self.ndk_triplet())] - linkflags += ['-fuse-ld=%s/bin/%s-ld.bfd' % (self.gen_gcc_toolchain_path(), self.ndk_triplet())] + if self.ndk_binutils: + linkflags += ['-fuse-ld=%s/bin/%s-ld.bfd' % (self.gen_gcc_toolchain_path(), self.ndk_triplet())] + else: + linkflags += ['-fuse-ld=lld'] linkflags += ['--unwindlib=none'] linkflags += ['--rtlib=libgcc'] @@ -543,6 +559,7 @@ def configure(conf): conf.environ['CC'] = android.cc() conf.environ['CXX'] = android.cxx() conf.environ['STRIP'] = android.strip() + conf.environ['OBJCOPY'] = android.objcopy() conf.env.CFLAGS += android.cflags() conf.env.CXXFLAGS += android.cflags(True) conf.env.LINKFLAGS += android.linkflags() From 88f76c6e1ec44db5792c2ea10f0023c54ac16ac4 Mon Sep 17 00:00:00 2001 From: mittorn Date: Thu, 2 Nov 2023 02:06:02 +0300 Subject: [PATCH 18/43] ref_gl: workaround using fake arb functions on GLES (Mali400 has stub glBindBufferARB somehow\!) --- ref/gl/gl_opengl.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ref/gl/gl_opengl.c b/ref/gl/gl_opengl.c index 7e4a18fa21..e526896865 100644 --- a/ref/gl/gl_opengl.c +++ b/ref/gl/gl_opengl.c @@ -556,13 +556,15 @@ qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char for( func = funcs; func && func->name; func++ ) { // functions are cleared before all the extensions are evaluated +#ifndef XASH_GLES // arb on gles is last (TODO: separate search tables for GLES/CORE) if(( *func->func = (void *)gEngfuncs.GL_GetProcAddress( func->name )) == NULL ) +#endif { string name; char *end; size_t i = 0; #ifdef XASH_GLES - const char *suffixes[] = { "", "EXT", "OES" }; + const char *suffixes[] = { "", "EXT", "OES", "ARB" }; #else const char *suffixes[] = { "", "EXT" }; #endif @@ -576,7 +578,9 @@ qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char else // I need Q_strstrnul { end = name + Q_strlen( name ); +#ifndef XASH_GLES i++; // skip empty suffix +#endif } for( ; i < sizeof( suffixes ) / sizeof( suffixes[0] ); i++ ) @@ -588,6 +592,9 @@ qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char if(( f = gEngfuncs.GL_GetProcAddress( name ))) { // GL_GetProcAddress prints errors about missing functions, so tell user that we found it with different name +#ifdef XASH_GLES + if(i != 0) +#endif gEngfuncs.Con_Printf( S_NOTE "found %s\n", name ); *func->func = f; From 16aa2e919fefa9ce76ac61cdec5a243346c2bf10 Mon Sep 17 00:00:00 2001 From: mittorn Date: Fri, 1 Dec 2023 07:05:11 +0300 Subject: [PATCH 19/43] vid/android: Remove pixelFormat selection code, it only used to set desktop bits per pixel --- engine/platform/android/vid_android.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index 21b885624c..d02e253775 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -199,11 +199,6 @@ static int Android_GetGLAttribute( int eglAttr ) return ret; } -int Android_GetSelectedPixelFormat( void ) -{ - return (*jni.env)->CallStaticIntMethod( jni.env, jni.actcls, jni.getSelectedPixelFormat ); -} - qboolean R_Init_Video( const int type ) { char buf[MAX_VA_STRING]; @@ -211,19 +206,6 @@ qboolean R_Init_Video( const int type ) cv_vid_scale = Cvar_FindVar( "vid_scale" ); cv_vid_rotate = Cvar_FindVar( "vid_rotate" ); - switch( Android_GetSelectedPixelFormat() ) - { - case 1: - refState.desktopBitsPixel = 16; - break; - case 2: - refState.desktopBitsPixel = 8; - break; - default: - refState.desktopBitsPixel = 32; - break; - } - if( FS_FileExists( GI->iconpath, true ) ) { Q_snprintf( buf, sizeof( buf ), "%s/%s/%s", COM_CheckStringEmpty( host.rodir ) ? host.rodir : host.rootdir, GI->gamefolder, GI->iconpath ); From e21d8df1fec8342a07e77c684e722aca01e87f77 Mon Sep 17 00:00:00 2001 From: mittorn Date: Fri, 1 Dec 2023 07:11:26 +0300 Subject: [PATCH 20/43] platform/android-nosdl: Move all bindings to XashBindings class, workaround broken messageBox from crashhandler --- engine/platform/android/android_nosdl.c | 102 +++++++++++++++--------- engine/platform/android/android_priv.h | 2 +- engine/platform/android/vid_android.c | 16 ++-- 3 files changed, 75 insertions(+), 45 deletions(-) diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index c226b45bca..55f3d3d9f8 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -21,6 +21,7 @@ GNU General Public License for more details. #include "errno.h" #include #include +#include #ifndef JNICALL #define JNICALL // a1ba: workaround for my IDE, where Java files are not included @@ -162,6 +163,9 @@ static struct { struct jnimethods_s jni; struct jnimouse_s jnimouse; +static jmp_buf crash_frame, restore_frame; +char crash_text[1024]; + #define Android_Lock() pthread_mutex_lock(&events.mutex); #define Android_Unlock() pthread_mutex_unlock(&events.mutex); #define Android_PushEvent() Android_Unlock() @@ -206,7 +210,7 @@ nativeSetPause */ #define VA_ARGS(...) , ##__VA_ARGS__ // GCC extension #define DECLARE_JNI_INTERFACE( ret, name, ... ) \ - JNIEXPORT ret JNICALL Java_su_xash_engine_XashActivity_##name( JNIEnv *env, jclass clazz VA_ARGS(__VA_ARGS__) ) + JNIEXPORT ret JNICALL Java_su_xash_engine_XashBinding_##name( JNIEnv *env, jclass clazz VA_ARGS(__VA_ARGS__) ) DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) { @@ -242,26 +246,39 @@ DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) /* Init callbacks. */ jni.env = env; - jni.actcls = (*env)->FindClass(env, "su/xash/engine/XashActivity"); - jni.enableTextInput = (*env)->GetStaticMethodID(env, jni.actcls, "showKeyboard", "(I)V"); - jni.vibrate = (*env)->GetStaticMethodID(env, jni.actcls, "vibrate", "(I)V" ); - jni.messageBox = (*env)->GetStaticMethodID(env, jni.actcls, "messageBox", "(Ljava/lang/String;Ljava/lang/String;)V"); - jni.notify = (*env)->GetStaticMethodID(env, jni.actcls, "engineThreadNotify", "()V"); - jni.setTitle = (*env)->GetStaticMethodID(env, jni.actcls, "setTitle", "(Ljava/lang/String;)V"); - jni.setIcon = (*env)->GetStaticMethodID(env, jni.actcls, "setIcon", "(Ljava/lang/String;)V"); - jni.getAndroidId = (*env)->GetStaticMethodID(env, jni.actcls, "getAndroidID", "()Ljava/lang/String;"); - jni.saveID = (*env)->GetStaticMethodID(env, jni.actcls, "saveID", "(Ljava/lang/String;)V"); - jni.loadID = (*env)->GetStaticMethodID(env, jni.actcls, "loadID", "()Ljava/lang/String;"); - jni.showMouse = (*env)->GetStaticMethodID(env, jni.actcls, "showMouse", "(I)V"); - jni.shellExecute = (*env)->GetStaticMethodID(env, jni.actcls, "shellExecute", "(Ljava/lang/String;)V"); - - jni.swapBuffers = (*env)->GetStaticMethodID(env, jni.actcls, "swapBuffers", "()V"); - jni.toggleEGL = (*env)->GetStaticMethodID(env, jni.actcls, "toggleEGL", "(I)V"); - jni.createGLContext = (*env)->GetStaticMethodID(env, jni.actcls, "createGLContext", "([I[I)Z"); - jni.getGLAttribute = (*env)->GetStaticMethodID(env, jni.actcls, "getGLAttribute", "(I)I"); - jni.deleteGLContext = (*env)->GetStaticMethodID(env, jni.actcls, "deleteGLContext", "()Z"); - jni.getSelectedPixelFormat = (*env)->GetStaticMethodID(env, jni.actcls, "getSelectedPixelFormat", "()I"); - jni.getSurface = (*env)->GetStaticMethodID(env, jni.actcls, "getNativeSurface", "(I)Landroid/view/Surface;"); + jni.bindcls = (*env)->FindClass(env, "su/xash/engine/XashBinding"); + jni.enableTextInput = (*env)->GetStaticMethodID(env, jni.bindcls, "showKeyboard", "(I)V"); + jni.vibrate = (*env)->GetStaticMethodID(env, jni.bindcls, "vibrate", "(I)V" ); + jni.messageBox = (*env)->GetStaticMethodID(env, jni.bindcls, "messageBox", "(Ljava/lang/String;Ljava/lang/String;)V"); + jni.notify = (*env)->GetStaticMethodID(env, jni.bindcls, "engineThreadNotify", "()V"); + jni.setTitle = (*env)->GetStaticMethodID(env, jni.bindcls, "setTitle", "(Ljava/lang/String;)V"); + jni.setIcon = (*env)->GetStaticMethodID(env, jni.bindcls, "setIcon", "(Ljava/lang/String;)V"); + jni.getAndroidId = (*env)->GetStaticMethodID(env, jni.bindcls, "getAndroidID", "()Ljava/lang/String;"); + jni.saveID = (*env)->GetStaticMethodID(env, jni.bindcls, "saveID", "(Ljava/lang/String;)V"); + jni.loadID = (*env)->GetStaticMethodID(env, jni.bindcls, "loadID", "()Ljava/lang/String;"); + jni.showMouse = (*env)->GetStaticMethodID(env, jni.bindcls, "showMouse", "(I)V"); + jni.shellExecute = (*env)->GetStaticMethodID(env, jni.bindcls, "shellExecute", "(Ljava/lang/String;)V"); + + jni.swapBuffers = (*env)->GetStaticMethodID(env, jni.bindcls, "swapBuffers", "()V"); + jni.toggleEGL = (*env)->GetStaticMethodID(env, jni.bindcls, "toggleEGL", "(I)V"); + jni.createGLContext = (*env)->GetStaticMethodID(env, jni.bindcls, "createGLContext", "([I[I)Z"); + jni.getGLAttribute = (*env)->GetStaticMethodID(env, jni.bindcls, "getGLAttribute", "(I)I"); + jni.deleteGLContext = (*env)->GetStaticMethodID(env, jni.bindcls, "deleteGLContext", "()Z"); + jni.getSurface = (*env)->GetStaticMethodID(env, jni.bindcls, "getNativeSurface", "(I)Landroid/view/Surface;"); + + // jni fails when called from signal handler callback, so jump here when called messagebox from handler + if( setjmp( crash_frame )) + { + // note: this will destroy stack and shutting down engine with return-from-main + // will be impossible, but Sys_Quit works + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.messageBox, (*jni.env)->NewStringUTF( jni.env, "crash" ), (*jni.env)->NewStringUTF( jni.env , crash_text ) ); + + // java shutdown callback here? + + // UB, but who cares, we already crashed! + longjmp( restore_frame, 1 ); + return 127; // unreach + } /* Run the application. */ @@ -598,7 +615,9 @@ Show virtual keyboard */ void Platform_EnableTextInput( qboolean enable ) { - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.enableTextInput, enable ); + if( host.crashed ) + return; + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.enableTextInput, enable ); } /* @@ -609,7 +628,7 @@ Android_Vibrate void Platform_Vibrate( float life, char flags ) { if( life ) - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.vibrate, (int)life ); + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.vibrate, (int)life ); } /* @@ -632,6 +651,8 @@ void *Android_GetNativeObject( const char *objName ) } else if( !strcasecmp( objName, "ActivityClass" ) ) { + if( !jni.actcls ) + jni.actcls = (*jni.env)->FindClass( jni.env, "su/xash/engine/XashActivity" ); object = (void*)jni.actcls; } @@ -648,7 +669,16 @@ Show messagebox and wait for OK button press #if XASH_MESSAGEBOX == MSGBOX_ANDROID void Platform_MessageBox( const char *title, const char *text, qboolean parentMainWindow ) { - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.messageBox, (*jni.env)->NewStringUTF( jni.env, title ), (*jni.env)->NewStringUTF( jni.env ,text ) ); + // jni calls from signal handler broken since some android version, so move to "safe" frame and pass text + if( host.crashed ) + { + if( setjmp( restore_frame ) ) + return; + Q_strncpy( crash_text, text, 1024 ); + longjmp( crash_frame, 1 ); + return; + } + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.messageBox, (*jni.env)->NewStringUTF( jni.env, title ), (*jni.env)->NewStringUTF( jni.env ,text ) ); } #endif // XASH_MESSAGEBOX == MSGBOX_ANDROID @@ -666,7 +696,7 @@ const char *Android_GetAndroidID( void ) if( id[0] ) return id; - resultJNIStr = (jstring)(*jni.env)->CallStaticObjectMethod( jni.env, jni.actcls, jni.getAndroidId ); + resultJNIStr = (jstring)(*jni.env)->CallStaticObjectMethod( jni.env, jni.bindcls, jni.getAndroidId ); resultCStr = (*jni.env)->GetStringUTFChars( jni.env, resultJNIStr, NULL ); Q_strncpy( id, resultCStr, 64 ); (*jni.env)->ReleaseStringUTFChars( jni.env, resultJNIStr, resultCStr ); @@ -685,7 +715,7 @@ Android_LoadID const char *Android_LoadID( void ) { static char id[65]; - jstring resultJNIStr = (jstring)(*jni.env)->CallStaticObjectMethod( jni.env, jni.actcls, jni.loadID ); + jstring resultJNIStr = (jstring)(*jni.env)->CallStaticObjectMethod( jni.env, jni.bindcls, jni.loadID ); const char *resultCStr = (*jni.env)->GetStringUTFChars( jni.env, resultJNIStr, NULL ); Q_strncpy( id, resultCStr, 64 ); (*jni.env)->ReleaseStringUTFChars( jni.env, resultJNIStr, resultCStr ); @@ -699,7 +729,7 @@ Android_SaveID */ void Android_SaveID( const char *id ) { - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.saveID, (*jni.env)->NewStringUTF( jni.env, id ) ); + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.saveID, (*jni.env)->NewStringUTF( jni.env, id ) ); } /* @@ -752,7 +782,7 @@ void Android_ShowMouse( qboolean show ) { if( m_ignore.value ) show = true; - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.showMouse, show ); + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.showMouse, show ); } /* @@ -771,7 +801,7 @@ void Platform_ShellExecute( const char *path, const char *parms ) jstr = (*jni.env)->NewStringUTF( jni.env, path ); // open browser - (*jni.env)->CallStaticVoidMethod(jni.env, jni.actcls, jni.shellExecute, jstr); + (*jni.env)->CallStaticVoidMethod(jni.env, jni.bindcls, jni.shellExecute, jstr); // no need to free jstr } @@ -858,7 +888,7 @@ static void Android_ProcessEvents( void ) if( !events.queue[i].arg ) { SNDDMA_Activate( true ); -// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); +// (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.toggleEGL, 1 ); Android_UpdateSurface( surface_active ); SetBits( gl_vsync.flags, FCVAR_CHANGED ); // set swap interval host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; @@ -868,17 +898,17 @@ static void Android_ProcessEvents( void ) { SNDDMA_Activate( false ); Android_UpdateSurface( !android_sleep->value ? surface_dummy : surface_pause ); -// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); +// (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.toggleEGL, 0 ); } - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify ); + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.notify ); break; case event_resize: // reinitialize EGL and change engine screen size if( ( host.status == HOST_FRAME || host.status == HOST_NOFOCUS ) &&( refState.width != jni.width || refState.height != jni.height )) { -// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); -// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); +// (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.toggleEGL, 0 ); +// (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.toggleEGL, 1 ); Con_DPrintf("resize event\n"); Android_UpdateSurface( surface_pause ); Android_UpdateSurface( surface_active ); @@ -920,7 +950,7 @@ static void Android_ProcessEvents( void ) case event_ondestroy: //host.skip_configs = true; // skip config save, because engine may be killed during config save Sys_Quit(); - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify ); + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.notify ); break; case event_onpause: #ifdef PARANOID_CONFIG_SAVE @@ -941,7 +971,7 @@ static void Android_ProcessEvents( void ) SNDDMA_Activate( false ); //host.status = HOST_SLEEP; // stop blocking UI thread - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify ); + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.notify ); break; case event_onresume: diff --git a/engine/platform/android/android_priv.h b/engine/platform/android/android_priv.h index e7776e4b3b..990c1f8568 100644 --- a/engine/platform/android/android_priv.h +++ b/engine/platform/android/android_priv.h @@ -9,6 +9,7 @@ extern struct jnimethods_s { jclass actcls; + jclass bindcls; JavaVM *vm; JNIEnv *env; jmethodID enableTextInput; @@ -27,7 +28,6 @@ extern struct jnimethods_s jmethodID createGLContext; jmethodID getGLAttribute; jmethodID deleteGLContext; - jmethodID getSelectedPixelFormat; jmethodID getSurface; int width, height; } jni; diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index d02e253775..75680535b4 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -68,7 +68,7 @@ Android_SetTitle */ static void Android_SetTitle( const char *title ) { - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.setTitle, (*jni.env)->NewStringUTF( jni.env, title ) ); + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.setTitle, (*jni.env)->NewStringUTF( jni.env, title ) ); } /* @@ -78,7 +78,7 @@ Android_SetIcon */ static void Android_SetIcon( const char *path ) { - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.setIcon, (*jni.env)->NewStringUTF( jni.env, path ) ); + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.setIcon, (*jni.env)->NewStringUTF( jni.env, path ) ); } /* @@ -108,7 +108,7 @@ void GL_SwapBuffers( void ) } else { - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.swapBuffers ); + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.swapBuffers ); } } @@ -147,7 +147,7 @@ void Android_UpdateSurface( surfacestate_t state ) jobject surf; if( vid_android.window ) nw.release( vid_android.window ); - surf = (*jni.env)->CallStaticObjectMethod( jni.env, jni.actcls, jni.getSurface, state ); + surf = (*jni.env)->CallStaticObjectMethod( jni.env, jni.bindcls, jni.getSurface, state ); Con_DPrintf("Surface handle %p\n", surf); if( surf ) { @@ -174,7 +174,7 @@ void Android_UpdateSurface( surfacestate_t state ) if( !vid_android.has_context ) return; - (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, (int)state ); + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.toggleEGL, (int)state ); host.status = HOST_FRAME; // active ? HOST_FRAME : HOST_SLEEP; // todo: check opengl context here and set HOST_SLEEP if not @@ -194,7 +194,7 @@ Android_GetGLAttribute */ static int Android_GetGLAttribute( int eglAttr ) { - int ret = (*jni.env)->CallStaticIntMethod( jni.env, jni.actcls, jni.getGLAttribute, eglAttr ); + int ret = (*jni.env)->CallStaticIntMethod( jni.env, jni.bindcls, jni.getGLAttribute, eglAttr ); // Con_Reportf( "Android_GetGLAttribute( %i ) => %i\n", eglAttr, ret ); return ret; } @@ -276,7 +276,7 @@ qboolean R_Init_Video( const int type ) void R_Free_Video( void ) { - // (*jni.env)->CallStaticBooleanMethod( jni.env, jni.actcls, jni.deleteGLContext ); + // (*jni.env)->CallStaticBooleanMethod( jni.env, jni.bindcls, jni.deleteGLContext ); // VID_DestroyWindow (); @@ -348,7 +348,7 @@ qboolean VID_SetMode( void ) R_ChangeDisplaySettings( 0, 0, WINDOW_MODE_WINDOWED ); // width and height are ignored anyway - if( (*jni.env)->CallStaticBooleanMethod( jni.env, jni.actcls, jni.createGLContext, attribs, contextAttribs ) ) + if( (*jni.env)->CallStaticBooleanMethod( jni.env, jni.bindcls, jni.createGLContext, attribs, contextAttribs ) ) { vid_android.has_context = true; return true; From 7841382fef486a1f481e83b0782e3399dc44e24c Mon Sep 17 00:00:00 2001 From: mittorn Date: Fri, 1 Dec 2023 16:12:12 +0300 Subject: [PATCH 21/43] platform/android-nosdl: fix declaration style (werror) --- engine/platform/android/vid_android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index 75680535b4..f619c403cd 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -121,8 +121,8 @@ Check if we may use native EGL without jni calls */ void Android_UpdateSurface( surfacestate_t state ) { - vid_android.nativeegl = false; qboolean active = state == surface_active; + vid_android.nativeegl = false; if( glw_state.software || ( eglstate.valid && !eglstate.imported )) { From 0d18753adb2552d1d8ead58dc3b3ba5e8f0d0efa Mon Sep 17 00:00:00 2001 From: mittorn Date: Fri, 1 Dec 2023 16:14:30 +0300 Subject: [PATCH 22/43] platform/android-nosdl: add Platform_Shutdown for preShutdown callback --- engine/platform/android/android_nosdl.c | 8 +++++--- engine/platform/android/android_priv.h | 1 + engine/platform/platform.h | 2 ++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index 55f3d3d9f8..9306ef017d 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -265,6 +265,7 @@ DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) jni.getGLAttribute = (*env)->GetStaticMethodID(env, jni.bindcls, "getGLAttribute", "(I)I"); jni.deleteGLContext = (*env)->GetStaticMethodID(env, jni.bindcls, "deleteGLContext", "()Z"); jni.getSurface = (*env)->GetStaticMethodID(env, jni.bindcls, "getNativeSurface", "(I)Landroid/view/Surface;"); + jni.preShutdown = (*env)->GetStaticMethodID(env, jni.bindcls, "preShutdown", "()V"); // jni fails when called from signal handler callback, so jump here when called messagebox from handler if( setjmp( crash_frame )) @@ -272,8 +273,7 @@ DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) // note: this will destroy stack and shutting down engine with return-from-main // will be impossible, but Sys_Quit works (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.messageBox, (*jni.env)->NewStringUTF( jni.env, "crash" ), (*jni.env)->NewStringUTF( jni.env , crash_text ) ); - - // java shutdown callback here? + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.preShutdown ); // UB, but who cares, we already crashed! longjmp( restore_frame, 1 ); @@ -603,7 +603,9 @@ void Android_Init( void ) void Android_Shutdown( void ) { - + if( host.crashed ) + return; + (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.preShutdown ); } /* diff --git a/engine/platform/android/android_priv.h b/engine/platform/android/android_priv.h index 990c1f8568..bad06f81ed 100644 --- a/engine/platform/android/android_priv.h +++ b/engine/platform/android/android_priv.h @@ -29,6 +29,7 @@ extern struct jnimethods_s jmethodID getGLAttribute; jmethodID deleteGLContext; jmethodID getSurface; + jmethodID preShutdown; int width, height; } jni; diff --git a/engine/platform/platform.h b/engine/platform/platform.h index d761fc7b99..73797fc9fd 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -139,6 +139,8 @@ static inline void Platform_Shutdown( void ) #if XASH_SDL SDLash_Shutdown( ); +#elif XASH_ANDROID + Android_Shutdown(); #endif } From 0901fd94e2a16a6e09c3636c58e653a9ebb59009 Mon Sep 17 00:00:00 2001 From: mittorn Date: Fri, 1 Dec 2023 20:35:19 +0300 Subject: [PATCH 23/43] platform/android-nosdl: optionally wait gdb in nativeInit, enable dumpable flag early as possible --- engine/platform/android/android_nosdl.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index 9306ef017d..e87fb0e955 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -212,6 +212,12 @@ nativeSetPause #define DECLARE_JNI_INTERFACE( ret, name, ... ) \ JNIEXPORT ret JNICALL Java_su_xash_engine_XashBinding_##name( JNIEnv *env, jclass clazz VA_ARGS(__VA_ARGS__) ) +static int _debugger_present = -1; +static void _sigtrap_handler(int signum) +{ + _debugger_present = 0; +} + DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) { int i; @@ -280,8 +286,18 @@ DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) return 127; // unreach } - /* Run the application. */ + if( getenv( "XASH3D_GDB_WAIT" )) + { + signal(SIGTRAP, _sigtrap_handler); + while(_debugger_present <= 0) + { + _debugger_present = 1; + INLINE_RAISE( SIGTRAP ); + INLINE_NANOSLEEP1(); + } + } + /* Run the application. */ status = Host_Main( argc, argv, getenv("XASH3D_GAMEDIR"), false, NULL ); /* Release the arguments. */ @@ -586,6 +602,7 @@ DECLARE_JNI_INTERFACE( int, nativeTestWritePermission, jstring jPath ) JNIEXPORT jint JNICALL JNI_OnLoad( JavaVM *vm, void *reserved ) { + prctl(PR_SET_DUMPABLE, 1); return JNI_VERSION_1_6; } From 91d9ca7810c7c542bfc170272d54ef5a0749ea14 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 2 Dec 2023 21:40:59 +0300 Subject: [PATCH 24/43] platform/android-nosdl: restore SIGTRAP handler --- engine/platform/android/android_nosdl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index e87fb0e955..5c154fe36d 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -212,10 +212,11 @@ nativeSetPause #define DECLARE_JNI_INTERFACE( ret, name, ... ) \ JNIEXPORT ret JNICALL Java_su_xash_engine_XashBinding_##name( JNIEnv *env, jclass clazz VA_ARGS(__VA_ARGS__) ) -static int _debugger_present = -1; +static volatile int _debugger_present = -1; static void _sigtrap_handler(int signum) { _debugger_present = 0; + __android_log_print( ANDROID_LOG_ERROR, "XashGDBWait", "It's a TRAP!" ); } DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) @@ -295,6 +296,7 @@ DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) INLINE_RAISE( SIGTRAP ); INLINE_NANOSLEEP1(); } + signal(SIGTRAP, SIG_DFL); } /* Run the application. */ From a5f3fce665bcbf2fc931fa826e337274f5ff4ccc Mon Sep 17 00:00:00 2001 From: mittorn Date: Sun, 3 Dec 2023 06:19:14 +0300 Subject: [PATCH 25/43] platform/android-nosdl: add workarounds for yama bullshit --- engine/platform/android/android_nosdl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index 5c154fe36d..3f4d1692ee 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -23,6 +23,11 @@ GNU General Public License for more details. #include #include +#ifndef PR_SET_PTRACER +#define PR_SET_PTRACER 0x59616d61 +#define PR_SET_PTRACER_ANY ((unsigned long)-1) +#endif + #ifndef JNICALL #define JNICALL // a1ba: workaround for my IDE, where Java files are not included #define JNIEXPORT @@ -249,6 +254,7 @@ DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) } argv[argc] = NULL; prctl(PR_SET_DUMPABLE, 1); + prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); /* Init callbacks. */ @@ -605,6 +611,7 @@ DECLARE_JNI_INTERFACE( int, nativeTestWritePermission, jstring jPath ) JNIEXPORT jint JNICALL JNI_OnLoad( JavaVM *vm, void *reserved ) { prctl(PR_SET_DUMPABLE, 1); + prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); return JNI_VERSION_1_6; } From 9484effd4c8f6d393421858a4fd2846c6891d01d Mon Sep 17 00:00:00 2001 From: mittorn Date: Fri, 8 Dec 2023 23:48:35 +0300 Subject: [PATCH 26/43] wscript: fix options in external projects --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index f978f896e6..03ac85b103 100644 --- a/wscript +++ b/wscript @@ -105,7 +105,7 @@ REFDLLS = [ ] def process_extra_projects_opts(ctx): - options, commands, envvars = ctx.parse_cmd_args() + options, commands, envvars = ctx.parse_cmd_args(allow_unknown = True) projs = options.EXTRA_PROJECTS if not projs: return From abeb26742051eb99071ed86d9d11d70d7061ebba Mon Sep 17 00:00:00 2001 From: mittorn Date: Sun, 10 Dec 2023 05:24:45 +0300 Subject: [PATCH 27/43] scripts/waifulib: save zip task to make it output reuseable --- scripts/waifulib/zip.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/waifulib/zip.py b/scripts/waifulib/zip.py index c3d0e9ef20..1e0794cac0 100644 --- a/scripts/waifulib/zip.py +++ b/scripts/waifulib/zip.py @@ -44,16 +44,16 @@ def create_zip_archive(self): self.path.get_bld().mkdir() target = self.path.get_bld().make_node(self.name) - tsk = self.create_task('ziparchive', files, target) + self.zip_task = self.create_task('ziparchive', files, target) - setattr(tsk, 'compresslevel', compresslevel) - setattr(tsk, 'relative_to', relative_to) + setattr(self.zip_task, 'compresslevel', compresslevel) + setattr(self.zip_task, 'relative_to', relative_to) try: inst_to = self.install_path self.install_task = self.add_install_files( install_to=inst_to, install_from=target, - chmod=Utils.O644, task=tsk) + chmod=Utils.O644, task=self.zip_task) except AttributeError: pass From 6fec0398b615ddf555ea75292bca13cacfdacf93 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sun, 10 Dec 2023 05:26:30 +0300 Subject: [PATCH 28/43] wscript: re-enable xash-extras on android --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index 03ac85b103..daae956830 100644 --- a/wscript +++ b/wscript @@ -69,7 +69,7 @@ SUBDIRS = [ Subproject('dllemu'), # disable only by engine feature, makes no sense to even parse subprojects in dedicated mode - Subproject('3rdparty/extras', lambda x: not x.env.DEDICATED and x.env.DEST_OS != 'android'), + Subproject('3rdparty/extras', lambda x: not x.env.DEDICATED), Subproject('3rdparty/nanogl', lambda x: not x.env.DEDICATED and x.env.NANOGL), Subproject('3rdparty/gl-wes-v2', lambda x: not x.env.DEDICATED and x.env.GLWES), Subproject('3rdparty/gl4es', lambda x: not x.env.DEDICATED and x.env.GL4ES), From 9c097976dfc9977d8af72ae87bd61eebf6d0fc1f Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 13 Dec 2023 21:24:49 +0300 Subject: [PATCH 29/43] wscript: only enable SINGLE_BINARY and XASH_SDLMAIN on android when SDL2 enabled --- wscript | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wscript b/wscript index daae956830..a740ab22f3 100644 --- a/wscript +++ b/wscript @@ -240,7 +240,8 @@ def configure(conf): conf.options.GL4ES = True conf.options.GLES3COMPAT = True conf.options.GL = False - conf.define('XASH_SDLMAIN', 1) + if conf.env.HAVE_SDL2: + conf.define('XASH_SDLMAIN', 1) elif conf.env.MAGX: conf.options.SDL12 = True conf.options.NO_VGUI = True From ffe1ff326a7b33f5ff4b8a2b55d07f4b6042db16 Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 13 Dec 2023 22:14:07 +0300 Subject: [PATCH 30/43] waifulib/xcompile: make clang work with original stlport --- scripts/waifulib/xcompile.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index 8f5a9e2880..37905afced 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -302,6 +302,9 @@ def cflags(self, cxx = False): if cxx and not self.is_clang() and self.toolchain not in ['4.8','4.9']: cflags += ['-fno-sized-deallocation'] + if cxx and (self.is_clang() or self.is_host()): + cflags += [ '-Wno-dynamic-exception-spec', '-fno-rtti' ] + if self.is_clang() or self.is_host(): # stpcpy() isn't available in early Android versions # disable it here so Clang won't use it From 96c367973a4bcd326da76aa84e67dc4b4e99b639 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 16 Dec 2023 20:17:51 +0300 Subject: [PATCH 31/43] platform/android/dlsym-weak: fix code style --- engine/platform/android/dlsym-weak.cpp | 96 +++++++++++++++----------- 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/engine/platform/android/dlsym-weak.cpp b/engine/platform/android/dlsym-weak.cpp index ae1af75b14..42d248d2a6 100644 --- a/engine/platform/android/dlsym-weak.cpp +++ b/engine/platform/android/dlsym-weak.cpp @@ -32,24 +32,26 @@ #include #include "linker.h" -static Elf_Sym* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name) { +static Elf_Sym* soinfo_elf_lookup( soinfo* si, unsigned hash, const char* name ) +{ Elf_Sym* symtab = si->symtab; const char* strtab = si->strtab; if( si->nbucket == 0 ) return NULL; - for (unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]) { + for( unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n] ) + { Elf_Sym* s = symtab + n; - if (strcmp(strtab + s->st_name, name)) continue; + if( strcmp( strtab + s->st_name, name )) continue; /* only concern ourselves with global and weak symbol definitions */ - switch (ELF_ST_BIND(s->st_info)) { + switch( ELF_ST_BIND( s->st_info )) + { case STB_GLOBAL: case STB_WEAK: - if (s->st_shndx == SHN_UNDEF) { + if( s->st_shndx == SHN_UNDEF ) continue; - } return s; } } @@ -57,26 +59,30 @@ static Elf_Sym* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name) { return NULL; } -static Elf_Sym* soinfo_elf_lookup_reverse(soinfo* si, size_t addr) { +static Elf_Sym* soinfo_elf_lookup_reverse(soinfo* si, size_t addr) +{ Elf_Sym* symtab = si->symtab; if( si->nbucket == 0 ) return NULL; - for(int j = 0; j < si->nbucket; j++) - for (unsigned n = si->bucket[j]; n != 0; n = si->chain[n]) { - Elf_Sym* s = symtab + n; - if (s->st_value != addr) continue; - - /* only concern ourselves with global and weak symbol definitions */ - switch (ELF_ST_BIND(s->st_info)) { - case STB_GLOBAL: - case STB_LOCAL: - case STB_WEAK: - if (s->st_shndx == SHN_UNDEF) { - continue; + for( int j = 0; j < si->nbucket; j++ ) + { + for( unsigned n = si->bucket[j]; n != 0; n = si->chain[n] ) + { + Elf_Sym* s = symtab + n; + if( s->st_value != addr )continue; + + /* only concern ourselves with global and weak symbol definitions */ + switch( ELF_ST_BIND( s->st_info )) + { + case STB_GLOBAL: + case STB_LOCAL: + case STB_WEAK: + if (s->st_shndx == SHN_UNDEF) + continue; + return s; } - return s; } } @@ -84,11 +90,13 @@ static Elf_Sym* soinfo_elf_lookup_reverse(soinfo* si, size_t addr) { } -static unsigned elfhash(const char* _name) { - const unsigned char* name = (const unsigned char*) _name; +static unsigned elfhash( const char* _name ) +{ + const unsigned char* name = ( const unsigned char* ) _name; unsigned h = 0, g; - while(*name) { + while(*name) + { h = (h << 4) + *name++; g = h & 0xf0000000; h ^= g; @@ -106,8 +114,9 @@ static unsigned elfhash(const char* _name) { Binary Interface) where in Chapter 5 it discuss resolving "Shared Object Dependencies" in breadth first search order. */ -static Elf_Sym* dlsym_handle_lookup(soinfo* si, const char* name) { - return soinfo_elf_lookup(si, elfhash(name), name); +static Elf_Sym* dlsym_handle_lookup( soinfo* si, const char* name ) +{ + return soinfo_elf_lookup( si, elfhash( name ), name ); } extern "C" void *ANDROID_LoadLibrary( const char *dllname ); @@ -117,7 +126,7 @@ static int dladdr_fallback( const void *addr, Dl_info *info ) Elf_Sym *sym; if( !server_info ) - server_info = (soinfo*)ANDROID_LoadLibrary( "server" ); + server_info = ( soinfo* )ANDROID_LoadLibrary( "server" ); if( !server_info ) return 0; //__android_log_print( ANDROID_LOG_ERROR, "dladdr_fb", "%p %p\n", addr, server_info ); @@ -141,35 +150,38 @@ static int dladdr_fallback( const void *addr, Dl_info *info ) extern "C" int __attribute__((visibility("hidden"))) dladdr( const void *addr, Dl_info *info ) { - static int (*pDladdr)( const void *addr, Dl_info *info ); - // __android_log_print( ANDROID_LOG_ERROR, "dladdr", "dladdr %p %p %p\n", addr, pDladdr, &dladdr ); + typedef int (*PFNDLADDR)( const void *addr, Dl_info *info ); + PFNDLADDR pfn_dladdr; - if(!pDladdr) + if( !pfn_dladdr ) { + /* android does not have libdl, but have soinfo record for it from linker + * use dlopen to get this record directly */ void *lib = dlopen( "libdl.so", RTLD_NOW ); + if( lib ) - *(void**)&pDladdr = dlsym( lib, "dladdr" ); - if( (void*)pDladdr == (void*)&dladdr ) - pDladdr = 0; - if( !pDladdr ) - pDladdr = dladdr_fallback; + pfn_dladdr = (PFNDLADDR)dlsym( lib, "dladdr" ); + if( pfn_dladdr == (PFNDLADDR)dladdr ) + pfn_dladdr = 0; + if( !pfn_dladdr ) + pfn_dladdr = (PFNDLADDR)dladdr_fallback; } - // __android_log_print( ANDROID_LOG_ERROR, "dladdr", "dladdr %p\n", addr ); - return pDladdr( addr, info ); + + return pfn_dladdr( addr, info ); } -extern "C" void* dlsym_weak(void* handle, const char* symbol) { +extern "C" void* dlsym_weak( void* handle, const char* symbol ) { soinfo* found = NULL; Elf_Sym* sym = NULL; - found = reinterpret_cast(handle); - sym = dlsym_handle_lookup(found, symbol); + found = reinterpret_cast( handle ); + sym = dlsym_handle_lookup( found, symbol ); - if (sym != NULL) { - return reinterpret_cast(sym->st_value + found->base/*load_bias*/); + if ( sym != NULL ) { + return reinterpret_cast( sym->st_value + found->base /*load_bias*/ ); } - __android_log_print(ANDROID_LOG_ERROR, "dlsym-weak", "Failed when looking up %s\n", symbol); + __android_log_print( ANDROID_LOG_ERROR, "dlsym-weak", "Failed when looking up %s\n", symbol ); return NULL; } #endif From 4953656ee0aa39da70c2840b377d96f00e3b8c16 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 16 Dec 2023 20:23:39 +0300 Subject: [PATCH 32/43] platform/android/dlsym-weak: do not call LoadLibrary to find server library instance --- engine/platform/android/dlsym-weak.cpp | 5 +++-- engine/platform/android/lib_android.c | 6 ++++++ engine/platform/android/lib_android.h | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/engine/platform/android/dlsym-weak.cpp b/engine/platform/android/dlsym-weak.cpp index 42d248d2a6..931ef8b317 100644 --- a/engine/platform/android/dlsym-weak.cpp +++ b/engine/platform/android/dlsym-weak.cpp @@ -119,14 +119,15 @@ static Elf_Sym* dlsym_handle_lookup( soinfo* si, const char* name ) return soinfo_elf_lookup( si, elfhash( name ), name ); } -extern "C" void *ANDROID_LoadLibrary( const char *dllname ); +#include "lib_android.h" + static int dladdr_fallback( const void *addr, Dl_info *info ) { static soinfo *server_info; Elf_Sym *sym; if( !server_info ) - server_info = ( soinfo* )ANDROID_LoadLibrary( "server" ); + server_info = ( soinfo* )ANDROID_GetServerLibrary(); if( !server_info ) return 0; //__android_log_print( ANDROID_LOG_ERROR, "dladdr_fb", "%p %p\n", addr, server_info ); diff --git a/engine/platform/android/lib_android.c b/engine/platform/android/lib_android.c index a8ba10f385..8b9810fad1 100644 --- a/engine/platform/android/lib_android.c +++ b/engine/platform/android/lib_android.c @@ -19,6 +19,12 @@ GNU General Public License for more details. #include "platform/android/lib_android.h" #include "platform/android/dlsym-weak.h" // Android < 5.0 +void *Android_GetServerLibrary( void ) +{ + return svgame.hInstance; +} + + void *ANDROID_LoadLibrary( const char *dllname ) { char path[MAX_SYSPATH]; diff --git a/engine/platform/android/lib_android.h b/engine/platform/android/lib_android.h index 5bead1f332..9e360dd6c8 100644 --- a/engine/platform/android/lib_android.h +++ b/engine/platform/android/lib_android.h @@ -20,6 +20,7 @@ GNU General Public License for more details. #define Platform_POSIX_LoadLibrary( x ) ANDROID_LoadLibrary(( x )) #define Platform_POSIX_GetProcAddress( x, y ) ANDROID_GetProcAddress(( x ), ( y )) +void *Android_GetServerLibrary( void ); void *ANDROID_LoadLibrary( const char *dllname ); void *ANDROID_GetProcAddress( void *hInstance, const char *name ); From 93e5faa26b34bc8de26edce6ff820509a709e56e Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 16 Dec 2023 20:39:47 +0300 Subject: [PATCH 33/43] Remove unused macro and spaces restored by revert --- common/backends.h | 1 - common/defaults.h | 1 - 2 files changed, 2 deletions(-) diff --git a/common/backends.h b/common/backends.h index 77bcced827..38e05f5e0e 100644 --- a/common/backends.h +++ b/common/backends.h @@ -56,5 +56,4 @@ GNU General Public License for more details. #define LIB_WIN32 2 #define LIB_STATIC 3 - #endif /* BACKENDS_H */ diff --git a/common/defaults.h b/common/defaults.h index f870e38966..c3cb22ef9c 100644 --- a/common/defaults.h +++ b/common/defaults.h @@ -71,7 +71,6 @@ SETUP BACKENDS DEFINITIONS #endif // XASH_MESSAGEBOX #define XASH_USE_EVDEV 1 - #define XASH_DYNAMIC_DLADDR #elif XASH_LINUX // we are building for Linux without SDL2, can draw only to framebuffer yet #ifndef XASH_VIDEO From 892c735c86d53e64e4546bb3e5a40806205310d9 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 16 Dec 2023 21:25:29 +0300 Subject: [PATCH 34/43] Replace harmless single-line pair assigns by multi-line, need more newlines! --- common/backends.h | 2 -- engine/platform/android/android_nosdl.c | 3 ++- engine/platform/android/vid_android.c | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/backends.h b/common/backends.h index 38e05f5e0e..3040eb196c 100644 --- a/common/backends.h +++ b/common/backends.h @@ -22,7 +22,6 @@ GNU General Public License for more details. #define VIDEO_FBDEV 3 #define VIDEO_DOS 4 - // audio backends (XASH_SOUND) #define SOUND_NULL 0 #define SOUND_SDL 1 @@ -49,7 +48,6 @@ GNU General Public License for more details. #define MSGBOX_WIN32 3 #define MSGBOX_NSWITCH 4 - // library loading (XASH_LIB) #define LIB_NULL 0 #define LIB_POSIX 1 diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index 3f4d1692ee..45b9440d45 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -324,7 +324,8 @@ DECLARE_JNI_INTERFACE( void, onNativeResize, jint width, jint height ) if( !width || !height ) return; - jni.width=width, jni.height=height; + jni.width = width; + jni.height = height; // alloc update event to change screen size event = Android_AllocEvent(); diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index f619c403cd..7ccf695952 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -90,7 +90,8 @@ Resolution got from last resize event */ static void Android_GetScreenRes( int *width, int *height ) { - *width=jni.width, *height=jni.height; + *width = jni.width; + *height = jni.height; } /* From ec92d05930a6e17292c3af8ce80ebc82a51cc4f7 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 16 Dec 2023 21:49:35 +0300 Subject: [PATCH 35/43] Fix wrong function name and macro on ARM target --- engine/platform/android/dlsym-weak.cpp | 2 +- engine/platform/android/lib_android.c | 2 +- engine/platform/android/lib_android.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/platform/android/dlsym-weak.cpp b/engine/platform/android/dlsym-weak.cpp index 931ef8b317..0268b9f1d3 100644 --- a/engine/platform/android/dlsym-weak.cpp +++ b/engine/platform/android/dlsym-weak.cpp @@ -118,7 +118,7 @@ static Elf_Sym* dlsym_handle_lookup( soinfo* si, const char* name ) { return soinfo_elf_lookup( si, elfhash( name ), name ); } - +#define XASH_ANDROID 1 #include "lib_android.h" static int dladdr_fallback( const void *addr, Dl_info *info ) diff --git a/engine/platform/android/lib_android.c b/engine/platform/android/lib_android.c index 8b9810fad1..f58eb6fdcc 100644 --- a/engine/platform/android/lib_android.c +++ b/engine/platform/android/lib_android.c @@ -19,7 +19,7 @@ GNU General Public License for more details. #include "platform/android/lib_android.h" #include "platform/android/dlsym-weak.h" // Android < 5.0 -void *Android_GetServerLibrary( void ) +void *ANDROID_GetServerLibrary( void ) { return svgame.hInstance; } diff --git a/engine/platform/android/lib_android.h b/engine/platform/android/lib_android.h index 9e360dd6c8..f3ac9351df 100644 --- a/engine/platform/android/lib_android.h +++ b/engine/platform/android/lib_android.h @@ -20,7 +20,7 @@ GNU General Public License for more details. #define Platform_POSIX_LoadLibrary( x ) ANDROID_LoadLibrary(( x )) #define Platform_POSIX_GetProcAddress( x, y ) ANDROID_GetProcAddress(( x ), ( y )) -void *Android_GetServerLibrary( void ); +void *ANDROID_GetServerLibrary( void ); void *ANDROID_LoadLibrary( const char *dllname ); void *ANDROID_GetProcAddress( void *hInstance, const char *name ); From dbc54fb52209f0e72d3c0a55993b36315216f0b5 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 16 Dec 2023 22:10:53 +0300 Subject: [PATCH 36/43] platform/android_nosdl: reduce unneeded logging --- engine/platform/android/android_nosdl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index 45b9440d45..cfc4d66722 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -499,7 +499,7 @@ DECLARE_JNI_INTERFACE( void, nativeAxis, jint id, jbyte axis, jshort val ) event->axis.axis = axis; event->axis.val = val; - __android_log_print(ANDROID_LOG_VERBOSE, "Xash", "axis %i %i", axis, val ); + // __android_log_print(ANDROID_LOG_VERBOSE, "Xash", "axis %i %i", axis, val ); Android_PushEvent(); } @@ -510,7 +510,7 @@ DECLARE_JNI_INTERFACE( void, nativeJoyButton, jint id, jbyte button, jboolean do event->arg = id; event->button.button = button; event->button.down = down; - __android_log_print(ANDROID_LOG_VERBOSE, "Xash", "button %i", button ); + //__android_log_print(ANDROID_LOG_VERBOSE, "Xash", "button %i", button ); Android_PushEvent(); } From be9dda2a5611665718d649a31f9e2053a9296830 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 16 Dec 2023 22:12:40 +0300 Subject: [PATCH 37/43] platform/android_nosdl: remove NULL native object case --- engine/platform/android/android_nosdl.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index cfc4d66722..8d7d73a9ad 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -667,14 +667,9 @@ Android_GetNativeObject */ void *Android_GetNativeObject( const char *objName ) { - static const char *availObjects[] = { "JNIEnv", "ActivityClass", NULL }; void *object = NULL; - if( !objName ) - { - object = (void*)availObjects; - } - else if( !strcasecmp( objName, "JNIEnv" ) ) + if( !strcasecmp( objName, "JNIEnv" ) ) { object = (void*)jni.env; } From d6cfc65ac0c2af694e977f62b691ddf7df562dbf Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 16 Dec 2023 22:28:42 +0300 Subject: [PATCH 38/43] platform/android_nosdl: remove dynamic cvars --- engine/platform/android/android_nosdl.c | 12 ++++++------ engine/platform/android/vid_android.c | 24 +----------------------- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index 8d7d73a9ad..b3b1fe41ca 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -33,7 +33,7 @@ GNU General Public License for more details. #define JNIEXPORT #endif -convar_t *android_sleep; +static CVAR_DEFINE_AUTO( android_sleep, "1", FCVAR_ARCHIVE, "Enable sleep in background" ); static const int s_android_scantokey[] = { @@ -346,7 +346,7 @@ DECLARE_JNI_INTERFACE( void, nativeSetPause, jint pause ) // if pause enabled, hold engine by locking frame mutex. // Engine will stop after event reading and will not continue untill unlock - if( android_sleep && android_sleep->value ) + if( android_sleep.value ) { if( pause ) pthread_mutex_lock( &events.framemutex ); @@ -358,7 +358,7 @@ DECLARE_JNI_INTERFACE( void, nativeSetPause, jint pause ) DECLARE_JNI_INTERFACE( void, nativeUnPause ) { // UnPause engine before sending critical events - if( android_sleep && android_sleep->value ) + if( android_sleep.value ) pthread_mutex_unlock( &events.framemutex ); } @@ -625,7 +625,7 @@ Initialize android-related cvars */ void Android_Init( void ) { - android_sleep = Cvar_Get( "android_sleep", "1", FCVAR_ARCHIVE, "Enable sleep in background" ); + Cvar_RegisterVariable( &android_sleep ); } void Android_Shutdown( void ) @@ -921,7 +921,7 @@ static void Android_ProcessEvents( void ) if( events.queue[i].arg ) { SNDDMA_Activate( false ); - Android_UpdateSurface( !android_sleep->value ? surface_dummy : surface_pause ); + Android_UpdateSurface( !android_sleep.value ? surface_dummy : surface_pause ); // (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.toggleEGL, 0 ); } (*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.notify ); @@ -1054,7 +1054,7 @@ void Platform_RunEvents( void ) Android_ProcessEvents(); // do not allow running frames while android_sleep is 1, but surface/context not restored - while( android_sleep->value && host.status == HOST_SLEEP ) + while( android_sleep.value && host.status == HOST_SLEEP ) { usleep( 20000 ); Android_ProcessEvents(); diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index 7ccf695952..d633ce3fe1 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -47,9 +47,6 @@ static dllfunc_t android_funcs[] = #undef NW_FF dll_info_t android_info = { "libandroid.so", android_funcs, false }; - -convar_t *cv_vid_scale; -convar_t *cv_vid_rotate; /* ======================== Android_SwapInterval @@ -204,8 +201,6 @@ qboolean R_Init_Video( const int type ) { char buf[MAX_VA_STRING]; qboolean retval; - cv_vid_scale = Cvar_FindVar( "vid_scale" ); - cv_vid_rotate = Cvar_FindVar( "vid_rotate" ); if( FS_FileExists( GI->iconpath, true ) ) { @@ -361,7 +356,6 @@ qboolean VID_SetMode( void ) rserr_t R_ChangeDisplaySettings( int width, int height, window_mode_t window_mode ) { int render_w, render_h; - uint rotate = cv_vid_rotate->value; Android_GetScreenRes(&width, &height); @@ -370,23 +364,7 @@ rserr_t R_ChangeDisplaySettings( int width, int height, window_mode_t window_m Con_Reportf( "R_ChangeDisplaySettings: forced resolution to %dx%d)\n", width, height); - if( ref.dllFuncs.R_SetDisplayTransform( rotate, 0, 0, cv_vid_scale->value, cv_vid_scale->value ) ) - { - if( rotate & 1 ) - { - int swap = render_w; - - render_w = render_h; - render_h = swap; - } - - render_h /= cv_vid_scale->value; - render_w /= cv_vid_scale->value; - } - else - { - Con_Printf( S_WARN "failed to setup screen transform\n" ); - } + VID_SetDisplayTransform( &render_w, &render_h ); R_SaveVideoMode( width, height, render_w, render_h, true ); From 26cbb1d6bff498a7d0799f73305f8c11dab8aea7 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 16 Dec 2023 22:31:17 +0300 Subject: [PATCH 39/43] platform/android_nosdl: use ARRAYSIZE --- engine/platform/android/android_nosdl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index b3b1fe41ca..0e5e4a4ca1 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -375,7 +375,7 @@ DECLARE_JNI_INTERFACE( void, nativeKey, jint down, jint code ) } else { - if( code >= ( sizeof( s_android_scantokey ) / sizeof( s_android_scantokey[0] ) ) ) + if( code >= ARRAYSIZE( s_android_scantokey )) { Con_DPrintf( "nativeKey: unknown Android key %d\n", code ); return; From bd5309b01828cb5e4811d2bfa23ff4ae0963b73c Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 16 Dec 2023 22:35:59 +0300 Subject: [PATCH 40/43] platform/android_nosdl: fix forgotten static --- engine/platform/android/android_nosdl.c | 2 +- engine/platform/android/eglutil.c | 2 +- engine/platform/android/eglutil.h | 14 ++++++++++++++ engine/platform/android/vid_android.c | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index 0e5e4a4ca1..ef4fa9e798 100644 --- a/engine/platform/android/android_nosdl.c +++ b/engine/platform/android/android_nosdl.c @@ -169,7 +169,7 @@ struct jnimethods_s jni; struct jnimouse_s jnimouse; static jmp_buf crash_frame, restore_frame; -char crash_text[1024]; +static char crash_text[1024]; #define Android_Lock() pthread_mutex_lock(&events.mutex); #define Android_Unlock() pthread_mutex_unlock(&events.mutex); diff --git a/engine/platform/android/eglutil.c b/engine/platform/android/eglutil.c index 17c4335186..2011adb7ad 100644 --- a/engine/platform/android/eglutil.c +++ b/engine/platform/android/eglutil.c @@ -48,7 +48,7 @@ static dllfunc_t egl_funcs[] = { NULL, NULL } }; #undef EGL_FF -dll_info_t egl_info = { "libEGL.so", egl_funcs, false }; +static dll_info_t egl_info = { "libEGL.so", egl_funcs, false }; struct eglstate_s eglstate; diff --git a/engine/platform/android/eglutil.h b/engine/platform/android/eglutil.h index ce30bfa3ea..a9ba5cf21b 100644 --- a/engine/platform/android/eglutil.h +++ b/engine/platform/android/eglutil.h @@ -1,3 +1,17 @@ +/* +eglutil.h - EGL context utility +Copyright (C) 2023 mittorn + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ #ifndef EGLUTIL_H #define EGLUTIL_H #include "platform/platform.h" diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index d633ce3fe1..6ba98d0412 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -45,7 +45,7 @@ static dllfunc_t android_funcs[] = { NULL, NULL } }; #undef NW_FF -dll_info_t android_info = { "libandroid.so", android_funcs, false }; +static dll_info_t android_info = { "libandroid.so", android_funcs, false }; /* ======================== From 42958bc746cff8b83a965051a816a972f38b6a49 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 16 Dec 2023 22:45:06 +0300 Subject: [PATCH 41/43] platform/android_nosdl: use FS_GetDiskPath for icon path --- engine/platform/android/vid_android.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index 6ba98d0412..4276cdfddb 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -199,13 +199,13 @@ static int Android_GetGLAttribute( int eglAttr ) qboolean R_Init_Video( const int type ) { - char buf[MAX_VA_STRING]; qboolean retval; + char buf[10]; // Sys_GetParmFromCmdLine if( FS_FileExists( GI->iconpath, true ) ) { - Q_snprintf( buf, sizeof( buf ), "%s/%s/%s", COM_CheckStringEmpty( host.rodir ) ? host.rodir : host.rootdir, GI->gamefolder, GI->iconpath ); - Android_SetIcon( buf ); + // TODO: convert icon to some android-readable format and place + Android_SetIcon( FS_GetDiskPath( GI->iconpath, false )); } Android_SetTitle( GI->title ); From 0442af7920104d19df43c2b133c3078c22ae57f3 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 16 Dec 2023 22:45:52 +0300 Subject: [PATCH 42/43] platform/android_nosdl/dlsym-weak: move includes/defines to the beginning --- engine/platform/android/dlsym-weak.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/engine/platform/android/dlsym-weak.cpp b/engine/platform/android/dlsym-weak.cpp index 0268b9f1d3..8f78bf647b 100644 --- a/engine/platform/android/dlsym-weak.cpp +++ b/engine/platform/android/dlsym-weak.cpp @@ -25,12 +25,15 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - -#if defined __ANDROID__ && !defined XASH_64BIT +#include "build.h" +#if !XASH_64BIT #include #include #include #include "linker.h" +extern "C" { +#include "lib_android.h" +} static Elf_Sym* soinfo_elf_lookup( soinfo* si, unsigned hash, const char* name ) { @@ -118,8 +121,6 @@ static Elf_Sym* dlsym_handle_lookup( soinfo* si, const char* name ) { return soinfo_elf_lookup( si, elfhash( name ), name ); } -#define XASH_ANDROID 1 -#include "lib_android.h" static int dladdr_fallback( const void *addr, Dl_info *info ) { From 38a3154de0ff5f7de011d0559ae717d63d081078 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sun, 17 Dec 2023 01:56:26 +0300 Subject: [PATCH 43/43] ref/gl: refactor gl extension checking again --- ref/gl/gl_opengl.c | 91 +++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/ref/gl/gl_opengl.c b/ref/gl/gl_opengl.c index e526896865..acdd114f6f 100644 --- a/ref/gl/gl_opengl.c +++ b/ref/gl/gl_opengl.c @@ -556,62 +556,71 @@ qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char for( func = funcs; func && func->name; func++ ) { // functions are cleared before all the extensions are evaluated -#ifndef XASH_GLES // arb on gles is last (TODO: separate search tables for GLES/CORE) - if(( *func->func = (void *)gEngfuncs.GL_GetProcAddress( func->name )) == NULL ) -#endif - { - string name; - char *end; - size_t i = 0; + string name; + char *end; + size_t i = 0; + qboolean first_try = true; + // NULL means just func->name, but empty suffix cuts ARB suffix if present #ifdef XASH_GLES - const char *suffixes[] = { "", "EXT", "OES", "ARB" }; + const char *suffixes[] = { "", "EXT", "OES", NULL, "ARB" }; #else - const char *suffixes[] = { "", "EXT" }; + const char *suffixes[] = { NULL, "", "EXT", "ARB" }; #endif - // HACK: fix ARB names - Q_strncpy( name, func->name, sizeof( name )); - if(( end = Q_strstr( name, "ARB" ))) + // Remove ARB suffix + Q_strncpy( name, func->name, sizeof( name )); + if(( end = Q_strstr( name, "ARB" ))) + { + *end = '\0'; + } + else // I need Q_strstrnul + { + end = name + Q_strlen( name ); + } + + for( ; i < sizeof( suffixes ) / sizeof( suffixes[0] ); i++ ) + { + void *f; + const char *pname = name; + size_t name_len = end - pname; + const char *orig_suffix = func->name + name_len; + + if( suffixes[i] ) { - *end = '\0'; + // if suffix is original suffix, it's handled with NULL + if( orig_suffix[0] && !Q_strcmp( orig_suffix, suffixes[i] )) + continue; + Q_strncat( name, suffixes[i], sizeof( name )); } - else // I need Q_strstrnul + else { - end = name + Q_strlen( name ); -#ifndef XASH_GLES - i++; // skip empty suffix -#endif + // if original name does not have a suffix, it will be handled with empty suffix + if( !orig_suffix[0] ) + continue; + pname = func->name; } - for( ; i < sizeof( suffixes ) / sizeof( suffixes[0] ); i++ ) + if(( f = gEngfuncs.GL_GetProcAddress( pname ))) { - void *f; - - Q_strncat( name, suffixes[i], sizeof( name )); - - if(( f = gEngfuncs.GL_GetProcAddress( name ))) - { - // GL_GetProcAddress prints errors about missing functions, so tell user that we found it with different name -#ifdef XASH_GLES - if(i != 0) -#endif - gEngfuncs.Con_Printf( S_NOTE "found %s\n", name ); + // if we already tried this function, notify about success after previous error from GL_GetProcAddress + if(!first_try) + gEngfuncs.Con_Printf( S_NOTE "found %s\n", pname ); + first_try = false; - *func->func = f; - break; - } - else - { - *end = '\0'; // cut suffix, try next - } + *func->func = f; + break; } - - // not found... - if( i == sizeof( suffixes ) / sizeof( suffixes[0] )) + else { - GL_SetExtension( r_ext, false ); + *end = '\0'; // cut suffix, try next } } + + // not found... + if( i == sizeof( suffixes ) / sizeof( suffixes[0] )) + { + GL_SetExtension( r_ext, false ); + } } #endif