Alibek Omarov
1 year ago
8 changed files with 4 additions and 2021 deletions
@ -1,993 +0,0 @@ |
|||||||
/*
|
|
||||||
android_nosdl.c - android backend |
|
||||||
Copyright (C) 2016-2019 mittorn |
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify |
|
||||||
it under the terms of the GNU General Public License as published by |
|
||||||
the Free Software Foundation, either version 3 of the License, or |
|
||||||
(at your option) any later version. |
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, |
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
GNU General Public License for more details. |
|
||||||
*/ |
|
||||||
#include "platform/platform.h" |
|
||||||
#if !defined(XASH_DEDICATED) |
|
||||||
#include "input.h" |
|
||||||
#include "client.h" |
|
||||||
#include "sound.h" |
|
||||||
#include "platform/android/android_priv.h" |
|
||||||
#include "errno.h" |
|
||||||
#include <pthread.h> |
|
||||||
#include <sys/prctl.h> |
|
||||||
|
|
||||||
#ifndef JNICALL |
|
||||||
#define JNICALL // a1ba: workaround for my IDE, where Java files are not included
|
|
||||||
#define JNIEXPORT |
|
||||||
#endif |
|
||||||
|
|
||||||
convar_t *android_sleep; |
|
||||||
|
|
||||||
static const int s_android_scantokey[] = |
|
||||||
{ |
|
||||||
0, K_LEFTARROW, K_RIGHTARROW, K_AUX26, K_ESCAPE, // 0
|
|
||||||
K_AUX26, K_AUX25, '0', '1', '2', // 5
|
|
||||||
'3', '4', '5', '6', '7', // 10
|
|
||||||
'8', '9', '*', '#', K_UPARROW, // 15
|
|
||||||
K_DOWNARROW, K_LEFTARROW, K_RIGHTARROW, K_ENTER, K_AUX32, // 20
|
|
||||||
K_AUX31, K_AUX29, K_AUX28, K_AUX27, 'a', // 25
|
|
||||||
'b', 'c', 'd', 'e', 'f', // 30
|
|
||||||
'g', 'h', 'i', 'j', 'k', // 35
|
|
||||||
'l', 'm', 'n', 'o', 'p', // 40
|
|
||||||
'q', 'r', 's', 't', 'u', // 45
|
|
||||||
'v', 'w', 'x', 'y', 'z', // 50
|
|
||||||
',', '.', K_ALT, K_ALT, K_SHIFT, // 55
|
|
||||||
K_SHIFT, K_TAB, K_SPACE, 0, 0, // 60
|
|
||||||
0, K_ENTER, K_BACKSPACE, '`', '-', // 65
|
|
||||||
'=', '[', ']', '\\', ';', // 70
|
|
||||||
'\'', '/', '@', K_KP_NUMLOCK, 0, // 75
|
|
||||||
0, '+', '`', 0, 0, // 80
|
|
||||||
0, 0, 0, 0, 0, // 85
|
|
||||||
0, 0, K_PGUP, K_PGDN, 0, // 90
|
|
||||||
0, K_AUX1, K_AUX2, K_AUX14, K_AUX3, // 95
|
|
||||||
K_AUX4, K_AUX15, K_AUX6, K_AUX7, K_JOY1, // 100
|
|
||||||
K_JOY2, K_AUX10, K_AUX11, K_ESCAPE, K_ESCAPE, // 105
|
|
||||||
0, K_ESCAPE, K_DEL, K_CTRL, K_CTRL, // 110
|
|
||||||
K_CAPSLOCK, 0, 0, 0, 0, // 115
|
|
||||||
0, K_PAUSE, K_HOME, K_END, K_INS, // 120
|
|
||||||
0, 0, 0, 0, 0, // 125
|
|
||||||
0, K_F1, K_F2, K_F3, K_F4, // 130
|
|
||||||
K_F5, K_F6, K_F7, K_F8, K_F9, // 135
|
|
||||||
K_F10, K_F11, K_F12, K_KP_NUMLOCK, K_KP_INS, // 140
|
|
||||||
K_KP_END, K_KP_DOWNARROW, K_KP_PGDN, K_KP_LEFTARROW, K_KP_5, // 145
|
|
||||||
K_KP_RIGHTARROW,K_KP_HOME, K_KP_UPARROW, K_KP_PGUP, K_KP_SLASH, // 150
|
|
||||||
0, K_KP_MINUS, K_KP_PLUS, K_KP_DEL, ',', // 155
|
|
||||||
K_KP_ENTER, '=', '(', ')' |
|
||||||
}; |
|
||||||
|
|
||||||
#define ANDROID_MAX_EVENTS 64 |
|
||||||
#define MAX_FINGERS 10 |
|
||||||
|
|
||||||
typedef enum event_type |
|
||||||
{ |
|
||||||
event_touch_down = 0, |
|
||||||
event_touch_up, |
|
||||||
event_touch_move, // compatible with touchEventType
|
|
||||||
event_key_down, |
|
||||||
event_key_up, |
|
||||||
event_set_pause, |
|
||||||
event_resize, |
|
||||||
event_joyhat, |
|
||||||
event_joyball, |
|
||||||
event_joybutton, |
|
||||||
event_joyaxis, |
|
||||||
event_joyadd, |
|
||||||
event_joyremove, |
|
||||||
event_onpause, |
|
||||||
event_ondestroy, |
|
||||||
event_onresume, |
|
||||||
event_onfocuschange, |
|
||||||
} eventtype_t; |
|
||||||
|
|
||||||
typedef struct touchevent_s |
|
||||||
{ |
|
||||||
float x; |
|
||||||
float y; |
|
||||||
float dx; |
|
||||||
float dy; |
|
||||||
} touchevent_t; |
|
||||||
|
|
||||||
typedef struct joyball_s |
|
||||||
{ |
|
||||||
short xrel; |
|
||||||
short yrel; |
|
||||||
byte ball; |
|
||||||
} joyball_t; |
|
||||||
|
|
||||||
typedef struct joyhat_s |
|
||||||
{ |
|
||||||
byte hat; |
|
||||||
byte key; |
|
||||||
} joyhat_t; |
|
||||||
|
|
||||||
typedef struct joyaxis_s |
|
||||||
{ |
|
||||||
short val; |
|
||||||
byte axis; |
|
||||||
} joyaxis_t; |
|
||||||
|
|
||||||
typedef struct joybutton_s |
|
||||||
{ |
|
||||||
int down; |
|
||||||
byte button; |
|
||||||
} joybutton_t; |
|
||||||
|
|
||||||
typedef struct keyevent_s |
|
||||||
{ |
|
||||||
int code; |
|
||||||
} keyevent_t; |
|
||||||
|
|
||||||
typedef struct event_s |
|
||||||
{ |
|
||||||
eventtype_t type; |
|
||||||
int arg; |
|
||||||
union |
|
||||||
{ |
|
||||||
touchevent_t touch; |
|
||||||
joyhat_t hat; |
|
||||||
joyball_t ball; |
|
||||||
joyaxis_t axis; |
|
||||||
joybutton_t button; |
|
||||||
keyevent_t key; |
|
||||||
}; |
|
||||||
} event_t; |
|
||||||
|
|
||||||
typedef struct finger_s |
|
||||||
{ |
|
||||||
float x, y; |
|
||||||
qboolean down; |
|
||||||
} finger_t; |
|
||||||
|
|
||||||
static struct { |
|
||||||
pthread_mutex_t mutex; // this mutex is locked while not running frame, used for events synchronization
|
|
||||||
pthread_mutex_t framemutex; // this mutex is locked while engine is running and unlocked while it reading events, used for pause in background.
|
|
||||||
event_t queue[ANDROID_MAX_EVENTS]; |
|
||||||
volatile int count; |
|
||||||
finger_t fingers[MAX_FINGERS]; |
|
||||||
char inputtext[256]; |
|
||||||
float mousex, mousey; |
|
||||||
} events = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }; |
|
||||||
|
|
||||||
struct jnimethods_s jni; |
|
||||||
struct jnimouse_s jnimouse; |
|
||||||
|
|
||||||
#define Android_Lock() pthread_mutex_lock(&events.mutex); |
|
||||||
#define Android_Unlock() pthread_mutex_unlock(&events.mutex); |
|
||||||
#define Android_PushEvent() Android_Unlock() |
|
||||||
|
|
||||||
typedef void (*pfnChangeGame)( const char *progname ); |
|
||||||
int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGame, pfnChangeGame func ); |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_AllocEvent |
|
||||||
|
|
||||||
Lock event queue and return pointer to next event. |
|
||||||
Caller must do Android_PushEvent() to unlock queue after setting parameters. |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
event_t *Android_AllocEvent( void ) |
|
||||||
{ |
|
||||||
Android_Lock(); |
|
||||||
if( events.count == ANDROID_MAX_EVENTS ) |
|
||||||
{ |
|
||||||
events.count--; //override last event
|
|
||||||
__android_log_print( ANDROID_LOG_ERROR, "Xash", "Too many events!!!" ); |
|
||||||
} |
|
||||||
return &events.queue[ events.count++ ]; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
===================================================== |
|
||||||
JNI callbacks |
|
||||||
|
|
||||||
On application start, setenv and onNativeResize called from |
|
||||||
ui thread to set up engine configuration |
|
||||||
nativeInit called directly from engine thread and will not return until exit. |
|
||||||
These functions may be called from other threads at any time: |
|
||||||
nativeKey |
|
||||||
nativeTouch |
|
||||||
onNativeResize |
|
||||||
nativeString |
|
||||||
nativeSetPause |
|
||||||
===================================================== |
|
||||||
*/ |
|
||||||
#define VA_ARGS(...) , ##__VA_ARGS__ // GCC extension
|
|
||||||
#define DECLARE_JNI_INTERFACE( ret, name, ... ) \ |
|
||||||
JNIEXPORT ret JNICALL Java_su_xash_engine_XashActivity_##name( JNIEnv *env, jclass clazz VA_ARGS(__VA_ARGS__) ) |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( int, nativeInit, jobject array ) |
|
||||||
{ |
|
||||||
int i; |
|
||||||
int argc; |
|
||||||
int status; |
|
||||||
/* Prepare the arguments. */ |
|
||||||
|
|
||||||
int len = (*env)->GetArrayLength(env, array); |
|
||||||
char** argv = calloc( 1 + len + 1, sizeof( char ** )); |
|
||||||
argc = 0; |
|
||||||
argv[argc++] = strdup("app_process"); |
|
||||||
for (i = 0; i < len; ++i) { |
|
||||||
const char* utf; |
|
||||||
char* arg = NULL; |
|
||||||
jstring string = (*env)->GetObjectArrayElement(env, array, i); |
|
||||||
if (string) { |
|
||||||
utf = (*env)->GetStringUTFChars(env, string, 0); |
|
||||||
if (utf) { |
|
||||||
arg = strdup(utf); |
|
||||||
(*env)->ReleaseStringUTFChars(env, string, utf); |
|
||||||
} |
|
||||||
(*env)->DeleteLocalRef(env, string); |
|
||||||
} |
|
||||||
if (!arg) { |
|
||||||
arg = strdup(""); |
|
||||||
} |
|
||||||
argv[argc++] = arg; |
|
||||||
} |
|
||||||
argv[argc] = NULL; |
|
||||||
prctl(PR_SET_DUMPABLE, 1); |
|
||||||
|
|
||||||
/* Init callbacks. */ |
|
||||||
|
|
||||||
jni.env = env; |
|
||||||
jni.actcls = (*env)->FindClass(env, "su/xash/engine/XashActivity"); |
|
||||||
jni.enableTextInput = (*env)->GetStaticMethodID(env, jni.actcls, "showKeyboard", "(I)V"); |
|
||||||
jni.vibrate = (*env)->GetStaticMethodID(env, jni.actcls, "vibrate", "(I)V" ); |
|
||||||
jni.messageBox = (*env)->GetStaticMethodID(env, jni.actcls, "messageBox", "(Ljava/lang/String;Ljava/lang/String;)V"); |
|
||||||
jni.notify = (*env)->GetStaticMethodID(env, jni.actcls, "engineThreadNotify", "()V"); |
|
||||||
jni.setTitle = (*env)->GetStaticMethodID(env, jni.actcls, "setTitle", "(Ljava/lang/String;)V"); |
|
||||||
jni.setIcon = (*env)->GetStaticMethodID(env, jni.actcls, "setIcon", "(Ljava/lang/String;)V"); |
|
||||||
jni.getAndroidId = (*env)->GetStaticMethodID(env, jni.actcls, "getAndroidID", "()Ljava/lang/String;"); |
|
||||||
jni.saveID = (*env)->GetStaticMethodID(env, jni.actcls, "saveID", "(Ljava/lang/String;)V"); |
|
||||||
jni.loadID = (*env)->GetStaticMethodID(env, jni.actcls, "loadID", "()Ljava/lang/String;"); |
|
||||||
jni.showMouse = (*env)->GetStaticMethodID(env, jni.actcls, "showMouse", "(I)V"); |
|
||||||
jni.shellExecute = (*env)->GetStaticMethodID(env, jni.actcls, "shellExecute", "(Ljava/lang/String;)V"); |
|
||||||
|
|
||||||
jni.swapBuffers = (*env)->GetStaticMethodID(env, jni.actcls, "swapBuffers", "()V"); |
|
||||||
jni.toggleEGL = (*env)->GetStaticMethodID(env, jni.actcls, "toggleEGL", "(I)V"); |
|
||||||
jni.createGLContext = (*env)->GetStaticMethodID(env, jni.actcls, "createGLContext", "([I[I)Z"); |
|
||||||
jni.getGLAttribute = (*env)->GetStaticMethodID(env, jni.actcls, "getGLAttribute", "(I)I"); |
|
||||||
jni.deleteGLContext = (*env)->GetStaticMethodID(env, jni.actcls, "deleteGLContext", "()Z"); |
|
||||||
jni.getSelectedPixelFormat = (*env)->GetStaticMethodID(env, jni.actcls, "getSelectedPixelFormat", "()I"); |
|
||||||
jni.getSurface = (*env)->GetStaticMethodID(env, jni.actcls, "getNativeSurface", "()Landroid/view/Surface;"); |
|
||||||
|
|
||||||
/* Run the application. */ |
|
||||||
|
|
||||||
status = Host_Main( argc, argv, getenv("XASH3D_GAMEDIR"), false, NULL ); |
|
||||||
|
|
||||||
/* Release the arguments. */ |
|
||||||
|
|
||||||
for (i = 0; i < argc; ++i) |
|
||||||
free(argv[i]); |
|
||||||
free(argv); |
|
||||||
|
|
||||||
return status; |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, onNativeResize, jint width, jint height ) |
|
||||||
{ |
|
||||||
event_t *event; |
|
||||||
|
|
||||||
if( !width || !height ) |
|
||||||
return; |
|
||||||
|
|
||||||
jni.width=width, jni.height=height; |
|
||||||
|
|
||||||
// alloc update event to change screen size
|
|
||||||
event = Android_AllocEvent(); |
|
||||||
event->type = event_resize; |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeQuit ) |
|
||||||
{ |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeSetPause, jint pause ) |
|
||||||
{ |
|
||||||
event_t *event = Android_AllocEvent(); |
|
||||||
event->type = event_set_pause; |
|
||||||
event->arg = pause; |
|
||||||
Android_PushEvent(); |
|
||||||
|
|
||||||
// if pause enabled, hold engine by locking frame mutex.
|
|
||||||
// Engine will stop after event reading and will not continue untill unlock
|
|
||||||
if( android_sleep && android_sleep->value ) |
|
||||||
{ |
|
||||||
if( pause ) |
|
||||||
pthread_mutex_lock( &events.framemutex ); |
|
||||||
else |
|
||||||
pthread_mutex_unlock( &events.framemutex ); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeUnPause ) |
|
||||||
{ |
|
||||||
// UnPause engine before sending critical events
|
|
||||||
if( android_sleep && android_sleep->value ) |
|
||||||
pthread_mutex_unlock( &events.framemutex ); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeKey, jint down, jint code ) |
|
||||||
{ |
|
||||||
event_t *event; |
|
||||||
|
|
||||||
if( code < 0 ) |
|
||||||
{ |
|
||||||
event = Android_AllocEvent(); |
|
||||||
event->arg = (-code) & 255; |
|
||||||
event->type = down?event_key_down:event_key_up; |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
if( code >= ( sizeof( s_android_scantokey ) / sizeof( s_android_scantokey[0] ) ) ) |
|
||||||
{ |
|
||||||
Con_DPrintf( "nativeKey: unknown Android key %d\n", code ); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
if( !s_android_scantokey[code] ) |
|
||||||
{ |
|
||||||
Con_DPrintf( "nativeKey: unmapped Android key %d\n", code ); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
event = Android_AllocEvent(); |
|
||||||
event->type = down?event_key_down:event_key_up; |
|
||||||
event->arg = s_android_scantokey[code]; |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeString, jobject string ) |
|
||||||
{ |
|
||||||
char* str = (char *) (*env)->GetStringUTFChars(env, string, NULL); |
|
||||||
|
|
||||||
Android_Lock(); |
|
||||||
strncat( events.inputtext, str, 256 ); |
|
||||||
Android_Unlock(); |
|
||||||
|
|
||||||
(*env)->ReleaseStringUTFChars(env, string, str); |
|
||||||
} |
|
||||||
|
|
||||||
#ifdef SOFTFP_LINK |
|
||||||
DECLARE_JNI_INTERFACE( void, nativeTouch, jint finger, jint action, jfloat x, jfloat y ) __attribute__((pcs("aapcs"))); |
|
||||||
#endif |
|
||||||
DECLARE_JNI_INTERFACE( void, nativeTouch, jint finger, jint action, jfloat x, jfloat y ) |
|
||||||
{ |
|
||||||
float dx, dy; |
|
||||||
event_t *event; |
|
||||||
|
|
||||||
// if something wrong with android event
|
|
||||||
if( finger > MAX_FINGERS ) |
|
||||||
return; |
|
||||||
|
|
||||||
// not touch action?
|
|
||||||
if( !( action >=0 && action <= 2 ) ) |
|
||||||
return; |
|
||||||
|
|
||||||
// 0.0f .. 1.0f
|
|
||||||
x /= jni.width; |
|
||||||
y /= jni.height; |
|
||||||
|
|
||||||
if( action ) |
|
||||||
dx = x - events.fingers[finger].x, dy = y - events.fingers[finger].y; |
|
||||||
else |
|
||||||
dx = dy = 0.0f; |
|
||||||
events.fingers[finger].x = x, events.fingers[finger].y = y; |
|
||||||
|
|
||||||
// check if we should skip some events
|
|
||||||
if( ( action == 2 ) && ( !dx && !dy ) ) |
|
||||||
return; |
|
||||||
|
|
||||||
if( ( action == 0 ) && events.fingers[finger].down ) |
|
||||||
return; |
|
||||||
|
|
||||||
if( ( action == 1 ) && !events.fingers[finger].down ) |
|
||||||
return; |
|
||||||
|
|
||||||
if( action == 2 && !events.fingers[finger].down ) |
|
||||||
action = 0; |
|
||||||
|
|
||||||
if( action == 0 ) |
|
||||||
events.fingers[finger].down = true; |
|
||||||
else if( action == 1 ) |
|
||||||
events.fingers[finger].down = false; |
|
||||||
|
|
||||||
event = Android_AllocEvent(); |
|
||||||
event->arg = finger; |
|
||||||
event->type = action; |
|
||||||
event->touch.x = x; |
|
||||||
event->touch.y = y; |
|
||||||
event->touch.dx = dx; |
|
||||||
event->touch.dy = dy; |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeBall, jint id, jbyte ball, jshort xrel, jshort yrel ) |
|
||||||
{ |
|
||||||
event_t *event = Android_AllocEvent(); |
|
||||||
|
|
||||||
event->type = event_joyball; |
|
||||||
event->arg = id; |
|
||||||
event->ball.ball = ball; |
|
||||||
event->ball.xrel = xrel; |
|
||||||
event->ball.yrel = yrel; |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeHat, jint id, jbyte hat, jbyte key, jboolean down ) |
|
||||||
{ |
|
||||||
event_t *event = Android_AllocEvent(); |
|
||||||
static byte engineKeys; |
|
||||||
|
|
||||||
if( !key ) |
|
||||||
engineKeys = 0; // centered;
|
|
||||||
|
|
||||||
if( down ) |
|
||||||
engineKeys |= key; |
|
||||||
else |
|
||||||
engineKeys &= ~key; |
|
||||||
|
|
||||||
event->type = event_joyhat; |
|
||||||
event->arg = id; |
|
||||||
event->hat.hat = hat; |
|
||||||
event->hat.key = engineKeys; |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeAxis, jint id, jbyte axis, jshort val ) |
|
||||||
{ |
|
||||||
event_t *event = Android_AllocEvent(); |
|
||||||
event->type = event_joyaxis; |
|
||||||
event->arg = id; |
|
||||||
event->axis.axis = axis; |
|
||||||
event->axis.val = val; |
|
||||||
|
|
||||||
__android_log_print(ANDROID_LOG_VERBOSE, "Xash", "axis %i %i", axis, val ); |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeJoyButton, jint id, jbyte button, jboolean down ) |
|
||||||
{ |
|
||||||
event_t *event = Android_AllocEvent(); |
|
||||||
event->type = event_joybutton; |
|
||||||
event->arg = id; |
|
||||||
event->button.button = button; |
|
||||||
event->button.down = down; |
|
||||||
__android_log_print(ANDROID_LOG_VERBOSE, "Xash", "button %i", button ); |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeJoyAdd, jint id ) |
|
||||||
{ |
|
||||||
event_t *event = Android_AllocEvent(); |
|
||||||
event->type = event_joyadd; |
|
||||||
event->arg = id; |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeJoyDel, jint id ) |
|
||||||
{ |
|
||||||
event_t *event = Android_AllocEvent(); |
|
||||||
event->type = event_joyremove; |
|
||||||
event->arg = id; |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeOnResume ) |
|
||||||
{ |
|
||||||
event_t *event = Android_AllocEvent(); |
|
||||||
event->type = event_onresume; |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeOnFocusChange ) |
|
||||||
{ |
|
||||||
event_t *event = Android_AllocEvent(); |
|
||||||
event->type = event_onfocuschange; |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeOnPause ) |
|
||||||
{ |
|
||||||
event_t *event = Android_AllocEvent(); |
|
||||||
event->type = event_onpause; |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeOnDestroy ) |
|
||||||
{ |
|
||||||
event_t *event = Android_AllocEvent(); |
|
||||||
event->type = event_ondestroy; |
|
||||||
Android_PushEvent(); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( int, setenv, jstring key, jstring value, jboolean overwrite ) |
|
||||||
{ |
|
||||||
char* k = (char *) (*env)->GetStringUTFChars(env, key, NULL); |
|
||||||
char* v = (char *) (*env)->GetStringUTFChars(env, value, NULL); |
|
||||||
int err = setenv(k, v, overwrite); |
|
||||||
(*env)->ReleaseStringUTFChars(env, key, k); |
|
||||||
(*env)->ReleaseStringUTFChars(env, value, v); |
|
||||||
return err; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( void, nativeMouseMove, jfloat x, jfloat y ) |
|
||||||
{ |
|
||||||
Android_Lock(); |
|
||||||
events.mousex += x; |
|
||||||
events.mousey += y; |
|
||||||
Android_Unlock(); |
|
||||||
} |
|
||||||
|
|
||||||
DECLARE_JNI_INTERFACE( int, nativeTestWritePermission, jstring jPath ) |
|
||||||
{ |
|
||||||
char *path = (char *)(*env)->GetStringUTFChars(env, jPath, NULL); |
|
||||||
FILE *fd; |
|
||||||
char testFile[PATH_MAX]; |
|
||||||
int ret = 0; |
|
||||||
|
|
||||||
// maybe generate new file everytime?
|
|
||||||
Q_snprintf( testFile, PATH_MAX, "%s/.testfile", path ); |
|
||||||
|
|
||||||
__android_log_print( ANDROID_LOG_VERBOSE, "Xash", "nativeTestWritePermission: file=%s", testFile ); |
|
||||||
|
|
||||||
fd = fopen( testFile, "w+" ); |
|
||||||
|
|
||||||
if( fd ) |
|
||||||
{ |
|
||||||
__android_log_print( ANDROID_LOG_VERBOSE, "Xash", "nativeTestWritePermission: passed" ); |
|
||||||
ret = 1; |
|
||||||
fclose( fd ); |
|
||||||
|
|
||||||
remove( testFile ); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
__android_log_print( ANDROID_LOG_VERBOSE, "Xash", "nativeTestWritePermission: error=%s", strerror( errno ) ); |
|
||||||
} |
|
||||||
|
|
||||||
(*env)->ReleaseStringUTFChars( env, jPath, path ); |
|
||||||
|
|
||||||
return ret; |
|
||||||
} |
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL JNI_OnLoad( JavaVM *vm, void *reserved ) |
|
||||||
{ |
|
||||||
return JNI_VERSION_1_6; |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Platform_Init |
|
||||||
|
|
||||||
Initialize android-related cvars |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
void Platform_Init( void ) |
|
||||||
{ |
|
||||||
android_sleep = Cvar_Get( "android_sleep", "1", FCVAR_ARCHIVE, "Enable sleep in background" ); |
|
||||||
} |
|
||||||
|
|
||||||
void Platform_Shutdown( void ) |
|
||||||
{ |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_EnableTextInput |
|
||||||
|
|
||||||
Show virtual keyboard |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
void Platform_EnableTextInput( qboolean enable ) |
|
||||||
{ |
|
||||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.enableTextInput, enable ); |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_Vibrate |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
void Platform_Vibrate( float life, char flags ) |
|
||||||
{ |
|
||||||
if( life ) |
|
||||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.vibrate, (int)life ); |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_GetNativeObject |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
void *Platform_GetNativeObject( const char *objName ) |
|
||||||
{ |
|
||||||
static const char *availObjects[] = { "JNIEnv", "ActivityClass", NULL }; |
|
||||||
void *object = NULL; |
|
||||||
|
|
||||||
if( !objName ) |
|
||||||
{ |
|
||||||
object = (void*)availObjects; |
|
||||||
} |
|
||||||
else if( !strcasecmp( objName, "JNIEnv" ) ) |
|
||||||
{ |
|
||||||
object = (void*)jni.env; |
|
||||||
} |
|
||||||
else if( !strcasecmp( objName, "ActivityClass" ) ) |
|
||||||
{ |
|
||||||
object = (void*)jni.actcls; |
|
||||||
} |
|
||||||
|
|
||||||
return object; |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_MessageBox |
|
||||||
|
|
||||||
Show messagebox and wait for OK button press |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
#if XASH_MESSAGEBOX == MSGBOX_ANDROID |
|
||||||
void Platform_MessageBox( const char *title, const char *text, qboolean parentMainWindow ) |
|
||||||
{ |
|
||||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.messageBox, (*jni.env)->NewStringUTF( jni.env, title ), (*jni.env)->NewStringUTF( jni.env ,text ) ); |
|
||||||
} |
|
||||||
#endif // XASH_MESSAGEBOX == MSGBOX_ANDROID
|
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_GetAndroidID |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
const char *Android_GetAndroidID( void ) |
|
||||||
{ |
|
||||||
static char id[65]; |
|
||||||
const char *resultCStr; |
|
||||||
jstring resultJNIStr; |
|
||||||
|
|
||||||
if( id[0] ) |
|
||||||
return id; |
|
||||||
|
|
||||||
resultJNIStr = (jstring)(*jni.env)->CallStaticObjectMethod( jni.env, jni.actcls, jni.getAndroidId ); |
|
||||||
resultCStr = (*jni.env)->GetStringUTFChars( jni.env, resultJNIStr, NULL ); |
|
||||||
Q_strncpy( id, resultCStr, 64 ); |
|
||||||
(*jni.env)->ReleaseStringUTFChars( jni.env, resultJNIStr, resultCStr ); |
|
||||||
|
|
||||||
if( !id[0] ) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
return id; |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_LoadID |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
const char *Android_LoadID( void ) |
|
||||||
{ |
|
||||||
static char id[65]; |
|
||||||
jstring resultJNIStr = (jstring)(*jni.env)->CallStaticObjectMethod( jni.env, jni.actcls, jni.loadID ); |
|
||||||
const char *resultCStr = (*jni.env)->GetStringUTFChars( jni.env, resultJNIStr, NULL ); |
|
||||||
Q_strncpy( id, resultCStr, 64 ); |
|
||||||
(*jni.env)->ReleaseStringUTFChars( jni.env, resultJNIStr, resultCStr ); |
|
||||||
return id; |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_SaveID |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
void Android_SaveID( const char *id ) |
|
||||||
{ |
|
||||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.saveID, (*jni.env)->NewStringUTF( jni.env, id ) ); |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_MouseMove |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
void Platform_MouseMove( float *x, float *y ) |
|
||||||
{ |
|
||||||
*x = jnimouse.x; |
|
||||||
*y = jnimouse.y; |
|
||||||
jnimouse.x = 0; |
|
||||||
jnimouse.y = 0; |
|
||||||
// Con_Reportf( "Android_MouseMove: %f %f\n", *x, *y );
|
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_AddMove |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
void Android_AddMove( float x, float y ) |
|
||||||
{ |
|
||||||
jnimouse.x += x; |
|
||||||
jnimouse.y += y; |
|
||||||
} |
|
||||||
|
|
||||||
void GAME_EXPORT Platform_GetMousePos( int *x, int *y ) |
|
||||||
{ |
|
||||||
// stub
|
|
||||||
} |
|
||||||
|
|
||||||
void GAME_EXPORT Platform_SetMousePos( int x, int y ) |
|
||||||
{ |
|
||||||
// stub
|
|
||||||
} |
|
||||||
|
|
||||||
int Platform_JoyInit( int numjoy ) |
|
||||||
{ |
|
||||||
// stub
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_ShowMouse |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
void Android_ShowMouse( qboolean show ) |
|
||||||
{ |
|
||||||
if( m_ignore->value ) |
|
||||||
show = true; |
|
||||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.showMouse, show ); |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_ShellExecute |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
void Platform_ShellExecute( const char *path, const char *parms ) |
|
||||||
{ |
|
||||||
jstring jstr; |
|
||||||
|
|
||||||
if( !path ) |
|
||||||
return; // useless
|
|
||||||
|
|
||||||
// get java.lang.String
|
|
||||||
jstr = (*jni.env)->NewStringUTF( jni.env, path ); |
|
||||||
|
|
||||||
// open browser
|
|
||||||
(*jni.env)->CallStaticVoidMethod(jni.env, jni.actcls, jni.shellExecute, jstr); |
|
||||||
|
|
||||||
// no need to free jstr
|
|
||||||
} |
|
||||||
|
|
||||||
int Platform_GetClipboardText( char *buffer, size_t size ) |
|
||||||
{ |
|
||||||
// stub
|
|
||||||
if( size ) buffer[0] = 0; |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
void Platform_SetClipboardText( const char *buffer ) |
|
||||||
{ |
|
||||||
// stub
|
|
||||||
} |
|
||||||
|
|
||||||
void Platform_SetCursorType( VGUI_DefaultCursor cursor ) |
|
||||||
{ |
|
||||||
// stub
|
|
||||||
} |
|
||||||
|
|
||||||
key_modifier_t Platform_GetKeyModifiers( void ) |
|
||||||
{ |
|
||||||
// stub
|
|
||||||
return KeyModifier_None; |
|
||||||
} |
|
||||||
|
|
||||||
void Platform_PreCreateMove( void ) |
|
||||||
{ |
|
||||||
// stub
|
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_RunEvents |
|
||||||
|
|
||||||
Execute all events from queue |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
void Platform_RunEvents( void ) |
|
||||||
{ |
|
||||||
int i; |
|
||||||
|
|
||||||
// enter events read
|
|
||||||
Android_Lock(); |
|
||||||
pthread_mutex_unlock( &events.framemutex ); |
|
||||||
|
|
||||||
for( i = 0; i < events.count; i++ ) |
|
||||||
{ |
|
||||||
switch( events.queue[i].type ) |
|
||||||
{ |
|
||||||
case event_touch_down: |
|
||||||
case event_touch_up: |
|
||||||
case event_touch_move: |
|
||||||
IN_TouchEvent( (touchEventType)events.queue[i].type, events.queue[i].arg, |
|
||||||
events.queue[i].touch.x, events.queue[i].touch.y, |
|
||||||
events.queue[i].touch.dx, events.queue[i].touch.dy ); |
|
||||||
break; |
|
||||||
|
|
||||||
case event_key_down: |
|
||||||
Key_Event( events.queue[i].arg, true ); |
|
||||||
|
|
||||||
if( events.queue[i].arg == K_AUX31 || events.queue[i].arg == K_AUX29 ) |
|
||||||
{ |
|
||||||
host.force_draw_version = true; |
|
||||||
host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; |
|
||||||
} |
|
||||||
break; |
|
||||||
case event_key_up: |
|
||||||
Key_Event( events.queue[i].arg, false ); |
|
||||||
|
|
||||||
if( events.queue[i].arg == K_AUX31 || events.queue[i].arg == K_AUX29 ) |
|
||||||
{ |
|
||||||
host.force_draw_version = true; |
|
||||||
host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; |
|
||||||
} |
|
||||||
break; |
|
||||||
|
|
||||||
case event_set_pause: |
|
||||||
// destroy EGL surface when hiding application
|
|
||||||
if( !events.queue[i].arg ) |
|
||||||
{ |
|
||||||
SNDDMA_Activate( true ); |
|
||||||
// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 );
|
|
||||||
Android_UpdateSurface( true ); |
|
||||||
host.status = HOST_FRAME; |
|
||||||
SetBits( gl_vsync->flags, FCVAR_CHANGED ); // set swap interval
|
|
||||||
host.force_draw_version = true; |
|
||||||
host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; |
|
||||||
} |
|
||||||
|
|
||||||
if( events.queue[i].arg ) |
|
||||||
{ |
|
||||||
SNDDMA_Activate( false ); |
|
||||||
Android_UpdateSurface( false ); |
|
||||||
host.status = HOST_NOFOCUS; |
|
||||||
// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 );
|
|
||||||
} |
|
||||||
break; |
|
||||||
|
|
||||||
case event_resize: |
|
||||||
// reinitialize EGL and change engine screen size
|
|
||||||
if( host.status == HOST_FRAME &&( refState.width != jni.width || refState.height != jni.height ) ) |
|
||||||
{ |
|
||||||
// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 );
|
|
||||||
// (*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 );
|
|
||||||
Android_UpdateSurface( true ); |
|
||||||
SetBits( gl_vsync->flags, FCVAR_CHANGED ); // set swap interval
|
|
||||||
VID_SetMode(); |
|
||||||
} |
|
||||||
break; |
|
||||||
case event_joyadd: |
|
||||||
Joy_AddEvent(); |
|
||||||
break; |
|
||||||
case event_joyremove: |
|
||||||
Joy_RemoveEvent(); |
|
||||||
break; |
|
||||||
case event_joyball: |
|
||||||
if( !Joy_IsActive() ) |
|
||||||
Joy_AddEvent(); |
|
||||||
Joy_BallMotionEvent( events.queue[i].ball.ball, |
|
||||||
events.queue[i].ball.xrel, events.queue[i].ball.yrel ); |
|
||||||
break; |
|
||||||
case event_joyhat: |
|
||||||
if( !Joy_IsActive() ) |
|
||||||
Joy_AddEvent(); |
|
||||||
Joy_HatMotionEvent( events.queue[i].hat.hat, events.queue[i].hat.key ); |
|
||||||
break; |
|
||||||
case event_joyaxis: |
|
||||||
if( !Joy_IsActive() ) |
|
||||||
Joy_AddEvent(); |
|
||||||
Joy_AxisMotionEvent( events.queue[i].axis.axis, events.queue[i].axis.val ); |
|
||||||
break; |
|
||||||
case event_joybutton: |
|
||||||
if( !Joy_IsActive() ) |
|
||||||
Joy_AddEvent(); |
|
||||||
Joy_ButtonEvent( events.queue[i].button.button, (byte)events.queue[i].button.down ); |
|
||||||
break; |
|
||||||
case event_ondestroy: |
|
||||||
//host.skip_configs = true; // skip config save, because engine may be killed during config save
|
|
||||||
Sys_Quit(); |
|
||||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify ); |
|
||||||
break; |
|
||||||
case event_onpause: |
|
||||||
#ifdef PARANOID_CONFIG_SAVE |
|
||||||
switch( host.status ) |
|
||||||
{ |
|
||||||
case HOST_INIT: |
|
||||||
case HOST_CRASHED: |
|
||||||
case HOST_ERR_FATAL: |
|
||||||
Con_Reportf( S_WARN "Abnormal host state during onPause (%d), skipping config save!\n", host.status ); |
|
||||||
break; |
|
||||||
default: |
|
||||||
// restore all latched cheat cvars
|
|
||||||
Cvar_SetCheatState( true ); |
|
||||||
Host_WriteConfig(); |
|
||||||
} |
|
||||||
#endif |
|
||||||
// disable sound during call/screen-off
|
|
||||||
SNDDMA_Activate( false ); |
|
||||||
// host.status = HOST_NOFOCUS;
|
|
||||||
// stop blocking UI thread
|
|
||||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.notify ); |
|
||||||
|
|
||||||
break; |
|
||||||
case event_onresume: |
|
||||||
// re-enable sound after onPause
|
|
||||||
// host.status = HOST_FRAME;
|
|
||||||
SNDDMA_Activate( true ); |
|
||||||
host.force_draw_version = true; |
|
||||||
host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; |
|
||||||
break; |
|
||||||
case event_onfocuschange: |
|
||||||
host.force_draw_version = true; |
|
||||||
host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
events.count = 0; // no more events
|
|
||||||
|
|
||||||
// text input handled separately to allow unicode symbols
|
|
||||||
for( i = 0; events.inputtext[i]; i++ ) |
|
||||||
{ |
|
||||||
int ch; |
|
||||||
|
|
||||||
// if engine does not use utf-8, we need to convert it to preferred encoding
|
|
||||||
if( !Q_stricmp( cl_charset->string, "utf-8" ) ) |
|
||||||
ch = (unsigned char)events.inputtext[i]; |
|
||||||
else |
|
||||||
ch = Con_UtfProcessCharForce( (unsigned char)events.inputtext[i] ); |
|
||||||
|
|
||||||
if( !ch ) // utf-8
|
|
||||||
continue; |
|
||||||
|
|
||||||
// some keyboards may send enter as text
|
|
||||||
if( ch == '\n' ) |
|
||||||
{ |
|
||||||
Key_Event( K_ENTER, true ); |
|
||||||
Key_Event( K_ENTER, false ); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
// otherwise just push it by char, text render will decode unicode strings
|
|
||||||
CL_CharEvent( ch ); |
|
||||||
} |
|
||||||
events.inputtext[0] = 0; // no more text
|
|
||||||
|
|
||||||
jnimouse.x += events.mousex; |
|
||||||
events.mousex = 0; |
|
||||||
jnimouse.y += events.mousey; |
|
||||||
events.mousey = 0; |
|
||||||
|
|
||||||
//end events read
|
|
||||||
Android_Unlock(); |
|
||||||
pthread_mutex_lock( &events.framemutex ); |
|
||||||
} |
|
||||||
|
|
||||||
#endif // XASH_DEDICATED
|
|
@ -1,46 +0,0 @@ |
|||||||
#pragma once |
|
||||||
#ifndef ANDROID_PRIV_H |
|
||||||
#define ANDROID_PRIV_H |
|
||||||
|
|
||||||
#include <EGL/egl.h> |
|
||||||
#include <android/log.h> |
|
||||||
#include <jni.h> |
|
||||||
|
|
||||||
extern struct jnimethods_s |
|
||||||
{ |
|
||||||
jclass actcls; |
|
||||||
JavaVM *vm; |
|
||||||
JNIEnv *env; |
|
||||||
jmethodID enableTextInput; |
|
||||||
jmethodID vibrate; |
|
||||||
jmethodID messageBox; |
|
||||||
jmethodID notify; |
|
||||||
jmethodID setTitle; |
|
||||||
jmethodID setIcon; |
|
||||||
jmethodID getAndroidId; |
|
||||||
jmethodID saveID; |
|
||||||
jmethodID loadID; |
|
||||||
jmethodID showMouse; |
|
||||||
jmethodID shellExecute; |
|
||||||
jmethodID swapBuffers; |
|
||||||
jmethodID toggleEGL; |
|
||||||
jmethodID createGLContext; |
|
||||||
jmethodID getGLAttribute; |
|
||||||
jmethodID deleteGLContext; |
|
||||||
jmethodID getSelectedPixelFormat; |
|
||||||
jmethodID getSurface; |
|
||||||
int width, height; |
|
||||||
} jni; |
|
||||||
|
|
||||||
|
|
||||||
extern struct jnimouse_s |
|
||||||
{ |
|
||||||
float x, y; |
|
||||||
} jnimouse; |
|
||||||
|
|
||||||
//
|
|
||||||
// vid_android.c
|
|
||||||
//
|
|
||||||
void Android_UpdateSurface( qboolean active ); |
|
||||||
|
|
||||||
#endif // ANDROID_PRIV_H
|
|
@ -1,277 +0,0 @@ |
|||||||
/*
|
|
||||||
Copyright (C) 2015 SiPlus, Chasseur de bots |
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or |
|
||||||
modify it under the terms of the GNU General Public License |
|
||||||
as published by the Free Software Foundation; either version 2 |
|
||||||
of the License, or (at your option) any later version. |
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, |
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|
||||||
|
|
||||||
See the GNU General Public License for more details. |
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License |
|
||||||
along with this program; if not, write to the Free Software |
|
||||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|
||||||
|
|
||||||
*/ |
|
||||||
|
|
||||||
#include "common.h" |
|
||||||
#include "platform/platform.h" |
|
||||||
#if XASH_SOUND == SOUND_OPENSLES |
|
||||||
#include <SLES/OpenSLES.h> |
|
||||||
#include "pthread.h" |
|
||||||
#include "sound.h" |
|
||||||
|
|
||||||
extern dma_t dma; |
|
||||||
|
|
||||||
static SLObjectItf snddma_android_engine = NULL; |
|
||||||
static SLObjectItf snddma_android_outputMix = NULL; |
|
||||||
static SLObjectItf snddma_android_player = NULL; |
|
||||||
static SLBufferQueueItf snddma_android_bufferQueue; |
|
||||||
static SLPlayItf snddma_android_play; |
|
||||||
|
|
||||||
static pthread_mutex_t snddma_android_mutex = PTHREAD_MUTEX_INITIALIZER; |
|
||||||
|
|
||||||
static int snddma_android_size; |
|
||||||
|
|
||||||
static const SLInterfaceID *pSL_IID_ENGINE; |
|
||||||
static const SLInterfaceID *pSL_IID_BUFFERQUEUE; |
|
||||||
static const SLInterfaceID *pSL_IID_PLAY; |
|
||||||
static SLresult SLAPIENTRY (*pslCreateEngine)( |
|
||||||
SLObjectItf *pEngine, |
|
||||||
SLuint32 numOptions, |
|
||||||
const SLEngineOption *pEngineOptions, |
|
||||||
SLuint32 numInterfaces, |
|
||||||
const SLInterfaceID *pInterfaceIds, |
|
||||||
const SLboolean * pInterfaceRequired |
|
||||||
); |
|
||||||
|
|
||||||
void SNDDMA_Activate( qboolean active ) |
|
||||||
{ |
|
||||||
if( !dma.initialized ) |
|
||||||
return; |
|
||||||
|
|
||||||
if( active ) |
|
||||||
{ |
|
||||||
memset( dma.buffer, 0, snddma_android_size * 2 ); |
|
||||||
(*snddma_android_bufferQueue)->Enqueue( snddma_android_bufferQueue, dma.buffer, snddma_android_size ); |
|
||||||
(*snddma_android_play)->SetPlayState( snddma_android_play, SL_PLAYSTATE_PLAYING ); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
(*snddma_android_play)->SetPlayState( snddma_android_play, SL_PLAYSTATE_STOPPED ); |
|
||||||
(*snddma_android_bufferQueue)->Clear( snddma_android_bufferQueue ); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void SNDDMA_Android_Callback( SLBufferQueueItf bq, void *context ) |
|
||||||
{ |
|
||||||
uint8_t *buffer2; |
|
||||||
|
|
||||||
pthread_mutex_lock( &snddma_android_mutex ); |
|
||||||
|
|
||||||
buffer2 = ( uint8_t * )dma.buffer + snddma_android_size; |
|
||||||
(*bq)->Enqueue( bq, buffer2, snddma_android_size ); |
|
||||||
memcpy( buffer2, dma.buffer, snddma_android_size ); |
|
||||||
memset( dma.buffer, 0, snddma_android_size ); |
|
||||||
dma.samplepos += dma.samples; |
|
||||||
|
|
||||||
pthread_mutex_unlock( &snddma_android_mutex ); |
|
||||||
} |
|
||||||
|
|
||||||
static const char *SNDDMA_Android_Init( void ) |
|
||||||
{ |
|
||||||
SLresult result; |
|
||||||
|
|
||||||
SLEngineItf engine; |
|
||||||
|
|
||||||
int freq; |
|
||||||
|
|
||||||
SLDataLocator_BufferQueue sourceLocator; |
|
||||||
SLDataFormat_PCM sourceFormat; |
|
||||||
SLDataSource source; |
|
||||||
|
|
||||||
SLDataLocator_OutputMix sinkLocator; |
|
||||||
SLDataSink sink; |
|
||||||
|
|
||||||
SLInterfaceID interfaceID; |
|
||||||
SLboolean interfaceRequired; |
|
||||||
|
|
||||||
int samples; |
|
||||||
void *handle = dlopen( "libOpenSLES.so", RTLD_LAZY ); |
|
||||||
|
|
||||||
if( !handle ) |
|
||||||
return "dlopen for libOpenSLES.so"; |
|
||||||
|
|
||||||
pslCreateEngine = dlsym( handle, "slCreateEngine" ); |
|
||||||
|
|
||||||
if( !pslCreateEngine ) |
|
||||||
return "resolve slCreateEngine"; |
|
||||||
|
|
||||||
pSL_IID_ENGINE = dlsym( handle, "SL_IID_ENGINE" ); |
|
||||||
|
|
||||||
if( !pSL_IID_ENGINE ) |
|
||||||
return "resolve SL_IID_ENGINE"; |
|
||||||
|
|
||||||
pSL_IID_PLAY = dlsym( handle, "SL_IID_PLAY" ); |
|
||||||
|
|
||||||
if( !pSL_IID_PLAY ) |
|
||||||
return "resolve SL_IID_PLAY"; |
|
||||||
|
|
||||||
pSL_IID_BUFFERQUEUE = dlsym( handle, "SL_IID_BUFFERQUEUE" ); |
|
||||||
|
|
||||||
if( !pSL_IID_BUFFERQUEUE ) |
|
||||||
return "resolve SL_IID_BUFFERQUEUE"; |
|
||||||
|
|
||||||
|
|
||||||
result = pslCreateEngine( &snddma_android_engine, 0, NULL, 0, NULL, NULL ); |
|
||||||
if( result != SL_RESULT_SUCCESS ) return "slCreateEngine"; |
|
||||||
result = (*snddma_android_engine)->Realize( snddma_android_engine, SL_BOOLEAN_FALSE ); |
|
||||||
if( result != SL_RESULT_SUCCESS ) return "engine->Realize"; |
|
||||||
result = (*snddma_android_engine)->GetInterface( snddma_android_engine, *pSL_IID_ENGINE, &engine ); |
|
||||||
if( result != SL_RESULT_SUCCESS ) return "engine->GetInterface(ENGINE)"; |
|
||||||
|
|
||||||
result = (*engine)->CreateOutputMix( engine, &snddma_android_outputMix, 0, NULL, NULL ); |
|
||||||
if( result != SL_RESULT_SUCCESS ) return "engine->CreateOutputMix"; |
|
||||||
result = (*snddma_android_outputMix)->Realize( snddma_android_outputMix, SL_BOOLEAN_FALSE ); |
|
||||||
if( result != SL_RESULT_SUCCESS ) return "outputMix->Realize"; |
|
||||||
|
|
||||||
freq = SOUND_DMA_SPEED; |
|
||||||
sourceLocator.locatorType = SL_DATALOCATOR_BUFFERQUEUE; |
|
||||||
sourceLocator.numBuffers = 2; |
|
||||||
sourceFormat.formatType = SL_DATAFORMAT_PCM; |
|
||||||
sourceFormat.numChannels = 2; // always stereo, because engine supports only stereo
|
|
||||||
sourceFormat.samplesPerSec = freq * 1000; |
|
||||||
sourceFormat.bitsPerSample = 16; // always 16 bit audio
|
|
||||||
sourceFormat.containerSize = sourceFormat.bitsPerSample; |
|
||||||
sourceFormat.channelMask = SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT; |
|
||||||
sourceFormat.endianness = SL_BYTEORDER_LITTLEENDIAN; |
|
||||||
source.pLocator = &sourceLocator; |
|
||||||
source.pFormat = &sourceFormat; |
|
||||||
|
|
||||||
sinkLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; |
|
||||||
sinkLocator.outputMix = snddma_android_outputMix; |
|
||||||
sink.pLocator = &sinkLocator; |
|
||||||
sink.pFormat = NULL; |
|
||||||
|
|
||||||
interfaceID = *pSL_IID_BUFFERQUEUE; |
|
||||||
interfaceRequired = SL_BOOLEAN_TRUE; |
|
||||||
|
|
||||||
result = (*engine)->CreateAudioPlayer( engine, &snddma_android_player, &source, &sink, 1, &interfaceID, &interfaceRequired ); |
|
||||||
if( result != SL_RESULT_SUCCESS ) return "engine->CreateAudioPlayer"; |
|
||||||
result = (*snddma_android_player)->Realize( snddma_android_player, SL_BOOLEAN_FALSE ); |
|
||||||
if( result != SL_RESULT_SUCCESS ) return "player->Realize"; |
|
||||||
result = (*snddma_android_player)->GetInterface( snddma_android_player, *pSL_IID_BUFFERQUEUE, &snddma_android_bufferQueue ); |
|
||||||
if( result != SL_RESULT_SUCCESS ) return "player->GetInterface(BUFFERQUEUE)"; |
|
||||||
result = (*snddma_android_player)->GetInterface( snddma_android_player, *pSL_IID_PLAY, &snddma_android_play ); |
|
||||||
if( result != SL_RESULT_SUCCESS ) return "player->GetInterface(PLAY)"; |
|
||||||
result = (*snddma_android_bufferQueue)->RegisterCallback( snddma_android_bufferQueue, SNDDMA_Android_Callback, NULL ); |
|
||||||
if( result != SL_RESULT_SUCCESS ) return "bufferQueue->RegisterCallback"; |
|
||||||
|
|
||||||
samples = s_samplecount.value; |
|
||||||
if( !samples ) |
|
||||||
samples = 4096; |
|
||||||
|
|
||||||
dma.format.channels = sourceFormat.numChannels; |
|
||||||
dma.samples = samples * sourceFormat.numChannels; |
|
||||||
dma.format.speed = freq; |
|
||||||
snddma_android_size = dma.samples * ( sourceFormat.bitsPerSample >> 3 ); |
|
||||||
dma.buffer = Z_Malloc( snddma_android_size * 2 ); |
|
||||||
dma.samplepos = 0; |
|
||||||
// dma.sampleframes = dma.samples / dma.format.channels;
|
|
||||||
dma.format.width = 2; |
|
||||||
if( !dma.buffer ) return "malloc"; |
|
||||||
|
|
||||||
//snddma_android_mutex = trap_Mutex_Create();
|
|
||||||
|
|
||||||
dma.initialized = true; |
|
||||||
|
|
||||||
SNDDMA_Activate( true ); |
|
||||||
|
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
qboolean SNDDMA_Init( void ) |
|
||||||
{ |
|
||||||
const char *initError; |
|
||||||
|
|
||||||
Msg( "OpenSL ES audio device initializing...\n" ); |
|
||||||
|
|
||||||
initError = SNDDMA_Android_Init(); |
|
||||||
if( initError ) |
|
||||||
{ |
|
||||||
Msg( S_ERROR "SNDDMA_Init: %s failed.\n", initError ); |
|
||||||
SNDDMA_Shutdown(); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
Msg( "OpenSL ES audio initialized.\n" ); |
|
||||||
dma.backendName = "OpenSL ES"; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
void SNDDMA_Shutdown( void ) |
|
||||||
{ |
|
||||||
Msg( "Closing OpenSL ES audio device...\n" ); |
|
||||||
|
|
||||||
if( snddma_android_player ) |
|
||||||
{ |
|
||||||
(*snddma_android_player)->Destroy( snddma_android_player ); |
|
||||||
snddma_android_player = NULL; |
|
||||||
} |
|
||||||
if( snddma_android_outputMix ) |
|
||||||
{ |
|
||||||
(*snddma_android_outputMix)->Destroy( snddma_android_outputMix ); |
|
||||||
snddma_android_outputMix = NULL; |
|
||||||
} |
|
||||||
if( snddma_android_engine ) |
|
||||||
{ |
|
||||||
(*snddma_android_engine)->Destroy( snddma_android_engine ); |
|
||||||
snddma_android_engine = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
if( dma.buffer ) |
|
||||||
{ |
|
||||||
Z_Free( dma.buffer ); |
|
||||||
dma.buffer = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
//if( snddma_android_mutex )
|
|
||||||
//trap_Mutex_Destroy( &snddma_android_mutex );
|
|
||||||
|
|
||||||
Msg( "OpenSL ES audio device shut down.\n" ); |
|
||||||
} |
|
||||||
|
|
||||||
void SNDDMA_Submit( void ) |
|
||||||
{ |
|
||||||
pthread_mutex_unlock( &snddma_android_mutex ); |
|
||||||
} |
|
||||||
|
|
||||||
void SNDDMA_BeginPainting( void ) |
|
||||||
{ |
|
||||||
pthread_mutex_lock( &snddma_android_mutex ); |
|
||||||
} |
|
||||||
|
|
||||||
qboolean VoiceCapture_Init( void ) |
|
||||||
{ |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
qboolean VoiceCapture_Activate( qboolean activate ) |
|
||||||
{ |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
qboolean VoiceCapture_Lock( qboolean lock ) |
|
||||||
{ |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
void VoiceCapture_Shutdown( void ) |
|
||||||
{ |
|
||||||
|
|
||||||
} |
|
||||||
#endif |
|
@ -1,665 +0,0 @@ |
|||||||
#include "platform/platform.h" |
|
||||||
#include "input.h" |
|
||||||
#include "client.h" |
|
||||||
#include "filesystem.h" |
|
||||||
#include "platform/android/android_priv.h" |
|
||||||
#include "vid_common.h" |
|
||||||
#include <android/native_window.h> |
|
||||||
#include <android/native_window_jni.h> |
|
||||||
#include <EGL/egl.h> |
|
||||||
#include <EGL/eglext.h> |
|
||||||
|
|
||||||
static struct vid_android_s |
|
||||||
{ |
|
||||||
int gl_attribs[REF_GL_ATTRIBUTES_COUNT]; |
|
||||||
qboolean gl_attribs_set[REF_GL_ATTRIBUTES_COUNT]; |
|
||||||
EGLint gl_api; |
|
||||||
qboolean gles1; |
|
||||||
void *libgles1, *libgles2; |
|
||||||
qboolean has_context; |
|
||||||
ANativeWindow* window; |
|
||||||
} vid_android; |
|
||||||
|
|
||||||
static struct nw_s |
|
||||||
{ |
|
||||||
void (*release)(ANativeWindow* window); |
|
||||||
int32_t (*getWidth)(ANativeWindow* window); |
|
||||||
int32_t (*getHeight)(ANativeWindow* window); |
|
||||||
int32_t (*getFormat)(ANativeWindow* window); |
|
||||||
int32_t (*setBuffersGeometry)(ANativeWindow* window, int32_t width, int32_t height, int32_t format); |
|
||||||
int32_t (*lock)(ANativeWindow* window, ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds); |
|
||||||
int32_t (*unlockAndPost)(ANativeWindow* window); |
|
||||||
ANativeWindow* (*fromSurface)(JNIEnv* env, jobject surface); |
|
||||||
} nw; |
|
||||||
|
|
||||||
#define NW_FF(x) {"ANativeWindow_"#x, (void*)&nw.x} |
|
||||||
|
|
||||||
|
|
||||||
static dllfunc_t android_funcs[] = |
|
||||||
{ |
|
||||||
NW_FF(release), |
|
||||||
NW_FF(getWidth), |
|
||||||
NW_FF(getHeight), |
|
||||||
NW_FF(getFormat), |
|
||||||
NW_FF(setBuffersGeometry), |
|
||||||
NW_FF(lock), |
|
||||||
NW_FF(unlockAndPost), |
|
||||||
NW_FF(fromSurface), |
|
||||||
{ NULL, NULL } |
|
||||||
}; |
|
||||||
#undef NW_FF |
|
||||||
dll_info_t android_info = { "libandroid.so", android_funcs, false }; |
|
||||||
|
|
||||||
static struct egl_s |
|
||||||
{ |
|
||||||
EGLSurface (*GetCurrentSurface)(EGLint readdraw); |
|
||||||
EGLDisplay (*GetCurrentDisplay)(void); |
|
||||||
EGLint (*GetError)(void); |
|
||||||
EGLBoolean (*SwapBuffers)(EGLDisplay dpy, EGLSurface surface); |
|
||||||
EGLBoolean (*SwapInterval)(EGLDisplay dpy, EGLint interval); |
|
||||||
void *(*GetProcAddress)(const char *procname); |
|
||||||
} egl; |
|
||||||
#undef GetProcAddress |
|
||||||
#define EGL_FF(x) {"egl"#x, (void*)&egl.x} |
|
||||||
static dllfunc_t egl_funcs[] = |
|
||||||
{ |
|
||||||
EGL_FF(SwapInterval), |
|
||||||
EGL_FF(SwapBuffers), |
|
||||||
EGL_FF(GetError), |
|
||||||
EGL_FF(GetCurrentDisplay), |
|
||||||
EGL_FF(GetCurrentSurface), |
|
||||||
EGL_FF(GetProcAddress), |
|
||||||
{ NULL, NULL } |
|
||||||
}; |
|
||||||
#undef EGL_FF |
|
||||||
dll_info_t egl_info = { "libEGL.so", egl_funcs, false }; |
|
||||||
|
|
||||||
static struct nativeegl_s |
|
||||||
{ |
|
||||||
qboolean valid; |
|
||||||
void *window; |
|
||||||
EGLDisplay dpy; |
|
||||||
EGLSurface surface; |
|
||||||
EGLContext context; |
|
||||||
EGLConfig cfg; |
|
||||||
EGLint numCfg; |
|
||||||
|
|
||||||
const char *extensions; |
|
||||||
} negl; |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_SwapInterval |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
static void Android_SwapInterval( int interval ) |
|
||||||
{ |
|
||||||
if( negl.valid ) |
|
||||||
egl.SwapInterval( negl.dpy, interval ); |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_SetTitle |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
static void Android_SetTitle( const char *title ) |
|
||||||
{ |
|
||||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.setTitle, (*jni.env)->NewStringUTF( jni.env, title ) ); |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_SetIcon |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
static void Android_SetIcon( const char *path ) |
|
||||||
{ |
|
||||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.setIcon, (*jni.env)->NewStringUTF( jni.env, path ) ); |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_GetScreenRes |
|
||||||
|
|
||||||
Resolution got from last resize event |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
static void Android_GetScreenRes( int *width, int *height ) |
|
||||||
{ |
|
||||||
*width=jni.width, *height=jni.height; |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_SwapBuffers |
|
||||||
|
|
||||||
Update screen. Use native EGL if possible |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
void GL_SwapBuffers( void ) |
|
||||||
{ |
|
||||||
if( negl.valid ) |
|
||||||
{ |
|
||||||
egl.SwapBuffers( negl.dpy, negl.surface ); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.swapBuffers ); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_UpdateSurface |
|
||||||
|
|
||||||
Check if we may use native EGL without jni calls |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
void Android_UpdateSurface( qboolean active ) |
|
||||||
{ |
|
||||||
negl.valid = false; |
|
||||||
|
|
||||||
if( nw.release ) |
|
||||||
{ |
|
||||||
if( vid_android.window && !active ) |
|
||||||
{ |
|
||||||
nw.release( vid_android.window ); |
|
||||||
vid_android.window = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
if( active ) |
|
||||||
{ |
|
||||||
jobject surf; |
|
||||||
if( vid_android.window ) |
|
||||||
nw.release( vid_android.window ); |
|
||||||
surf = (*jni.env)->CallStaticObjectMethod(jni.env, jni.actcls, jni.getSurface); |
|
||||||
Con_Printf("s %p\n", surf); |
|
||||||
vid_android.window = nw.fromSurface(jni.env, surf); |
|
||||||
Con_Printf("w %p\n", vid_android.window); |
|
||||||
nw.setBuffersGeometry(vid_android.window, 0, 0, WINDOW_FORMAT_RGB_565 ); |
|
||||||
(*jni.env)->DeleteLocalRef( jni.env, surf ); |
|
||||||
} |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
if( !vid_android.has_context ) |
|
||||||
return; |
|
||||||
|
|
||||||
if( ( active && host.status == HOST_FRAME ) || !active ) |
|
||||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 0 ); |
|
||||||
|
|
||||||
if( active ) |
|
||||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.actcls, jni.toggleEGL, 1 ); |
|
||||||
|
|
||||||
if( !Sys_CheckParm("-nativeegl") || !active ) |
|
||||||
return; // enabled by user
|
|
||||||
|
|
||||||
if( !egl.GetCurrentDisplay ) |
|
||||||
return; |
|
||||||
|
|
||||||
negl.dpy = egl.GetCurrentDisplay(); |
|
||||||
|
|
||||||
if( negl.dpy == EGL_NO_DISPLAY ) |
|
||||||
return; |
|
||||||
|
|
||||||
negl.surface = egl.GetCurrentSurface(EGL_DRAW); |
|
||||||
|
|
||||||
if( negl.surface == EGL_NO_SURFACE ) |
|
||||||
return; |
|
||||||
|
|
||||||
// now check if swapBuffers does not give error
|
|
||||||
if( egl.SwapBuffers( negl.dpy, negl.surface ) == EGL_FALSE ) |
|
||||||
return; |
|
||||||
|
|
||||||
// double check
|
|
||||||
if( egl.GetError() != EGL_SUCCESS ) |
|
||||||
return; |
|
||||||
|
|
||||||
__android_log_print( ANDROID_LOG_VERBOSE, "Xash", "native EGL enabled" ); |
|
||||||
|
|
||||||
negl.valid = true; |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
======================== |
|
||||||
Android_GetGLAttribute |
|
||||||
======================== |
|
||||||
*/ |
|
||||||
static int Android_GetGLAttribute( int eglAttr ) |
|
||||||
{ |
|
||||||
int ret = (*jni.env)->CallStaticIntMethod( jni.env, jni.actcls, jni.getGLAttribute, eglAttr ); |
|
||||||
// Con_Reportf( "Android_GetGLAttribute( %i ) => %i\n", eglAttr, ret );
|
|
||||||
return ret; |
|
||||||
} |
|
||||||
|
|
||||||
int Android_GetSelectedPixelFormat( void ) |
|
||||||
{ |
|
||||||
return (*jni.env)->CallStaticIntMethod( jni.env, jni.actcls, jni.getSelectedPixelFormat ); |
|
||||||
} |
|
||||||
|
|
||||||
qboolean R_Init_Video( const int type ) |
|
||||||
{ |
|
||||||
char buf[MAX_VA_STRING]; |
|
||||||
qboolean retval; |
|
||||||
|
|
||||||
switch( Android_GetSelectedPixelFormat() ) |
|
||||||
{ |
|
||||||
case 1: |
|
||||||
refState.desktopBitsPixel = 16; |
|
||||||
break; |
|
||||||
case 2: |
|
||||||
refState.desktopBitsPixel = 8; |
|
||||||
break; |
|
||||||
default: |
|
||||||
refState.desktopBitsPixel = 32; |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
if( FS_FileExists( GI->iconpath, true ) ) |
|
||||||
{ |
|
||||||
Q_snprintf( buf, sizeof( buf ), "%s/%s/%s", COM_CheckStringEmpty( host.rodir ) ? host.rodir : host.rootdir, GI->gamefolder, GI->iconpath ); |
|
||||||
Android_SetIcon( buf ); |
|
||||||
} |
|
||||||
|
|
||||||
Android_SetTitle( GI->title ); |
|
||||||
|
|
||||||
VID_StartupGamma(); |
|
||||||
|
|
||||||
switch( type ) |
|
||||||
{ |
|
||||||
case REF_SOFTWARE: |
|
||||||
glw_state.software = true; |
|
||||||
break; |
|
||||||
case REF_GL: |
|
||||||
glw_state.software = false; |
|
||||||
Sys_LoadLibrary( &egl_info ); |
|
||||||
|
|
||||||
if( !glw_state.safe && Sys_GetParmFromCmdLine( "-safegl", buf ) ) |
|
||||||
glw_state.safe = bound( SAFE_NO, Q_atoi( buf ), SAFE_DONTCARE ); |
|
||||||
|
|
||||||
break; |
|
||||||
default: |
|
||||||
Host_Error( "Can't initialize unknown context type %d!\n", type ); |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
if( glw_state.software ) |
|
||||||
{ |
|
||||||
uint arg; |
|
||||||
// Con_Reportf( S_ERROR "Native software mode isn't supported on Android yet! :(\n" );
|
|
||||||
// return false;
|
|
||||||
Sys_LoadLibrary( &android_info ); |
|
||||||
Android_UpdateSurface( true ); |
|
||||||
if( !SW_CreateBuffer( jni.width, jni.height, &arg, &arg, &arg, &arg, &arg ) ) |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
while( !(retval = VID_SetMode()) ) |
|
||||||
{ |
|
||||||
glw_state.safe++; |
|
||||||
if( glw_state.safe > SAFE_LAST ) |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
switch( type ) |
|
||||||
{ |
|
||||||
case REF_GL: |
|
||||||
// refdll also can check extensions
|
|
||||||
ref.dllFuncs.GL_InitExtensions(); |
|
||||||
break; |
|
||||||
case REF_SOFTWARE: |
|
||||||
default: |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
host.renderinfo_changed = false; |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
void R_Free_Video( void ) |
|
||||||
{ |
|
||||||
// (*jni.env)->CallStaticBooleanMethod( jni.env, jni.actcls, jni.deleteGLContext );
|
|
||||||
|
|
||||||
// VID_DestroyWindow ();
|
|
||||||
|
|
||||||
// R_FreeVideoModes();
|
|
||||||
Sys_FreeLibrary( &android_info ); |
|
||||||
Sys_FreeLibrary( &egl_info ); |
|
||||||
vid_android.has_context = false; |
|
||||||
ref.dllFuncs.GL_ClearExtensions(); |
|
||||||
} |
|
||||||
|
|
||||||
#define COPY_ATTR_IF_SET( refattr, attr ) \ |
|
||||||
if( vid_android.gl_attribs_set[refattr] ) \ |
|
||||||
{ \ |
|
||||||
attribs[i++] = attr; \ |
|
||||||
attribs[i++] = vid_android.gl_attribs[refattr]; \ |
|
||||||
} |
|
||||||
|
|
||||||
static size_t VID_GenerateConfig( EGLint *attribs, size_t size ) |
|
||||||
{ |
|
||||||
size_t i = 0; |
|
||||||
|
|
||||||
memset( attribs, 0, size * sizeof( EGLint ) ); |
|
||||||
vid_android.gles1 = false; |
|
||||||
memset( vid_android.gl_attribs, 0, sizeof( vid_android.gl_attribs )); |
|
||||||
memset( vid_android.gl_attribs_set, 0, sizeof( vid_android.gl_attribs_set )); |
|
||||||
|
|
||||||
// refdll can request some attributes
|
|
||||||
ref.dllFuncs.GL_SetupAttributes( glw_state.safe ); |
|
||||||
|
|
||||||
COPY_ATTR_IF_SET( REF_GL_RED_SIZE, EGL_RED_SIZE ); |
|
||||||
COPY_ATTR_IF_SET( REF_GL_GREEN_SIZE, EGL_GREEN_SIZE ); |
|
||||||
COPY_ATTR_IF_SET( REF_GL_BLUE_SIZE, EGL_BLUE_SIZE ); |
|
||||||
COPY_ATTR_IF_SET( REF_GL_ALPHA_SIZE, EGL_ALPHA_SIZE ); |
|
||||||
COPY_ATTR_IF_SET( REF_GL_DEPTH_SIZE, EGL_DEPTH_SIZE ); |
|
||||||
COPY_ATTR_IF_SET( REF_GL_STENCIL_SIZE, EGL_STENCIL_SIZE ); |
|
||||||
COPY_ATTR_IF_SET( REF_GL_MULTISAMPLEBUFFERS, EGL_SAMPLE_BUFFERS ); |
|
||||||
COPY_ATTR_IF_SET( REF_GL_MULTISAMPLESAMPLES, EGL_SAMPLES ); |
|
||||||
|
|
||||||
if( vid_android.gl_attribs_set[REF_GL_ACCELERATED_VISUAL] ) |
|
||||||
{ |
|
||||||
attribs[i++] = EGL_CONFIG_CAVEAT; |
|
||||||
attribs[i++] = vid_android.gl_attribs[REF_GL_ACCELERATED_VISUAL] ? EGL_NONE : EGL_DONT_CARE; |
|
||||||
} |
|
||||||
|
|
||||||
// BigGL support
|
|
||||||
attribs[i++] = EGL_RENDERABLE_TYPE; |
|
||||||
vid_android.gl_api = EGL_OPENGL_ES_API; |
|
||||||
|
|
||||||
if( vid_android.gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] && |
|
||||||
!( vid_android.gl_attribs[REF_GL_CONTEXT_PROFILE_MASK] & REF_GL_CONTEXT_PROFILE_ES )) |
|
||||||
{ |
|
||||||
attribs[i++] = EGL_OPENGL_BIT; |
|
||||||
vid_android.gl_api = EGL_OPENGL_API; |
|
||||||
} |
|
||||||
else if( vid_android.gl_attribs_set[REF_GL_CONTEXT_MAJOR_VERSION] && |
|
||||||
vid_android.gl_attribs[REF_GL_CONTEXT_MAJOR_VERSION] >= 2 ) |
|
||||||
{ |
|
||||||
attribs[i++] = EGL_OPENGL_ES2_BIT; |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
i--; // erase EGL_RENDERABLE_TYPE
|
|
||||||
vid_android.gles1 = true; |
|
||||||
} |
|
||||||
|
|
||||||
attribs[i++] = EGL_NONE; |
|
||||||
|
|
||||||
return i; |
|
||||||
} |
|
||||||
|
|
||||||
static size_t VID_GenerateContextConfig( EGLint *attribs, size_t size ) |
|
||||||
{ |
|
||||||
size_t i = 0; |
|
||||||
|
|
||||||
memset( attribs, 0, size * sizeof( EGLint )); |
|
||||||
|
|
||||||
/*if( Q_strcmp( negl.extensions, " EGL_KHR_create_context ") )
|
|
||||||
{ |
|
||||||
if( vid_android.gl_attribs_set[REF_GL_CONTEXT_FLAGS] ) |
|
||||||
{ |
|
||||||
attribs[i++] = 0x30FC; // EGL_CONTEXT_FLAGS_KHR
|
|
||||||
attribs[i++] = vid_android.gl_attribs[REF_GL_CONTEXT_FLAGS] & ((REF_GL_CONTEXT_ROBUST_ACCESS_FLAG << 1) - 1); |
|
||||||
} |
|
||||||
|
|
||||||
if( vid_android.gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] ) |
|
||||||
{ |
|
||||||
int val = vid_android.gl_attribs[REF_GL_CONTEXT_PROFILE_MASK]; |
|
||||||
|
|
||||||
if( val & ( (REF_GL_CONTEXT_PROFILE_COMPATIBILITY << 1) - 1 ) ) |
|
||||||
{ |
|
||||||
attribs[i++] = 0x30FD; // EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
|
|
||||||
attribs[i++] = val; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
COPY_ATTR_IF_SET( REF_GL_CONTEXT_MAJOR_VERSION, EGL_CONTEXT_CLIENT_VERSION ); |
|
||||||
COPY_ATTR_IF_SET( REF_GL_CONTEXT_MINOR_VERSION, 0x30FB ); |
|
||||||
} |
|
||||||
else*/ |
|
||||||
{ |
|
||||||
// without extension we can set only major version
|
|
||||||
COPY_ATTR_IF_SET( REF_GL_CONTEXT_MAJOR_VERSION, EGL_CONTEXT_CLIENT_VERSION ); |
|
||||||
} |
|
||||||
|
|
||||||
attribs[i++] = EGL_NONE; |
|
||||||
|
|
||||||
return i; |
|
||||||
} |
|
||||||
|
|
||||||
qboolean VID_SetMode( void ) |
|
||||||
{ |
|
||||||
EGLint format; |
|
||||||
jintArray attribs, contextAttribs; |
|
||||||
static EGLint nAttribs[32+1], nContextAttribs[32+1]; |
|
||||||
const size_t attribsSize = ARRAYSIZE( nAttribs ); |
|
||||||
size_t s1, s2; |
|
||||||
|
|
||||||
if( vid_android.has_context ) |
|
||||||
{ |
|
||||||
R_ChangeDisplaySettings( 0, 0, false ); // width and height are ignored anyway
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
s1 = VID_GenerateConfig(nAttribs, attribsSize); |
|
||||||
s2 = VID_GenerateContextConfig(nContextAttribs, attribsSize); |
|
||||||
|
|
||||||
attribs = (*jni.env)->NewIntArray( jni.env, s1 ); |
|
||||||
contextAttribs = (*jni.env)->NewIntArray( jni.env, s2 ); |
|
||||||
|
|
||||||
(*jni.env)->SetIntArrayRegion( jni.env, attribs, 0, s1, nAttribs ); |
|
||||||
(*jni.env)->SetIntArrayRegion( jni.env, contextAttribs, 0, s2, nContextAttribs ); |
|
||||||
|
|
||||||
R_ChangeDisplaySettings( 0, 0, false ); // width and height are ignored anyway
|
|
||||||
|
|
||||||
if( glw_state.software ) |
|
||||||
return true; |
|
||||||
|
|
||||||
if( (*jni.env)->CallStaticBooleanMethod( jni.env, jni.actcls, jni.createGLContext, attribs, contextAttribs ) ) |
|
||||||
{ |
|
||||||
vid_android.has_context = true; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ) |
|
||||||
{ |
|
||||||
int render_w, render_h; |
|
||||||
uint rotate = vid_rotate->value; |
|
||||||
|
|
||||||
Android_GetScreenRes(&width, &height); |
|
||||||
|
|
||||||
render_w = width; |
|
||||||
render_h = height; |
|
||||||
|
|
||||||
Con_Reportf( "R_ChangeDisplaySettings: forced resolution to %dx%d)\n", width, height); |
|
||||||
|
|
||||||
if( ref.dllFuncs.R_SetDisplayTransform( rotate, 0, 0, vid_scale->value, vid_scale->value ) ) |
|
||||||
{ |
|
||||||
if( rotate & 1 ) |
|
||||||
{ |
|
||||||
int swap = render_w; |
|
||||||
|
|
||||||
render_w = render_h; |
|
||||||
render_h = swap; |
|
||||||
} |
|
||||||
|
|
||||||
render_h /= vid_scale->value; |
|
||||||
render_w /= vid_scale->value; |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
Con_Printf( S_WARN "failed to setup screen transform\n" ); |
|
||||||
} |
|
||||||
|
|
||||||
R_SaveVideoMode( width, height, render_w, render_h ); |
|
||||||
|
|
||||||
refState.wideScreen = true; // V_AdjustFov will check for widescreen
|
|
||||||
|
|
||||||
return rserr_ok; |
|
||||||
} |
|
||||||
|
|
||||||
int GL_SetAttribute( int attr, int val ) |
|
||||||
{ |
|
||||||
if( attr < 0 || attr >= REF_GL_ATTRIBUTES_COUNT ) |
|
||||||
return -1; |
|
||||||
|
|
||||||
vid_android.gl_attribs[attr] = val; |
|
||||||
vid_android.gl_attribs_set[attr] = true; |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
int GL_GetAttribute( int attr, int *val ) |
|
||||||
{ |
|
||||||
EGLBoolean ret; |
|
||||||
|
|
||||||
if( attr < 0 || attr >= REF_GL_ATTRIBUTES_COUNT ) |
|
||||||
return -1; |
|
||||||
|
|
||||||
if( !val ) |
|
||||||
return -1; |
|
||||||
|
|
||||||
switch( attr ) |
|
||||||
{ |
|
||||||
case REF_GL_RED_SIZE: |
|
||||||
*val = Android_GetGLAttribute( EGL_RED_SIZE ); |
|
||||||
return 0; |
|
||||||
case REF_GL_GREEN_SIZE: |
|
||||||
*val = Android_GetGLAttribute( EGL_GREEN_SIZE ); |
|
||||||
return 0; |
|
||||||
case REF_GL_BLUE_SIZE: |
|
||||||
*val = Android_GetGLAttribute( EGL_BLUE_SIZE ); |
|
||||||
return 0; |
|
||||||
case REF_GL_ALPHA_SIZE: |
|
||||||
*val = Android_GetGLAttribute( EGL_ALPHA_SIZE ); |
|
||||||
return 0; |
|
||||||
case REF_GL_DEPTH_SIZE: |
|
||||||
*val = Android_GetGLAttribute( EGL_DEPTH_SIZE ); |
|
||||||
return 0; |
|
||||||
case REF_GL_STENCIL_SIZE: |
|
||||||
*val = Android_GetGLAttribute( EGL_STENCIL_SIZE ); |
|
||||||
return 0; |
|
||||||
case REF_GL_MULTISAMPLESAMPLES: |
|
||||||
*val = Android_GetGLAttribute( EGL_SAMPLES ); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
int R_MaxVideoModes( void ) |
|
||||||
{ |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
vidmode_t* R_GetVideoMode( int num ) |
|
||||||
{ |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
void* GL_GetProcAddress( const char *name ) // RenderAPI requirement
|
|
||||||
{ |
|
||||||
void *gles; |
|
||||||
void *addr; |
|
||||||
|
|
||||||
if( vid_android.gles1 ) |
|
||||||
{ |
|
||||||
if( !vid_android.libgles1 ) |
|
||||||
vid_android.libgles1 = dlopen("libGLESv1_CM.so", RTLD_NOW); |
|
||||||
gles = vid_android.libgles1; |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
if( !vid_android.libgles2 ) |
|
||||||
vid_android.libgles2 = dlopen("libGLESv2.so", RTLD_NOW); |
|
||||||
gles = vid_android.libgles2; |
|
||||||
} |
|
||||||
|
|
||||||
if( gles && ( addr = dlsym(gles, name ) ) ) |
|
||||||
return addr; |
|
||||||
|
|
||||||
if( !egl.GetProcAddress ) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
return egl.GetProcAddress( name ); |
|
||||||
} |
|
||||||
|
|
||||||
void GL_UpdateSwapInterval( void ) |
|
||||||
{ |
|
||||||
// disable VSync while level is loading
|
|
||||||
if( cls.state < ca_active ) |
|
||||||
{ |
|
||||||
Android_SwapInterval( 0 ); |
|
||||||
SetBits( gl_vsync->flags, FCVAR_CHANGED ); |
|
||||||
} |
|
||||||
else if( FBitSet( gl_vsync->flags, FCVAR_CHANGED )) |
|
||||||
{ |
|
||||||
ClearBits( gl_vsync->flags, FCVAR_CHANGED ); |
|
||||||
Android_SwapInterval( gl_vsync->value ); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void *SW_LockBuffer( void ) |
|
||||||
{ |
|
||||||
ANativeWindow_Buffer buffer; |
|
||||||
if( !nw.lock || !vid_android.window ) |
|
||||||
return NULL; |
|
||||||
if( nw.lock( vid_android.window, &buffer, NULL ) ) |
|
||||||
return NULL; |
|
||||||
if( buffer.width < refState.width || buffer.height < refState.height ) |
|
||||||
return NULL; |
|
||||||
return buffer.bits; |
|
||||||
} |
|
||||||
|
|
||||||
void SW_UnlockBuffer( void ) |
|
||||||
{ |
|
||||||
if( nw.unlockAndPost ) |
|
||||||
nw.unlockAndPost( vid_android.window ); |
|
||||||
} |
|
||||||
|
|
||||||
qboolean SW_CreateBuffer( int width, int height, uint *stride, uint *bpp, uint *r, uint *g, uint *b ) |
|
||||||
{ |
|
||||||
ANativeWindow_Buffer buffer; |
|
||||||
int lock; |
|
||||||
if( !nw.lock || !vid_android.window ) |
|
||||||
return false; |
|
||||||
nw.unlockAndPost( vid_android.window ); |
|
||||||
|
|
||||||
if( ( lock = nw.lock( vid_android.window, &buffer, NULL ) ) ) |
|
||||||
{ |
|
||||||
Con_Printf( "SW_CreateBuffer: lock %d\n", lock ); |
|
||||||
return false; |
|
||||||
} |
|
||||||
nw.unlockAndPost( vid_android.window ); |
|
||||||
Con_Printf( "SW_CreateBuffer: buffer %d %d %x %d %p\n", buffer.width, buffer.height, buffer.format, buffer.stride, buffer.bits ); |
|
||||||
if( width > buffer.width || height > buffer.height ) |
|
||||||
{ |
|
||||||
Con_Printf( "SW_CreateBuffer: buffer too small %d %d\n", width, height ); |
|
||||||
// resize event missed?
|
|
||||||
if( jni.width < buffer.width ) |
|
||||||
jni.width = buffer.width; |
|
||||||
if( jni.height < buffer.height ) |
|
||||||
jni.width = buffer.height; |
|
||||||
VID_SetMode(); |
|
||||||
Android_UpdateSurface( 1 ); |
|
||||||
return false; |
|
||||||
} |
|
||||||
if( buffer.format != WINDOW_FORMAT_RGB_565 ) |
|
||||||
{ |
|
||||||
Con_Printf( "SW_CreateBuffer: wrong format %d\n", buffer.format ); |
|
||||||
return false; |
|
||||||
} |
|
||||||
Con_Printf( "SW_CreateBuffer: ok\n" ); |
|
||||||
*stride = buffer.stride; |
|
||||||
|
|
||||||
*bpp = 2; |
|
||||||
*r = (((1 << 5) - 1) << (5+6)); |
|
||||||
*g = (((1 << 6) - 1) << (5)); |
|
||||||
*b = (((1 << 5) - 1) << (0)); |
|
||||||
return true; |
|
||||||
} |
|
Loading…
Reference in new issue