fteqw/engine/client/sys_droid.c

1320 lines
42 KiB
C

#include <jni.h>
#include <errno.h>
#include <android/log.h>
#include "quakedef.h"
#include <unistd.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pthread.h>
#include "glquake.h"
#ifndef ANDROID
#error ANDROID wasnt defined
#endif
#include <android/keycodes.h>
#include <android/native_window_jni.h>
#ifndef isDedicated
#ifdef SERVERONLY
qboolean isDedicated = true;
#else
qboolean isDedicated = false;
#endif
#endif
extern int r_blockvidrestart;
float sys_dpi_x, sys_dpi_y;
static void *sys_memheap;
//static unsigned int vibrateduration;
static char sys_binarydir[MAX_OSPATH];
static char sys_basedir[MAX_OSPATH];
static char sys_basepak[MAX_OSPATH];
extern jmp_buf host_abort;
extern qboolean r_forceheadless;
static qboolean r_forcevidrestart;
ANativeWindow *sys_nativewindow;
//cvar_t sys_vibrate = CVARFD("sys_vibrate", "1", CVAR_ARCHIVE, "Enables the system vibrator for damage events and such things. The value provided is a duration scaler.");
static cvar_t sys_osk = CVAR("sys_osk", "0"); //to be toggled
static cvar_t sys_keepscreenon = CVARFD("sys_keepscreenon", "1", CVAR_ARCHIVE, "If set, the screen will never darken. This might cost some extra battery power, but then so will running a 3d engine."); //to be toggled
cvar_t sys_orientation = CVARFD("sys_orientation", "landscape", CVAR_ARCHIVE, "Specifies what angle to render quake at.\nValid values are: sensor (autodetect), landscape, portrait, reverselandscape, reverseportrait");
extern cvar_t vid_conautoscale;
void VID_Register(void);
static qboolean sys_wantshutdown;
static JavaVM* sys_javavm;
static jobject *sys_activity;
static jobject *sys_cursurface; //surface we're currently trying to draw to
static jobject *sys_cursholder; //silly android junk
static jobject *sys_newsurface; //surface we're meant to be switching our gl context to
static jobject *sys_newsholder; //silly android junk
static void *sys_mainthread;
static void *sys_mainconditional;
#undef LOGI
#undef LOGW
#undef LOGE
#ifndef LOGI
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, DISTRIBUTION"Droid", __VA_ARGS__))
#endif
#ifndef LOGW
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, DISTRIBUTION"Droid", __VA_ARGS__))
#endif
#ifndef LOGE
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, DISTRIBUTION"Droid", __VA_ARGS__))
#endif
void INS_Move(void)
{
}
void INS_Commands(void)
{
}
void INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid))
{
}
void INS_Init(void)
{
}
void INS_ReInit(void)
{
}
void INS_Shutdown(void)
{
}
void INS_Rumble(int joy, uint16_t amp_low, uint16_t amp_high, uint32_t duration)
{
}
void INS_RumbleTriggers(int joy, uint16_t left, uint16_t right, uint32_t duration)
{
}
void INS_SetLEDColor(int id, vec3_t color)
{
}
void INS_SetTriggerFX(int id, const void *data, size_t size)
{
}
void Sys_Vibrate(float count)
{
// if (count < 0)
// count = 0;
// vibrateduration += count*10*sys_vibrate.value;
}
qboolean INS_KeyToLocalName(int qkey, char *buf, size_t bufsize)
{ //onscreen keyboard? erk.
return false;
}
static int mapkey(int androidkey)
{
switch(androidkey)
{
case AKEYCODE_SOFT_LEFT: return K_LEFTARROW;
case AKEYCODE_SOFT_RIGHT: return K_RIGHTARROW;
case AKEYCODE_HOME: return K_HOME; //not quite right, but w/e
case AKEYCODE_BACK: return K_ESCAPE;
// case AKEYCODE_CALL: return K_;
// case AKEYCODE_ENDCALL: return K_;
case AKEYCODE_0: return '0';
case AKEYCODE_1: return '1';
case AKEYCODE_2: return '2';
case AKEYCODE_3: return '3';
case AKEYCODE_4: return '4';
case AKEYCODE_5: return '5';
case AKEYCODE_6: return '6';
case AKEYCODE_7: return '7';
case AKEYCODE_8: return '8';
case AKEYCODE_9: return '9';
case AKEYCODE_STAR: return '*';
case AKEYCODE_POUND: return '#'; //americans don't know what a pound symbol looks like.
case AKEYCODE_DPAD_UP: return K_GP_DPAD_UP;
case AKEYCODE_DPAD_DOWN: return K_GP_DPAD_DOWN;
case AKEYCODE_DPAD_LEFT: return K_GP_DPAD_LEFT;
case AKEYCODE_DPAD_RIGHT: return K_GP_DPAD_RIGHT;
case AKEYCODE_DPAD_CENTER: return K_ENTER;
case AKEYCODE_VOLUME_UP: return K_VOLUP;
case AKEYCODE_VOLUME_DOWN: return K_VOLDOWN;
case AKEYCODE_POWER: return K_POWER;
// case AKEYCODE_CAMERA: return K_CAMERA;
// case AKEYCODE_CLEAR: return K_;
case AKEYCODE_A: return 'a';
case AKEYCODE_B: return 'b';
case AKEYCODE_C: return 'c';
case AKEYCODE_D: return 'd';
case AKEYCODE_E: return 'e';
case AKEYCODE_F: return 'f';
case AKEYCODE_G: return 'g';
case AKEYCODE_H: return 'h';
case AKEYCODE_I: return 'i';
case AKEYCODE_J: return 'j';
case AKEYCODE_K: return 'k';
case AKEYCODE_L: return 'l';
case AKEYCODE_M: return 'm';
case AKEYCODE_N: return 'n';
case AKEYCODE_O: return 'o';
case AKEYCODE_P: return 'p';
case AKEYCODE_Q: return 'q';
case AKEYCODE_R: return 'r';
case AKEYCODE_S: return 's';
case AKEYCODE_T: return 't';
case AKEYCODE_U: return 'u';
case AKEYCODE_V: return 'v';
case AKEYCODE_W: return 'w';
case AKEYCODE_X: return 'x';
case AKEYCODE_Y: return 'y';
case AKEYCODE_Z: return 'z';
case AKEYCODE_COMMA: return ',';
case AKEYCODE_PERIOD: return '.';
case AKEYCODE_ALT_LEFT: return K_LALT;
case AKEYCODE_ALT_RIGHT: return K_RALT;
case AKEYCODE_SHIFT_LEFT: return K_LSHIFT;
case AKEYCODE_SHIFT_RIGHT: return K_RSHIFT;
case AKEYCODE_TAB: return K_TAB;
case AKEYCODE_SPACE: return K_SPACE;
// case AKEYCODE_SYM: return K_IMEMODE_SYMBOL;
// case AKEYCODE_EXPLORER: return K_MM_APP_FILES;
// case AKEYCODE_ENVELOPE: return K_MM_APP_EMAIL;
case AKEYCODE_ENTER: return K_ENTER;
case AKEYCODE_DEL: return K_BACKSPACE;
case AKEYCODE_GRAVE: return '`';
case AKEYCODE_MINUS: return '-';
case AKEYCODE_EQUALS: return '=';
case AKEYCODE_LEFT_BRACKET: return '[';
case AKEYCODE_RIGHT_BRACKET:return ']';
case AKEYCODE_BACKSLASH: return '#'; //this kinda sums up keymaps like this.
case AKEYCODE_SEMICOLON: return ';';
case AKEYCODE_APOSTROPHE: return '\'';
case AKEYCODE_SLASH: return '/';
case AKEYCODE_AT: return '@';
// case AKEYCODE_NUM: return K_;
// case AKEYCODE_HEADSETHOOK: return K_;
// case AKEYCODE_FOCUS: return K_CAMERAFOCUS;
case AKEYCODE_PLUS: return '+';
case AKEYCODE_MENU: return K_APP;
// case AKEYCODE_NOTIFICATION: return K_;
case AKEYCODE_SEARCH: return K_SEARCH;
case AKEYCODE_MEDIA_PLAY_PAUSE: return K_MM_TRACK_PLAYPAUSE;
case AKEYCODE_MEDIA_STOP: return K_MM_TRACK_STOP;
case AKEYCODE_MEDIA_NEXT: return K_MM_TRACK_NEXT;
case AKEYCODE_MEDIA_PREVIOUS: return K_MM_TRACK_PREV;
// case AKEYCODE_MEDIA_REWIND: return K_MM_TRACK_REWIND;
// case AKEYCODE_MEDIA_FAST_FORWARD: return K_MM_TRACK_FASTFWD;
case AKEYCODE_MUTE: return K_MM_VOLUME_MUTE;
case AKEYCODE_PAGE_UP: return K_PGUP;
case AKEYCODE_PAGE_DOWN: return K_PGDN;
// case AKEYCODE_PICTSYMBOLS: return K_IMEMODE_EMOJI;
// case AKEYCODE_SWITCH_CHARSET: return K_IMEMODE_CHARSET;
case AKEYCODE_BUTTON_A: return K_GP_A;
case AKEYCODE_BUTTON_B: return K_GP_B;
// case AKEYCODE_BUTTON_C: return K_GP_C;
case AKEYCODE_BUTTON_X: return K_GP_X;
case AKEYCODE_BUTTON_Y: return K_GP_Y;
// case AKEYCODE_BUTTON_Z: return K_GP_Z;
case AKEYCODE_BUTTON_L1: return K_GP_LEFT_SHOULDER;
case AKEYCODE_BUTTON_R1: return K_GP_RIGHT_SHOULDER;
case AKEYCODE_BUTTON_L2: return K_GP_LEFT_TRIGGER;
case AKEYCODE_BUTTON_R2: return K_GP_RIGHT_TRIGGER;
case AKEYCODE_BUTTON_THUMBL: return K_GP_LEFT_STICK;
case AKEYCODE_BUTTON_THUMBR: return K_GP_RIGHT_STICK;
case AKEYCODE_BUTTON_START: return K_GP_START;
case AKEYCODE_BUTTON_SELECT: return K_GP_BACK;
case AKEYCODE_BUTTON_MODE: return K_GP_GUIDE;
//And this is the part where you start to see quite why I hate android so much
case 111/*AKEYCODE_ESCAPE*/: return K_ESCAPE;
case 112/*AKEYCODE_FORWARD_DEL*/: return K_DEL;
case 113/*AKEYCODE_CTRL_LEFT*/: return K_LCTRL;
case 114/*AKEYCODE_CTRL_RIGHT*/: return K_RCTRL;
case 115/*AKEYCODE_CAPS_LOCK*/: return K_CAPSLOCK;
case 116/*AKEYCODE_SCROLL_LOCK*/: return K_SCRLCK;
case 117/*AKEYCODE_META_LEFT*/: return K_LWIN;
case 118/*AKEYCODE_META_RIGHT*/: return K_RWIN;
// case 119/*AKEYCODE_FUNCTION*/: return K_FUNCTION;
// case 120/*AKEYCODE_SYSRQ*/: return K_SYSRQ;
case 121/*AKEYCODE_BREAK*/: return K_PAUSE;
case 122/*AKEYCODE_MOVE_HOME*/: return K_HOME;
case 123/*AKEYCODE_MOVE_END*/: return K_END;
case 124/*AKEYCODE_INSERT*/: return K_INS;
// case 125/*AKEYCODE_FORWARD*/: return K_FORWARD;
// case 126/*AKEYCODE_MEDIA_PLAY*/: return K_MEDIA_PLAY;
// case 127/*AKEYCODE_MEDIA_PAUSE*/: return K_MEDIA_PAUSE;
// case 128/*AKEYCODE_MEDIA_CLOSE*/: return K_MEDIA_CLOSE;
// case 129/*AKEYCODE_MEDIA_EJECT*/: return K_MEDIA_EJECT;
// case 130/*AKEYCODE_MEDIA_RECORD*/: return K_MEDIA_RECORD;
case 131/*AKEYCODE_F1*/: return K_F1;
case 132/*AKEYCODE_F2*/: return K_F2;
case 133/*AKEYCODE_F3*/: return K_F3;
case 134/*AKEYCODE_F4*/: return K_F4;
case 135/*AKEYCODE_F5*/: return K_F5;
case 136/*AKEYCODE_F6*/: return K_F6;
case 137/*AKEYCODE_F7*/: return K_F7;
case 138/*AKEYCODE_F8*/: return K_F8;
case 139/*AKEYCODE_F9*/: return K_F9;
case 140/*AKEYCODE_F10*/: return K_F10;
case 141/*AKEYCODE_F11*/: return K_F11;
case 142/*AKEYCODE_F12*/: return K_F12;
case 143/*AKEYCODE_NUM_LOCK*/: return K_KP_NUMLOCK;
case 144/*AKEYCODE_NUMPAD_0*/: return K_KP_INS;
case 145/*AKEYCODE_NUMPAD_1*/: return K_KP_END;
case 146/*AKEYCODE_NUMPAD_2*/: return K_KP_DOWNARROW;
case 147/*AKEYCODE_NUMPAD_3*/: return K_KP_PGDN;
case 148/*AKEYCODE_NUMPAD_4*/: return K_KP_LEFTARROW;
case 149/*AKEYCODE_NUMPAD_5*/: return K_KP_5;
case 150/*AKEYCODE_NUMPAD_6*/: return K_KP_RIGHTARROW;
case 151/*AKEYCODE_NUMPAD_7*/: return K_KP_HOME;
case 152/*AKEYCODE_NUMPAD_8*/: return K_KP_UPARROW;
case 153/*AKEYCODE_NUMPAD_9*/: return K_KP_PGUP;
case 154/*AKEYCODE_NUMPAD_DIVIDE*/: return K_KP_SLASH;
case 155/*AKEYCODE_NUMPAD_MULTIPLY*/: return K_KP_STAR;
case 156/*AKEYCODE_NUMPAD_SUBTRACT*/: return K_KP_MINUS;
case 157/*AKEYCODE_NUMPAD_ADD*/: return K_KP_PLUS;
case 158/*AKEYCODE_NUMPAD_DOT*/: return K_KP_DEL;
// case 159/*AKEYCODE_NUMPAD_COMMA*/: return K_KP_COMMA;
case 160/*AKEYCODE_NUMPAD_ENTER*/: return K_KP_ENTER;
case 161/*AKEYCODE_NUMPAD_EQUALS*/: return K_KP_EQUALS;
// case 162/*AKEYCODE_NUMPAD_LEFT_PAREN*/: return K_KP_;
// case 163/*AKEYCODE_NUMPAD_RIGHT_PAREN*/:return K_KP_;
// case 164/*AKEYCODE_VOLUME_MUTE*/: return K_;
// case 165/*AKEYCODE_INFO*/: return K_;
// case 166/*AKEYCODE_CHANNEL_UP*/: return K_;
// case 167/*AKEYCODE_CHANNEL_DOWN*/: return K_;
// case 168/*AKEYCODE_ZOOM_IN*/: return K_;
// case 169/*AKEYCODE_ZOOM_OUT*/: return K_;
// case 170/*AKEYCODE_TV*/: return K_;
// case 171/*AKEYCODE_WINDOW*/: return K_;
// case 172/*AKEYCODE_GUIDE*/: return K_;
// case 173/*AKEYCODE_DVR*/: return K_;
// case 174/*AKEYCODE_BOOKMARK*/: return K_;
// case 175/*AKEYCODE_CAPTIONS*/: return K_;
// case 176/*AKEYCODE_SETTINGS*/: return K_;
// case 177/*AKEYCODE_TV_POWER*/: return K_;
// case 178/*AKEYCODE_TV_INPUT*/: return K_;
// case 179/*AKEYCODE_STB_POWER*/: return K_;
// case 180/*AKEYCODE_STB_INPUT*/: return K_;
// case 181/*AKEYCODE_AVR_POWER*/: return K_;
// case 182/*AKEYCODE_AVR_INPUT*/: return K_;
// case 183/*AKEYCODE_PROG_RED*/: return K_;
// case 184/*AKEYCODE_PROG_GREEN*/: return K_;
// case 185/*AKEYCODE_PROG_YELLOW*/: return K_;
// case 186/*AKEYCODE_PROG_BLUE*/: return K_;
// case 187/*AKEYCODE_APP_SWITCH*/: return K_;
case 188/*AKEYCODE_BUTTON_1*/: return K_AUX1;
case 189/*AKEYCODE_BUTTON_2*/: return K_AUX2;
case 190/*AKEYCODE_BUTTON_3*/: return K_AUX3;
case 191/*AKEYCODE_BUTTON_4*/: return K_AUX4;
case 192/*AKEYCODE_BUTTON_5*/: return K_AUX5;
case 193/*AKEYCODE_BUTTON_6*/: return K_AUX6;
case 194/*AKEYCODE_BUTTON_7*/: return K_AUX7;
case 195/*AKEYCODE_BUTTON_8*/: return K_AUX8;
case 196/*AKEYCODE_BUTTON_9*/: return K_AUX9;
case 197/*AKEYCODE_BUTTON_10*/: return K_AUX10;
case 198/*AKEYCODE_BUTTON_11*/: return K_AUX11;
case 199/*AKEYCODE_BUTTON_12*/: return K_AUX12;
case 200/*AKEYCODE_BUTTON_13*/: return K_AUX13;
case 201/*AKEYCODE_BUTTON_14*/: return K_AUX14;
case 202/*AKEYCODE_BUTTON_15*/: return K_AUX15;
case 203/*AKEYCODE_BUTTON_16*/: return K_AUX16;
// case 204/*AKEYCODE_LANGUAGE_SWITCH*/: return K_; //like shift+space
// case 205/*AKEYCODE_MANNER_MODE*/: return K_; //toggles silent-mode
// case 206/*AKEYCODE_3D_MODE*/: return K_;
// case 207/*AKEYCODE_CONTACTS*/: return K_MM_APP_CONTACTS;
// case 208/*AKEYCODE_CALENDAR*/: return K_MM_APP_CALENDAR;
// case 209/*AKEYCODE_MUSIC*/: return K_MM_APP_MUSIC;
// case 210/*AKEYCODE_CALCULATOR*/: return K_MM_APP_CALCULATOR;
// case 211/*AKEYCODE_ZENKAKU_HANKAKU*/: return K_IME_;
// case 212/*AKEYCODE_EISU*/: return K_IME_;
// case 213/*AKEYCODE_MUHENKAN*/: return K_IME_;
// case 214/*AKEYCODE_HENKAN*/: return K_IME_;
// case 215/*AKEYCODE_KATAKANA_HIRAGANA*/: return K_IME_;
// case 216/*AKEYCODE_YEN*/: return K_;
// case 217/*AKEYCODE_RO*/: return K_;
// case 218/*AKEYCODE_KANA*/: return K_;
// case 219/*AKEYCODE_ASSIST*/: return K_MM_APP_ASSIST;
// case 220/*AKEYCODE_BRIGHTNESS_DOWN*/: return K_;
// case 221/*AKEYCODE_BRIGHTNESS_UP*/: return K_;
// case 222/*AKEYCODE_MEDIA_AUDIO_TRACK*/: return K_;
// case 223/*AKEYCODE_SLEEP*/: return K_;
// case 224/*AKEYCODE_WAKEUP*/: return K_;
// case 225/*AKEYCODE_PAIRING*/: return K_;
// case 226/*AKEYCODE_MEDIA_TOP_MENU*/: return K_;
// case 227/*AKEYCODE_11*/: return K_;
// case 228/*AKEYCODE_12*/: return K_;
// case 229/*AKEYCODE_LAST_CHANNEL*/: return K_;
// case 230/*AKEYCODE_TV_DATA_SERVICE*/: return K_;
// case 231/*AKEYCODE_VOICE_ASSIST*/: return K_MM_APP_VOICE;
// case 232/*AKEYCODE_TV_RADIO_SERVICE*/: return K_;
// case 233/*AKEYCODE_TV_TELETEXT*/: return K_;
// case 234/*AKEYCODE_TV_NUMBER_ENTRY*/: return K_;
// case 235/*AKEYCODE_TV_TERRESTRIAL_ANALOG*/: return K_;
// case 236/*AKEYCODE_TV_TERRESTRIAL_DIGITAL*/: return K_;
// case 237/*AKEYCODE_TV_SATELLITE*/: return K_;
// case 238/*AKEYCODE_TV_SATELLITE_BS*/: return K_;
// case 239/*AKEYCODE_TV_SATELLITE_CS*/: return K_;
// case 240/*AKEYCODE_TV_SATELLITE_SERVICE*/: return K_;
// case 241/*AKEYCODE_TV_NETWORK*/: return K_;
// case 242/*AKEYCODE_TV_ANTENNA_CABLE*/: return K_;
// case 243/*AKEYCODE_TV_INPUT_HDMI_1*/: return K_;
// case 244/*AKEYCODE_TV_INPUT_HDMI_2*/: return K_;
// case 245/*AKEYCODE_TV_INPUT_HDMI_3*/: return K_;
// case 246/*AKEYCODE_TV_INPUT_HDMI_4*/: return K_;
// case 247/*AKEYCODE_TV_INPUT_COMPOSITE_1*/: return K_;
// case 248/*AKEYCODE_TV_INPUT_COMPOSITE_2*/: return K_;
// case 249/*AKEYCODE_TV_INPUT_COMPONENT_1*/: return K_;
// case 250/*AKEYCODE_TV_INPUT_COMPONENT_2*/: return K_;
// case 251/*AKEYCODE_TV_INPUT_VGA_1*/: return K_;
// case 252/*AKEYCODE_TV_AUDIO_DESCRIPTION*/: return K_;
// case 253/*AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP*/: return K_;
// case 254/*AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN*/: return K_;
// case 255/*AKEYCODE_TV_ZOOM_MODE*/: return K_;
// case 256/*AKEYCODE_TV_CONTENTS_MENU*/: return K_;
// case 257/*AKEYCODE_TV_MEDIA_CONTEXT_MENU*/: return K_;
// case 258/*AKEYCODE_TV_TIMER_PROGRAMMING*/: return K_;
// case 259/*AKEYCODE_HELP*/: return K_;
// case 260/*AKEYCODE_NAVIGATE_PREVIOUS*/: return K_;
// case 261/*AKEYCODE_NAVIGATE_NEXT*/: return K_;
// case 262/*AKEYCODE_NAVIGATE_IN*/: return K_;
// case 263/*AKEYCODE_NAVIGATE_OUT*/: return K_;
// case 264/*AKEYCODE_STEM_PRIMARY*/: return K_;
// case 265/*AKEYCODE_STEM_1*/: return K_;
// case 266/*AKEYCODE_STEM_2*/: return K_;
// case 267/*AKEYCODE_STEM_3*/: return K_;
// case 268/*AKEYCODE_DPAD_UP_LEFT*/: return K_UPLEFTARROW;
// case 269/*AKEYCODE_DPAD_DOWN_LEFT*/: return K_DOWNLEFTARROW;
// case 270/*AKEYCODE_DPAD_UP_RIGHT*/: return K_UPRIGHTARROW;
// case 271/*AKEYCODE_DPAD_DOWN_RIGHT*/: return K_DOWNRIGHTARROW;
// case 272/*AKEYCODE_MEDIA_SKIP_FORWARD*/: return K_;
// case 273/*AKEYCODE_MEDIA_SKIP_BACKWARD*/: return K_;
// case 274/*AKEYCODE_MEDIA_STEP_FORWARD*/: return K_;
// case 275/*AKEYCODE_MEDIA_STEP_BACKWARD*/: return K_;
// case 276/*AKEYCODE_SOFT_SLEEP*/: return K_;
// case 277/*AKEYCODE_CUT*/: return K_;
// case 278/*AKEYCODE_COPY*/: return K_;
// case 279/*AKEYCODE_PASTE*/: return K_;
case 280/*KEYCODE_SYSTEM_NAVIGATION_UP*/: return K_UPARROW;
case 281/*KEYCODE_SYSTEM_NAVIGATION_DOWN*/: return K_DOWNARROW;
case 282/*KEYCODE_SYSTEM_NAVIGATION_LEFT*/: return K_LEFTARROW;
case 283/*KEYCODE_SYSTEM_NAVIGATION_RIGHT*/: return K_RIGHTARROW;
// case 284/*AKEYCODE_ALL_APPS */: return K_;
// case 285/*AKEYCODE_REFRESH */: return K_;
default:
Con_DPrintf("Android keycode %i is not supported\n", androidkey);
}
return 0;
}
#if 0
static void run_intent_url(void)
{
jobject act = sys_activity;
JNIEnv *jni;
if (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))
{
jobject intent = (*jni)->CallObjectMethod(jni, act, (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), "getIntent", "()Landroid/content/Intent;"));
if (intent)
{
jstring data = (*jni)->CallObjectMethod(jni, intent, (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, intent), "getDataString", "()Ljava/lang/String;"));
if (data)
{
const char *url = (*jni)->GetStringUTFChars(jni, data, NULL);
if (url)
{
if (!strncmp(url, "content:", 8))
{
Con_Printf(CON_ERROR"Content uris are not supported\n");
/*Java:
Cursor cursor = this.getContentResolver().query(data, null, null, null, null);
cursor.moveToFirst();
String myloc = cursor.getString(0);
cursor.close();
*/
}
else
Host_RunFile(url, strlen(url), NULL);
(*jni)->ReleaseStringUTFChars(jni, data, url);
}
}
}
//FIXME: do we need to release methodids/objects?
(*sys_javavm)->DetachCurrentThread(sys_javavm);
}
}
#endif
static qboolean read_apk_path(char *out, size_t outsize)
{
qboolean res = false;
jobject act = sys_activity;
JNIEnv *jni;
if (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))
{
jstring result = (*jni)->CallObjectMethod(jni, act, (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), "getPackageCodePath", "()Ljava/lang/String;"));
const char *tmp = (*jni)->GetStringUTFChars(jni, result, NULL);
if (tmp)
{
res = true;
Q_strncpyz(out, tmp, outsize);
(*jni)->ReleaseStringUTFChars(jni, result, tmp);
}
//FIXME: do we need to release methodids/objects?
(*sys_javavm)->DetachCurrentThread(sys_javavm);
}
return res;
}
static void setsoftkeyboard(int flags)
{ //the NDK is unusably buggy when it comes to keyboards, so call into java.
jobject act = sys_activity;
JNIEnv *jni;
if (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))
{
jmethodID func = (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), "showKeyboard", "(I)V" );
if (func)
(*jni)->CallVoidMethod(jni, act, func, flags);
(*sys_javavm)->DetachCurrentThread(sys_javavm);
}
}
static void showMessageAndQuit(const char *errormsg)
{ //no nice way to do this from native.
jobject act = sys_activity;
JNIEnv *jni;
if (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))
{
jmethodID func = (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), "showMessageAndQuit", "(Ljava/lang/String;)V" );
if (func)
(*jni)->CallVoidMethod(jni, act, func, (*jni)->NewStringUTF(jni, errormsg));
(*sys_javavm)->DetachCurrentThread(sys_javavm);
}
}
static void updateOrientation(const char *neworientation)
{ //no nice way to do this from native.
jobject act = sys_activity;
JNIEnv *jni;
if (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))
{
jmethodID func = (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), "updateOrientation", "(Ljava/lang/String;)V" );
if (func)
(*jni)->CallVoidMethod(jni, act, func, (*jni)->NewStringUTF(jni, neworientation));
(*sys_javavm)->DetachCurrentThread(sys_javavm);
}
}
static void updateScreenKeepOn(jboolean keepon)
{ //the NDK is unusably buggy when it comes to keyboards, so call into java.
jobject act = sys_activity;
JNIEnv *jni;
if (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))
{
jmethodID func = (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), "updateScreenKeepOn", "(Z)V" );
if (func)
(*jni)->CallVoidMethod(jni, act, func, keepon);
(*sys_javavm)->DetachCurrentThread(sys_javavm);
}
}
static void setCursorVisibility(jboolean visible)
{ //this is meant to use the nvidia-added setCursorVisibility function
//but its fatal if it doesn't exist, and it doesn't seem to exist.
#if 0
jobject act = sys_activity;
JNIEnv *jni;
if (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))
{
jobject inputManager = NULL;
jmethodID setvis = NULL;
jmethodID func = (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;" );
if (func)
inputManager = (*jni)->CallObjectMethod(jni, act, func, (*jni)->NewStringUTF(jni, "input"));
if (inputManager)
setvis = (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, inputManager), "setCursorVisibility", "(Z)V" );
if (setvis)
(*jni)->CallVoidMethod(jni, inputManager, setvis, visible);
(*sys_javavm)->DetachCurrentThread(sys_javavm);
}
#endif
}
static void FTENativeActivity_keypress(JNIEnv *env, jobject this, jint devid, jboolean down, jint keycode, jint unicode)
{
int qkeycode = mapkey(keycode);
// Sys_Printf("FTENativeActivity_keypress: d=%i s=%i a=%i,q=%i u=%i\n", devid, down, keycode, qkeycode, unicode);
if (devid < 0)
devid = 0;
IN_KeyEvent(devid, down, qkeycode, unicode);
}
static jboolean FTENativeActivity_wantrelative(JNIEnv *env, jobject this)
{
if (!in_windowed_mouse.ival) //emulators etc have no grabs so we're effectively always windowed in such situations.
return false;
return !Key_MouseShouldBeFree();
}
static void FTENativeActivity_mousepress(JNIEnv *env, jobject this, jint devid, jint buttonbits)
{
static int heldbuttons;
jint changed = buttonbits^heldbuttons;
// Sys_Printf("FTENativeActivity_mousepress: d=%i bits=%x (changed=%x)\n", devid, buttonbits, changed);
static int qbutton[] = {
K_MOUSE1, //primary
K_MOUSE2, //secondary
K_MOUSE3, //tertiary
K_MOUSE4, //back
K_MOUSE5, //forward
K_MOUSE1, //stylus_primary
K_MOUSE2, //stylus_secondary
};
size_t i;
if (devid < 0)
devid = 0;
heldbuttons = buttonbits;
if (changed)
for (i = 0; i < countof(qbutton); i++)
{
if (changed&(1<<i))
IN_KeyEvent(devid, buttonbits&(1<<i), qbutton[i], 0);
}
}
static void FTENativeActivity_motion(JNIEnv *env, jobject this, jint ptrid, jint act, jfloat x, jfloat y, jfloat z, jfloat size)
{
if (ptrid < 0)
ptrid = 0;
// Sys_Printf("FTENativeActivity_motion: d=%i a=%i x=%f y=%f z=%f s=%f\n", ptrid, act, x, y, z, size);
switch(act)
{
case 2: //mouse down
case 3: //mouse up
IN_KeyEvent(ptrid, act==2, K_MOUSE1, 0);
break;
case 1: //relative motion
case 0: //absolute motion (android sucks)
IN_MouseMove(ptrid, act==0, x, y, z, size);
break;
};
}
static void FTENativeActivity_axis(JNIEnv *env, jobject this, jint devid, jint axis, jfloat value)
{
if (devid < 0)
devid = 0;
IN_JoystickAxisEvent(devid, axis, value);
}
//static void FTENativeActivity_accelerometer(JNIEnv *env, jobject obj, jint devid, jfloat x, jfloat y, jfloat z)
//{
// IN_Accelerometer(devid, x, y, z);
//}
//static void FTENativeActivity_gryoscope(JNIEnv *env, jobject obj, jint devid, jfloat pitch, jfloat yaw, jfloat roll)
//{
// IN_Gyroscope(devid, pitch, yaw, roll);
//}
static int FTEDroid_MainThread(void *arg)
{
int osk = 0, wantgrabs = 0, t;
double newtime,oldtime=Sys_DoubleTime(), time, sleeptime;
r_forceheadless = true;
sys_nativewindow = NULL;
vid.activeapp = true;
if (!host_initialized)
{
static const char *args [] =
{
"ftedroid", /*binary name, not really meaningful*/
"-basepack",
sys_basepak, /*filled in later*/
"",
""
};
static quakeparms_t parms;
if (sys_memheap)
free(sys_memheap);
memset(&parms, 0, sizeof(parms));
parms.binarydir = sys_binarydir;
parms.basedir = sys_basedir; /*filled in later*/
parms.argc = read_apk_path(sys_basepak, sizeof(sys_basepak))?3:1;
parms.argv = args;
#ifdef CONFIG_MANIFEST_TEXT
parms.manifest = CONFIG_MANIFEST_TEXT;
#endif
sys_dpi_x = 72; //no idea
sys_dpi_y = 72; //no idea
Sys_Printf("Starting up (apk=%s, usr=%s)\n", sys_basepak, parms.basedir);
VID_Register();
COM_InitArgv(parms.argc, parms.argv);
TL_InitLanguages(sys_basedir);
Host_Init(&parms);
Sys_Printf("Host Inited\n");
}
else
Sys_Printf("Restarting up!\n");
sys_orientation.modified = false;
updateOrientation(sys_orientation.string);
sys_keepscreenon.modified = false;
updateScreenKeepOn(sys_keepscreenon.ival);
// run_intent_url();
/*if (state->savedState != NULL)
{ //oh look, we're pretending to already be running...
//oh.
}*/
//we're sufficiently done loading. let the ui thread resume.
Sys_LockConditional(sys_mainconditional);
Sys_ConditionSignal(sys_mainconditional);
Sys_UnlockConditional(sys_mainconditional);
while (!sys_wantshutdown)
{
//handle things if the UI thread is blocking for us (video restarts)
Sys_LockConditional(sys_mainconditional);
if (r_forcevidrestart)
{
ANativeWindow *oldwnd = NULL;
jobject oldholder = NULL;
jobject oldsurf = NULL;
JNIEnv *env = NULL;
if (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &env, NULL))
{
oldholder = sys_cursholder;
sys_cursholder = (*env)->NewGlobalRef(env, sys_newsholder);
oldsurf = sys_cursurface;
sys_cursurface = (*env)->NewGlobalRef(env, sys_newsurface);
oldwnd = sys_nativewindow;
if (sys_cursurface)
sys_nativewindow = ANativeWindow_fromSurface(env, sys_cursurface);
else
sys_nativewindow = NULL;
ANativeWindow_acquire(sys_nativewindow);
}
r_forceheadless = r_forcevidrestart&1;
r_forcevidrestart = 0;
Sys_ConditionSignal(sys_mainconditional); //let the java ui thread thread wake up now that we've got a handle to the new+old surfaces
Sys_UnlockConditional(sys_mainconditional);
LOGI("Video Restart...\n");
R_RestartRenderer_f();
LOGI("Video Restarted...\n");
//main thread can wake up now.
if (oldwnd)
ANativeWindow_release(oldwnd);
if (oldsurf)
(*env)->DeleteGlobalRef(env, oldsurf);
if (oldholder)
(*env)->DeleteGlobalRef(env, oldholder);
if (env)
(*sys_javavm)->DetachCurrentThread(sys_javavm);
continue;
}
if (sys_nativewindow && vid.activeapp && !r_forcevidrestart)
{
// find time spent rendering last frame
newtime = Sys_DoubleTime ();
time = newtime - oldtime;
sleeptime = Host_Frame(time);
oldtime = newtime;
if (sleeptime)
Sys_Sleep(sleeptime);
}
else
sleeptime = 0.25;
Sys_UnlockConditional(sys_mainconditional);
t = 0;
if (sys_osk.ival >= 0)
{
if (Key_Dest_Has(kdm_console|kdm_message))
t |= 1;
if (!Key_Dest_Has(~kdm_game) && cls.state == ca_disconnected)
t |= 1;
if (sys_osk.ival)
t |= 2;
}
if (osk != t)
{
setsoftkeyboard(t);
osk = t;
}
if (sys_orientation.modified)
{
sys_orientation.modified = false;
updateOrientation(sys_orientation.string);
}
if (sys_keepscreenon.modified)
{
sys_keepscreenon.modified = false;
updateScreenKeepOn(sys_keepscreenon.ival);
}
t = FTENativeActivity_wantrelative(NULL,NULL);
if (wantgrabs != t)
{
wantgrabs = t;
setCursorVisibility(wantgrabs);
}
if (sleeptime)
Sys_Sleep(sleeptime);
}
//don't permanently hold these when there's no active activity.
//(hopefully there's no gl context active right now...)
JNIEnv *env = NULL;
if (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &env, NULL))
{
if (sys_nativewindow)
ANativeWindow_release(sys_nativewindow);
sys_nativewindow = NULL;
if (sys_cursurface)
(*env)->DeleteGlobalRef(env, sys_cursurface);
sys_cursurface = NULL;
if (sys_cursholder)
(*env)->DeleteGlobalRef(env, sys_cursholder);
sys_cursholder = NULL;
if (env)
(*sys_javavm)->DetachCurrentThread(sys_javavm);
}
return 0;
}
static int secbase;
#ifdef _POSIX_TIMERS
double Sys_DoubleTime(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
if (!secbase)
{
secbase = ts.tv_sec;
return ts.tv_nsec/1000000000.0;
}
return (ts.tv_sec - secbase) + ts.tv_nsec/1000000000.0;
}
unsigned int Sys_Milliseconds(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
if (!secbase)
{
secbase = ts.tv_sec;
return ts.tv_nsec/1000000;
}
return (ts.tv_sec - secbase)*1000 + ts.tv_nsec/1000000;
}
#else
double Sys_DoubleTime(void)
{
struct timeval tp;
struct timezone tzp;
gettimeofday(&tp, &tzp);
if (!secbase)
{
secbase = tp.tv_sec;
return tp.tv_usec/1000000.0;
}
return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
}
unsigned int Sys_Milliseconds(void)
{
struct timeval tp;
struct timezone tzp;
gettimeofday(&tp, &tzp);
if (!secbase)
{
secbase = tp.tv_sec;
return tp.tv_usec/1000;
}
return (tp.tv_sec - secbase)*1000 + tp.tv_usec/1000;
}
#endif
void Sys_Shutdown(void)
{
free(sys_memheap);
}
void Sys_Quit(void)
{
#ifndef SERVERONLY
Host_Shutdown ();
#else
SV_Shutdown();
#endif
LOGI("%s", "quitting");
showMessageAndQuit("");
longjmp(host_abort, 1);
exit(0);
}
void Sys_Error (const char *error, ...)
{
va_list argptr;
char string[1024];
va_start (argptr, error);
vsnprintf (string,sizeof(string)-1, error,argptr);
va_end (argptr);
COM_WorkerAbort(string);
if (!*string)
strcpy(string, "no error");
LOGE("e: %s", string);
showMessageAndQuit(string);
host_initialized = false; //don't keep calling Host_Frame, because it'll screw stuff up more. Can't trust Host_Shutdown either. :(
vid.activeapp = false; //make sure we don't busyloop.
longjmp(host_abort, 1);
exit(1);
}
void Sys_Printf (char *fmt, ...)
{
va_list argptr;
char *e;
//android doesn't do \ns properly *sigh*
//this means we have to buffer+split it ourselves.
//and because of lots of threads, we have to mutex it too.
static char linebuf[2048];
static char *endbuf = linebuf;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&lock);
//append the new data
va_start (argptr, fmt);
vsnprintf (endbuf,sizeof(linebuf)-(endbuf-linebuf)-1, fmt,argptr);
va_end (argptr);
endbuf += strlen(endbuf);
//split it on linebreaks
while ((e = strchr(linebuf, '\n')))
{
*e = 0;
LOGI("%s", linebuf);
memmove(linebuf, e+1, endbuf-(e+1));
linebuf[endbuf-(e+1)] = 0;
endbuf -= (e+1)-linebuf;
}
pthread_mutex_unlock(&lock);
}
void Sys_Warn (char *fmt, ...)
{
va_list argptr;
char string[1024];
va_start (argptr, fmt);
vsnprintf (string,sizeof(string)-1, fmt,argptr);
va_end (argptr);
LOGW("w: %s", string);
}
void Sys_CloseLibrary(dllhandle_t *lib)
{
if (lib)
dlclose(lib);
}
void *Sys_GetAddressForName(dllhandle_t *module, const char *exportname)
{
return dlsym(module, exportname);
}
dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)
{
size_t i;
dllhandle_t *h;
h = dlopen(va("%s.so", name), RTLD_LAZY|RTLD_LOCAL);
if (!h)
h = dlopen(name, RTLD_LAZY|RTLD_LOCAL);
if (!h)
{
Con_DLPrintf(2,"%s\n", dlerror());
return NULL;
}
if (h && funcs)
{
for (i = 0; funcs[i].name; i++)
{
*funcs[i].funcptr = dlsym(h, funcs[i].name);
if (!*funcs[i].funcptr)
break;
}
if (funcs[i].name)
{
Con_DPrintf("Unable to find symbol \"%s\" in \"%s\"\n", funcs[i].name, name);
Sys_CloseLibrary(h);
h = NULL;
}
}
return h;
}
char *Sys_ConsoleInput (void)
{
return NULL;
}
void Sys_mkdir (const char *path) //not all pre-unix systems have directories (including dos 1)
{
mkdir(path, 0755);
}
qboolean Sys_rmdir (const char *path)
{
if (rmdir (path) == 0)
return true;
if (errno == ENOENT)
return true;
return false;
}
qboolean Sys_remove (const char *path)
{
return !unlink(path);
}
qboolean Sys_Rename (const char *oldfname, const char *newfname)
{
return !rename(oldfname, newfname);
}
#if _POSIX_C_SOURCE >= 200112L
#include <sys/statvfs.h>
#endif
qboolean Sys_GetFreeDiskSpace(const char *path, quint64_t *freespace)
{
#if _POSIX_C_SOURCE >= 200112L
//posix 2001
struct statvfs inf;
if(0==statvfs(path, &inf))
{
*freespace = inf.f_bsize*(quint64_t)inf.f_bavail;
return true;
}
#endif
return false;
}
void Sys_SendKeyEvents(void)
{
}
char *Sys_URIScheme_NeedsRegistering(void)
{ //android does its mime/etc registrations via android xml junk. dynamically registering stuff isn't supported, so pretend that its already registered to avoid annoying prompts.
return NULL;
}
void Sys_Init(void)
{
Cvar_Register(&sys_keepscreenon, "android stuff");
Cvar_Register(&sys_orientation, "android stuff");
Cvar_Register(&sys_osk, "android stuff");
}
qboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate)
{
*width = 320;
*height = 240;
*bpp = 16;
*refreshrate = 60;
return false;
}
qboolean Sys_RandomBytes(qbyte *string, int len)
{
qboolean res = false;
int fd = open("/dev/urandom", 0);
if (fd >= 0)
{
res = (read(fd, string, len) == len);
close(fd);
}
return res;
}
void Sys_ServerActivity(void)
{
/*FIXME: flash window*/
}
#ifdef WEBCLIENT
qboolean Sys_RunInstaller(void)
{ //not implemented
return false;
}
#endif
#ifndef MULTITHREAD
void Sys_Sleep (double seconds)
{
struct timespec ts;
ts.tv_sec = (time_t)seconds;
seconds -= ts.tv_sec;
ts.tv_nsec = seconds * 1000000000.0;
nanosleep(&ts, NULL);
}
#endif
qboolean Sys_InitTerminal(void)
{
/*switching to dedicated mode, show text window*/
return false;
}
void Sys_CloseTerminal(void)
{
}
#define SYS_CLIPBOARD_SIZE 256
static char clipboard_buffer[SYS_CLIPBOARD_SIZE] = {0};
void Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, const char *utf8), void *ctx)
{
callback(ctx, clipboard_buffer);
}
void Sys_SaveClipboard(clipboardtype_t cbt, const char *text)
{
Q_strncpyz(clipboard_buffer, text, SYS_CLIPBOARD_SIZE);
}
int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)
{
DIR *dir;
char apath[MAX_OSPATH];
char file[MAX_OSPATH];
char truepath[MAX_OSPATH];
char *s;
struct dirent *ent;
struct stat st;
//printf("path = %s\n", gpath);
//printf("match = %s\n", match);
if (!gpath)
gpath = "";
*apath = '\0';
Q_strncpyz(apath, match, sizeof(apath));
for (s = apath+strlen(apath)-1; s >= apath; s--)
{
if (*s == '/')
{
s[1] = '\0';
match += s - apath+1;
break;
}
}
if (s < apath) //didn't find a '/'
*apath = '\0';
Q_snprintfz(truepath, sizeof(truepath), "%s/%s", gpath, apath);
//printf("truepath = %s\n", truepath);
//printf("gamepath = %s\n", gpath);
//printf("apppath = %s\n", apath);
//printf("match = %s\n", match);
dir = opendir(truepath);
if (!dir)
{
Con_DPrintf("Failed to open dir %s\n", truepath);
return true;
}
do
{
ent = readdir(dir);
if (!ent)
break;
if (*ent->d_name != '.')
{
if (wildcmp(match, ent->d_name))
{
Q_snprintfz(file, sizeof(file), "%s/%s", truepath, ent->d_name);
if (stat(file, &st) == 0)
{
Q_snprintfz(file, sizeof(file), "%s%s%s", apath, ent->d_name, S_ISDIR(st.st_mode)?"/":"");
if (!func(file, st.st_size, st.st_mtime, parm, spath))
{
closedir(dir);
return false;
}
}
else
printf("Stat failed for \"%s\"\n", file);
}
}
} while(1);
closedir(dir);
return true;
}
static jboolean FTENativeActivity_startup(JNIEnv *jni, jobject this, jstring externalDataPath, jstring libraryPath)
{
const char *tmp;
if (sys_mainthread)
return false;
if (!sys_activity)
{
sys_activity = (*jni)->NewGlobalRef(jni, this);
tmp = (*jni)->GetStringUTFChars(jni, externalDataPath, NULL);
if (tmp)
{
Q_strncpyz(sys_basedir, tmp, sizeof(sys_basedir));
if (*sys_basedir && sys_basedir[strlen(sys_basedir)-1] != '/')
Q_strncatz(sys_basedir, "/", sizeof(sys_basedir));
(*jni)->ReleaseStringUTFChars(jni, externalDataPath, tmp);
}
else
*sys_basedir = 0;
tmp = (*jni)->GetStringUTFChars(jni, libraryPath, NULL);
if (tmp)
{
Q_strncpyz(sys_binarydir, tmp, sizeof(sys_binarydir));
if (*sys_binarydir && sys_binarydir[strlen(sys_binarydir)-1] != '/')
Q_strncatz(sys_binarydir, "/", sizeof(sys_binarydir));
(*jni)->ReleaseStringUTFChars(jni, libraryPath, tmp);
}
else
*sys_binarydir = 0;
LOGI("FTENativeActivity_startup: basedir=%s binarydir=%s\n", sys_basedir, sys_binarydir);
sys_wantshutdown = false;
sys_mainconditional = Sys_CreateConditional();
Sys_LockConditional(sys_mainconditional);
sys_mainthread = Sys_CreateThread("ftedroid", FTEDroid_MainThread, NULL, THREADP_NORMAL, -1);
if (sys_mainthread)
Sys_ConditionWait(sys_mainconditional);
Sys_UnlockConditional(sys_mainconditional);
return !!sys_mainthread;
}
LOGI("conflicting FTENativeActivity_startup. ignoring.\n");
return false;
}
static void FTENativeActivity_surfacechange(JNIEnv *env, jobject this, jboolean teardown, jboolean recreate, jobject holder, jobject surface)
{
if (!(*env)->IsSameObject(env, this, sys_activity))
{
LOGI("FTENativeActivity_surfacechange: inactive %p, active %p\n", this, sys_activity);
return; //wasn't me...
}
LOGI("FTENativeActivity_surfacechange: %i %i %p\n", teardown, recreate, surface);
//FIXME: if teardown&&recreate then this is a window RESIZE.
//there shouldn't be a need to destroy the entire context but anbox crashes when simply moving the window if we early out here.
// if (teardown && recreate && (*env)->IsSameObject(env, surface, sys_newsurface))
// return;
// LOGI("FTENativeActivity_surfacechange: %i %i (%p==%p)==%i\n", teardown, recreate, surface, sys_newsurface, (*env)->IsSameObject(env, surface, sys_newsurface));
Sys_LockConditional(sys_mainconditional);
//get the main thread to let us know when its done...
if (qrenderer || (r_forceheadless && recreate))
r_forcevidrestart = recreate?2:1;
if (sys_newsurface)
(*env)->DeleteGlobalRef(env, sys_newsurface);
sys_newsurface = surface?(*env)->NewGlobalRef(env, surface):NULL;
if (sys_newsholder)
(*env)->DeleteGlobalRef(env, sys_newsholder);
sys_newsholder = holder?(*env)->NewGlobalRef(env, holder):NULL;
//and wake up then
Sys_ConditionWait(sys_mainconditional);
//and we're done...
Sys_UnlockConditional(sys_mainconditional);
}
static void FTENativeActivity_shutdown(JNIEnv *env, jobject this)
{
if (!(*env)->IsSameObject(env, this, sys_activity))
{
LOGI("FTENativeActivity_shutdown: inactive %p, active %p\n", this, sys_activity);
return; //wasn't me...
}
LOGI("FTENativeActivity_shutdown\n");
sys_wantshutdown = true;
if (sys_mainthread)
Sys_WaitOnThread(sys_mainthread);
sys_mainthread = NULL;
if (sys_mainconditional)
Sys_DestroyConditional(sys_mainconditional);
sys_mainconditional = NULL;
(*env)->DeleteGlobalRef(env, sys_newsurface);
sys_newsurface = NULL;
(*env)->DeleteGlobalRef(env, sys_newsholder);
sys_newsholder = NULL;
(*env)->DeleteGlobalRef(env, sys_activity);
sys_activity = NULL;
}
//FIXME: we need a version of this that takes a byte array instead of a filename, for android's content gibberish.
static void FTENativeActivity_openfile(JNIEnv *env, jobject this, jstring filename)
{
const char *tmp = (*env)->GetStringUTFChars(env, filename, NULL);
if (tmp)
{
Sys_Printf("FTENativeActivity_openfile: %s\n", tmp);
Host_RunFile(tmp, strlen(tmp), NULL);
(*env)->ReleaseStringUTFChars(env, filename, tmp);
}
}
static JNINativeMethod methods[] = {
//
{"startup", "(Ljava/lang/String;Ljava/lang/String;)Z", FTENativeActivity_startup}, //creates our 'main' thread too
{"surfacechange", "(ZZLandroid/view/SurfaceHolder;Landroid/view/Surface;)V", FTENativeActivity_surfacechange}, //syncs
{"shutdown", "()V", FTENativeActivity_shutdown}, //joins 'main' thread.
{"openfile", "(Ljava/lang/String;)V", FTENativeActivity_openfile},
//inputs. these use our in_generic.c ringbuffer so don't need to sync at all
{"keypress", "(IZII)V", FTENativeActivity_keypress},
{"mousepress", "(II)V", FTENativeActivity_mousepress},
{"motion", "(IIFFFF)V", FTENativeActivity_motion},
{"wantrelative", "()Z", FTENativeActivity_wantrelative}, //so the java code knows if it should use (often buggy) relative mouse movement or (limited) abs cursor coords.
{"axis", "(IIF)V", FTENativeActivity_axis},
// {"accelerometer", "(IFFF)V", FTENativeActivity_accelerometer},
// {"gyroscope", "(IFFF)V", FTENativeActivity_gyroscope},
};
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv *jni;
sys_javavm = vm;
if (JNI_OK == (*vm)->GetEnv(vm, (void**)&jni, JNI_VERSION_1_2))
{
jclass naclass = (*jni)->FindClass(jni, "com/fteqw/FTENativeActivity");
if (naclass)
{
(*jni)->RegisterNatives(jni, naclass, methods, countof(methods));
return JNI_VERSION_1_2;
}
}
return -1;
}