QuelSolaar/b_android_main.c

1274 lines
48 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "betray.h"
#include "imagine.h"
#include <jni.h>
#include <errno.h>
#include <math.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <android/sensor.h>
#include <android/input.h>
#include <android/log.h>
#include <android/api-level.h>
#include <android/native_window.h>
#include <android/window.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include "b_android_native_app_glue.h"
static boolean input_focus = TRUE;
//static int b_window_size_x = 800;
//static int b_window_size_y = 600;
static int b_window_center_x = 400;
static int b_window_center_y = 300;
static int b_window_pos_x = 800;
static int b_window_pos_y = 600;
struct android_app* betray_android_app_state;
struct saved_state {
float angle;
int32_t x;
int32_t y;
};
uint betray_android_pointer_allocations[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
typedef struct
{
JavaVM vm;
jobject activity;
const char* app_dir;
} AssetThreadAttributes;
typedef struct{
struct android_app* app;
ASensorManager* sensorManager;
const ASensor* accelerometerSensor;
const ASensor* gyroscopicSensor;
ASensorEventQueue* sensorEventQueue;
uint device_id;
uint axis_up;
uint axis_forward;
uint accelerometer;
uint pointers[16];
float axis_matrix[16];
float last_accelerometer[3];
float delta_accelerometer[3];
char app_dir[256];
int animating;
EGLDisplay display;
EGLSurface surface;
EGLContext context;
EGLConfig config;
EGLint format;
pthread_t asset_thread;
int32_t width;
int32_t height;
struct saved_state state;
}BetrayAndroidState;
BetrayAndroidState betray_android_state = {};
uint betray_plugin_axis_allocate(uint user_id, uint device_id, char *name, BAxisType type, uint axis_count);
void betray_plugin_axis_set(uint id, float axis_x, float axis_y, float axis_z);
extern void betray_plugin_callback_main(BInputState *input);
extern void betray_plugin_pointer_clean();
extern void betray_action(BActionMode mode);
extern void betray_reshape_view(uint x_size, uint y_size);
extern uint betray_plugin_pointer_allocate(uint user_id, uint device_id, uint button_count, float x, float y, float z, float *origin, char *name, boolean draw);
extern void betray_plugin_pointer_set(uint id, float x, float y, float z, float *origin, boolean *buttons);
extern void betray_plugin_pointer_free(uint id);
extern void betray_plugin_button_set(uint user_id, uint id, boolean state, uint character);
void betray_glBindFramebufferEXT(GLenum target, GLuint framebuffer)
{
}
void betray_screen_mode_safe_get(BetraySafeArea safe_area, float *left, float *right, float *top, float *bottom)
{
//FK: TODO
const float aspect = (float)betray_android_state.height / (float)betray_android_state.width;
*left = -1.0f;
*right = 1.0f;
*top = aspect;
*bottom = -aspect;
}
boolean b_win32_system_wrapper_set_display(uint size_x, uint size_y, boolean full_screen)
{
}
uint betray_support_functionality(BSupportedFunctionality funtionality)
{
uint array[] = {
1, /*B_SF_USER_COUNT_MAX*/
16, /*B_SF_POINTER_COUNT_MAX*/
1, /*B_SF_POINTER_BUTTON_COUNT*/
TRUE, /*B_SF_FULLSCREEN*/
FALSE, /*B_SF_WINDOWED*/
FALSE, /*B_SF_VIEW_POSITION*/
FALSE, /*B_SF_VIEW_ROTATION*/
FALSE, /*B_SF_MOUSE_WARP*/
FALSE, /*B_SF_EXECUTE*/
FALSE, /*B_SF_REQUESTER*/
TRUE}; /*B_SF_CLIPBOARD*/
if(funtionality >= B_SF_COUNT)
return FALSE;
return array[funtionality];
}
void betray_requester_save_func(void *data)
{
}
char *betray_requester_save_get(void *id)
{
return NULL;
}
void betray_requester_save(char **types, uint type_count, void *id)
{
}
void betray_requester_load_func(void *data)
{
}
char *betray_requester_load_get(void *id)
{
return NULL;
}
void betray_requester_load(char **types, uint type_count, void *id)
{
}
char *betray_clipboard_get()
{
return NULL;
}
void betray_clipboard_set(char *text)
{
}
void betray_url_launch(char *url)
{
JavaVM lJavaVM = *betray_android_app_state->activity->vm;
JNIEnv* env;
int result;
env = NULL;
result = lJavaVM->AttachCurrentThread(betray_android_app_state->activity->vm, &env, NULL);
jstring url_string = (*env)->NewStringUTF(env, url);
jclass uri_class = (*env)->FindClass(env, "android/net/Uri");
jmethodID uri_parse = (*env)->GetStaticMethodID(env, uri_class, "parse", "(Ljava/lang/String;)Landroid/net/Uri;");
jobject uri = (*env)->CallStaticObjectMethod(env, uri_class, uri_parse, url_string);
jclass intent_class = (*env)->FindClass(env, "android/content/Intent");
jfieldID action_view_id = (*env)->GetStaticFieldID(env, intent_class, "ACTION_VIEW", "Ljava/lang/String;");
jobject action_view = (*env)->GetStaticObjectField(env, intent_class, action_view_id);
jmethodID new_intent = (*env)->GetMethodID(env, intent_class, "<init>", "(Ljava/lang/String;Landroid/net/Uri;)V");
jobject intent = (*env)->AllocObject(env, intent_class);
(*env)->CallVoidMethod(env, intent, new_intent, action_view, uri);
jclass activity_class = (*env)->FindClass(env, "android/app/Activity");
jmethodID start_activity = (*env)->GetMethodID(env, activity_class, "startActivity", "(Landroid/content/Intent;)V");
(*env)->CallVoidMethod(env, betray_android_app_state->activity->clazz, start_activity, intent);
result = 0;
}
/*
void get_network()
{
JavaVM lJavaVM = *betray_android_app_state->activity->vm;
JNIEnv* env;
int result;
env = NULL;
result = lJavaVM->AttachCurrentThread(betray_android_app_state->activity->vm, &env, NULL);
jclass InterfaceAddress = (*env)->FindClass(env, "java/net/InterfaceAddress");
jmethodID hashCode = (*env)->GetMethodID(env, InterfaceAddress, "hashCode", "()I");
result = (*env)->CallIntMethod(env, InterfaceAddress, hashCode);
result = 0;
}*/
int betray_android_unicode_convert(int eventType, int keyCode, int metaState)
{
JavaVM lJavaVM = *betray_android_app_state->activity->vm;
JNIEnv* env;
env = NULL;
int result;
result = lJavaVM->AttachCurrentThread(betray_android_app_state->activity->vm, &env, NULL);
jclass class_key_event = (*env)->FindClass(env, "android/view/KeyEvent");
int unicodeKey;
if(metaState == 0)
{
jmethodID method_get_unicode_char = (*env)->GetMethodID(env, class_key_event, "getUnicodeChar", "()I");
jmethodID eventConstructor = (*env)->GetMethodID(env, class_key_event, "<init>", "(II)V");
jobject eventObj = (*env)->NewObject(env, class_key_event, eventConstructor, eventType, keyCode);
unicodeKey = (*env)->CallIntMethod(env, eventObj, method_get_unicode_char);
}else
{
jmethodID method_get_unicode_char = (*env)->GetMethodID(env, class_key_event, "getUnicodeChar", "(I)I");
jmethodID eventConstructor = (*env)->GetMethodID(env, class_key_event, "<init>", "(II)V");
jobject eventObj = (*env)->NewObject(env, class_key_event, eventConstructor, eventType, keyCode);
unicodeKey = (*env)->CallIntMethod(env, eventObj, method_get_unicode_char, metaState);
}
return unicodeKey;
}
void betray_device_init()
{
}
void b_win32_window_close()
{
}
void betray_desktop_size_get(uint *size_x, uint *size_y)
{
*size_x = 0;
}
boolean betray_activate_context(void *context)
{
}
void *b_create_context()
{
}
boolean b_init_display_opengl(uint size_x, uint size_y, boolean fullscreenflag, uint samples, char* title, boolean *sterioscopic)
{
}
#ifdef BETRAY_CONTEXT_OPENGLES
void betray_button_keyboard(uint user_id, boolean show)
{
// Attaches the current thread to the JVM.
jint lResult;
jint lFlags = 1;
JavaVM *vm;
JNIEnv* env;
int result;
JavaVM lJavaVM = *betray_android_app_state->activity->vm;
env = NULL;
result = lJavaVM->AttachCurrentThread(betray_android_app_state->activity->vm, &env, NULL);
vm = betray_android_app_state->activity->vm;
JavaVMAttachArgs lJavaVMAttachArgs;
lJavaVMAttachArgs.version = JNI_VERSION_1_6;
lJavaVMAttachArgs.name = "NativeThread";
lJavaVMAttachArgs.group = NULL;
// lResult = betray_android_app_state->activity->vm[0]->AttachCurrentThread(betray_android_app_state->activity->vm, betray_android_app_state->activity->env, &lJavaVMAttachArgs);
// if(lResult == -1)
// return;
// Retrieves NativeActivity.
jobject lNativeActivity = betray_android_app_state->activity->clazz;
jclass ClassNativeActivity = (*env)->GetObjectClass(env, lNativeActivity);
// Retrieves Context.INPUT_METHOD_SERVICE.
jclass ClassContext = (*env)->FindClass(env, "android/content/Context");
jfieldID FieldINPUT_METHOD_SERVICE = (*env)->GetStaticFieldID(env, ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;");
jobject INPUT_METHOD_SERVICE = (*env)->GetStaticObjectField(env, ClassContext,FieldINPUT_METHOD_SERVICE);
// jniCheck(INPUT_METHOD_SERVICE);
// Runs getSystemService(Context.INPUT_METHOD_SERVICE).
jclass ClassInputMethodManager = (*env)->FindClass(env, "android/view/inputmethod/InputMethodManager");
jmethodID MethodGetSystemService = (*env)->GetMethodID(env, ClassNativeActivity, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
jobject lInputMethodManager = (*env)->CallObjectMethod(env, lNativeActivity, MethodGetSystemService, INPUT_METHOD_SERVICE);
// Runs getWindow().getDecorView().
jmethodID MethodGetWindow = (*env)->GetMethodID(env, ClassNativeActivity, "getWindow", "()Landroid/view/Window;");
jobject lWindow = (*env)->CallObjectMethod(env, lNativeActivity, MethodGetWindow);
jclass ClassWindow = (*env)->FindClass(env, "android/view/Window");
jmethodID MethodGetDecorView = (*env)->GetMethodID(env, ClassWindow, "getDecorView", "()Landroid/view/View;");
jobject lDecorView = (*env)->CallObjectMethod(env, lWindow, MethodGetDecorView);
if(show)
{
// Runs lInputMethodManager.showSoftInput(...).
jmethodID MethodShowSoftInput = (*env)->GetMethodID(env, ClassInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z");
lResult = (*env)->CallBooleanMethod(env, lInputMethodManager, MethodShowSoftInput, lDecorView, lFlags);
}else
{
// Runs lWindow.getViewToken()
jclass ClassView = (*env)->FindClass(env, "android/view/View");
jmethodID MethodGetWindowToken = (*env)->GetMethodID(env, ClassView, "getWindowToken", "()Landroid/os/IBinder;");
jobject lBinder = (*env)->CallObjectMethod(env, lDecorView, MethodGetWindowToken);
// lInputMethodManager.hideSoftInput(...).
jmethodID MethodHideSoftInput = (*env)->GetMethodID(env, ClassInputMethodManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z");
jboolean lRes = (*env)->CallBooleanMethod(env, lInputMethodManager, MethodHideSoftInput, lBinder, lFlags);
}
show = 0;
// Finished with the JVM.
// betray_android_app_state->activity->vm[0]->DetachCurrentThread(betray_android_app_state->activity->vm);
}
int betray_create_directory_tree( const char* pBaseDir, const char* pDirTree )
{
char tempDirectoryPath[512] = {0};
strcat( tempDirectoryPath, pBaseDir );
strcat( tempDirectoryPath, "/" );
const size_t baseDirLength = strlen( pBaseDir );
const size_t dirTreeLength = strlen( pDirTree );
for( size_t dirTreeIndex = 0u; dirTreeIndex < dirTreeLength; ++dirTreeIndex )
{
tempDirectoryPath[ baseDirLength + 1 + dirTreeIndex ] = pDirTree[ dirTreeIndex ];
boolean callMkDir = false;
if( pDirTree[ dirTreeIndex ] == '/' )
{
tempDirectoryPath[ baseDirLength + 1 + dirTreeIndex + 1] = 0;
callMkDir = true;
}
else if( dirTreeIndex + 1 == dirTreeLength )
{
tempDirectoryPath[ baseDirLength + 1 + dirTreeIndex + 1 ] = '/';
tempDirectoryPath[ baseDirLength + 1 + dirTreeIndex + 2 ] = 0;
callMkDir = true;
}
if( callMkDir )
{
const int result = mkdir( tempDirectoryPath, S_IRWXU );
if( result == -1 )
{
if( errno != EEXIST )
{
return -1;
}
}
}
}
return 1;
}
void betray_recreate_asset_directory_tree_recursively( const char* pAssetDirectory, const char* pApplicationDirectory, JNIEnv* env, jobject assetManager, jmethodID listAssets )
{
jobject assetDir = (*env)->NewStringUTF(env, pAssetDirectory);
jarray assetList = (jarray)(*env)->CallObjectMethod(env, assetManager, listAssets, assetDir);
jsize assetListLength = 0u;
jsize assetIndex = 0u;
char assetPath[512] = {0};
strcat( assetPath, pAssetDirectory );
if( strlen( assetPath ) > 0 )
{
strcat( assetPath, "/");
}
const size_t assetBasePathPos = strlen( assetPath );
assetListLength = (*env)->GetArrayLength(env, assetList);
for( assetIndex = 0u; assetIndex < assetListLength; ++assetIndex )
{
jobject assetName = (*env)->GetObjectArrayElement(env, assetList, assetIndex);
const char* pAssetName = (*env)->GetStringUTFChars(env, assetName, NULL);
assetPath[ assetBasePathPos ] = 0;
strcat( assetPath, pAssetName );
char applicationAssetPath[512] = {0};
strcat( applicationAssetPath, pApplicationDirectory );
strcat( applicationAssetPath, "/" );
strcat( applicationAssetPath, assetPath );
//FK: Try to open asset, if this fails this is a directory
AAsset* pAsset = AAssetManager_open(betray_android_app_state->activity->assetManager, assetPath, AASSET_MODE_STREAMING);
if( pAsset == NULL )
{
__android_log_print( ANDROID_LOG_INFO, "betray", "Creating directory \"%s\" to recreate asset directory tree", applicationAssetPath );
if( betray_create_directory_tree( pApplicationDirectory, assetPath ) == -1 )
{
__android_log_print( ANDROID_LOG_ERROR, "betray", "Couldn't create directory \"%s\". errno:%d", applicationAssetPath, errno );
continue;
}
betray_recreate_asset_directory_tree_recursively( assetPath, pApplicationDirectory, env, assetManager, listAssets );
}
else
{
__android_log_print( ANDROID_LOG_INFO, "betray", "Dumping asset \"%s\"...", assetPath );
FILE* out = fopen(applicationAssetPath, "wb");
if( out == NULL )
{
__android_log_print( ANDROID_LOG_ERROR, "betray", "Couldn't dump asset \"%s\", errno:%d", assetPath, errno );
continue;
}
char buf[4096];
int nb_read = 0;
while((nb_read = AAsset_read(pAsset, buf, 4096)) > 0)
fwrite(buf, nb_read, 1, out);
fclose(out);
AAsset_close(pAsset);
}
(*env)->ReleaseStringUTFChars(env, assetName, pAssetName);
}
(*env)->DeleteLocalRef(env, assetDir);
}
void* betray_thread_recreate_asset_directory_tree(void* params)
{
AssetThreadAttributes* thread_attributes = (AssetThreadAttributes*)params;
JNIEnv* env;
thread_attributes->vm->AttachCurrentThread(betray_android_app_state->activity->vm, &env, NULL);
jclass activityClass = (*env)->FindClass(env, "android/app/NativeActivity");
jmethodID getAssets = (*env)->GetMethodID(env, activityClass, "getAssets", "()Landroid/content/res/AssetManager;");
jobject assetManager = (*env)->CallObjectMethod(env, thread_attributes->activity, getAssets);
jclass assetManagerClass = (*env)->FindClass(env, "android/content/res/AssetManager");
jmethodID listAssets = (*env)->GetMethodID(env, assetManagerClass, "list", "(Ljava/lang/String;)[Ljava/lang/String;");
betray_recreate_asset_directory_tree_recursively("", thread_attributes->app_dir, env, assetManager, listAssets);
thread_attributes->vm->DetachCurrentThread(betray_android_app_state->activity->vm);
}
void betray_get_android_app_dir(char* out_app_dir, jobject activity, JavaVM* vm)
{
JNIEnv* env;
(*vm)->AttachCurrentThread(betray_android_app_state->activity->vm, &env, NULL);
jclass activityClass = (*env)->FindClass(env, "android/app/NativeActivity");
jmethodID getCacheDir = (*env)->GetMethodID(env, activityClass, "getFilesDir", "()Ljava/io/File;");
jobject file = (*env)->CallObjectMethod(env, activity, getCacheDir);
jclass fileClass = (*env)->FindClass(env, "java/io/File");
jmethodID getAbsolutePath = (*env)->GetMethodID(env, fileClass, "getAbsolutePath", "()Ljava/lang/String;");
jstring jpath = (jstring)(*env)->CallObjectMethod(env, file, getAbsolutePath);
const char* app_dir = (*env)->GetStringUTFChars(env, jpath, NULL);
const size_t app_dir_length = strlen( app_dir );
memcpy( out_app_dir, app_dir, app_dir_length );
out_app_dir[ app_dir_length ] = 0;
(*env)->ReleaseStringUTFChars(env, jpath, app_dir);
(*vm)->DetachCurrentThread(betray_android_app_state->activity->vm);
}
void betray_set_directory()
{
const char expected_sentinel_content[] = "zenith.sentinel.v1";
char app_dir[256] = {0};
betray_get_android_app_dir( app_dir, betray_android_app_state->activity->clazz, betray_android_app_state->activity->vm );
//FK: Check if sentinel file is accessible - if it is, it means we have already dumped the asset folder
char sentinel_file_path[512] = {0};
strcat( sentinel_file_path, app_dir );
strcat( sentinel_file_path, "/.sentinel" );
__android_log_print( ANDROID_LOG_INFO, "betray", "Trying to search for sentinel file at '%s'.", sentinel_file_path );
FILE* sentinel_file = fopen( sentinel_file_path, "r" );
boolean dump_assets = ( sentinel_file == NULL );
if( sentinel_file != NULL )
{
__android_log_print( ANDROID_LOG_INFO, "betray", "Found sentinel file, checking content..." );
char sentinel_content[64];
fread( sentinel_content, 1, sizeof( expected_sentinel_content ), sentinel_file );
if( memcmp( sentinel_content, expected_sentinel_content, sizeof( expected_sentinel_content ) ) != 0 )
{
__android_log_print( ANDROID_LOG_WARN, "betray", "Content of sentinel file not equal to what was expected, re-dumping assets..." );
//FK: content differs from what is expected, dump assets again...
dump_assets = TRUE;
}
else
{
__android_log_print( ANDROID_LOG_INFO, "betray", "Content of sentinel file is equal to what was expected, no need to re-dump assets..." );
}
fclose( sentinel_file );
}
if( dump_assets )
{
//FK: Sentinel file not present - dump asset dir
//FK: Recreate directory tree from asset folder in case there is available functionality that relies on an existing directory structure
AssetThreadAttributes* thread_attributes = (AssetThreadAttributes*)malloc( sizeof( AssetThreadAttributes ) );
thread_attributes->activity = betray_android_app_state->activity->clazz;
thread_attributes->vm = *betray_android_app_state->activity->vm;
thread_attributes->app_dir = app_dir;
pthread_create( &betray_android_state.asset_thread, NULL, betray_thread_recreate_asset_directory_tree, thread_attributes );
//FK: TODO: Waiting for the thread synchonously doesn't make much sense here.
// I have to figure out where the best place for this would be...
pthread_join( betray_android_state.asset_thread, NULL );
//FK: Create sentinel file after dumping all assets
__android_log_print( ANDROID_LOG_INFO, "betray", "Trying to create sentinel file at '%s'...", sentinel_file_path );
FILE* sentinel_file = fopen( sentinel_file_path, "w" );
if( sentinel_file != NULL )
{
__android_log_print( ANDROID_LOG_INFO, "betray", "Successfully created sentinel file, writing content..." );
fwrite( expected_sentinel_content, 1, sizeof( expected_sentinel_content ), sentinel_file );
fclose( sentinel_file );
}
else
{
__android_log_print( ANDROID_LOG_ERROR, "betray", "Couldn't create sentinel file, unfortunately assets have to get re-dumped next time... (errno:%d)", errno );
}
free(thread_attributes);
}
chdir(app_dir);
}
void betray_immersive_mode()
{
boolean success = TRUE;
int version;
JNIEnv* env;
int result, api_version;
JavaVM lJavaVM = *betray_android_app_state->activity->vm;
env = NULL;
// api_version = android_get_device_api_level();
ANativeActivity_setWindowFlags(betray_android_app_state->activity, AWINDOW_FLAG_FULLSCREEN/*AWINDOW_FLAG_FULLSCREEN*/, AWINDOW_FLAG_FORCE_NOT_FULLSCREEN);
lJavaVM->AttachCurrentThread(betray_android_app_state->activity->vm, &env, NULL);
version = (*env)->GetVersion(env);
jclass activityClass = (*env)->FindClass(env, "android/app/NativeActivity");
jclass windowClass = (*env)->FindClass(env, "android/view/Window");
jclass viewClass = (*env)->FindClass(env, "android/view/View");
jmethodID getWindow = (*env)->GetMethodID(env, activityClass, "getWindow", "()Landroid/view/Window;");
jmethodID getDecorView = (*env)->GetMethodID(env, windowClass, "getDecorView", "()Landroid/view/View;");
jmethodID setSystemUiVisibility = (*env)->GetMethodID(env, viewClass, "setSystemUiVisibility", "(I)V");
jmethodID getSystemUiVisibility = (*env)->GetMethodID(env, viewClass, "getSystemUiVisibility", "()I");
jmethodID setFitsSystemWindows = (*env)->GetMethodID(env, viewClass, "setFitsSystemWindows", "(Z)V");
// (*env)->CallVoidMethodA(env, viewClass, setFitsSystemWindows, &boolean_value);
/* jclass layout_params = env->FindClass(env, "android/WindowManager/LayoutParams");
jfieldID id_LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES = env->GetStaticFieldID(env, windowClass, "LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES", "I");
jclass visibilityClass = env->FindClass(env, "android/view/View");
*/
jobject windowObj = (*env)->CallObjectMethod(env, betray_android_app_state->activity->clazz, getWindow);
jobject decorViewObj = (*env)->CallObjectMethod(env, windowObj, getDecorView);
jvalue boolean_value;
boolean_value.z = FALSE;
(*env)->CallVoidMethod(env, decorViewObj, setFitsSystemWindows, (uint8)1);
// Get flag ids
jfieldID id_SYSTEM_UI_FLAG_LAYOUT_STABLE = (*env)->GetStaticFieldID(env, viewClass, "SYSTEM_UI_FLAG_LAYOUT_STABLE", "I");
jfieldID id_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = (*env)->GetStaticFieldID(env, viewClass, "SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION", "I");
jfieldID id_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = (*env)->GetStaticFieldID(env, viewClass, "SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN", "I");
jfieldID id_SYSTEM_UI_FLAG_HIDE_NAVIGATION = (*env)->GetStaticFieldID(env, viewClass, "SYSTEM_UI_FLAG_HIDE_NAVIGATION", "I");
jfieldID id_SYSTEM_UI_FLAG_FULLSCREEN = (*env)->GetStaticFieldID(env, viewClass, "SYSTEM_UI_FLAG_FULLSCREEN", "I");
jfieldID id_SYSTEM_UI_FLAG_IMMERSIVE_STICKY = (*env)->GetStaticFieldID(env, viewClass, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY", "I");
jfieldID id_SYSTEM_UI_FLAG_IMMERSIVE = (*env)->GetStaticFieldID(env, viewClass, "SYSTEM_UI_FLAG_IMMERSIVE", "I");
// Get flags
const int flag_SYSTEM_UI_FLAG_LAYOUT_STABLE = (*env)->GetStaticIntField(env, viewClass, id_SYSTEM_UI_FLAG_LAYOUT_STABLE);
const int flag_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = (*env)->GetStaticIntField(env, viewClass, id_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
const int flag_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = (*env)->GetStaticIntField(env, viewClass, id_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
const int flag_SYSTEM_UI_FLAG_HIDE_NAVIGATION = (*env)->GetStaticIntField(env, viewClass, id_SYSTEM_UI_FLAG_HIDE_NAVIGATION);
const int flag_SYSTEM_UI_FLAG_FULLSCREEN = (*env)->GetStaticIntField(env, viewClass, id_SYSTEM_UI_FLAG_FULLSCREEN);
const int flag_SYSTEM_UI_FLAG_IMMERSIVE_STICKY = (*env)->GetStaticIntField(env, viewClass, id_SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
const int flag_SYSTEM_UI_FLAG_IMMERSIVE = (*env)->GetStaticIntField(env, viewClass, id_SYSTEM_UI_FLAG_IMMERSIVE);
// Get current immersiveness
int currentVisibility = (*env)->CallIntMethod(env, decorViewObj, getSystemUiVisibility);
bool is_SYSTEM_UI_FLAG_LAYOUT_STABLE = (currentVisibility & flag_SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
bool is_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = (currentVisibility & flag_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0;
bool is_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = (currentVisibility & flag_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0;
bool is_SYSTEM_UI_FLAG_HIDE_NAVIGATION = (currentVisibility & flag_SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
bool is_SYSTEM_UI_FLAG_FULLSCREEN = (currentVisibility & flag_SYSTEM_UI_FLAG_FULLSCREEN) != 0;
bool is_SYSTEM_UI_FLAG_IMMERSIVE_STICKY = (currentVisibility & flag_SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
const auto isAlreadyImmersive =
is_SYSTEM_UI_FLAG_LAYOUT_STABLE &&
is_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION &&
is_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN &&
is_SYSTEM_UI_FLAG_HIDE_NAVIGATION &&
is_SYSTEM_UI_FLAG_FULLSCREEN &&
is_SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
/* PORTIS_LOGD()
<< "set_immersive data"
<< is_SYSTEM_UI_FLAG_LAYOUT_STABLE
<< is_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
<< is_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
<< is_SYSTEM_UI_FLAG_HIDE_NAVIGATION
<< is_SYSTEM_UI_FLAG_FULLSCREEN
<< is_SYSTEM_UI_FLAG_IMMERSIVE_STICKY;*/
if (true) {
const int flag =
flag_SYSTEM_UI_FLAG_LAYOUT_STABLE |
flag_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
flag_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
flag_SYSTEM_UI_FLAG_HIDE_NAVIGATION |
flag_SYSTEM_UI_FLAG_FULLSCREEN |
flag_SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
0;
(*env)->CallVoidMethod(env, decorViewObj, setSystemUiVisibility, flag);
if((*env)->ExceptionCheck(env))
{
// Read exception msg
jthrowable e = (*env)->ExceptionOccurred(env);
(*env)->ExceptionClear(env); // clears the exception; e seems to remain valid
jclass clazz = (*env)->GetObjectClass(env, e);
jmethodID getMessage = (*env)->GetMethodID(env, clazz, "getMessage", "()Ljava/lang/String;");
jstring message = (jstring)(*env)->CallObjectMethod(env, e, getMessage);
const char *mstr = (*env)->GetStringUTFChars(env, message, NULL);
// const auto exception_msg = std::string{mstr};
(*env)->ReleaseStringUTFChars(env, message, mstr);
(*env)->DeleteLocalRef(env, message);
(*env)->DeleteLocalRef(env, clazz);
(*env)->DeleteLocalRef(env, e);
success = FALSE;
}
}
currentVisibility = (*env)->CallIntMethod(env, decorViewObj, getSystemUiVisibility);
is_SYSTEM_UI_FLAG_LAYOUT_STABLE = (currentVisibility & flag_SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
is_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = (currentVisibility & flag_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0;
is_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = (currentVisibility & flag_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0;
is_SYSTEM_UI_FLAG_HIDE_NAVIGATION = (currentVisibility & flag_SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
is_SYSTEM_UI_FLAG_FULLSCREEN = (currentVisibility & flag_SYSTEM_UI_FLAG_FULLSCREEN) != 0;
is_SYSTEM_UI_FLAG_IMMERSIVE_STICKY = (currentVisibility & flag_SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
jmethodID addFlags = (*env)->GetMethodID(env, windowClass, "addFlags", "(I)V");
(*env)->CallVoidMethod(env, windowObj, addFlags, 0x00000400 /*FLAG_FULLSCREEN*/ &
0x00800000 /*FLAG_IMMERSIVE */);
jmethodID clearFlags = (*env)->GetMethodID(env, windowClass, "clearFlags", "(I)V");
(*env)->CallVoidMethod(env, windowObj, clearFlags, 0x00000800 /*FLAG_FORCE_NOT_FULLSCREEN*/);
/* jmethodID setNavigationBarColor = (*env)->GetMethodID(env, windowClass, "setNavigationBarColor", "(I)V");
(*env)->CallVoidMethod(env, windowObj, setNavigationBarColor, 0xAA883388);
jmethodID setStatusBarColor = (*env)->GetMethodID(env, windowClass, "setStatusBarColor", "(I)V");
(*env)->CallVoidMethod(env, windowObj, setStatusBarColor, 0xAA883388);
*/
static const int WindowManager_LayoutParams_LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES = 0x00000001;
jmethodID getAttributes = (*env)->GetMethodID(env, windowClass, "getAttributes", "()Landroid/view/WindowManager$LayoutParams;");
jobject attributes = (*env)->CallObjectMethod(env, windowObj, getAttributes);
jclass clazz = (*env)->GetObjectClass(env, attributes);
jfieldID mi = (*env)->GetFieldID(env, clazz, "layoutInDisplayCutoutMode", "I");
(*env)->SetIntField(env, attributes, mi, WindowManager_LayoutParams_LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES);
/*
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); // 0x80000000
window.setStatusBarColor(Color.BLUE);
*/
(*env)->DeleteLocalRef(env, windowObj);
(*env)->DeleteLocalRef(env, decorViewObj);
/* {
jmethodID getWindow = env->GetMethodID(env, activityClass, "getWindow", "()Landroid/view/Window;");
static const int WindowManager_LayoutParams_LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES = 0x00000001;
jobject window = _glfmCallJavaMethod(env, iandroid_app->activity->clazz, "getWindow", "()Landroid/view/Window;", Object);
jobject attributes = _glfmCallJavaMethod(env, window, "getAttributes", "()Landroid/view/WindowManager$LayoutParams;", Object);
jclass clazz = env->GetObjectClass(env, attributes);
jfieldID mi = env->GetFieldID(env, clazz, "layoutInDisplayCutoutMode", "I");
env->SetIntField(env, attributes, mi, WindowManager_LayoutParams_LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES);
env->DeleteLocalRef(env, clazz);
env->DeleteLocalRef(env, attributes);
env->DeleteLocalRef(env, window);
}*/
/*
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(lp);
protected void onCreate(Bundle savedInstanceState)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
{
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(layoutParams);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
super.onCreate(savedInstanceState);
}
*/
// betray_url_launch("http://www.google.com");
success = TRUE;
}
#endif
void system_wrapper_lose_focus(void)
{
input_focus = FALSE;
}
void betray_set_mouse_warp(boolean warp)
{
}
void betray_set_mouse_move(float x, float y)
{
}
uint betray_support_context(BContextType context_type)
{
return context_type == B_CT_OPENGLES2 || context_type == B_CT_OPENGL_OR_ES;
}
extern uint BGlobal_draw_state_fbo;
/*
void APIENTRY betray_glBindFramebufferEXT(GLenum target, GLuint framebuffer)
{
static void (APIENTRY *internal_glBindFramebufferEXT)(GLenum target, GLuint framebuffer) = NULL;
if(internal_glBindFramebufferEXT == NULL)
internal_glBindFramebufferEXT = (void (APIENTRY __stdcall *)(GLenum , GLuint))wglGetProcAddress("glBindFramebufferEXT");
if(framebuffer == 0)
internal_glBindFramebufferEXT(target, BGlobal_draw_state_fbo);
else
internal_glBindFramebufferEXT(target, framebuffer);
}
void *betray_gl_proc_address_get_internal(const char *text)
{
#ifdef BETRAY_CONTEXT_OPENGL
if(b_win32_opengl_context_current == b_win32_opengl_context)
{
uint i;
char *extension = "glBindFramebuffer";
for(i = 0; extension[i] == text[i] && extension[i] != 0; i++);
if(extension[i] == 0)
return betray_glBindFramebufferEXT;
}
return wglGetProcAddress(text);
#endif
}
*/
void *betray_gl_proc_address_get()
{
return (void *)eglGetProcAddress;
}
extern void betray_time_update(void);
int my_nCmdShow;
/**
* Initialize an EGL context for the current display.
*/
void betray_immersive_mode();
static int engine_init_surface(BetrayAndroidState *state)
{
state->surface = eglCreateWindowSurface(state->display, state->config, state->app->window, NULL);
if(state->surface == EGL_NO_SURFACE)
return 0;
eglMakeCurrent(state->display, state->surface, state->surface, state->context);
ANativeWindow_setBuffersGeometry(state->app->window, 0, 0, state->format);
eglQuerySurface(state->display, state->surface, EGL_WIDTH, &state->width);
eglQuerySurface(state->display, state->surface, EGL_HEIGHT, &state->height);
return 1;
}
static void engine_term_surface(BetrayAndroidState* state)
{
if( state->surface != EGL_NO_SURFACE )
{
state->width = 0;
state->height = 0;
eglMakeCurrent(state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, state->context);
eglDestroySurface(state->display, state->surface);
state->surface = EGL_NO_SURFACE;
}
}
static int engine_init_context(BetrayAndroidState *state)
{
// initialize OpenGL ES and EGL
/*
* Here specify the attributes of the desired configuration.
* Below, we select an EGLConfig with at least 8 bits per color
* component compatible with on-screen windows
*/
const EGLint attribs[] = {
// EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
EGLint aEGLContextAttributes[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLint w, h, format, i;
EGLint numConfigs;
EGLConfig config[16];
EGLSurface surface;
EGLContext context;
// betray_immersive_mode();
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);/* EGL_MULTISAMPLE_RESOLVE_DEFAULT */
//sqrt((double)d_sqrt(3894576));
if(!eglInitialize(display, 0, 0))
exit(0);
/* Here, the application chooses the configuration it desires. In this
* sample, we have a very simplified selection process, where we pick
* the first EGLConfig that matches our criteria */
if(!eglChooseConfig(display, attribs, &config, 16, &numConfigs))
exit(0);
for(i = 0; i < numConfigs; i++)
{
EGLint x, y, z;
eglGetConfigAttrib(display, config[i], EGL_DEPTH_SIZE, &x);
eglGetConfigAttrib(display, config[i], EGL_HEIGHT, &y);
}
/* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
* guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
* As soon as we picked a EGLConfig, we can safely reconfigure the
* ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */
if(!eglGetConfigAttrib(display, config[0], EGL_NATIVE_VISUAL_ID, &format))
exit(0);
context = eglCreateContext(display, config[0], EGL_NO_CONTEXT, aEGLContextAttributes);
betray_android_state.display = display;
betray_android_state.context = context;
betray_android_state.config = config[0];
betray_android_state.format = format;
betray_android_state.state.angle = 0;
betray_android_state.axis_matrix[0] = 1;
betray_android_state.axis_matrix[1] = 0;
betray_android_state.axis_matrix[2] = 0;
betray_android_state.axis_matrix[3] = 0;
betray_android_state.axis_matrix[4] = 0;
betray_android_state.axis_matrix[5] = 1;
betray_android_state.axis_matrix[6] = 0;
betray_android_state.axis_matrix[7] = 0;
betray_android_state.axis_matrix[8] = 0;
betray_android_state.axis_matrix[9] = 0;
betray_android_state.axis_matrix[10] = 1;
betray_android_state.axis_matrix[11] = 0;
betray_android_state.axis_matrix[12] = 0;
betray_android_state.axis_matrix[13] = 0;
betray_android_state.axis_matrix[14] = 0;
betray_android_state.axis_matrix[15] = 1;
betray_android_state.last_accelerometer[0] = 0;
betray_android_state.last_accelerometer[1] = 0;
betray_android_state.last_accelerometer[2] = 0;
betray_android_state.delta_accelerometer[0] = 0;
betray_android_state.delta_accelerometer[1] = 0;
betray_android_state.delta_accelerometer[2] = 0;
if( engine_init_surface(state) == FALSE )
exit(0);
betray_reshape_view(betray_android_state.width, betray_android_state.height);
return 0;
}
/**
* Tear down the EGL context currently associated with the display.
*/
static void engine_term_context(BetrayAndroidState *state)
{
if(state->display != EGL_NO_DISPLAY)
{
engine_term_surface(state);
if(state->context != EGL_NO_CONTEXT)
eglDestroyContext(state->display, state->context);
eglTerminate(state->display);
}
state->animating = 0;
state->display = EGL_NO_DISPLAY;
state->context = EGL_NO_CONTEXT;
state->surface = EGL_NO_SURFACE;
// Cube_tearDownGL();
}
static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
{
struct engine* engine = (struct engine*)app->userData;
BInputState *input;
uint count, i, j, id, max_pointer_count;
uint32 key_val, unicode;
input = betray_get_input_state();
max_pointer_count = betray_support_functionality(B_SF_POINTER_COUNT_MAX);
switch(AInputEvent_getType(event))
{
case AINPUT_EVENT_TYPE_MOTION:
{
count = AMotionEvent_getPointerCount(event);
for(i = 0; i < count && i < betray_support_functionality(B_SF_POINTER_COUNT_MAX); i++)
{
id = AMotionEvent_getPointerId(event, i);
for(j = 0; j < max_pointer_count && betray_android_state.pointers[j] != id; j++);
if(j == max_pointer_count)
{
j = betray_plugin_pointer_allocate(0, betray_android_state.device_id, 1,
2.0 * (AMotionEvent_getX(event, i) - ((float)betray_android_state.width / 2.0)) / (float)betray_android_state.width,
-2.0 * (AMotionEvent_getY(event, i) - ((float)betray_android_state.height / 2.0)) / (float)betray_android_state.width,
-1, NULL, "Touch", FALSE);
if(j < max_pointer_count)
betray_android_state.pointers[j] = id;
}
if(j < max_pointer_count)
{
BInputPointerState *p;
float origin[3] = {0, 0, 0};
uint action;
boolean button;
button = input->pointers[j].button[0];
action = AKeyEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK;
if(action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_POINTER_UP)
button = FALSE;
if(action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_POINTER_DOWN)
button = TRUE;
betray_plugin_pointer_set(j,
2.0 * (AMotionEvent_getX(event, i) - ((float)betray_android_state.width / 2.0)) / (float)betray_android_state.width,
-2.0 * (AMotionEvent_getY(event, i) - ((float)betray_android_state.height / 2.0)) / (float)betray_android_state.width,
-1, origin, &button);
}
}
}
break;
case AINPUT_EVENT_TYPE_KEY:
key_val = (uint32)AKeyEvent_getKeyCode(event);
unicode = betray_android_unicode_convert(AINPUT_EVENT_TYPE_KEY, key_val, AKeyEvent_getMetaState(event));
if(unicode == 0)
unicode = -1;
if(AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
betray_plugin_button_set(0, key_val, TRUE, unicode);
else
betray_plugin_button_set(0, key_val, FALSE, unicode);
break;
}
return 1;
}
/**
* Process the next main command.
*/
static void engine_handle_cmd(struct android_app* app, int32_t cmd)
{
struct engine* engine = (struct engine*)app->userData;
// printf("command %u\n", cmd);
switch(cmd)
{
case APP_CMD_SAVE_STATE:
// The system has asked us to save our current state. Do so.
betray_android_state.app->savedState = malloc(sizeof(struct saved_state));
*((struct saved_state*)betray_android_state.app->savedState) = betray_android_state.state;
betray_android_state.app->savedStateSize = sizeof(struct saved_state);
break;
case APP_CMD_INIT_WINDOW:
// The window is being shown, get it ready.
if(betray_android_state.context == NULL)
{
engine_init_context(&betray_android_state);
}
else
{
engine_init_surface(&betray_android_state);
}
break;
case APP_CMD_TERM_WINDOW:
// The window is being hidden or closed, clean it up.
engine_term_surface(&betray_android_state);
break;
case APP_CMD_WINDOW_RESIZED :
{
uint x_size, y_size;
eglQuerySurface(betray_android_state.display, betray_android_state.surface, EGL_WIDTH, &x_size);
eglQuerySurface(betray_android_state.display, betray_android_state.surface, EGL_HEIGHT, &y_size);
betray_reshape_view(x_size, y_size);
}
break;
/* case APP_CMD_WINDOW_RESIZED :
{
uint x_size, y_size;
eglQuerySurface(engine->display, engine->surface, EGL_WIDTH, &x_size);
eglQuerySurface(engine->display, engine->surface, EGL_HEIGHT, &y_size);
betray_reshape_view(x_size, y_size);
}
break; */
case APP_CMD_GAINED_FOCUS:
// When our app gains focus, we start monitoring the accelerometer.
if(betray_android_state.accelerometerSensor != NULL)
{
ASensorEventQueue_enableSensor(betray_android_state.sensorEventQueue, betray_android_state.accelerometerSensor);
ASensorEventQueue_setEventRate(betray_android_state.sensorEventQueue, betray_android_state.accelerometerSensor, (1000L / 60) * 1000);
}
if(betray_android_state.gyroscopicSensor != NULL)
{
ASensorEventQueue_enableSensor(betray_android_state.sensorEventQueue, betray_android_state.gyroscopicSensor);
ASensorEventQueue_setEventRate(betray_android_state.sensorEventQueue, betray_android_state.gyroscopicSensor, (1000L / 60) * 1000);
}
betray_android_state.animating = TRUE;
break;
case APP_CMD_LOST_FOCUS:
// When our app loses focus, we stop monitoring the accelerometer.
// This is to avoid consuming battery while not being used.
if(betray_android_state.accelerometerSensor != NULL)
ASensorEventQueue_disableSensor(betray_android_state.sensorEventQueue, betray_android_state.accelerometerSensor);
if(betray_android_state.gyroscopicSensor != NULL)
ASensorEventQueue_disableSensor(betray_android_state.sensorEventQueue, betray_android_state.gyroscopicSensor);
// Also stop animating.
betray_android_state.animating = FALSE;
break;
}
}
/**
* This is the main entry point of a native application that is using
* android_native_app_glue. It runs in its own thread, with its own
* event loop for receiving input events and doing other things.
*/
void android_start(struct android_app* state)
{
}
boolean betray_get_package_name( char* pTarget, size_t targetCapacity)
{
JavaVM lJavaVM = *betray_android_app_state->activity->vm;
JNIEnv* env = NULL;
lJavaVM->AttachCurrentThread(betray_android_app_state->activity->vm, &env, NULL);
jobject activity = betray_android_app_state->activity->clazz;
jclass activityClass = (*env)->GetObjectClass( env, activity );
jmethodID getPackageNameMethod = (*env)->GetMethodID( env, activityClass, "getPackageName", "()Ljava/lang/String;" );
jstring packageName = (jstring)(*env)->CallObjectMethod( env, activity, getPackageNameMethod );
const char* pPackageName = (*env)->GetStringUTFChars( env, packageName, 0 );
const size_t packageNameLength = strlen( pPackageName );
const boolean canCopy = ( packageNameLength + 1 > targetCapacity );
if( canCopy )
{
memcpy( pTarget, pPackageName, packageNameLength + 1 );
}
(*env)->ReleaseStringUTFChars( env, packageName, pPackageName );
return canCopy;
}
boolean b_android_init_display(uint *window_size_x, uint *window_size_y, char *name)
{
char packageName[256];
betray_get_package_name( packageName, sizeof( packageName ) );
// get_network();
memset(&betray_android_state, 0, sizeof(betray_android_state));
//FK: TODO: Check if a mouse is present and get correct id...
const uint max_pointer_count = betray_support_functionality(B_SF_POINTER_COUNT_MAX);
for( uint pointer_index = 0u; pointer_index < max_pointer_count; ++pointer_index )
{
betray_android_state.pointers[ pointer_index ] = -1;
}
betray_android_app_state->userData = &betray_android_state;
betray_android_app_state->onAppCmd = engine_handle_cmd;
betray_android_app_state->onInputEvent = engine_handle_input;
betray_android_state.surface = EGL_NO_SURFACE;
betray_android_state.app = betray_android_app_state;
// Prepare to monitor accelerometer ;
betray_android_state.device_id = betray_plugin_input_device_allocate(betray_plugin_user_allocate(), "Device");
betray_android_state.sensorManager = ASensorManager_getInstanceForPackage( packageName );
betray_android_state.accelerometerSensor = ASensorManager_getDefaultSensor(betray_android_state.sensorManager, ASENSOR_TYPE_ACCELEROMETER);
betray_android_state.gyroscopicSensor = ASensorManager_getDefaultSensor(betray_android_state.sensorManager, ASENSOR_TYPE_GYROSCOPE);
if(betray_android_state.gyroscopicSensor != NULL)
{
betray_android_state.axis_up = betray_plugin_axis_allocate(0, betray_android_state.device_id, "Orientation up", B_AXIS_SCREEN_UP, 3);
betray_android_state.axis_forward = betray_plugin_axis_allocate(0, betray_android_state.device_id, "Orinentation forward", B_AXIS_SCREEN_FORWARD, 3);
}
if(betray_android_state.accelerometerSensor != NULL)
{
betray_android_state.accelerometer = betray_plugin_axis_allocate(0, betray_android_state.device_id, "Accelerometer", B_AXIS_SCREEN_ACCELEROMETER, 3);
}
betray_android_state.sensorEventQueue = ASensorManager_createEventQueue(betray_android_state.sensorManager, betray_android_app_state->looper, LOOPER_ID_USER, NULL, NULL);
if(betray_android_app_state->savedState != NULL)
{
// We are starting with a previous saved state; restore from it.
betray_android_state.state = *(struct saved_state*)betray_android_app_state->savedState;
}
betray_android_state.animating = 1;
while(betray_android_state.surface == EGL_NO_SURFACE)
{
// Read all pending events.
int ident;
int events;
struct android_poll_source* source;
if((ident = ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0)
{
if (source != NULL)
source->process(betray_android_app_state, source);
if (betray_android_app_state->destroyRequested != 0)
return FALSE;
}
}
*window_size_x = betray_android_state.width;
*window_size_y = betray_android_state.height;
betray_reshape_view(betray_android_state.width, betray_android_state.height);
return TRUE;
}
void betray_launch_main_loop(void)
{
BInputState *input;
input = betray_get_input_state();
while(TRUE)
{
uint i, j;
int ident;
int events;
float vector[3], counter[3], m[16];
struct android_poll_source* source;
while((ident = ALooper_pollAll(betray_android_state.animating ? 0 : -1, NULL, &events, (void**)&source)) >= 0)
{
if(source != NULL)
source->process(betray_android_app_state, source);
if(ident == LOOPER_ID_USER)
{
ASensorEvent event;
while(ASensorEventQueue_getEvents(betray_android_state.sensorEventQueue, &event, 1) > 0)
{
switch(event.type)
{
case ASENSOR_TYPE_ACCELEROMETER :
betray_plugin_axis_set(betray_android_state.accelerometer, event.acceleration.x, event.acceleration.y, event.acceleration.z);
/* f_matrix_reverse4f(m, engine.axis_matrix);
vector[0] = m[4] * 0.995 + 0.005 * event.acceleration.x;
vector[1] = m[5] * 0.995 + 0.005 * event.acceleration.y;
vector[2] = m[6] * 0.995 + 0.005 * event.acceleration.z;
counter[0] = m[8];
counter[1] = m[9];
counter[2] = m[10];
f_matrixyzf(m, NULL, vector, counter);
f_matrix_reverse4f(engine.axis_matrix, m);
betray_plugin_axis_set(engine.axis_up, engine.axis_matrix[4], engine.axis_matrix[5], engine.axis_matrix[6]);
betray_plugin_axis_set(engine.axis_forward, engine.axis_matrix[8], engine.axis_matrix[9], engine.axis_matrix[10]);*/
break;
case ASENSOR_TYPE_GYROSCOPE :
vector[0] = betray_android_state.axis_matrix[4] + event.uncalibrated_gyro.x_uncalib * betray_android_state.axis_matrix[8] * 0.02 - event.uncalibrated_gyro.z_uncalib * betray_android_state.axis_matrix[0] * 0.02;
vector[1] = betray_android_state.axis_matrix[5] + event.uncalibrated_gyro.x_uncalib * betray_android_state.axis_matrix[9] * 0.02 - event.uncalibrated_gyro.z_uncalib * betray_android_state.axis_matrix[1] * 0.02;
vector[2] = betray_android_state.axis_matrix[6] + event.uncalibrated_gyro.x_uncalib * betray_android_state.axis_matrix[10] * 0.02 - event.uncalibrated_gyro.z_uncalib * betray_android_state.axis_matrix[2] * 0.02;
counter[0] = betray_android_state.axis_matrix[8] + event.uncalibrated_gyro.y_uncalib * betray_android_state.axis_matrix[0] * 0.02;
counter[1] = betray_android_state.axis_matrix[9] + event.uncalibrated_gyro.y_uncalib * betray_android_state.axis_matrix[1] * 0.02;
counter[2] = betray_android_state.axis_matrix[10] + event.uncalibrated_gyro.y_uncalib * betray_android_state.axis_matrix[2] * 0.02;
f_matrixyzf(betray_android_state.axis_matrix, NULL, vector, counter);
betray_plugin_axis_set(betray_android_state.axis_up, betray_android_state.axis_matrix[4], betray_android_state.axis_matrix[5], betray_android_state.axis_matrix[6]);
betray_plugin_axis_set(betray_android_state.axis_forward, betray_android_state.axis_matrix[8], betray_android_state.axis_matrix[9], betray_android_state.axis_matrix[10]);
break;
}
}
}
if(betray_android_app_state->destroyRequested != 0)
return;
}
betray_time_update();
if(betray_android_state.animating)
{
BInputState *input;
input = betray_get_input_state();
if(betray_android_state.animating)
betray_action(BAM_EVENT);
if(betray_android_state.display != NULL)
{
betray_action(BAM_DRAW);
eglSwapBuffers(betray_android_state.display, betray_android_state.surface);
}
input->frame_number++;
// betray_event_reset(input);
}
betray_action(BAM_MAIN);
input->button_event_count = 0;
for(i = 0; i < 16; i++)
{
if(betray_android_state.pointers[i] != -1)
{
input->pointers[i].delta_pointer_x = 0;
input->pointers[i].delta_pointer_y = 0;
for(j = 0; j < input->pointers[i].button_count; j++)
input->pointers[i].last_button[j] = input->pointers[i].button[j];
}
}
}
}
extern int main(int argc, char **argv);
void android_main(struct android_app* state)
{
betray_android_app_state = state;
// android_start(state);
char *argument = "file.exe";
betray_set_directory();
betray_immersive_mode();
main(1, &argument);
engine_term_context(&betray_android_state);
}