mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-23 04:11:53 +00:00
cf31dcccad
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6021 fc73d0e0-1445-4013-8a0c-d673dee63da5
1315 lines
42 KiB
C
1315 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;
|
|
}
|
|
|
|
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;
|
|
}
|