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

1812 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[];
2017-09-28 20:54:10 +03:00
public static final int sdk = Integer.valueOf( Build.VERSION.SDK );
2016-05-09 14:03:50 +00:00
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
2017-09-28 20:54:10 +03:00
static
{
System.loadLibrary( "gpgs_support" );
System.loadLibrary( "xash" );
}
2016-05-09 14:03:50 +00:00
// Setup
@Override
2017-09-28 20:54:10 +03:00
protected void onCreate( Bundle savedInstanceState )
{
Log.v( TAG, "onCreate()" );
super.onCreate( savedInstanceState );
mEngineReady = false;
2017-09-28 20:54:10 +03:00
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
2017-09-28 20:54:10 +03:00
requestWindowFeature( Window.FEATURE_NO_TITLE );
2017-09-28 20:54:10 +03:00
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 )
2017-09-28 20:54:10 +03:00
setRequestedOrientation( 0 );
2017-09-28 20:54:10 +03:00
mPref = this.getSharedPreferences( "engine", 0 );
2017-09-28 20:54:10 +03:00
if( mPref.getBoolean( "folderask", true ) )
{
2017-09-28 20:54:10 +03:00
Log.v( TAG, "folderask == true. Opening FPicker..." );
2017-09-28 20:54:10 +03:00
Intent intent = new Intent( this, in.celest.xash3d.FPicker.class );
startActivityForResult( intent, FPICKER_RESULT );
}
else
{
2017-09-28 20:54:10 +03:00
Log.v( TAG, "folderask == false. Checking write permission..." );
2016-05-06 16:49:42 +00:00
// check write permission and run engine, if possible
2017-09-28 20:54:10 +03:00
String basedir = getStringExtraFromIntent( getIntent(), "basedir", mPref.getString( "basedir", "/sdcard/xash/" ) );
checkWritePermission( basedir );
}
}
@Override
2017-09-28 20:54:10 +03:00
public void onActivityResult( int requestCode, int resultCode, Intent resultData )
{
if( resultCode != RESULT_OK )
{
2017-09-28 20:54:10 +03:00
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 )
{
2017-09-28 20:54:10 +03:00
String newBaseDir = resultData.getStringExtra( "GetPath" );
setNewBasedir( newBaseDir );
setFolderAsk( false ); // don't ask on next run
2017-09-28 20:54:10 +03:00
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() {
2017-09-28 20:54:10 +03:00
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() {
2017-09-28 20:54:10 +03:00
Log.v( TAG, "onResume()" );
if( mEngineReady )
nativeOnResume();
2017-09-28 20:54:10 +03:00
mEnginePaused = false;
2017-09-28 20:54:10 +03:00
super.onResume();
}
@Override
protected void onStop() {
2017-09-28 20:54:10 +03:00
Log.v( TAG, "onStop()" );
/*if( mEngineReady )
{
nativeSetPause(0);
// let engine properly exit, instead of killing it's thread
nativeOnStop();
// wait for engine
mSurface.engineThreadWait();
2017-09-28 20:54:10 +03:00
}*/
super.onStop();
}
@Override
protected void onDestroy() {
2017-09-28 20:54:10 +03:00
Log.v( TAG, "onDestroy()" );
if( mEngineReady )
{
nativeUnPause();
2017-09-28 20:54:10 +03:00
// let engine a chance to properly exit
nativeOnDestroy();
//mSurface.engineThreadWait();
2017-09-28 20:54:10 +03:00
// wait until Xash will exit
mSurface.engineThreadJoin();
}
super.onDestroy();
}
@Override
2017-09-28 20:54:10 +03:00
public void onWindowFocusChanged( boolean hasFocus )
{
if( mEngineReady )
2017-09-28 20:54:10 +03:00
{
nativeOnFocusChange();
2017-09-28 20:54:10 +03:00
}
super.onWindowFocusChanged( hasFocus );
if( mImmersiveMode != null )
2017-09-28 20:54:10 +03:00
{
mImmersiveMode.apply();
2017-09-28 20:54:10 +03:00
}
}
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 )
{
2017-09-28 20:54:10 +03:00
String ret = intent.getStringExtra( extraString );
if( ret == null )
{
ret = ifNotFound;
}
return ret;
}
private void checkWritePermission( String basedir )
{
2017-09-28 20:54:10 +03:00
Log.v( TAG, "Checking write permissions..." );
if( nativeTestWritePermission( basedir ) == 0 )
{
2017-09-28 20:54:10 +03:00
Log.v( TAG, "First check has failed!" );
String msg = null;
if( sdk > 20 )
{
2017-09-28 20:54:10 +03:00
// 5.0 and higher _allows_ writing to SD card, but have broken fopen()/open() call. So, no Xash here. F*ck you, Google!
msg = getString( R.string.lollipop_write_fail_msg );
}
else if( sdk > 18 )
{
// 4.4 and 4.4W does not allow SD card write at all
2017-09-28 20:54:10 +03:00
msg = getString( R.string.kitkat_write_fail_msg );
}
else
{
// Read-only filesystem
// Logically should be never reached
2017-09-28 20:54:10 +03:00
msg = getString( R.string.readonly_fs_fail_msg );
}
2017-09-28 20:54:10 +03:00
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()
{
2017-09-28 20:54:10 +03:00
Log.v( TAG, "Everything is OK. Launching engine..." );
setupEnvironment();
2017-09-28 20:54:10 +03:00
InstallReceiver.extractPAK( this, false );
// Set up the surface
2017-09-28 20:54:10 +03:00
mSurface = new EngineSurface( getApplication() );
2017-09-28 20:54:10 +03:00
mLayout = new FrameLayout( this );
mLayout.addView( mSurface );
setContentView( mLayout );
SurfaceHolder holder = mSurface.getHolder();
2017-09-28 20:54:10 +03:00
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();
2017-09-28 20:54:10 +03:00
mHasVibrator = mHasVibrator && ( handler.hasVibrator() );
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
2017-09-28 20:54:10 +03:00
Boolean enableImmersive = ( sdk >= 19 ) && ( mPref.getBoolean( "immersive_mode", true ) );
if( enableImmersive )
mImmersiveMode = new ImmersiveMode_v19();
2017-09-28 20:54:10 +03:00
else mImmersiveMode = new ImmersiveMode();
mDecorView = getWindow().getDecorView();
2017-09-28 20:54:10 +03:00
mVibrator = ( Vibrator )getSystemService( Context.VIBRATOR_SERVICE );
2017-09-28 20:54:10 +03:00
if( mPref.getBoolean( "resolution_fixed", false ) )
2017-09-24 06:26:51 +07:00
{
2017-09-28 20:54:10 +03:00
if( mPref.getBoolean( "resolution_custom", false ) )
2017-09-24 06:26:51 +07:00
{
mForceWidth = mPref.getInt( "resolution_width", 854 );
mForceHeight = mPref.getInt( "resolution_height", 480 );
if( mForceWidth < 10 || mForceHeight < 10 )
mForceWidth = mForceHeight = 0;
}
else
2017-09-28 20:54:10 +03:00
{
mScale = mPref.getFloat( "resolution_scale", 1 );
}
2017-09-24 06:26:51 +07:00
if( mScale < 0.5 )
mScale = 0;
}
if( sdk >= 5 )
2017-09-28 20:54:10 +03:00
startService( new Intent( getBaseContext(), XashService.class ) );
mEngineReady = true;
}
private void setupEnvironment()
{
Intent intent = getIntent();
final String enginedir = getFilesDir().getParentFile().getPath() + "/lib";
2017-09-28 20:54:10 +03:00
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 || Debug.isDebuggerConnected() )
{
fGDBSafe = true;
2017-09-28 20:54:10 +03:00
Log.e( TAG, "GDBSafe mode enabled!" );
}
mArgv = argv.split( " " );
setenv( "XASH3D_BASEDIR", basedir, true );
setenv( "XASH3D_ENGLIBDIR", enginedir, true );
setenv( "XASH3D_GAMELIBDIR", gamelibdir, true );
setenv( "XASH3D_GAMEDIR", gamedir, true );
setenv( "XASH3D_EXTRAS_PAK1", getFilesDir().getPath() + "/extras.pak", true );
2017-09-28 20:54:10 +03:00
String pakfile = intent.getStringExtra( "pakfile" );
2016-05-04 21:02:09 +00:00
if( pakfile != null && pakfile != "" )
2017-09-28 20:54:10 +03:00
setenv( "XASH3D_EXTRAS_PAK2", pakfile, true );
2017-09-28 20:54:10 +03: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
{
2017-09-28 20:54:10 +03:00
for( int i = 0; i + 1 < env.length; i += 2 )
{
2017-09-28 20:54:10 +03:00
setenv( env[i], env[i + 1], true );
}
}
2017-09-28 20:54:10 +03:00
catch( Exception e )
{
e.printStackTrace();
2016-05-04 21:02:09 +00:00
}
}
2016-05-09 14:03:50 +00:00
}
2017-09-28 20:54:10 +03:00
public static native int nativeInit( Object arguments );
2016-05-09 14:03:50 +00:00
public static native void nativeQuit();
2017-09-28 20:54:10 +03:00
public static native void onNativeResize( int x, int y );
public static native void nativeTouch( int pointerFingerId, int action, float x, float y );
2016-05-09 14:03:50 +00:00
public static native void nativeKey( int down, int code );
public static native void nativeString( String text );
2017-09-28 20:54:10 +03:00
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();
2017-09-28 20:54:10 +03:00
public static native void nativeHat( int id, byte hat, byte keycode, boolean down ) ;
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
2017-09-28 20:54:10 +03:00
public static native void nativeBall( int id, byte ball, short xrel, short yrel );
2016-07-26 22:39:54 +06:00
public static native void nativeJoyAdd( int id );
public static native void nativeJoyDel( int id );
// libjnisetenv
2017-09-28 20:54:10 +03:00
public static native int setenv( String key, String value, boolean overwrite );
2016-05-09 14:03:50 +00:00
// Java functions called from C
2017-09-28 20:54:10 +03:00
public static boolean createGLContext()
{
2016-05-09 14:03:50 +00:00
return mSurface.InitGL();
}
2017-09-28 20:54:10 +03:00
public static void swapBuffers()
{
2016-05-09 14:03:50 +00:00
mSurface.SwapBuffers();
}
2017-09-28 20:54:10 +03:00
public static void engineThreadNotify()
{
mSurface.engineThreadNotify();
}
2017-09-28 20:54:10 +03:00
public static Surface getNativeSurface()
{
return XashActivity.mSurface.getNativeSurface();
}
2017-09-28 20:54:10 +03:00
public static void vibrate( int time )
{
if( mHasVibrator )
{
mVibrator.vibrate( time );
}
2016-05-09 14:03:50 +00:00
}
2017-09-28 20:54:10 +03:00
public static void toggleEGL( int toggle )
{
mSurface.toggleEGL( toggle );
2016-05-09 14:03:50 +00:00
}
2017-09-28 20:54:10 +03:00
public static boolean deleteGLContext()
{
mSurface.ShutdownGL();
return true;
}
2016-05-09 14:03:50 +00:00
2017-09-28 20:54:10 +03:00
public static Context getContext()
{
2016-05-09 14:03:50 +00:00
return mSingleton;
}
2016-05-09 15:05:28 +00:00
protected final String[] messageboxData = new String[2];
2017-09-28 20:54:10 +03:00
public static void messageBox( String title, String text )
2016-05-09 15:05:28 +00:00
{
mSingleton.messageboxData[0] = title;
mSingleton.messageboxData[1] = text;
2017-09-28 20:54:10 +03:00
mSingleton.runOnUiThread( new Runnable()
2016-05-09 15:05:28 +00:00
{
2017-09-28 20:54:10 +03:00
@Override
public void run()
{
new AlertDialog.Builder( mSingleton )
.setTitle( mSingleton.messageboxData[0] )
.setMessage( mSingleton.messageboxData[1] )
.setPositiveButton( "Ok", new DialogInterface.OnClickListener()
2016-05-09 15:05:28 +00:00
{
2017-09-28 20:54:10 +03:00
public void onClick( DialogInterface dialog, int whichButton )
{
synchronized( mSingleton.messageboxData )
{
mSingleton.messageboxData.notify();
}
}
})
.setCancelable( false )
.show();
2016-05-09 15:05:28 +00:00
}
});
2017-09-28 20:54:10 +03:00
synchronized( mSingleton.messageboxData )
{
try
{
2016-05-09 15:05:28 +00:00
mSingleton.messageboxData.wait();
2017-09-28 20:54:10 +03:00
}
catch( InterruptedException ex )
{
2016-05-09 15:05:28 +00:00
ex.printStackTrace();
}
}
}
2016-05-09 14:03:50 +00:00
public static boolean handleKey( int keyCode, KeyEvent event )
{
2017-09-28 20:54:10 +03: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;
2017-09-28 20:54:10 +03:00
final int source = XashActivity.handler.getSource( event );
final int action = event.getAction();
2017-09-28 20:54:10 +03:00
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;
2017-09-28 20:54:10 +03:00
Log.d( TAG, "DPAD button: " + keyCode );
2016-08-01 19:11:11 +00:00
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;
2017-09-28 20:54:10 +03:00
default:
return performEngineKeyEvent( action, keyCode, event );
2016-08-01 19:11:11 +00:00
}
2017-09-28 20:54:10 +03:00
if( action == KeyEvent.ACTION_DOWN )
{
nativeHat( id, hat, val, true );
}
else if( action == KeyEvent.ACTION_UP )
{
nativeHat( id, hat, val, false );
}
2016-08-01 19:11:11 +00:00
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 )
{
2017-09-28 20:54:10 +03:00
val = ( byte )( ( keyCode - KeyEvent.KEYCODE_BUTTON_1 ) + 15);
2016-07-26 22:39:54 +06:00
}
2017-09-28 20:54:10 +03: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
2017-09-28 20:54:10 +03: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 );
}
2017-09-28 20:54:10 +03:00
public static boolean performEngineKeyEvent( int action, int keyCode, KeyEvent event )
{
//Log.v(TAG, "EngineKeyEvent( " + action +", " + keyCode +" "+ event.isCtrlPressed() +" )");
if( action == KeyEvent.ACTION_DOWN )
2016-05-09 14:03:50 +00:00
{
if( event.isPrintingKey() || keyCode == 62 )// space is printing too
2017-09-28 20:54:10 +03:00
XashActivity.nativeString( String.valueOf( ( char )event.getUnicodeChar() ) );
XashActivity.nativeKey( 1, keyCode );
2017-09-28 20:54:10 +03:00
2016-05-08 22:57:35 +00:00
return true;
2016-05-09 14:03:50 +00:00
}
/*else if( action == KeyEvent.ACTION_MULTIPLE )
{
if( keyCode == KeyEvent.KEYCODE_UNKNOWN )
{
XashActivity.nativeString( event.getCharacters() );
}
else
{
// maybe unneeded
}
}*/
else if( action == KeyEvent.ACTION_UP )
2016-05-09 14:03:50 +00:00
{
XashActivity.nativeKey( 0, keyCode );
2017-09-28 20:54:10 +03:00
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;
2017-09-28 20:54:10 +03:00
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;
2017-09-28 20:54:10 +03:00
public ShowTextInputTask( int show1 )
{
2016-05-08 22:57:35 +00:00
show = show1;
}
@Override
2017-09-28 20:54:10 +03:00
public void run()
{
InputMethodManager imm = ( InputMethodManager )getContext().getSystemService( Context.INPUT_METHOD_SERVICE );
if( mTextEdit == null )
2016-05-08 22:57:35 +00:00
{
2017-09-28 20:54:10 +03:00
mTextEdit = new DummyEdit( getContext() );
mLayout.addView( mTextEdit );
2016-05-08 22:57:35 +00:00
}
if( show == 1 )
{
2017-09-28 20:54:10 +03:00
mTextEdit.setVisibility( View.VISIBLE );
2016-05-09 14:03:50 +00:00
mTextEdit.requestFocus();
2017-09-28 20:54:10 +03: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
{
2017-09-28 20:54:10 +03:00
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
2017-09-28 20:54:10 +03:00
mSingleton.runOnUiThread( new ShowTextInputTask( show ) );
//if( show == 1 )
// mSurface.getHolder().setSizeFromLayout();
2016-05-08 22:57:35 +00:00
}
2017-09-28 20:54:10 +03:00
public static void setIcon( String path )
{
if( fGDBSafe )
return;
2017-09-28 20:54:10 +03:00
Log.v( TAG, "setIcon(" + path + ")" );
if( sdk < 5 )
return;
2017-09-28 20:54:10 +03:00
try
{
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
2017-09-28 20:54:10 +03:00
Bitmap icon = BitmapFactory.decodeFile( path, o );
if( icon.getWidth() < 16 )
return;
2017-09-28 20:54:10 +03:00
XashService.notification.contentView.setImageViewUri( XashService.status_image, Uri.parse( "file://" + path ) );
NotificationManager nm = ( NotificationManager )mSingleton.getApplicationContext().getSystemService( Context.NOTIFICATION_SERVICE );
nm.notify( 100, XashService.notification );
}
catch( Exception e )
{
}
}
2017-09-28 20:54:10 +03:00
public static void setTitle( String title )
{
2017-09-28 20:54:10 +03:00
Log.v( TAG, "setTitle(" + title + ")" );
if( sdk < 5 )
return;
2017-09-28 20:54:10 +03:00
XashService.notification.contentView.setTextViewText( XashService.status_text, title );
NotificationManager nm = ( NotificationManager )mSingleton.getApplicationContext().getSystemService( Context.NOTIFICATION_SERVICE );
nm.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 );
2017-09-28 20:54:10 +03:00
2017-03-28 23:32:39 +00:00
if( str == null )
return "";
2017-09-28 20:54:10 +03:00
2017-03-28 23:32:39 +00:00
return str;
}
public static String loadID()
{
2017-09-28 20:54:10 +03:00
return mPref.getString( "xash_id", "" );
2017-03-28 23:32:39 +00:00
}
2017-09-28 20:54:10 +03:00
public static void saveID( String id )
2017-03-28 23:32:39 +00:00
{
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-09-28 20:54:10 +03:00
handler.showMouse( fMouseShown );
2017-03-30 00:25:51 +00:00
}
}
/**
Simple nativeInit() runnable
*/
2017-09-28 20:54:10 +03:00
class XashMain implements Runnable
{
2016-05-09 14:03:50 +00:00
public void run()
{
2017-09-28 20:54:10 +03:00
XashActivity.nativeInit( XashActivity.mArgv );
2016-05-09 14:03:50 +00:00
}
}
/**
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
{
2017-09-28 20:54:10 +03:00
public static final String TAG = "XASH3D-EngineSurface";
// 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
// Sensors
// Startup
2017-09-28 20:54:10 +03:00
public EngineSurface( Context context )
2016-05-09 14:03:50 +00:00
{
2017-09-28 20:54:10 +03:00
super( context );
getHolder().addCallback( this );
2016-05-09 14:03:50 +00:00
2017-09-28 20:54:10 +03:00
setFocusable( true );
setFocusableInTouchMode( true );
2016-05-09 14:03:50 +00:00
requestFocus();
2017-09-28 20:54:10 +03:00
setOnKeyListener( this );
2016-05-09 14:03:50 +00:00
if( XashActivity.sdk >= 5 )
2017-09-28 20:54:10 +03:00
setOnTouchListener( new EngineTouchListener_v5() );
2016-05-09 14:03:50 +00:00
else
2017-09-28 20:54:10 +03:00
setOnTouchListener( new EngineTouchListener_v1() );
2016-05-09 14:03:50 +00:00
}
2017-09-28 20:54:10 +03:00
2016-05-09 14:03:50 +00:00
// Called when we have a valid drawing surface
2017-09-28 20:54:10 +03:00
public void surfaceCreated( SurfaceHolder holder )
2016-05-09 14:03:50 +00:00
{
2017-09-28 20:54:10 +03:00
Log.v( TAG, "surfaceCreated()" );
2016-05-09 14:03:50 +00:00
if( mEGL == null )
return;
2017-09-28 20:54:10 +03:00
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
2017-09-28 20:54:10 +03:00
public void surfaceDestroyed( SurfaceHolder holder )
2016-05-09 14:03:50 +00:00
{
2017-09-28 20:54:10 +03:00
Log.v( TAG, "surfaceDestroyed()" );
2016-05-09 14:03:50 +00:00
if( mEGL == null )
return;
2017-09-28 20:54:10 +03:00
2016-05-09 14:03:50 +00:00
XashActivity.nativeSetPause(1);
}
// Called when the surface is resized
2017-09-28 20:54:10 +03:00
public void surfaceChanged( SurfaceHolder holder, int format, int width, int height )
2016-05-09 14:03:50 +00:00
{
2017-09-28 20:54:10 +03:00
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
{
2017-09-28 20:54:10 +03:00
newWidth = ( int )( getWidth() / XashActivity.mScale );
newHeight = ( int )( getHeight() / XashActivity.mScale );
2017-09-24 06:26:51 +07:00
}
holder.setFixedSize( newWidth, newHeight );
2017-09-28 20:54:10 +03:00
XashActivity.mTouchScaleX = ( float )newWidth / getWidth();
XashActivity.mTouchScaleY = ( float )newHeight / getHeight();
2017-09-24 06:26:51 +07:00
return;
}
2016-05-09 14:03:50 +00:00
2017-09-28 20:54:10 +03:00
XashActivity.onNativeResize( width, height );
// holder.setFixedSize( width / 2, height / 2 );
2016-05-09 14:03:50 +00:00
// Now start up the C app thread
2017-09-28 20:54:10 +03:00
if( mEngThread == null )
{
mEngThread = new Thread( new XashMain(), "EngineThread" );
2016-05-09 14:03:50 +00:00
mEngThread.start();
}
2017-09-24 06:26:51 +07:00
resizing = false;
2016-05-09 14:03:50 +00:00
}
public void engineThreadJoin()
{
2017-09-28 20:54:10 +03:00
Log.v( TAG, "engineThreadJoin()" );
try
{
2017-09-28 20:54:10 +03:00
mEngThread.join( 5000 ); // wait until Xash will quit
}
2017-09-28 20:54:10 +03:00
catch( InterruptedException e )
{
}
}
public void engineThreadWait()
{
if( XashActivity.fGDBSafe )
return;
2017-09-28 20:54:10 +03:00
Log.v( TAG, "engineThreadWait()" );
synchronized( mPauseLock )
{
try
{
2017-09-28 20:54:10 +03:00
mPauseLock.wait( 5000 ); // wait until engine notify
}
2017-09-28 20:54:10 +03:00
catch( InterruptedException e )
{
}
}
}
public void engineThreadNotify()
{
if( XashActivity.fGDBSafe )
return;
2017-09-28 20:54:10 +03:00
Log.v( TAG, "engineThreadNotify()" );
synchronized( mPauseLock )
{
2017-07-22 13:49:07 +07:00
mPauseLock.notify(); // send notify
}
}
2017-09-28 20:54:10 +03:00
2016-05-09 14:03:50 +00:00
// unused
2017-09-28 20:54:10 +03:00
public void onDraw( Canvas canvas )
{
}
// first, initialize native backend
public Surface getNativeSurface()
{
return getHolder().getSurface();
}
2017-09-28 20:54:10 +03:00
2016-05-09 14:03:50 +00:00
// EGL functions
2017-09-28 20:54:10 +03:00
public boolean InitGL()
{
2016-05-09 14:03:50 +00:00
try
{
2017-09-28 20:54:10 +03:00
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
2017-09-28 20:54:10 +03:00
EGLDisplay dpy = egl.eglGetDisplay( EGL10.EGL_DEFAULT_DISPLAY );
2016-05-09 14:03:50 +00:00
if( dpy == null )
{
Log.e( TAG, "Cannot get display" );
return false;
}
2016-05-09 14:03:50 +00:00
int[] version = new int[2];
2017-09-28 20:54:10 +03:00
if( !egl.eglInitialize( dpy, version ) )
{
2017-09-28 20:54:10 +03:00
Log.e( TAG, "No EGL config available" );
return false;
}
int[][] configSpec =
2017-09-28 20:54:10 +03: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
}
};
2016-05-09 14:03:50 +00:00
EGLConfig[] configs = new EGLConfig[1];
int[] num_config = new int[1];
2017-09-28 20:54:10 +03:00
if( !egl.eglChooseConfig( dpy, configSpec[XashActivity.mPixelFormat], configs, 1, num_config ) || num_config[0] == 0 )
2016-05-09 14:03:50 +00:00
{
2017-09-28 20:54:10 +03:00
Log.e( TAG, "No EGL config available" );
2016-05-09 14:03:50 +00:00
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
};
2017-09-28 20:54:10 +03:00
EGLContext ctx = egl.eglCreateContext( dpy, config, EGL10.EGL_NO_CONTEXT, contextAttrs );
if( ctx == EGL10.EGL_NO_CONTEXT )
{
Log.e( TAG, "Couldn't create context" );
2016-05-09 14:03:50 +00:00
return false;
}
2017-09-28 20:54:10 +03:00
EGLSurface surface = egl.eglCreateWindowSurface( dpy, config, this, null );
if( surface == EGL10.EGL_NO_SURFACE )
{
Log.e( TAG, "Couldn't create surface" );
2016-05-09 14:03:50 +00:00
return false;
}
2017-09-28 20:54:10 +03:00
if( !egl.eglMakeCurrent( dpy, surface, surface, ctx ) )
{
Log.e( TAG, "Couldn't make context current" );
2016-05-09 14:03:50 +00:00
return false;
}
2017-09-28 20:54:10 +03:00
2016-05-09 14:03:50 +00:00
mEGLContext = ctx;
mEGLDisplay = dpy;
mEGLSurface = surface;
mEGL = egl;
mEGLConfig = config;
}
2017-09-28 20:54:10 +03:00
catch( Exception e )
{
2017-09-28 20:54:10 +03:00
Log.v( TAG, e + "" );
for( StackTraceElement s : e.getStackTrace() )
{
2017-09-28 20:54:10 +03:00
Log.v( TAG, s.toString() );
2016-05-09 14:03:50 +00:00
}
}
2017-09-28 20:54:10 +03:00
2016-05-09 14:03:50 +00:00
return true;
}
// EGL buffer flip
public void SwapBuffers()
{
if( mEGLSurface == null )
return;
2017-09-28 20:54:10 +03:00
mEGL.eglSwapBuffers( mEGLDisplay, mEGLSurface );
2016-05-09 14:03:50 +00:00
}
2017-09-28 20:54:10 +03:00
public void toggleEGL( int toggle )
2016-05-09 14:03:50 +00:00
{
if( toggle != 0 )
{
2017-09-28 20:54:10 +03:00
mEGLSurface = mEGL.eglCreateWindowSurface( mEGLDisplay, mEGLConfig, this, null );
mEGL.eglMakeCurrent( mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext );
2016-05-09 14:03:50 +00:00
}
else
{
2017-09-28 20:54:10 +03:00
mEGL.eglMakeCurrent( mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT );
mEGL.eglDestroySurface( mEGLDisplay, mEGLSurface );
2016-05-09 14:03:50 +00:00
mEGLSurface = null;
}
}
2017-09-28 20:54:10 +03:00
public void ShutdownGL()
{
2017-09-28 20:54:10 +03:00
mEGL.eglDestroyContext( mEGLDisplay, mEGLContext );
mEGLContext = null;
2017-09-28 20:54:10 +03:00
mEGL.eglDestroySurface( mEGLDisplay, mEGLSurface );
mEGLSurface = null;
2017-09-28 20:54:10 +03:00
mEGL.eglTerminate( mEGLDisplay );
mEGLDisplay = null;
}
2017-09-28 20:54:10 +03:00
2016-05-08 22:57:35 +00:00
@Override
2017-09-28 20:54:10 +03:00
public boolean onKey( View v, int keyCode, KeyEvent event )
2016-07-26 22:39:54 +06:00
{
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
2017-09-28 20:54:10 +03:00
public boolean onKeyPreIme( int keyCode, KeyEvent event )
{
Log.v( TAG, "PreIme: " + keyCode );
return super.dispatchKeyEvent( event );
2017-04-05 22:03:19 +00:00
}
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
*/
2017-09-28 20:54:10 +03:00
class DummyEdit extends View implements View.OnKeyListener
{
2016-05-08 22:57:35 +00:00
InputConnection ic;
2017-09-28 20:54:10 +03:00
public DummyEdit( Context context )
{
super( context );
setFocusableInTouchMode( true );
setFocusable( true );
setOnKeyListener( this );
2016-05-08 22:57:35 +00:00
}
@Override
2017-09-28 20:54:10 +03:00
public boolean onCheckIsTextEditor()
{
2016-05-08 22:57:35 +00:00
return true;
}
@Override
2017-09-28 20:54:10 +03:00
public boolean onKey( View v, int keyCode, KeyEvent event )
{
2016-05-08 22:57:35 +00:00
return XashActivity.handleKey( keyCode, event );
}
@Override
2017-09-28 20:54:10 +03:00
public InputConnection onCreateInputConnection( EditorInfo outAttrs )
{
ic = new XashInputConnection( this, true );
2016-05-08 22:57:35 +00:00
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
| 33554432 /* API 11: EditorInfo.IME_FLAG_NO_FULLSCREEN */;
2017-09-28 20:54:10 +03:00
2016-05-08 22:57:35 +00:00
return ic;
}
}
2017-09-28 20:54:10 +03:00
class XashInputConnection extends BaseInputConnection
{
public XashInputConnection( View targetView, boolean fullEditor )
{
super( targetView, fullEditor );
2016-05-08 22:57:35 +00:00
}
@Override
2017-09-28 20:54:10 +03:00
public boolean sendKeyEvent( KeyEvent event )
{
2016-05-08 22:57:35 +00:00
if( XashActivity.handleKey( event.getKeyCode(), event ) )
2016-05-05 20:01:43 +00:00
return true;
2017-09-28 20:54:10 +03:00
return super.sendKeyEvent( event );
2016-05-08 22:57:35 +00:00
}
@Override
2017-09-28 20:54:10 +03:00
public boolean commitText( CharSequence text, int newCursorPosition )
{
// nativeCommitText(text.toString(), newCursorPosition);
if( text.toString().equals( "\n" ) )
2016-08-13 20:31:00 +00:00
{
2017-09-28 20:54:10 +03:00
XashActivity.nativeKey( 1, KeyEvent.KEYCODE_ENTER );
XashActivity.nativeKey( 0, KeyEvent.KEYCODE_ENTER );
2016-08-13 20:31:00 +00:00
}
2017-09-28 20:54:10 +03:00
XashActivity.nativeString( text.toString() );
return super.commitText( text, newCursorPosition );
2016-05-08 22:57:35 +00:00
}
2017-09-28 20:54:10 +03:00
2016-05-08 22:57:35 +00:00
@Override
2017-09-28 20:54:10 +03:00
public boolean setComposingText( CharSequence text, int newCursorPosition )
{
// a1batross:
// This method is intended to show composed text immediately
// that after will be replaced by text from "commitText" method
// Just leaving this unimplemented fixes "twice" input on T9/Swype-like keyboards
//ativeSetComposingText(text.toString(), newCursorPosition);
// XashActivity.nativeString( text.toString() );
2017-09-28 20:54:10 +03:00
return super.setComposingText( text, newCursorPosition );
2016-05-08 22:57:35 +00:00
}
2017-09-28 20:54:10 +03:00
public native void nativeSetComposingText( String text, int newCursorPosition );
2016-05-08 22:57:35 +00:00
@Override
2017-09-28 20:54:10 +03:00
public boolean deleteSurroundingText( int beforeLength, int afterLength )
{
2016-05-08 22:57:35 +00:00
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
if( beforeLength > 0 && afterLength == 0 )
2017-09-28 20:54:10 +03:00
{
boolean ret = true;
// backspace(s)
while( beforeLength-- > 0 )
{
boolean ret_key = sendKeyEvent( new KeyEvent( KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL ) )
&& sendKeyEvent( new KeyEvent( KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL ) );
ret = ret && ret_key;
}
return ret;
2016-05-05 20:01:43 +00:00
}
return super.deleteSurroundingText(beforeLength, afterLength);
2016-05-08 22:57:35 +00:00
}
2016-05-06 08:58:01 +00:00
}
2017-09-28 20:54:10 +03:00
class EngineTouchListener_v1 implements View.OnTouchListener
{
2016-05-09 14:03:50 +00:00
// Touch events
2017-09-28 20:54:10 +03:00
public boolean onTouch( View v, MotionEvent event )
2016-05-09 14:03:50 +00:00
{
2017-09-28 20:54:10 +03:00
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
2017-09-28 20:54:10 +03:00
class EngineTouchListener_v5 implements View.OnTouchListener
{
float lx = 0, ly = 0;
boolean secondarypressed = false;
2017-09-28 20:54:10 +03:00
2016-05-09 14:03:50 +00:00
// Touch events
2017-09-28 20:54:10 +03:00
public boolean onTouch( View v, MotionEvent event )
2016-05-09 14:03:50 +00:00
{
2017-09-28 20:54:10 +03:00
final int touchDevId = event.getDeviceId();
2016-05-09 14:03:50 +00:00
final int pointerCount = event.getPointerCount();
int action = event.getActionMasked();
2017-09-28 20:54:10 +03:00
int pointerFingerId, mouseButton, i = -1;
float x, y;
switch( action )
{
case MotionEvent.ACTION_MOVE:
if( ( !XashActivity.fMouseShown ) && ( ( XashActivity.handler.getSource( event ) & InputDevice.SOURCE_MOUSE ) == InputDevice.SOURCE_MOUSE ) )
{
x = event.getX();
y = event.getY();
2017-09-28 20:54:10 +03:00
XashActivity.nativeMouseMove( x - lx, y - ly );
lx = x;
ly = y;
return true;
}
for( i = 0; i < pointerCount; i++ )
{
pointerFingerId = event.getPointerId( i );
x = event.getX( i ) * XashActivity.mTouchScaleX;
y = event.getY( i ) * XashActivity.mTouchScaleY;
XashActivity.nativeTouch( pointerFingerId, 2, x, y );
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
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 );
int buttonState = XashActivity.handler.getButtonState( event );
if( down && ( buttonState & MotionEvent.BUTTON_SECONDARY ) != 0 )
{
XashActivity.nativeKey( 1, -243 );
secondarypressed = true;
2017-03-30 21:39:08 +00:00
return true;
}
2017-09-28 20:54:10 +03:00
else if( !down && secondarypressed && ( buttonState & MotionEvent.BUTTON_SECONDARY ) == 0 )
{
secondarypressed = false;
XashActivity.nativeKey( 0, -243 );
return true;
}
2017-09-28 20:54:10 +03:00
XashActivity.nativeKey( down ? 1 : 0, -241 );
return true;
}
i = 0;
// fallthrough
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
// Non primary pointer up/down
if( i == -1 )
{
i = event.getActionIndex();
}
pointerFingerId = event.getPointerId( i );
x = event.getX( i ) * XashActivity.mTouchScaleX;
y = event.getY( i ) * XashActivity.mTouchScaleY;
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 );
x = event.getX( i ) * XashActivity.mTouchScaleX;
y = event.getY( i ) * XashActivity.mTouchScaleY;
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
}
2017-09-28 20:54:10 +03:00
class AndroidBug5497Workaround
{
2016-05-09 15:05:28 +00:00
// 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.
2017-09-28 20:54:10 +03:00
public static void assistActivity ( Activity activity )
{
new AndroidBug5497Workaround( activity );
2016-05-09 15:05:28 +00:00
}
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
2017-09-28 20:54:10 +03:00
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()
{
2016-05-09 15:05:28 +00:00
possiblyResizeChildOfContent();
}
});
2017-09-28 20:54:10 +03:00
frameLayoutParams = ( FrameLayout.LayoutParams )mChildOfContent.getLayoutParams();
2016-05-09 15:05:28 +00:00
}
2017-09-28 20:54:10 +03:00
private void possiblyResizeChildOfContent()
{
2016-05-09 15:05:28 +00:00
int usableHeightNow = computeUsableHeight();
2017-09-28 20:54:10 +03:00
if( usableHeightNow != usableHeightPrevious )
{
2016-05-09 15:05:28 +00:00
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
2017-09-28 20:54:10 +03:00
if( heightDifference > ( usableHeightSansKeyboard / 4 ) )
{
2016-05-09 15:05:28 +00:00
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
2016-08-09 10:50:52 +00:00
XashActivity.keyboardVisible = true;
2017-09-28 20:54:10 +03:00
}
else
{
2016-05-09 15:05:28 +00:00
// 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
}
2017-09-28 20:54:10 +03:00
2016-08-09 10:50:52 +00:00
if( XashActivity.mImmersiveMode != null )
XashActivity.mImmersiveMode.apply();
2017-09-28 20:54:10 +03:00
2016-05-09 15:05:28 +00:00
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
2017-09-28 20:54:10 +03:00
private int computeUsableHeight()
{
2016-05-09 15:05:28 +00:00
Rect r = new Rect();
2017-09-28 20:54:10 +03:00
mChildOfContent.getWindowVisibleDisplayFrame( r );
return r.bottom - r.top;
2016-05-09 15:05:28 +00:00
}
}
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
{
2017-09-28 20:54:10 +03:00
public int getSource( KeyEvent event )
{
return InputDevice.SOURCE_UNKNOWN;
}
2017-09-28 20:54:10 +03:00
public int getSource( MotionEvent event )
{
return InputDevice.SOURCE_UNKNOWN;
}
2017-09-28 20:54:10 +03:00
public boolean handleAxis( MotionEvent event )
{
return false;
}
2017-09-28 20:54:10 +03:00
public boolean isGamepadButton( int keyCode )
{
return false;
}
2017-09-28 20:54:10 +03:00
public String keyCodeToString( int keyCode )
{
2017-09-28 20:54:10 +03:00
return String.valueOf( keyCode );
}
2017-09-28 20:54:10 +03:00
public void init()
{
}
2017-09-28 20:54:10 +03:00
public boolean hasVibrator()
{
return true;
}
2017-09-28 20:54:10 +03:00
2017-03-30 00:25:51 +00:00
public void showMouse( boolean show )
{
}
2017-09-28 20:54:10 +03:00
public int getButtonState( MotionEvent event )
2017-03-30 23:32:27 +00:00
{
return 0;
}
2017-03-30 00:25:51 +00:00
}
2017-09-28 20:54:10 +03: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;
//**************************************************************************
static
2017-03-30 00:25:51 +00:00
{
2017-09-28 20:54:10 +03:00
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;
2017-03-30 00:25:51 +00:00
}
}
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;
2017-09-28 20:54:10 +03:00
static
{
try
{
Wrap_NVMouseExtensions.checkAvailable();
mNVMouseExtensions = true;
}
catch( Throwable t )
{
mNVMouseExtensions = false;
}
2017-03-30 00:25:51 +00:00
}
@Override
public void init()
{
2017-09-28 20:54:10 +03:00
XashActivity.mSurface.setOnGenericMotionListener( new MotionListener() );
Log.d( XashActivity.TAG, "mNVMouseExtensions = " + mNVMouseExtensions );
}
@Override
2017-09-28 20:54:10 +03:00
public int getSource( KeyEvent event )
{
return event.getSource();
}
@Override
2017-09-28 20:54:10 +03:00
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:
2017-09-28 20:54:10 +03:00
prevSide = XashActivity.performEngineAxisEvent( cur, XashActivity.JOY_AXIS_SIDE, prevSide, dead );
break;
case MotionEvent.AXIS_Y:
2017-09-28 20:54:10 +03:00
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:
2017-09-28 20:54:10 +03:00
prevPtch = XashActivity.performEngineAxisEvent( -cur, XashActivity.JOY_AXIS_PITCH, prevPtch, dead );
break;
case MotionEvent.AXIS_RZ:
2017-09-28 20:54:10 +03:00
prevYaw = XashActivity.performEngineAxisEvent( -cur, XashActivity.JOY_AXIS_YAW, prevYaw, dead );
break;
// trigger
case MotionEvent.AXIS_RTRIGGER:
2017-09-28 20:54:10 +03:00
prevLT = XashActivity.performEngineAxisEvent( cur, XashActivity.JOY_AXIS_RT, prevLT, dead );
break;
case MotionEvent.AXIS_LTRIGGER:
2017-09-28 20:54:10 +03:00
prevRT = XashActivity.performEngineAxisEvent( cur, XashActivity.JOY_AXIS_LT, prevRT, dead );
break;
// hats
case MotionEvent.AXIS_HAT_X:
2017-09-28 20:54:10 +03:00
prevHX = XashActivity.performEngineHatEvent( cur, true, prevHX );
break;
case MotionEvent.AXIS_HAT_Y:
2017-09-28 20:54:10 +03:00
prevHY = XashActivity.performEngineHatEvent( cur, false, prevHY );
break;
}
}
return true;
}
@Override
2017-09-28 20:54:10 +03:00
public boolean isGamepadButton( int keyCode )
{
2017-09-28 20:54:10 +03:00
return KeyEvent.isGamepadButton( keyCode );
}
@Override
2017-09-28 20:54:10 +03:00
public String keyCodeToString( int keyCode )
{
2017-09-28 20:54:10 +03:00
return KeyEvent.keyCodeToString( keyCode );
}
class MotionListener implements View.OnGenericMotionListener
{
@Override
2017-09-28 20:54:10 +03:00
public boolean onGenericMotion( View view, MotionEvent event )
{
2017-09-28 20:54:10 +03:00
final int source = XashActivity.handler.getSource( event );
final int axisDevices = InputDevice.SOURCE_CLASS_JOYSTICK | InputDevice.SOURCE_GAMEPAD;
2017-09-28 20:54:10 +03:00
if( ( ( source & InputDevice.SOURCE_MOUSE ) == InputDevice.SOURCE_MOUSE ) && mNVMouseExtensions )
2017-03-30 00:25:51 +00:00
{
2017-09-28 20:54:10 +03: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 )
{
2017-09-28 20:54:10 +03:00
XashActivity.nativeKey( 1, -239 );
XashActivity.nativeKey( 0, -239 );
return true;
}
else
2017-09-28 20:54:10 +03:00
{
XashActivity.nativeKey( 1, -240 );
XashActivity.nativeKey( 0, -240 );
}
return true;
}
2017-09-28 20:54:10 +03:00
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;
}
2017-09-28 20:54:10 +03:00
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-09-28 20:54:10 +03:00
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();
}
2017-09-28 20:54:10 +03:00
}