diff --git a/engine/platform/android/android_nosdl.c b/engine/platform/android/android_nosdl.c index c226b45b..55f3d3d9 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.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.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.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 e7776e4b..990c1f85 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 d02e2537..75680535 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;