Xrasher/src/in/celest/xash3d/XashActivity.java

1714 lines
46 KiB
Java
Raw Normal View History

package in.celest.xash3d;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.egl.*;
import android.app.*;
import android.content.*;
import android.view.*;
import android.os.*;
import android.util.*;
import android.graphics.*;
import android.text.method.*;
import android.text.*;
import android.media.*;
import android.hardware.*;
import android.content.*;
2016-05-08 22:57:35 +00:00
import android.widget.*;
import android.content.pm.*;
import android.net.Uri;
import android.provider.*;
import android.database.*;
2016-05-08 22:57:35 +00:00
import android.view.inputmethod.*;
import java.lang.*;
2017-03-30 00:25:51 +00:00
import java.lang.reflect.*;
import java.util.List;
import java.security.MessageDigest;
import in.celest.xash3d.hl.R;
import in.celest.xash3d.XashConfig;
import in.celest.xash3d.JoystickHandler;
import in.celest.xash3d.CertCheck;
2017-03-28 23:32:39 +00:00
import android.provider.Settings.Secure;
/**
Xash Activity
*/
public class XashActivity extends Activity {
2016-05-09 14:03:50 +00:00
// Main components
protected static XashActivity mSingleton;
protected static View mTextEdit;
protected static ViewGroup mLayout;
2016-08-01 11:27:51 +00:00
public static EngineSurface mSurface;
2016-05-09 14:03:50 +00:00
public static String mArgv[];
public static final int sdk = Integer.valueOf(Build.VERSION.SDK);
public static final String TAG = "XASH3D:XashActivity";
public static int mPixelFormat;
2016-08-01 11:27:51 +00:00
public static JoystickHandler handler;
2016-08-09 10:50:52 +00:00
public static ImmersiveMode mImmersiveMode;
public static boolean keyboardVisible = false;
public static boolean mEngineReady = false;
public static boolean mEnginePaused = false;
public static Vibrator mVibrator;
public static boolean fMouseShown = true;
public static boolean fGDBSafe = false;
2017-09-24 06:26:51 +07:00
public static float mScale = 0, mTouchScaleX = 1, mTouchScaleY = 1;
public static int mForceHeight = 0, mForceWidth = 0;
private static boolean mHasVibrator;
private int mReturingWithResultCode = 0;
private static int OPEN_DOCUMENT_TREE_RESULT = 1;
private static int FPICKER_RESULT = 2;
2016-08-01 11:27:51 +00:00
// Joystick constants
public final static byte JOY_HAT_CENTERED = 0; // bitmasks for hat current status
public final static byte JOY_HAT_UP = 1;
public final static byte JOY_HAT_RIGHT = 2;
public final static byte JOY_HAT_DOWN = 4;
public final static byte JOY_HAT_LEFT = 8;
public final static byte JOY_AXIS_SIDE = 0; // this represents default
public final static byte JOY_AXIS_FWD = 1; // engine binding: SFPYRL
public final static byte JOY_AXIS_PITCH = 2;
public final static byte JOY_AXIS_YAW = 3;
public final static byte JOY_AXIS_RT = 4;
public final static byte JOY_AXIS_LT = 5;
2016-05-09 14:03:50 +00:00
2016-07-26 22:39:54 +06:00
// Preferences
2016-05-09 14:03:50 +00:00
public static SharedPreferences mPref = null;
2016-05-31 09:03:38 +00:00
private static boolean mUseVolume;
2016-08-05 17:43:46 +00:00
public static View mDecorView;
// Load the .so
static {
System.loadLibrary("gpgs_support");
System.loadLibrary("xash");
}
2016-05-09 14:03:50 +00:00
// Setup
@Override
2016-05-09 14:03:50 +00:00
protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "onCreate()");
super.onCreate(savedInstanceState);
mEngineReady = false;
if( CertCheck.dumbAntiPDALifeCheck(this) )
{
finish();
return;
}
2016-05-09 14:03:50 +00:00
// So we can call stuff from static callbacks
mSingleton = this;
// fullscreen
2016-05-06 16:49:42 +00:00
requestWindowFeature(Window.FEATURE_NO_TITLE);
int flags = WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
getWindow().setFlags(flags, flags);
2016-05-06 16:49:42 +00:00
2016-05-08 07:59:53 +00:00
// landscapeSensor is not supported until API9
if( sdk < 9 )
setRequestedOrientation(0);
mPref = this.getSharedPreferences("engine", 0);
if( mPref.getBoolean("folderask", true ) )
{
Log.v(TAG, "folderask == true. Opening FPicker...");
Intent intent = new Intent(this, in.celest.xash3d.FPicker.class);
startActivityForResult( intent, FPICKER_RESULT );
}
else
{
Log.v(TAG, "folderask == false. Checking write permission...");
2016-05-06 16:49:42 +00:00
// check write permission and run engine, if possible
String basedir = getStringExtraFromIntent( getIntent(), "basedir", mPref.getString("basedir","/sdcard/xash/"));
checkWritePermission( basedir );
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData)
{
if( resultCode != RESULT_OK )
{
Log.v(TAG, "onActivityResult: result is not OK. ReqCode: " + requestCode + ". ResCode: " + resultCode);
}
else
{
// it's not possible to create dialogs here
// so most work will be done after Activity resuming, in onPostResume()
mReturingWithResultCode = requestCode;
if( requestCode == FPICKER_RESULT )
{
String newBaseDir = resultData.getStringExtra("GetPath");
setNewBasedir( newBaseDir );
setFolderAsk( false ); // don't ask on next run
Log.v(TAG, "Got new basedir from FPicker: " + newBaseDir );
}
}
}
@Override
public void onPostResume()
{
super.onPostResume();
if( mReturingWithResultCode != 0 )
{
if( mReturingWithResultCode == FPICKER_RESULT )
{
String basedir = mPref.getString( "basedir", "/sdcard/xash/" );
checkWritePermission( basedir );
}
/*else if( mReturingWithResultCode == OPEN_DOCUMENT_TREE_RESULT )
{
String basedir = getStringExtraFromIntent( getIntent(), "basedir", mPref.getString("basedir","/sdcard/xash/"));
Log.v(TAG, "Got permissions. Checking writing again...");
if( nativeTestWritePermission( basedir ) == 0 )
{
Log.v(TAG, "Write test has failed twice!");
String msg = getString(R.string.lollipop_request_permission_fail_msg) + getString(R.string.ask_about_new_basedir_msg);
new AlertDialog.Builder(this)
.setTitle( R.string.write_failed )
.setMessage( msg )
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton)
{
XashActivity act = XashActivity.this;
act.setFolderAsk( true );
act.finish();
}
})
.setCancelable(false)
.show();
}
else
{
launchSurfaceAndEngine();
}
}*/
mReturingWithResultCode = 0;
}
}
// Events
@Override
protected void onPause() {
Log.v(TAG, "onPause()");
if( mEngineReady )
{
// let engine save all configs before exiting.
nativeOnPause();
// wait until Xash will save all configs
mSurface.engineThreadWait();
}
super.onPause();
}
@Override
protected void onResume() {
Log.v(TAG, "onResume()");
if( mEngineReady )
nativeOnResume();
mEnginePaused = false;
super.onResume();
}
@Override
protected void onStop() {
Log.v(TAG, "onStop()");
/*
if( mEngineReady )
{
nativeSetPause(0);
// let engine properly exit, instead of killing it's thread
nativeOnStop();
// wait for engine
mSurface.engineThreadWait();
}
*/
super.onStop();
}
@Override
protected void onDestroy() {
Log.v(TAG, "onDestroy()");
if( mEngineReady )
{
nativeUnPause();
// let engine a chance to properly exit
nativeOnDestroy();
//mSurface.engineThreadWait();
// wait until Xash will exit
mSurface.engineThreadJoin();
}
super.onDestroy();
}
@Override
public void onWindowFocusChanged(boolean hasFocus)
{
if( mEngineReady )
nativeOnFocusChange();
super.onWindowFocusChanged(hasFocus);
if( mImmersiveMode != null )
mImmersiveMode.apply();
}
public void setFolderAsk( Boolean b )
{
SharedPreferences.Editor editor = mPref.edit();
editor.putBoolean( "folderask", b );
editor.commit();
}
private void setNewBasedir( String baseDir )
{
SharedPreferences.Editor editor = mPref.edit();
editor.putString( "basedir", baseDir );
editor.commit();
}
private String getStringExtraFromIntent( Intent intent, String extraString, String ifNotFound )
{
String ret = intent.getStringExtra(extraString);
if( ret == null ) ret = ifNotFound;
return ret;
}
private void checkWritePermission( String basedir )
{
Log.v(TAG, "Checking write permissions...");
if( nativeTestWritePermission( basedir ) == 0 )
{
Log.v(TAG, "First check has failed!");
if( sdk > 20 )
{
// 5.0 and higher _allows_ writing to SD card, but have broken fopen() call. So, no Xash here. F*ck you, Google!
String msg = getString(R.string.lollipop_write_fail_msg) + getString(R.string.ask_about_new_basedir_msg);
new AlertDialog.Builder(this)
.setTitle( R.string.write_failed )
.setMessage( msg )
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton)
{
XashActivity act = XashActivity.this;
act.setFolderAsk( true );
act.finish();
}
})
.setCancelable(false)
.show();
}
else if( sdk > 18 )
{
// 4.4 and 4.4W does not allow SD card write at all
String msg = getString(R.string.kitkat_write_fail_msg) + getString(R.string.ask_about_new_basedir_msg);
new AlertDialog.Builder(this)
.setTitle( R.string.write_failed )
.setMessage( msg )
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton)
{
XashActivity act = XashActivity.this;
act.setFolderAsk( true );
act.finish();
}
})
.setCancelable(false)
.show();
}
else
{
String msg = getString(R.string.readonly_fs_fail_msg) + getString(R.string.ask_about_new_basedir_msg);
// Read-only filesystem
// Logically should be never reached
new AlertDialog.Builder(this)
.setTitle( R.string.write_failed )
.setMessage( msg )
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton)
{
XashActivity act = XashActivity.this;
act.setFolderAsk( true );
act.finish();
}
})
.setCancelable(false)
.show();
}
}
else
{
// everything is normal, so launch engine
launchSurfaceAndEngine();
}
}
private void launchSurfaceAndEngine()
{
Log.v(TAG, "Everything is OK. Launching engine...");
setupEnvironment();
InstallReceiver.extractPAK(this, false);
// Set up the surface
mSurface = new EngineSurface(getApplication());
mLayout = new FrameLayout(this);
mLayout.addView(mSurface);
setContentView(mLayout);
SurfaceHolder holder = mSurface.getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
2017-03-30 23:32:27 +00:00
if( sdk >= 14 )
handler = new JoystickHandler_v14();
else if( sdk >= 12 )
handler = new JoystickHandler_v12();
2017-03-30 23:32:27 +00:00
else
handler = new JoystickHandler();
handler.init();
mPixelFormat = mPref.getInt("pixelformat", 0);
mUseVolume = mPref.getBoolean("usevolume", false);
if( mPref.getBoolean("enableResizeWorkaround", true) )
AndroidBug5497Workaround.assistActivity(this);
// Immersive Mode is available only at >KitKat
Boolean enableImmersive = (sdk >= 19) && (mPref.getBoolean("immersive_mode", true));
if( enableImmersive )
mImmersiveMode = new ImmersiveMode_v19();
mDecorView = getWindow().getDecorView();
mVibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
2017-09-24 06:26:51 +07:00
if( mPref.getBoolean("resolution_fixed", false) )
{
if( mPref.getBoolean("resolution_custom", false) )
{
mForceWidth = mPref.getInt( "resolution_width", 854 );
mForceHeight = mPref.getInt( "resolution_height", 480 );
if( mForceWidth < 10 || mForceHeight < 10 )
mForceWidth = mForceHeight = 0;
}
else
mScale = mPref.getFloat( "resolution_scale", 1 );
if( mScale < 0.5 )
mScale = 0;
}
mHasVibrator = ( mVibrator != null );
if( sdk >= 11 )
mHasVibrator = ( mVibrator != null ) && ( handler.hasVibrator() );
if( sdk >= 5 )
startService(new Intent(getBaseContext(), XashService.class));
mEngineReady = true;
}
private void setupEnvironment()
{
Intent intent = getIntent();
final String enginedir = getFilesDir().getParentFile().getPath() + "/lib";
String argv = getStringExtraFromIntent(intent, "argv", mPref.getString("argv", "-dev 3 -log"));
String gamelibdir = getStringExtraFromIntent(intent, "gamelibdir", enginedir);
String gamedir = getStringExtraFromIntent(intent, "gamedir", "valve");
String basedir = getStringExtraFromIntent(intent, "basedir", mPref.getString("basedir","/sdcard/xash/"));
String gdbsafe = intent.getStringExtra("gdbsafe");
if( gdbsafe != null )
fGDBSafe = true;
if( Debug.isDebuggerConnected() )
fGDBSafe = true;
if( fGDBSafe )
Log.e(TAG, "GDBSafe mode enabled!");
mArgv = argv.split(" ");
2016-05-09 14:03:50 +00:00
setenv("XASH3D_BASEDIR", basedir, true);
setenv("XASH3D_ENGLIBDIR", enginedir, true);
2016-05-04 21:02:09 +00:00
setenv("XASH3D_GAMELIBDIR", gamelibdir, true);
setenv("XASH3D_GAMEDIR", gamedir, true);
2016-05-04 21:02:09 +00:00
setenv("XASH3D_EXTRAS_PAK1", getFilesDir().getPath() + "/extras.pak", true);
2016-05-04 21:02:09 +00:00
String pakfile = intent.getStringExtra("pakfile");
if( pakfile != null && pakfile != "" )
setenv("XASH3D_EXTRAS_PAK2", pakfile, true);
2016-05-04 21:02:09 +00:00
String[] env = intent.getStringArrayExtra("env");
if( env != null )
2016-05-04 21:02:09 +00:00
{
try
2016-05-04 21:02:09 +00:00
{
for(int i = 0; i+1 < env.length; i+=2)
{
setenv(env[i], env[i+1], true);
}
}
catch(Exception e)
{
e.printStackTrace();
2016-05-04 21:02:09 +00:00
}
}
2016-05-09 14:03:50 +00:00
}
public static native int nativeInit(Object arguments);
2016-05-09 14:03:50 +00:00
public static native void nativeQuit();
public static native void onNativeResize(int x, int y);
public static native void nativeTouch(int pointerFingerId, int action, float x, float y);
public static native void nativeKey( int down, int code );
public static native void nativeString( String text );
public static native void nativeSetPause(int pause);
public static native void nativeOnDestroy();
public static native void nativeOnResume();
public static native void nativeOnFocusChange();
public static native void nativeOnPause();
public static native void nativeUnPause();
public static native void nativeHat(int id, byte hat, byte keycode, boolean down);
2016-07-26 22:39:54 +06:00
public static native void nativeAxis(int id, byte axis, short value);
public static native void nativeJoyButton(int id, byte button, boolean down);
public static native int nativeTestWritePermission( String path );
2017-03-30 00:25:51 +00:00
public static native void nativeMouseMove( float x, float y );
2016-07-26 22:39:54 +06:00
// for future expansion
public static native void nativeBall(int id, byte ball, short xrel, short yrel);
public static native void nativeJoyAdd( int id );
public static native void nativeJoyDel( int id );
// libjnisetenv
2016-05-09 14:03:50 +00:00
public static native int setenv(String key, String value, boolean overwrite);
// Java functions called from C
public static boolean createGLContext() {
return mSurface.InitGL();
}
public static void swapBuffers() {
mSurface.SwapBuffers();
}
public static void engineThreadNotify() {
mSurface.engineThreadNotify();
}
2016-05-09 14:03:50 +00:00
public static Surface getNativeSurface() {
return XashActivity.mSurface.getNativeSurface();
}
2016-05-09 14:03:50 +00:00
public static void vibrate( int time ) {
if( mHasVibrator ) mVibrator.vibrate( time );
2016-05-09 14:03:50 +00:00
}
public static void toggleEGL(int toggle) {
mSurface.toggleEGL(toggle);
}
public static boolean deleteGLContext() {
mSurface.ShutdownGL();
return true;
}
2016-05-09 14:03:50 +00:00
public static Context getContext() {
return mSingleton;
}
2016-05-09 15:05:28 +00:00
protected final String[] messageboxData = new String[2];
public static void messageBox(String title, String text)
{
mSingleton.messageboxData[0] = title;
mSingleton.messageboxData[1] = text;
mSingleton.runOnUiThread(new Runnable() {
@Override
public void run()
{
new AlertDialog.Builder(mSingleton)
.setTitle(mSingleton.messageboxData[0])
.setMessage(mSingleton.messageboxData[1])
.setPositiveButton( "Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
synchronized(mSingleton.messageboxData)
{
mSingleton.messageboxData.notify();
}
}
})
.setCancelable(false)
.show();
}
});
synchronized (mSingleton.messageboxData) {
try {
mSingleton.messageboxData.wait();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
2016-05-09 14:03:50 +00:00
public static boolean handleKey( int keyCode, KeyEvent event )
{
2016-07-26 22:39:54 +06:00
if ( mUseVolume && ( keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
2016-05-31 09:03:38 +00:00
keyCode == KeyEvent.KEYCODE_VOLUME_UP ) )
2016-07-26 22:39:54 +06:00
return false;
2016-08-01 11:27:51 +00:00
final int source = XashActivity.handler.getSource(event);
final int action = event.getAction();
final boolean isGamePad = (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK;
final boolean isDPad = (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD;
2016-08-01 19:11:11 +00:00
if( isDPad )
{
byte val;
final byte hat = 0;
final int id = 0;
Log.d(TAG, "DPAD button: " + keyCode );
switch( keyCode )
{
case KeyEvent.KEYCODE_DPAD_CENTER: val = JOY_HAT_CENTERED; break;
case KeyEvent.KEYCODE_DPAD_UP: val = JOY_HAT_UP; break;
case KeyEvent.KEYCODE_DPAD_RIGHT: val = JOY_HAT_RIGHT; break;
case KeyEvent.KEYCODE_DPAD_DOWN: val = JOY_HAT_DOWN; break;
case KeyEvent.KEYCODE_DPAD_LEFT: val = JOY_HAT_LEFT; break;
default: return performEngineKeyEvent( action, keyCode, event );
2016-08-01 19:11:11 +00:00
}
if(action == KeyEvent.ACTION_DOWN)
nativeHat(id, hat, val, true);
else if(action == KeyEvent.ACTION_UP)
nativeHat(id, hat, val, false);
return true;
}
// Engine will bind these to AUX${val} virtual keys
// Android may send event without source flags set to GAMEPAD or CLASS_JOYSTICK
// so check for gamepad buttons anyway
if( isGamePad || isJoystick || XashActivity.handler.isGamepadButton( keyCode ) )
2016-07-26 22:39:54 +06:00
{
final int id = 0;
byte val = 15;
2016-07-26 22:39:54 +06:00
switch( keyCode )
{
// main buttons. DONT CHANGE THIS!!!!111oneone
case KeyEvent.KEYCODE_BUTTON_A: val = 0; break;
case KeyEvent.KEYCODE_BUTTON_B: val = 1; break;
case KeyEvent.KEYCODE_BUTTON_X: val = 2; break;
case KeyEvent.KEYCODE_BUTTON_Y: val = 3; break;
case KeyEvent.KEYCODE_BUTTON_L1: val = 4; break;
case KeyEvent.KEYCODE_BUTTON_R1: val = 5; break;
case KeyEvent.KEYCODE_BUTTON_SELECT: val = 6; break;
case KeyEvent.KEYCODE_BUTTON_MODE: val = 7; break;
case KeyEvent.KEYCODE_BUTTON_START: val = 8; break;
case KeyEvent.KEYCODE_BUTTON_THUMBL: val = 9; break;
case KeyEvent.KEYCODE_BUTTON_THUMBR: val = 10; break;
2016-07-26 22:39:54 +06:00
// other
case KeyEvent.KEYCODE_BUTTON_C: val = 11; break;
case KeyEvent.KEYCODE_BUTTON_Z: val = 12; break;
case KeyEvent.KEYCODE_BUTTON_L2: val = 13; break;
case KeyEvent.KEYCODE_BUTTON_R2: val = 14; break;
2016-07-26 22:39:54 +06:00
default:
if( keyCode >= KeyEvent.KEYCODE_BUTTON_1 && keyCode <= KeyEvent.KEYCODE_BUTTON_16 )
{
val = (byte)((keyCode - KeyEvent.KEYCODE_BUTTON_1) + 15);
2016-07-26 22:39:54 +06:00
}
2016-08-01 11:27:51 +00:00
else if( XashActivity.handler.isGamepadButton(keyCode) )
2016-07-26 22:39:54 +06:00
{
// maybe never reached, as all possible gamepad buttons are checked before
2016-08-01 11:27:51 +00:00
Log.d(TAG, "Unhandled GamePad button: " + XashActivity.handler.keyCodeToString(keyCode) );
2016-07-26 22:39:54 +06:00
return false;
}
2016-08-01 19:11:11 +00:00
else
{
// must be never reached too
return performEngineKeyEvent( action, keyCode, event );
2016-08-01 19:11:11 +00:00
}
2016-07-26 22:39:54 +06:00
}
2017-04-05 22:03:19 +00:00
if( action == KeyEvent.ACTION_DOWN )
{
nativeJoyButton( id, val, true );
return true;
}
2017-04-05 22:03:19 +00:00
else if( action == KeyEvent.ACTION_UP )
{
nativeJoyButton( id, val, false );
return true;
}
return false;
2016-07-26 22:39:54 +06:00
}
2016-08-01 19:11:11 +00:00
return performEngineKeyEvent( action, keyCode, event );
}
public static boolean performEngineKeyEvent( int action, int keyCode, KeyEvent event)
{
//Log.v(TAG, "EngineKeyEvent( " + action +", " + keyCode +" "+ event.isCtrlPressed() +" )");
2017-04-05 22:03:19 +00:00
if( action == KeyEvent.ACTION_DOWN )
2016-05-09 14:03:50 +00:00
{
if( event.isPrintingKey() || keyCode == 62 )// space is printing too
XashActivity.nativeString( String.valueOf( (char) event.getUnicodeChar() ) );
2016-05-09 14:03:50 +00:00
XashActivity.nativeKey( 1, keyCode );
2016-05-09 14:03:50 +00:00
2016-05-08 22:57:35 +00:00
return true;
2016-05-09 14:03:50 +00:00
}
else if( action == KeyEvent.ACTION_UP )
2016-05-09 14:03:50 +00:00
{
//if( keyCode == 62 && event.isCtrlPressed() )
//XashActivity.nativeKey( 1, keyCode );
XashActivity.nativeKey( 0, keyCode );
2016-05-08 22:57:35 +00:00
return true;
}
return false;
}
2016-07-26 22:39:54 +06:00
2016-08-01 11:27:51 +00:00
public static float performEngineAxisEvent( float current, byte engineAxis, float prev, float flat )
2016-07-26 22:39:54 +06:00
{
if( prev != current )
2016-07-26 22:39:54 +06:00
{
final int id = 0;
final short SHRT_MAX = 32767;
if( current <= flat && current >= -flat )
current = 0;
nativeAxis( id, engineAxis, (short)(current * SHRT_MAX) );
2016-07-26 22:39:54 +06:00
}
return current;
}
2016-08-01 11:27:51 +00:00
public static float performEngineHatEvent( float curr, boolean isXAxis, float prev )
2016-07-26 22:39:54 +06:00
{
if( prev != curr )
2016-07-26 22:39:54 +06:00
{
final int id = 0;
final byte hat = 0;
if( isXAxis )
{
if( curr > 0 ) nativeHat( id, hat, JOY_HAT_RIGHT, true );
else if( curr < 0 ) nativeHat( id, hat, JOY_HAT_LEFT, true );
// unpress previous if curr centered
else if( prev > 0 ) nativeHat( id, hat, JOY_HAT_RIGHT, false );
else if( prev < 0 ) nativeHat( id, hat, JOY_HAT_LEFT, false );
}
2016-07-26 22:39:54 +06:00
else
{
if( curr > 0 ) nativeHat( id, hat, JOY_HAT_DOWN, true );
else if( curr < 0 ) nativeHat( id, hat, JOY_HAT_UP, true );
// unpress previous if curr centered
else if( prev > 0 ) nativeHat( id, hat, JOY_HAT_DOWN, false );
else if( prev < 0 ) nativeHat( id, hat, JOY_HAT_UP, false );
2016-07-26 22:39:54 +06:00
}
}
return curr;
}
2016-05-09 14:03:50 +00:00
static class ShowTextInputTask implements Runnable
{
2016-05-08 22:57:35 +00:00
/*
* This is used to regulate the pan&scan method to have some offset from
* the bottom edge of the input region and the top edge of an input
* method (soft keyboard)
*/
private int show;
public ShowTextInputTask(int show1) {
show = show1;
}
@Override
public void run() {
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (mTextEdit == null)
{
mTextEdit = new DummyEdit(getContext());
mLayout.addView(mTextEdit);
}
if( show == 1 )
{
2016-05-09 14:03:50 +00:00
mTextEdit.setVisibility(View.VISIBLE);
mTextEdit.requestFocus();
2016-05-08 22:57:35 +00:00
2016-05-09 14:03:50 +00:00
imm.showSoftInput(mTextEdit, 0);
2016-08-09 10:50:52 +00:00
keyboardVisible = true;
if( XashActivity.mImmersiveMode != null )
XashActivity.mImmersiveMode.apply();
2016-05-08 22:57:35 +00:00
}
else
{
mTextEdit.setVisibility(View.GONE);
imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
2016-08-09 10:50:52 +00:00
keyboardVisible = false;
if( XashActivity.mImmersiveMode != null )
XashActivity.mImmersiveMode.apply();
2016-05-08 22:57:35 +00:00
}
}
}
/**
* This method is called by engine using JNI.
2016-05-08 22:57:35 +00:00
*/
2016-05-09 14:03:50 +00:00
public static void showKeyboard( int show )
{
2016-05-08 22:57:35 +00:00
// Transfer the task to the main thread as a Runnable
mSingleton.runOnUiThread(new ShowTextInputTask(show));
2017-09-24 06:26:51 +07:00
//if( show == 1 )mSurface.getHolder().setSizeFromLayout();
2016-05-08 22:57:35 +00:00
}
public static void setIcon(String path)
{
if( fGDBSafe )
return;
Log.v(TAG,"setIcon("+path+")");
if( sdk < 5 )
return;
try{
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
Bitmap icon = BitmapFactory.decodeFile(path,o);
if( icon.getWidth() < 16 )
return;
XashService.notification.contentView.setImageViewUri (XashService.status_image, Uri.parse("file://"+path));
((NotificationManager) mSingleton.getApplicationContext()
.getSystemService(Context.NOTIFICATION_SERVICE)).notify(100,XashService.notification);
}
catch( Exception e )
{
}
}
public static void setTitle(String title)
{
Log.v(TAG,"setTitle("+title+")");
if( sdk < 5 )
return;
XashService.notification.contentView.setTextViewText(XashService.status_text, title);
((NotificationManager) mSingleton.getApplicationContext()
.getSystemService(Context.NOTIFICATION_SERVICE)).notify(100,XashService.notification);
}
2017-03-28 23:32:39 +00:00
public static String getAndroidID()
{
String str = Secure.getString( mSingleton.getContentResolver(), Secure.ANDROID_ID );
if( str == null )
return "";
return str;
}
public static String loadID()
{
return mPref.getString("xash_id", "");
}
public static void saveID(String id)
{
SharedPreferences.Editor editor = mPref.edit();
editor.putString( "xash_id", id );
editor.commit();
}
2017-03-30 00:25:51 +00:00
public static void showMouse( int show )
{
fMouseShown = show != 0;
2017-03-30 00:25:51 +00:00
handler.showMouse( show != 0 );
}
}
/**
Simple nativeInit() runnable
*/
class XashMain implements Runnable {
2016-05-09 14:03:50 +00:00
public void run()
{
XashActivity.nativeInit(XashActivity.mArgv);
}
}
/**
EngineSurface. This is what we draw on, so we need to know when it's created
2016-05-04 21:02:09 +00:00
in order to do anything useful.
Because of this, that's where we set up the Xash3D thread
*/
class EngineSurface extends SurfaceView implements SurfaceHolder.Callback, View.OnKeyListener
{
// This is what Xash3D runs in. It invokes main(), eventually
2016-08-09 10:50:52 +00:00
private static Thread mEngThread = null;
private static Object mPauseLock = new Object();
2016-05-09 14:03:50 +00:00
// EGL private objects
private EGLContext mEGLContext;
private EGLSurface mEGLSurface;
private EGLDisplay mEGLDisplay;
private EGL10 mEGL;
private EGLConfig mEGLConfig;
2017-09-24 06:26:51 +07:00
private boolean resizing = false;
2016-05-09 14:03:50 +00:00
public static final String TAG = "XASH3D-EngineSurface";
// Sensors
// Startup
public EngineSurface(Context context)
{
super(context);
getHolder().addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
if( XashActivity.sdk >= 5 )
setOnTouchListener(new EngineTouchListener_v5());
else
setOnTouchListener(new EngineTouchListener_v1());
}
// Called when we have a valid drawing surface
public void surfaceCreated(SurfaceHolder holder)
{
Log.v(TAG, "surfaceCreated()");
if( mEGL == null )
return;
XashActivity.nativeSetPause(0);
XashActivity.mEnginePaused = false;
2017-09-24 06:26:51 +07:00
//holder.setFixedSize(640,480);
//SurfaceHolder.setFixedSize(640,480);
2016-05-09 14:03:50 +00:00
}
// Called when we lose the surface
public void surfaceDestroyed(SurfaceHolder holder)
{
Log.v(TAG, "surfaceDestroyed()");
if( mEGL == null )
return;
XashActivity.nativeSetPause(1);
}
// Called when the surface is resized
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
Log.v(TAG, "surfaceChanged()");
2017-09-24 06:26:51 +07:00
if( ( XashActivity.mForceHeight!= 0 && XashActivity.mForceWidth!= 0 || XashActivity.mScale!= 0 ) && !resizing )
{
int newWidth, newHeight;
resizing = true;
if( XashActivity.mForceHeight != 0 && XashActivity.mForceWidth != 0 )
{
newWidth = XashActivity.mForceWidth;
newHeight = XashActivity.mForceHeight;
}
else
{
newWidth = (int)(getWidth() / XashActivity.mScale);
newHeight = (int)(getHeight() / XashActivity.mScale);
}
holder.setFixedSize( newWidth, newHeight );
XashActivity.mTouchScaleX = (float)newWidth / getWidth();
XashActivity.mTouchScaleY = (float)newHeight / getHeight();
return;
}
2016-05-09 14:03:50 +00:00
XashActivity.onNativeResize(width, height);
2017-09-24 06:26:51 +07:00
//holder.setFixedSize(width/2,height/2);
2016-05-09 14:03:50 +00:00
// Now start up the C app thread
if (mEngThread == null) {
mEngThread = new Thread(new XashMain(), "EngineThread");
mEngThread.start();
}
2017-09-24 06:26:51 +07:00
resizing = false;
2016-05-09 14:03:50 +00:00
}
public void engineThreadJoin()
{
Log.v(TAG, "engineThreadJoin()");
try
{
2017-07-22 13:49:07 +07:00
mEngThread.join(5000); // wait until Xash will quit
}
catch(InterruptedException e)
{
}
}
public void engineThreadWait()
{
if( XashActivity.fGDBSafe )
return;
Log.v(TAG, "engineThreadWait()");
synchronized(mPauseLock)
{
try
{
2017-07-22 13:49:07 +07:00
mPauseLock.wait(5000); // wait until engine notify
}
catch(InterruptedException e)
{
}
}
}
public void engineThreadNotify()
{
if( XashActivity.fGDBSafe )
return;
Log.v(TAG, "engineThreadNotify()");
synchronized(mPauseLock)
{
2017-07-22 13:49:07 +07:00
mPauseLock.notify(); // send notify
}
}
2016-05-09 14:03:50 +00:00
// unused
public void onDraw(Canvas canvas) {}
// first, initialize native backend
public Surface getNativeSurface()
{
return getHolder().getSurface();
}
2016-05-09 14:03:50 +00:00
// EGL functions
public boolean InitGL() {
try
{
EGL10 egl = (EGL10)EGLContext.getEGL();
if( egl == null )
{
Log.e( TAG, "Cannot get EGL from context" );
return false;
}
2016-05-09 14:03:50 +00:00
EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
if( dpy == null )
{
Log.e( TAG, "Cannot get display" );
return false;
}
2016-05-09 14:03:50 +00:00
int[] version = new int[2];
if( !egl.eglInitialize(dpy, version) )
{
Log.e(TAG, "No EGL config available");
return false;
}
int[][] configSpec =
{{
2016-05-09 14:03:50 +00:00
EGL10.EGL_DEPTH_SIZE, 8,
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8,
EGL10.EGL_ALPHA_SIZE, 8,
EGL10.EGL_NONE
}, {
EGL10.EGL_DEPTH_SIZE, 8,
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8,
EGL10.EGL_ALPHA_SIZE, 0,
EGL10.EGL_NONE
}, {
EGL10.EGL_DEPTH_SIZE, 8,
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 6,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_ALPHA_SIZE, 0,
EGL10.EGL_NONE
}, {
EGL10.EGL_DEPTH_SIZE, 8,
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 5,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_ALPHA_SIZE, 1,
EGL10.EGL_NONE
}, {
EGL10.EGL_DEPTH_SIZE, 8,
EGL10.EGL_RED_SIZE, 4,
EGL10.EGL_GREEN_SIZE, 4,
EGL10.EGL_BLUE_SIZE, 4,
EGL10.EGL_ALPHA_SIZE, 4,
EGL10.EGL_NONE
}, {
EGL10.EGL_DEPTH_SIZE, 8,
EGL10.EGL_RED_SIZE, 3,
EGL10.EGL_GREEN_SIZE, 3,
EGL10.EGL_BLUE_SIZE, 2,
EGL10.EGL_ALPHA_SIZE, 0,
EGL10.EGL_NONE
}};
EGLConfig[] configs = new EGLConfig[1];
int[] num_config = new int[1];
if (!egl.eglChooseConfig(dpy, configSpec[XashActivity.mPixelFormat], configs, 1, num_config) || num_config[0] == 0)
{
Log.e(TAG, "No EGL config available");
return false;
}
EGLConfig config = configs[0];
int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
2016-05-09 14:03:50 +00:00
int contextAttrs[] = new int[]
{
EGL_CONTEXT_CLIENT_VERSION, 1,
EGL10.EGL_NONE
};
EGLContext ctx = egl.eglCreateContext(dpy, config, EGL10.EGL_NO_CONTEXT, contextAttrs);
if (ctx == EGL10.EGL_NO_CONTEXT) {
Log.e(TAG, "Couldn't create context");
return false;
}
EGLSurface surface = egl.eglCreateWindowSurface(dpy, config, this, null);
if (surface == EGL10.EGL_NO_SURFACE) {
Log.e(TAG, "Couldn't create surface");
return false;
}
if (!egl.eglMakeCurrent(dpy, surface, surface, ctx)) {
Log.e(TAG, "Couldn't make context current");
return false;
}
mEGLContext = ctx;
mEGLDisplay = dpy;
mEGLSurface = surface;
mEGL = egl;
mEGLConfig = config;
}
catch(Exception e)
{
2016-05-09 14:03:50 +00:00
Log.v(TAG, e + "");
for (StackTraceElement s : e.getStackTrace())
{
2016-05-09 14:03:50 +00:00
Log.v(TAG, s.toString());
}
}
return true;
}
// EGL buffer flip
public void SwapBuffers()
{
if( mEGLSurface == null )
return;
mEGL.eglSwapBuffers(mEGLDisplay, mEGLSurface);
}
public void toggleEGL(int toggle)
{
if( toggle != 0 )
{
mEGLSurface = mEGL.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, this, null);
mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
}
else
{
mEGL.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
mEGL.eglDestroySurface(mEGLDisplay, mEGLSurface);
mEGLSurface = null;
}
}
public void ShutdownGL()
{
mEGL.eglDestroyContext(mEGLDisplay, mEGLContext);
mEGLContext = null;
mEGL.eglDestroySurface(mEGLDisplay, mEGLSurface);
mEGLSurface = null;
mEGL.eglTerminate(mEGLDisplay);
mEGLDisplay = null;
}
2016-05-08 22:57:35 +00:00
@Override
2016-07-26 22:39:54 +06:00
public boolean onKey(View v, int keyCode, KeyEvent event)
{
2016-05-08 22:57:35 +00:00
return XashActivity.handleKey( keyCode, event );
}
2016-08-01 11:27:51 +00:00
2017-04-05 22:03:19 +00:00
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
Log.v(TAG, "PreIme: " + keyCode );
return super.dispatchKeyEvent(event);
}
2016-05-08 22:57:35 +00:00
}
/* This is a fake invisible editor view that receives the input and defines the
* pan&scan region
*/
class DummyEdit extends View implements View.OnKeyListener {
InputConnection ic;
public DummyEdit(Context context) {
super(context);
setFocusableInTouchMode(true);
setFocusable(true);
setOnKeyListener(this);
}
@Override
public boolean onCheckIsTextEditor() {
return true;
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
return XashActivity.handleKey( keyCode, event );
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
ic = new XashInputConnection(this, true);
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
| 33554432 /* API 11: EditorInfo.IME_FLAG_NO_FULLSCREEN */;
return ic;
}
}
class XashInputConnection extends BaseInputConnection {
public XashInputConnection(View targetView, boolean fullEditor) {
super(targetView, fullEditor);
}
@Override
public boolean sendKeyEvent(KeyEvent event) {
if( XashActivity.handleKey( event.getKeyCode(), event ) )
2016-05-05 20:01:43 +00:00
return true;
2016-05-08 22:57:35 +00:00
return super.sendKeyEvent(event);
}
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
//nativeCommitText(text.toString(), newCursorPosition);
2016-08-13 20:31:00 +00:00
if(text.toString().equals("\n"))
{
XashActivity.nativeKey(KeyEvent.KEYCODE_ENTER,1);
XashActivity.nativeKey(KeyEvent.KEYCODE_ENTER,0);
}
2016-05-08 22:57:35 +00:00
XashActivity.nativeString(text.toString());
return super.commitText(text, newCursorPosition);
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
//nativeSetComposingText(text.toString(), newCursorPosition);
XashActivity.nativeString(text.toString());
return super.setComposingText(text, newCursorPosition);
}
public native void nativeSetComposingText(String text, int newCursorPosition);
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
if (beforeLength == 1 && afterLength == 0) {
// backspace
XashActivity.nativeKey(1,KeyEvent.KEYCODE_DEL);
XashActivity.nativeKey(0,KeyEvent.KEYCODE_DEL);
2016-05-05 20:01:43 +00:00
}
2016-05-08 22:57:35 +00:00
return super.deleteSurroundingText(beforeLength, afterLength);
}
2016-05-06 08:58:01 +00:00
}
class EngineTouchListener_v1 implements View.OnTouchListener{
2016-05-09 14:03:50 +00:00
// Touch events
public boolean onTouch(View v, MotionEvent event)
{
XashActivity.nativeTouch(0, event.getAction(), event.getX(), event.getY());
2016-05-06 08:58:01 +00:00
return true;
}
}
2016-05-04 21:34:15 +00:00
2016-05-06 08:58:01 +00:00
class EngineTouchListener_v5 implements View.OnTouchListener{
float lx=0, ly=0;
boolean secondarypressed = false;
2016-05-09 14:03:50 +00:00
// Touch events
public boolean onTouch(View v, MotionEvent event)
{
final int touchDevId = event.getDeviceId();
final int pointerCount = event.getPointerCount();
int action = event.getActionMasked();
int pointerFingerId;
int mouseButton;
int i = -1;
float x,y;
2016-05-09 14:03:50 +00:00
switch(action) {
case MotionEvent.ACTION_MOVE:
2017-04-05 22:03:19 +00:00
if( (!XashActivity.fMouseShown) && ( (XashActivity.handler.getSource(event) & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE ) )
{
x = event.getX();
y = event.getY();
XashActivity.nativeMouseMove(x - lx, y -ly);
lx = x;
ly = y;
2017-03-30 21:39:08 +00:00
return true;
}
2016-05-09 14:03:50 +00:00
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
2017-09-24 06:26:51 +07:00
x = event.getX(i)*XashActivity.mTouchScaleX;
y = event.getY(i)*XashActivity.mTouchScaleY;
2016-05-09 14:03:50 +00:00
XashActivity.nativeTouch(pointerFingerId, 2, x, y);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
2017-04-05 22:03:19 +00:00
if( !XashActivity.fMouseShown && ( (XashActivity.handler.getSource(event) & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE ) )
{
lx = event.getX();
ly = event.getY();
boolean down = action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN;
2017-03-30 23:32:27 +00:00
int buttonState = XashActivity.handler.getButtonState( event );
if( down && (buttonState & MotionEvent.BUTTON_SECONDARY) != 0 )
{
XashActivity.nativeKey( 1,-243 );
secondarypressed = true;
return true;
}
2017-03-30 23:32:27 +00:00
else if( !down && secondarypressed && (buttonState & MotionEvent.BUTTON_SECONDARY) == 0 )
{
secondarypressed = false;
XashActivity.nativeKey( 0,-243 );
return true;
}
XashActivity.nativeKey( down?1:0,-241 );
return true;
}
2016-05-09 14:03:50 +00:00
i = 0;
2017-03-31 18:34:08 +00:00
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
2016-05-09 14:03:50 +00:00
// Non primary pointer up/down
if (i == -1) {
i = event.getActionIndex();
}
pointerFingerId = event.getPointerId(i);
2017-09-24 06:26:51 +07:00
x = event.getX(i)*XashActivity.mTouchScaleX;
y = event.getY(i)*XashActivity.mTouchScaleY;
2016-05-09 14:03:50 +00:00
if( action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP )
XashActivity.nativeTouch(pointerFingerId,1, x, y);
if( action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN )
XashActivity.nativeTouch(pointerFingerId,0, x, y);
break;
case MotionEvent.ACTION_CANCEL:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
2017-09-24 06:26:51 +07:00
x = event.getX(i)*XashActivity.mTouchScaleX;
y = event.getY(i)*XashActivity.mTouchScaleY;
2016-05-09 14:03:50 +00:00
XashActivity.nativeTouch(pointerFingerId, 1, x, y);
}
break;
default:
break;
}
return true;
2016-05-04 21:02:09 +00:00
}
2016-05-09 14:03:50 +00:00
}
2016-05-09 15:05:28 +00:00
class AndroidBug5497Workaround {
// For more information, see https://code.google.com/p/android/issues/detail?id=5497
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
public static void assistActivity (Activity activity) {
new AndroidBug5497Workaround(activity);
}
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private AndroidBug5497Workaround(Activity activity) {
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
});
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard/4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
2016-08-09 10:50:52 +00:00
XashActivity.keyboardVisible = true;
2016-05-09 15:05:28 +00:00
} else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
2016-08-09 10:50:52 +00:00
XashActivity.keyboardVisible = false;
2016-05-09 15:05:28 +00:00
}
2016-08-09 10:50:52 +00:00
if( XashActivity.mImmersiveMode != null )
XashActivity.mImmersiveMode.apply();
2016-05-09 15:05:28 +00:00
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);
}
}
2016-08-01 11:27:51 +00:00
2016-08-05 17:43:46 +00:00
class ImmersiveMode
{
void apply()
{
//stub
}
}
class ImmersiveMode_v19 extends ImmersiveMode
{
@Override
void apply()
{
2016-08-09 10:50:52 +00:00
if( !XashActivity.keyboardVisible )
XashActivity.mDecorView.setSystemUiVisibility(
0x00000100 // View.SYSTEM_UI_FLAG_LAYOUT_STABLE
2016-08-09 10:50:52 +00:00
| 0x00000200 // View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| 0x00000400 // View.SYSTEM_UI_FLAG_LAYOUT_FULSCREEN
| 0x00000002 // View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
| 0x00000004 // View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
| 0x00001000 // View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
else
XashActivity.mDecorView.setSystemUiVisibility( 0 );
2016-08-05 17:43:46 +00:00
}
2016-08-09 10:50:52 +00:00
}
class JoystickHandler
{
public int getSource(KeyEvent event)
{
return InputDevice.SOURCE_UNKNOWN;
}
public int getSource(MotionEvent event)
{
return InputDevice.SOURCE_UNKNOWN;
}
public boolean handleAxis(MotionEvent event)
{
return false;
}
public boolean isGamepadButton(int keyCode)
{
return false;
}
public String keyCodeToString(int keyCode)
{
return String.valueOf(keyCode);
}
public void init()
{
}
public boolean hasVibrator()
{
return true;
}
2017-03-30 00:25:51 +00:00
public void showMouse( boolean show )
{
}
2017-03-30 23:32:27 +00:00
public int getButtonState( MotionEvent event)
{
return 0;
}
2017-03-30 00:25:51 +00:00
}
class Wrap_NVMouseExtensions{
private static Object inputManager;
private static Method mInputManager_setCursorVisibility;
public static int nMotionEvent_AXIS_RELATIVE_X = 0;
public static int nMotionEvent_AXIS_RELATIVE_Y = 0;
2017-03-30 00:25:51 +00:00
//**************************************************************************
static {
try { mInputManager_setCursorVisibility =
Class.forName("android.hardware.input.InputManager").getMethod("setCursorVisibility", boolean.class);
inputManager = XashActivity.mSingleton.getSystemService("input");
}
catch (Exception ex) { }
/* DO THE SAME FOR RELATIVEY */
}
//**************************************************************************
public static void checkAvailable() throws Exception {
Field fieldMotionEvent_AXIS_RELATIVE_X = MotionEvent.class.getField("AXIS_RELATIVE_X");
nMotionEvent_AXIS_RELATIVE_X = (Integer)fieldMotionEvent_AXIS_RELATIVE_X.get(null);
Field fieldMotionEvent_AXIS_RELATIVE_Y = MotionEvent.class.getField("AXIS_RELATIVE_Y");
nMotionEvent_AXIS_RELATIVE_Y = (Integer)fieldMotionEvent_AXIS_RELATIVE_Y.get(null);
};
//**************************************************************************
public static void setCursorVisibility(boolean fVisibility) {
try { mInputManager_setCursorVisibility.invoke(inputManager, fVisibility); }
catch (Exception e)
{
}
}
//**************************************************************************
public static int getAxisRelativeX() { return nMotionEvent_AXIS_RELATIVE_X; };
public static int getAxisRelativeY() { return nMotionEvent_AXIS_RELATIVE_Y; };
}
class JoystickHandler_v12 extends JoystickHandler
{
private static float prevSide, prevFwd, prevYaw, prevPtch, prevLT, prevRT, prevHX, prevHY;
2017-03-30 00:25:51 +00:00
public static boolean mNVMouseExtensions = false;
static {
try { Wrap_NVMouseExtensions.checkAvailable();
mNVMouseExtensions = true;
}
catch (Throwable t) { mNVMouseExtensions = false; }
}
@Override
public void init()
{
XashActivity.mSurface.setOnGenericMotionListener(new MotionListener());
2017-03-30 00:25:51 +00:00
Log.d(XashActivity.TAG, "mNVMouseExtensions = " + mNVMouseExtensions );
}
@Override
public int getSource(KeyEvent event)
{
return event.getSource();
}
@Override
public int getSource(MotionEvent event)
{
return event.getSource();
}
@Override
public boolean handleAxis( MotionEvent event )
{
// how event can be from null device, Android?
final InputDevice device = event.getDevice();
if( device == null )
return false;
// maybe I need to cache this...
for( InputDevice.MotionRange range: device.getMotionRanges() )
{
// normalize in -1.0..1.0 (copied from SDL2)
final float cur = ( event.getAxisValue( range.getAxis(), event.getActionIndex() ) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
final float dead = range.getFlat(); // get axis dead zone
switch( range.getAxis() )
{
// typical axes
// move
case MotionEvent.AXIS_X:
prevSide = XashActivity.performEngineAxisEvent(cur, XashActivity.JOY_AXIS_SIDE, prevSide, dead);
break;
case MotionEvent.AXIS_Y:
prevFwd = XashActivity.performEngineAxisEvent(cur, XashActivity.JOY_AXIS_FWD, prevFwd, dead);
break;
// rotate. Invert, so by default this works as it's should
case MotionEvent.AXIS_Z:
prevPtch = XashActivity.performEngineAxisEvent(-cur, XashActivity.JOY_AXIS_PITCH, prevPtch, dead);
break;
case MotionEvent.AXIS_RZ:
prevYaw = XashActivity.performEngineAxisEvent(-cur, XashActivity.JOY_AXIS_YAW, prevYaw, dead);
break;
// trigger
case MotionEvent.AXIS_RTRIGGER:
prevLT = XashActivity.performEngineAxisEvent(cur, XashActivity.JOY_AXIS_RT, prevLT, dead);
break;
case MotionEvent.AXIS_LTRIGGER:
prevRT = XashActivity.performEngineAxisEvent(cur, XashActivity.JOY_AXIS_LT, prevRT, dead);
break;
// hats
case MotionEvent.AXIS_HAT_X:
prevHX = XashActivity.performEngineHatEvent(cur, true, prevHX);
break;
case MotionEvent.AXIS_HAT_Y:
prevHY = XashActivity.performEngineHatEvent(cur, false, prevHY);
break;
}
}
return true;
}
@Override
public boolean isGamepadButton(int keyCode)
{
return KeyEvent.isGamepadButton(keyCode);
}
@Override
public String keyCodeToString(int keyCode)
{
return KeyEvent.keyCodeToString(keyCode);
}
class MotionListener implements View.OnGenericMotionListener
{
@Override
public boolean onGenericMotion(View view, MotionEvent event)
{
final int source = XashActivity.handler.getSource(event);
final int axisDevices = InputDevice.SOURCE_CLASS_JOYSTICK | InputDevice.SOURCE_GAMEPAD;
2017-04-05 22:03:19 +00:00
if( ( (source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE ) && mNVMouseExtensions )
2017-03-30 00:25:51 +00:00
{
float x = event.getAxisValue(Wrap_NVMouseExtensions.getAxisRelativeX(), 0);
float y = event.getAxisValue(Wrap_NVMouseExtensions.getAxisRelativeY(), 0);
switch (event.getAction()) {
case MotionEvent.ACTION_SCROLL:
if (event.getAxisValue(MotionEvent.AXIS_VSCROLL) < 0.0f)
{
XashActivity.nativeKey(1,-239);
XashActivity.nativeKey(0,-239);
return true;
}
else
{
XashActivity.nativeKey(1,-240);
XashActivity.nativeKey(0,-240);
}
return true;
}
2017-03-30 00:25:51 +00:00
XashActivity.nativeMouseMove( x, y );
2017-04-25 21:55:30 +00:00
//Log.v("XashInput", "MouseMove: " +x + " " + y );
2017-03-30 00:25:51 +00:00
return true;
}
if( (source & axisDevices) != 0 )
return XashActivity.handler.handleAxis( event );
// TODO: Add it someday
// else if( (event.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) == InputDevice.SOURCE_CLASS_TRACKBALL )
// return XashActivity.handleBall( event );
//return super.onGenericMotion( view, event );
return false;
}
}
public boolean hasVibrator()
{
return XashActivity.mVibrator.hasVibrator();
}
2017-03-30 00:25:51 +00:00
public void showMouse( boolean show )
{
if( mNVMouseExtensions )
Wrap_NVMouseExtensions.setCursorVisibility( show );
}
}
2017-03-30 23:32:27 +00:00
class JoystickHandler_v14 extends JoystickHandler_v12
{
public int getButtonState( MotionEvent event )
{
return event.getButtonState();
}
};