mirror of
https://github.com/FWGS/xash3d-fwgs
synced 2024-11-25 11:19:59 +01:00
Revert "engine: partially remove legacy Android port, in preparation of new port merge"
This reverts commit ef663a8790
.
This commit is contained in:
parent
8f819a2fde
commit
6f03d31a30
@ -18,17 +18,21 @@ GNU General Public License for more details.
|
||||
// video backends (XASH_VIDEO)
|
||||
#define VIDEO_NULL 0
|
||||
#define VIDEO_SDL 1
|
||||
#define VIDEO_ANDROID 2
|
||||
#define VIDEO_FBDEV 3
|
||||
#define VIDEO_DOS 4
|
||||
|
||||
|
||||
// audio backends (XASH_SOUND)
|
||||
#define SOUND_NULL 0
|
||||
#define SOUND_SDL 1
|
||||
#define SOUND_OPENSLES 2
|
||||
#define SOUND_ALSA 3
|
||||
|
||||
// input (XASH_INPUT)
|
||||
#define INPUT_NULL 0
|
||||
#define INPUT_SDL 1
|
||||
#define INPUT_ANDROID 2
|
||||
#define INPUT_EVDEV 3
|
||||
|
||||
// timer (XASH_TIMER)
|
||||
@ -41,13 +45,16 @@ GNU General Public License for more details.
|
||||
// messageboxes (XASH_MESSAGEBOX)
|
||||
#define MSGBOX_STDERR 0
|
||||
#define MSGBOX_SDL 1
|
||||
#define MSGBOX_ANDROID 2
|
||||
#define MSGBOX_WIN32 3
|
||||
#define MSGBOX_NSWITCH 4
|
||||
|
||||
|
||||
// library loading (XASH_LIB)
|
||||
#define LIB_NULL 0
|
||||
#define LIB_POSIX 1
|
||||
#define LIB_WIN32 2
|
||||
#define LIB_STATIC 3
|
||||
|
||||
|
||||
#endif /* BACKENDS_H */
|
||||
|
@ -52,6 +52,26 @@ SETUP BACKENDS DEFINITIONS
|
||||
#endif
|
||||
#endif // XASH_MESSAGEBOX
|
||||
#endif
|
||||
#elif XASH_ANDROID
|
||||
// we are building for Android platform, use Android APIs
|
||||
#ifndef XASH_VIDEO
|
||||
#define XASH_VIDEO VIDEO_ANDROID
|
||||
#endif // XASH_VIDEO
|
||||
|
||||
#ifndef XASH_INPUT
|
||||
#define XASH_INPUT INPUT_ANDROID
|
||||
#endif // XASH_INPUT
|
||||
|
||||
#ifndef XASH_SOUND
|
||||
#define XASH_SOUND SOUND_OPENSLES
|
||||
#endif // XASH_SOUND
|
||||
|
||||
#ifndef XASH_MESSAGEBOX
|
||||
#define XASH_MESSAGEBOX MSGBOX_ANDROID
|
||||
#endif // XASH_MESSAGEBOX
|
||||
|
||||
#define XASH_USE_EVDEV 1
|
||||
#define XASH_DYNAMIC_DLADDR
|
||||
#elif XASH_LINUX
|
||||
// we are building for Linux without SDL2, can draw only to framebuffer yet
|
||||
#ifndef XASH_VIDEO
|
||||
@ -151,8 +171,6 @@ Default build-depended cvar and constant values
|
||||
#define DEFAULT_MODE_WIDTH 960
|
||||
#define DEFAULT_MODE_HEIGHT 544
|
||||
#define DEFAULT_ALLOWCONSOLE 1
|
||||
#elif XASH_ANDROID
|
||||
#define DEFAULT_TOUCH_ENABLE "1"
|
||||
#elif XASH_MOBILE_PLATFORM
|
||||
#define DEFAULT_TOUCH_ENABLE "1"
|
||||
#define DEFAULT_M_IGNORE "1"
|
||||
|
@ -14,7 +14,7 @@ GNU General Public License for more details.
|
||||
*/
|
||||
#include "platform/platform.h"
|
||||
|
||||
#if !defined(XASH_DEDICATED)
|
||||
#if !defined(XASH_DEDICATED) && XASH_SDL
|
||||
|
||||
#include "input.h"
|
||||
#include "client.h"
|
||||
|
997
engine/platform/android/android_nosdl.c
Normal file
997
engine/platform/android/android_nosdl.c
Normal file
@ -0,0 +1,997 @@
|
||||
/*
|
||||
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) && !XASH_SDL
|
||||
#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 )
|
||||
{
|
||||
if( cursor == dc_arrow )
|
||||
Android_ShowMouse( true );
|
||||
else
|
||||
Android_ShowMouse( false );
|
||||
|
||||
}
|
||||
|
||||
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
|
46
engine/platform/android/android_priv.h
Normal file
46
engine/platform/android/android_priv.h
Normal file
@ -0,0 +1,46 @@
|
||||
#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
|
277
engine/platform/android/snd_opensles.c
Normal file
277
engine/platform/android/snd_opensles.c
Normal file
@ -0,0 +1,277 @@
|
||||
/*
|
||||
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
|
667
engine/platform/android/vid_android.c
Normal file
667
engine/platform/android/vid_android.c
Normal file
@ -0,0 +1,667 @@
|
||||
#include "platform/platform.h"
|
||||
#in !XASH_SDL
|
||||
#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;
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user