From 16404b2e2f6bd7e22f3fa129d92519dbd181862c Mon Sep 17 00:00:00 2001 From: Gerhard Stein Date: Mon, 14 Oct 2013 18:50:23 +0200 Subject: [PATCH] Forget some files... Here they are. --- project/javaSDL2/Accelerometer.java | 129 ++ project/javaSDL2/Advertisement.java | 50 + project/javaSDL2/Audio.java | 307 +++++ project/javaSDL2/DataDownloader.java | 758 ++++++++++++ project/javaSDL2/GLSurfaceView_SDL.java | 1276 ++++++++++++++++++++ project/javaSDL2/Keycodes.java | 592 +++++++++ project/javaSDL2/Settings.java | 782 ++++++++++++ project/javaSDL2/SettingsMenu.java | 257 ++++ project/javaSDL2/SettingsMenuKeyboard.java | 843 +++++++++++++ project/javaSDL2/SettingsMenuMisc.java | 755 ++++++++++++ project/javaSDL2/SettingsMenuMouse.java | 771 ++++++++++++ project/javaSDL2/Video.java | 845 +++++++++++++ 12 files changed, 7365 insertions(+) create mode 100644 project/javaSDL2/Accelerometer.java create mode 100644 project/javaSDL2/Advertisement.java create mode 100644 project/javaSDL2/Audio.java create mode 100644 project/javaSDL2/DataDownloader.java create mode 100644 project/javaSDL2/GLSurfaceView_SDL.java create mode 100644 project/javaSDL2/Keycodes.java create mode 100644 project/javaSDL2/Settings.java create mode 100644 project/javaSDL2/SettingsMenu.java create mode 100644 project/javaSDL2/SettingsMenuKeyboard.java create mode 100644 project/javaSDL2/SettingsMenuMisc.java create mode 100644 project/javaSDL2/SettingsMenuMouse.java create mode 100644 project/javaSDL2/Video.java diff --git a/project/javaSDL2/Accelerometer.java b/project/javaSDL2/Accelerometer.java new file mode 100644 index 000000000..978020850 --- /dev/null +++ b/project/javaSDL2/Accelerometer.java @@ -0,0 +1,129 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import android.os.Vibrator; +import android.hardware.SensorManager; +import android.hardware.SensorEventListener; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.util.Log; +import android.widget.TextView; + + +class AccelerometerReader implements SensorEventListener +{ + + private SensorManager _manager = null; + public boolean openedBySDL = false; + public static final GyroscopeListener gyro = new GyroscopeListener(); + + public AccelerometerReader(Activity context) + { + _manager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + } + + public synchronized void stop() + { + if( _manager != null ) + { + Log.i("SDL", "libSDL: stopping accelerometer/gyroscope"); + _manager.unregisterListener(this); + _manager.unregisterListener(gyro); + } + } + + public synchronized void start() + { + if( (Globals.UseAccelerometerAsArrowKeys || Globals.AppUsesAccelerometer) && + _manager != null && _manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null ) + { + Log.i("SDL", "libSDL: starting accelerometer"); + _manager.registerListener(this, _manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME); + } + if( Globals.AppUsesGyroscope && _manager != null && _manager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null ) + { + Log.i("SDL", "libSDL: starting gyroscope"); + _manager.registerListener(gyro, _manager.getDefaultSensor(Sensor.TYPE_GYROSCOPE), SensorManager.SENSOR_DELAY_GAME); + } + } + + public void onSensorChanged(SensorEvent event) + { + if( Globals.HorizontalOrientation ) + nativeAccelerometer(event.values[1], -event.values[0], event.values[2]); + else + nativeAccelerometer(event.values[0], event.values[1], event.values[2]); // TODO: not tested! + } + public void onAccuracyChanged(Sensor s, int a) + { + } + + static class GyroscopeListener implements SensorEventListener + { + public float x1, x2, xc, y1, y2, yc, z1, z2, zc; + public GyroscopeListener() + { + } + public void onSensorChanged(SensorEvent event) + { + // TODO: vertical orientation + //if( Globals.HorizontalOrientation ) + if( event.values[0] < x1 || event.values[0] > x2 || + event.values[1] < y1 || event.values[1] > y2 || + event.values[2] < z1 || event.values[2] > z2 ) + nativeGyroscope(event.values[0] - xc, event.values[1] - yc, event.values[2] - zc); + } + public void onAccuracyChanged(Sensor s, int a) + { + } + public boolean available(Activity context) + { + SensorManager manager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + return ( manager != null && manager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null ); + } + public void registerListener(Activity context, SensorEventListener l) + { + SensorManager manager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + if ( manager == null && manager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) == null ) + return; + manager.registerListener(l, manager.getDefaultSensor(Sensor.TYPE_GYROSCOPE), SensorManager.SENSOR_DELAY_GAME); + } + public void unregisterListener(Activity context,SensorEventListener l) + { + SensorManager manager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + if ( manager == null ) + return; + manager.unregisterListener(l); + } + } + + private static native void nativeAccelerometer(float accX, float accY, float accZ); + private static native void nativeGyroscope(float X, float Y, float Z); +} diff --git a/project/javaSDL2/Advertisement.java b/project/javaSDL2/Advertisement.java new file mode 100644 index 000000000..43b007fd7 --- /dev/null +++ b/project/javaSDL2/Advertisement.java @@ -0,0 +1,50 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import android.app.Activity; +import android.content.Context; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import android.widget.TextView; +import android.view.View; + +class Advertisement +{ + MainActivity parent; + + public Advertisement(MainActivity p) + { + parent = p; + } + + public View getView() + { + return null; + } + + public void requestNewAd() + { + } +} diff --git a/project/javaSDL2/Audio.java b/project/javaSDL2/Audio.java new file mode 100644 index 000000000..080cbf97f --- /dev/null +++ b/project/javaSDL2/Audio.java @@ -0,0 +1,307 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import android.media.AudioTrack; +import android.media.AudioManager; +import android.media.AudioFormat; +import android.media.AudioRecord; +import android.media.MediaRecorder.AudioSource; +import java.io.*; +import android.util.Log; +import java.util.concurrent.Semaphore; + + + +class AudioThread +{ + + private MainActivity mParent; + private AudioTrack mAudio; + private byte[] mAudioBuffer; + private int mVirtualBufSize; + + public AudioThread(MainActivity parent) + { + mParent = parent; + mAudio = null; + mAudioBuffer = null; + //nativeAudioInitJavaCallbacks(); + } + + public int fillBuffer() + { + if( mParent.isPaused() ) + { + try{ + Thread.sleep(500); + } catch (InterruptedException e) {} + } + else + { + //if( Globals.AudioBufferConfig == 0 ) // Gives too much spam to logcat, makes things worse + // mAudio.flush(); + + mAudio.write( mAudioBuffer, 0, mVirtualBufSize ); + } + + return 1; + } + + public int initAudio(int rate, int channels, int encoding, int bufSize) + { + if( mAudio == null ) + { + channels = ( channels == 1 ) ? AudioFormat.CHANNEL_CONFIGURATION_MONO : + AudioFormat.CHANNEL_CONFIGURATION_STEREO; + encoding = ( encoding == 1 ) ? AudioFormat.ENCODING_PCM_16BIT : + AudioFormat.ENCODING_PCM_8BIT; + + mVirtualBufSize = bufSize; + + if( AudioTrack.getMinBufferSize( rate, channels, encoding ) > bufSize ) + bufSize = AudioTrack.getMinBufferSize( rate, channels, encoding ); + + if(Globals.AudioBufferConfig != 0) { // application's choice - use minimal buffer + bufSize = (int)((float)bufSize * (((float)(Globals.AudioBufferConfig - 1) * 2.5f) + 1.0f)); + mVirtualBufSize = bufSize; + } + mAudioBuffer = new byte[bufSize]; + + mAudio = new AudioTrack(AudioManager.STREAM_MUSIC, + rate, + channels, + encoding, + bufSize, + AudioTrack.MODE_STREAM ); + mAudio.play(); + } + return mVirtualBufSize; + } + + public byte[] getBuffer() + { + return mAudioBuffer; + } + + public int deinitAudio() + { + if( mAudio != null ) + { + mAudio.stop(); + mAudio.release(); + mAudio = null; + } + mAudioBuffer = null; + return 1; + } + + public int initAudioThread() + { + // Make audio thread priority higher so audio thread won't get underrun + Thread.currentThread().setPriority(Thread.MAX_PRIORITY); + return 1; + } + + public int pauseAudioPlayback() + { + if( mAudio != null ) + { + mAudio.pause(); + } + if( mRecordThread != null ) + { + mRecordThread.pauseRecording(); + } + return 1; + } + + public int resumeAudioPlayback() + { + if( mAudio != null ) + { + mAudio.play(); + } + if( mRecordThread != null ) + { + mRecordThread.resumeRecording(); + } + return 1; + } + + private native int nativeAudioInitJavaCallbacks(); + + // ----- Audio recording ----- + + private RecordingThread mRecordThread = null; + private AudioRecord mRecorder = null; + private int mRecorderBufferSize = 0; + + private byte[] startRecording(int rate, int channels, int encoding, int bufsize) + { + if( mRecordThread == null ) + { + mRecordThread = new RecordingThread(); + mRecordThread.start(); + } + if( !mRecordThread.isStopped() ) + { + Log.i("SDL", "SDL: error: application already opened audio recording device"); + return null; + } + + mRecordThread.init(bufsize); + + int channelConfig = ( channels == 1 ) ? AudioFormat.CHANNEL_IN_MONO : + AudioFormat.CHANNEL_IN_STEREO; + int encodingConfig = ( encoding == 1 ) ? AudioFormat.ENCODING_PCM_16BIT : + AudioFormat.ENCODING_PCM_8BIT; + + int minBufDevice = AudioRecord.getMinBufferSize(rate, channelConfig, encodingConfig); + int minBufferSize = Math.max(bufsize * 8, minBufDevice + (bufsize - (minBufDevice % bufsize))); + Log.i("SDL", "SDL: app opened recording device, rate " + rate + " channels " + channels + " sample size " + (encoding+1) + " bufsize " + bufsize + " internal bufsize " + minBufferSize); + if( mRecorder == null || mRecorder.getSampleRate() != rate || + mRecorder.getChannelCount() != channels || + mRecorder.getAudioFormat() != encodingConfig || + mRecorderBufferSize != minBufferSize ) + { + if( mRecorder != null ) + mRecorder.release(); + mRecorder = null; + try { + mRecorder = new AudioRecord(AudioSource.DEFAULT, rate, channelConfig, encodingConfig, minBufferSize); + mRecorderBufferSize = minBufferSize; + } catch (IllegalArgumentException e) { + Log.i("SDL", "SDL: error: failed to open recording device!"); + return null; + } + } + else + { + Log.i("SDL", "SDL: reusing old recording device"); + } + mRecordThread.startRecording(); + return mRecordThread.mRecordBuffer; + } + + private void stopRecording() + { + if( mRecordThread == null || mRecordThread.isStopped() ) + { + Log.i("SDL", "SDL: error: application already closed audio recording device"); + return; + } + mRecordThread.stopRecording(); + Log.i("SDL", "SDL: app closed recording device"); + } + + private class RecordingThread extends Thread + { + private boolean stopped = true; + byte[] mRecordBuffer; + private Semaphore waitStarted = new Semaphore(0); + private boolean sleep = false; + + RecordingThread() + { + super(); + } + + void init(int bufsize) + { + if( mRecordBuffer == null || mRecordBuffer.length != bufsize ) + mRecordBuffer = new byte[bufsize]; + } + + public void run() + { + while( true ) + { + waitStarted.acquireUninterruptibly(); + waitStarted.drainPermits(); + stopped = false; + sleep = false; + + while( !sleep ) + { + int got = mRecorder.read(mRecordBuffer, 0, mRecordBuffer.length); + if( got != mRecordBuffer.length ) + { + // Audio is stopped here, sleep a bit. + try{ + Thread.sleep(1000); + } catch (InterruptedException e) {} + } + else + { + //Log.i("SDL", "SDL: nativeAudioRecordCallback with len " + mRecordBuffer.length); + nativeAudioRecordCallback(); + //Log.i("SDL", "SDL: nativeAudioRecordCallback returned"); + } + } + + stopped = true; + mRecorder.stop(); + } + } + + public void startRecording() + { + mRecorder.startRecording(); + waitStarted.release(); + } + public void stopRecording() + { + sleep = true; + while( !stopped ) + { + try{ + Thread.sleep(100); + } catch (InterruptedException e) {} + } + } + public void pauseRecording() + { + if( !stopped ) + mRecorder.stop(); + } + public void resumeRecording() + { + if( !stopped ) + mRecorder.startRecording(); + } + public boolean isStopped() + { + return stopped; + } + } + + private native void nativeAudioRecordCallback(); +} diff --git a/project/javaSDL2/DataDownloader.java b/project/javaSDL2/DataDownloader.java new file mode 100644 index 000000000..b5e22d130 --- /dev/null +++ b/project/javaSDL2/DataDownloader.java @@ -0,0 +1,758 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import android.os.Environment; + +import android.widget.TextView; +import org.apache.http.client.methods.*; +import org.apache.http.*; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.conn.*; +import org.apache.http.conn.params.*; +import org.apache.http.conn.scheme.*; +import org.apache.http.conn.ssl.*; +import org.apache.http.impl.*; +import org.apache.http.impl.client.*; +import org.apache.http.impl.conn.SingleClientConnManager; +import java.security.cert.*; +import java.security.SecureRandom; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import java.util.zip.*; +import java.io.*; +import android.util.Log; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; + +import android.content.Context; +import android.content.res.Resources; +import java.lang.String; +import android.text.SpannedString; +import android.app.AlertDialog; +import android.content.DialogInterface; + + +class CountingInputStream extends BufferedInputStream +{ + + private long bytesReadMark = 0; + private long bytesRead = 0; + + public CountingInputStream(InputStream in, int size) { + + super(in, size); + } + + public CountingInputStream(InputStream in) { + + super(in); + } + + public long getBytesRead() { + + return bytesRead; + } + + public synchronized int read() throws IOException { + + int read = super.read(); + if (read >= 0) { + bytesRead++; + } + return read; + } + + public synchronized int read(byte[] b, int off, int len) throws IOException { + + int read = super.read(b, off, len); + if (read >= 0) { + bytesRead += read; + } + return read; + } + + public synchronized long skip(long n) throws IOException { + + long skipped = super.skip(n); + if (skipped >= 0) { + bytesRead += skipped; + } + return skipped; + } + + public synchronized void mark(int readlimit) { + + super.mark(readlimit); + bytesReadMark = bytesRead; + } + + public synchronized void reset() throws IOException { + + super.reset(); + bytesRead = bytesReadMark; + } +} + + +class DataDownloader extends Thread +{ + + public static final String DOWNLOAD_FLAG_FILENAME = "libsdl-DownloadFinished-"; + + class StatusWriter + { + private TextView Status; + private MainActivity Parent; + private SpannedString oldText = new SpannedString(""); + + public StatusWriter( TextView _Status, MainActivity _Parent ) + { + Status = _Status; + Parent = _Parent; + } + public void setParent( TextView _Status, MainActivity _Parent ) + { + synchronized(DataDownloader.this) { + Status = _Status; + Parent = _Parent; + setText( oldText.toString() ); + } + } + + public void setText(final String str) + { + class Callback implements Runnable + { + public TextView Status; + public SpannedString text; + public void run() + { + Status.setText(text); + } + } + synchronized(DataDownloader.this) { + Callback cb = new Callback(); + oldText = new SpannedString(str); + cb.text = new SpannedString(str); + cb.Status = Status; + if( Parent != null && Status != null ) + Parent.runOnUiThread(cb); + } + } + + } + public DataDownloader( MainActivity _Parent, TextView _Status ) + { + Parent = _Parent; + Status = new StatusWriter( _Status, _Parent ); + //Status.setText( "Connecting to " + Globals.DataDownloadUrl ); + outFilesDir = Globals.DataDir; + DownloadComplete = false; + this.start(); + } + + public void setStatusField(TextView _Status) + { + synchronized(this) { + Status.setParent( _Status, Parent ); + } + } + + @Override + public void run() + { + Parent.keyListener = new BackKeyListener(Parent); + String [] downloadFiles = Globals.DataDownloadUrl; + int total = 0; + int count = 0; + for( int i = 0; i < downloadFiles.length; i++ ) + { + if( downloadFiles[i].length() > 0 && + ( Globals.OptionalDataDownload.length > i && Globals.OptionalDataDownload[i] ) || + ( Globals.OptionalDataDownload.length <= i && downloadFiles[i].indexOf("!") == 0 ) ) + total += 1; + } + for( int i = 0; i < downloadFiles.length; i++ ) + { + if( downloadFiles[i].length() > 0 && + ( Globals.OptionalDataDownload.length > i && Globals.OptionalDataDownload[i] ) || + ( Globals.OptionalDataDownload.length <= i && downloadFiles[i].indexOf("!") == 0 ) ) + { + if( ! DownloadDataFile(downloadFiles[i], DOWNLOAD_FLAG_FILENAME + String.valueOf(i) + ".flag", count+1, total, i) ) + { + DownloadFailed = true; + return; + } + count += 1; + } + } + DownloadComplete = true; + Parent.keyListener = null; + initParent(); + } + + public boolean DownloadDataFile(final String DataDownloadUrl, final String DownloadFlagFileName, int downloadCount, int downloadTotal, int downloadIndex) + { + DownloadCanBeResumed = false; + Resources res = Parent.getResources(); + + String [] downloadUrls = DataDownloadUrl.split("[|]"); + if( downloadUrls.length < 2 ) + { + Log.i("SDL", "Error: download string invalid: '" + DataDownloadUrl + "', your AndroidAppSettigns.cfg is broken"); + Status.setText( res.getString(R.string.error_dl_from, DataDownloadUrl) ); + return false; + } + + boolean forceOverwrite = false; + String path = getOutFilePath(DownloadFlagFileName); + InputStream checkFile = null; + try { + checkFile = new FileInputStream( path ); + } catch( FileNotFoundException e ) { + } catch( SecurityException e ) { }; + if( checkFile != null ) + { + try { + byte b[] = new byte[ Globals.DataDownloadUrl[downloadIndex].getBytes("UTF-8").length + 1 ]; + int readed = checkFile.read(b); + String compare = ""; + if( readed > 0 ) + compare = new String( b, 0, readed, "UTF-8" ); + boolean matched = false; + //Log.i("SDL", "Read URL: '" + compare + "'"); + for( int i = 1; i < downloadUrls.length; i++ ) + { + //Log.i("SDL", "Comparing: '" + downloadUrls[i] + "'"); + if( compare.compareTo(downloadUrls[i]) == 0 ) + matched = true; + } + //Log.i("SDL", "Matched: " + String.valueOf(matched)); + if( ! matched ) + throw new IOException(); + Status.setText( res.getString(R.string.download_unneeded) ); + return true; + } catch ( IOException e ) { + forceOverwrite = true; + new File(path).delete(); + } + } + checkFile = null; + + // Create output directory (not necessary for phone storage) + Log.i("SDL", "Downloading data to: '" + outFilesDir + "'"); + try { + File outDir = new File( outFilesDir ); + if( !(outDir.exists() && outDir.isDirectory()) ) + outDir.mkdirs(); + OutputStream out = new FileOutputStream( getOutFilePath(".nomedia") ); + out.flush(); + out.close(); + } + catch( SecurityException e ) {} + catch( FileNotFoundException e ) {} + catch( IOException e ) {}; + + HttpResponse response = null, responseError = null; + HttpGet request; + long totalLen = 0; + CountingInputStream stream; + byte[] buf = new byte[16384]; + boolean DoNotUnzip = false; + boolean FileInAssets = false; + String url = ""; + long partialDownloadLen = 0; + + int downloadUrlIndex = 1; + while( downloadUrlIndex < downloadUrls.length ) + { + Log.i("SDL", "Processing download " + downloadUrls[downloadUrlIndex]); + url = new String(downloadUrls[downloadUrlIndex]); + DoNotUnzip = false; + if(url.indexOf(":") == 0) + { + path = getOutFilePath(url.substring( 1, url.indexOf(":", 1) )); + url = url.substring( url.indexOf(":", 1) + 1 ); + DoNotUnzip = true; + DownloadCanBeResumed = true; + File partialDownload = new File( path ); + if( partialDownload.exists() && !partialDownload.isDirectory() && !forceOverwrite ) + partialDownloadLen = partialDownload.length(); + } + Status.setText( downloadCount + "/" + downloadTotal + ": " + res.getString(R.string.connecting_to, url) ); + if( url.indexOf("http://") == -1 && url.indexOf("https://") == -1 ) // File inside assets + { + InputStream stream1 = null; + try { + stream1 = Parent.getAssets().open(url); + stream1.close(); + } catch( Exception e ) { + try { + stream1 = Parent.getAssets().open(url + "000"); + stream1.close(); + } catch( Exception ee ) { + Log.i("SDL", "Failed to open file in assets: " + url); + downloadUrlIndex++; + continue; + } + } + FileInAssets = true; + Log.i("SDL", "Fetching file from assets: " + url); + break; + } + else + { + Log.i("SDL", "Connecting to: " + url); + request = new HttpGet(url); + request.addHeader("Accept", "*/*"); + if( partialDownloadLen > 0 ) { + request.addHeader("Range", "bytes=" + partialDownloadLen + "-"); + Log.i("SDL", "Trying to resume download at pos " + partialDownloadLen); + } + try { + DefaultHttpClient client = HttpWithDisabledSslCertCheck(); + client.getParams().setBooleanParameter("http.protocol.handle-redirects", true); + response = client.execute(request); + } catch (IOException e) { + Log.i("SDL", "Failed to connect to " + url); + downloadUrlIndex++; + }; + if( response != null ) + { + if( response.getStatusLine().getStatusCode() != 200 && response.getStatusLine().getStatusCode() != 206 ) + { + Log.i("SDL", "Failed to connect to " + url + " with error " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase()); + responseError = response; + response = null; + downloadUrlIndex++; + } + else + break; + } + } + } + if( FileInAssets ) + { + int multipartCounter = 0; + InputStream multipart = null; + while( true ) + { + try { + // Make string ".zip000", ".zip001" etc for multipart archives + String url1 = url + String.format("%03d", multipartCounter); + CountingInputStream stream1 = new CountingInputStream(Parent.getAssets().open(url1), 8192); + while( stream1.skip(65536) > 0 ) { }; + totalLen += stream1.getBytesRead(); + stream1.close(); + InputStream s = Parent.getAssets().open(url1); + if( multipart == null ) + multipart = s; + else + multipart = new SequenceInputStream(multipart, s); + Log.i("SDL", "Multipart archive found: " + url1); + } catch( IOException e ) { + break; + } + multipartCounter += 1; + } + if( multipart != null ) + stream = new CountingInputStream(multipart, 8192); + else + { + try { + stream = new CountingInputStream(Parent.getAssets().open(url), 8192); + while( stream.skip(65536) > 0 ) { }; + totalLen += stream.getBytesRead(); + stream.close(); + stream = new CountingInputStream(Parent.getAssets().open(url), 8192); + } catch( IOException e ) { + Log.i("SDL", "Unpacking from assets '" + url + "' - error: " + e.toString()); + Status.setText( res.getString(R.string.error_dl_from, url) ); + return false; + } + } + } + else + { + if( response == null ) + { + Log.i("SDL", "Error connecting to " + url); + Status.setText( res.getString(R.string.failed_connecting_to, url) + (responseError == null ? "" : ": " + responseError.getStatusLine().getStatusCode() + " " + responseError.getStatusLine().getReasonPhrase()) ); + return false; + } + + Status.setText( downloadCount + "/" + downloadTotal + ": " + res.getString(R.string.dl_from, url) ); + totalLen = response.getEntity().getContentLength(); + try { + stream = new CountingInputStream(response.getEntity().getContent(), 8192); + } catch( java.io.IOException e ) { + Status.setText( res.getString(R.string.error_dl_from, url) ); + return false; + } + } + + long updateStatusTime = 0; + + if(DoNotUnzip) + { + Log.i("SDL", "Saving file '" + path + "'"); + OutputStream out = null; + try { + try { + File outDir = new File( path.substring(0, path.lastIndexOf("/") )); + if( !(outDir.exists() && outDir.isDirectory()) ) + outDir.mkdirs(); + } catch( SecurityException e ) { }; + + if( partialDownloadLen > 0 ) + { + try { + Header[] range = response.getHeaders("Content-Range"); + if( range.length > 0 && range[0].getValue().indexOf("bytes") == 0 ) + { + //Log.i("SDL", "Resuming download of file '" + path + "': Content-Range: " + range[0].getValue()); + String[] skippedBytes = range[0].getValue().split("/")[0].split("-")[0].split(" "); + if( skippedBytes.length >= 2 && Long.parseLong(skippedBytes[1]) == partialDownloadLen ) + { + out = new FileOutputStream( path, true ); + Log.i("SDL", "Resuming download of file '" + path + "' at pos " + partialDownloadLen); + } + } + else + Log.i("SDL", "Server does not support partial downloads. " + (range.length == 0 ? "" : range[0].getValue())); + } catch (Exception e) { } + } + if( out == null ) + { + out = new FileOutputStream( path ); + partialDownloadLen = 0; + } + } catch( FileNotFoundException e ) { + Log.i("SDL", "Saving file '" + path + "' - error creating output file: " + e.toString()); + } catch( SecurityException e ) { + Log.i("SDL", "Saving file '" + path + "' - error creating output file: " + e.toString()); + }; + if( out == null ) + { + Status.setText( res.getString(R.string.error_write, path) ); + Log.i("SDL", "Saving file '" + path + "' - error creating output file"); + return false; + } + + try { + int len = stream.read(buf); + while (len >= 0) + { + if(len > 0) + out.write(buf, 0, len); + len = stream.read(buf); + + float percent = 0.0f; + if( totalLen > 0 ) + percent = (stream.getBytesRead() + partialDownloadLen) * 100.0f / (totalLen + partialDownloadLen); + if( System.currentTimeMillis() > updateStatusTime + 1000 ) + { + updateStatusTime = System.currentTimeMillis(); + Status.setText( downloadCount + "/" + downloadTotal + ": " + res.getString(R.string.dl_progress, percent, path) ); + } + } + out.flush(); + out.close(); + out = null; + } catch( java.io.IOException e ) { + Status.setText( res.getString(R.string.error_write, path) + ": " + e.getMessage() ); + Log.i("SDL", "Saving file '" + path + "' - error writing: " + e.toString()); + return false; + } + Log.i("SDL", "Saving file '" + path + "' done"); + } + else + { + Log.i("SDL", "Reading from zip file '" + url + "'"); + ZipInputStream zip = new ZipInputStream(stream); + + while(true) + { + ZipEntry entry = null; + try { + entry = zip.getNextEntry(); + if( entry != null ) + Log.i("SDL", "Reading from zip file '" + url + "' entry '" + entry.getName() + "'"); + } catch( java.io.IOException e ) { + Status.setText( res.getString(R.string.error_dl_from, url) ); + Log.i("SDL", "Error reading from zip file '" + url + "': " + e.toString()); + return false; + } + if( entry == null ) + { + Log.i("SDL", "Reading from zip file '" + url + "' finished"); + break; + } + if( entry.isDirectory() ) + { + Log.i("SDL", "Creating dir '" + getOutFilePath(entry.getName()) + "'"); + try { + File outDir = new File( getOutFilePath(entry.getName()) ); + if( !(outDir.exists() && outDir.isDirectory()) ) + outDir.mkdirs(); + } catch( SecurityException e ) { }; + continue; + } + + OutputStream out = null; + path = getOutFilePath(entry.getName()); + float percent = 0.0f; + + Log.i("SDL", "Saving file '" + path + "'"); + + try { + File outDir = new File( path.substring(0, path.lastIndexOf("/") )); + if( !(outDir.exists() && outDir.isDirectory()) ) + outDir.mkdirs(); + } catch( SecurityException e ) { }; + + try { + CheckedInputStream check = new CheckedInputStream( new FileInputStream(path), new CRC32() ); + while( check.read(buf, 0, buf.length) >= 0 ) {}; + check.close(); + if( check.getChecksum().getValue() != entry.getCrc() ) + { + File ff = new File(path); + ff.delete(); + throw new Exception(); + } + Log.i("SDL", "File '" + path + "' exists and passed CRC check - not overwriting it"); + if( totalLen > 0 ) + percent = stream.getBytesRead() * 100.0f / totalLen; + if( System.currentTimeMillis() > updateStatusTime + 1000 ) + { + updateStatusTime = System.currentTimeMillis(); + Status.setText( downloadCount + "/" + downloadTotal + ": " + res.getString(R.string.dl_progress, percent, path) ); + } + continue; + } catch( Exception e ) { } + + try { + out = new FileOutputStream( path ); + } catch( FileNotFoundException e ) { + Log.i("SDL", "Saving file '" + path + "' - cannot create file: " + e.toString()); + } catch( SecurityException e ) { + Log.i("SDL", "Saving file '" + path + "' - cannot create file: " + e.toString()); + }; + if( out == null ) + { + Status.setText( res.getString(R.string.error_write, path) ); + Log.i("SDL", "Saving file '" + path + "' - cannot create file"); + return false; + } + + if( totalLen > 0 ) + percent = stream.getBytesRead() * 100.0f / totalLen; + if( System.currentTimeMillis() > updateStatusTime + 1000 ) + { + updateStatusTime = System.currentTimeMillis(); + Status.setText( downloadCount + "/" + downloadTotal + ": " + res.getString(R.string.dl_progress, percent, path) ); + } + + try { + int len = zip.read(buf); + while (len >= 0) + { + if(len > 0) + out.write(buf, 0, len); + len = zip.read(buf); + + percent = 0.0f; + if( totalLen > 0 ) + percent = stream.getBytesRead() * 100.0f / totalLen; + if( System.currentTimeMillis() > updateStatusTime + 1000 ) + { + updateStatusTime = System.currentTimeMillis(); + Status.setText( downloadCount + "/" + downloadTotal + ": " + res.getString(R.string.dl_progress, percent, path) ); + } + } + out.flush(); + out.close(); + out = null; + } catch( java.io.IOException e ) { + Status.setText( res.getString(R.string.error_write, path) + ": " + e.getMessage() ); + Log.i("SDL", "Saving file '" + path + "' - error writing or downloading: " + e.toString()); + return false; + } + + try { + long count = 0, ret = 0; + CheckedInputStream check = new CheckedInputStream( new FileInputStream(path), new CRC32() ); + while( ret >= 0 ) + { + count += ret; + ret = check.read(buf, 0, buf.length); + } + check.close(); + if( check.getChecksum().getValue() != entry.getCrc() || count != entry.getSize() ) + { + File ff = new File(path); + ff.delete(); + Log.i("SDL", "Saving file '" + path + "' - CRC check failed, ZIP: " + + String.format("%x", entry.getCrc()) + " actual file: " + String.format("%x", check.getChecksum().getValue()) + + " file size in ZIP: " + entry.getSize() + " actual size " + count ); + throw new Exception(); + } + } catch( Exception e ) { + Status.setText( res.getString(R.string.error_write, path) + ": " + e.getMessage() ); + return false; + } + Log.i("SDL", "Saving file '" + path + "' done"); + } + }; + + OutputStream out = null; + path = getOutFilePath(DownloadFlagFileName); + try { + out = new FileOutputStream( path ); + out.write(downloadUrls[downloadUrlIndex].getBytes("UTF-8")); + out.flush(); + out.close(); + } catch( FileNotFoundException e ) { + } catch( SecurityException e ) { + } catch( java.io.IOException e ) { + Status.setText( res.getString(R.string.error_write, path) + ": " + e.getMessage() ); + return false; + }; + Status.setText( downloadCount + "/" + downloadTotal + ": " + res.getString(R.string.dl_finished) ); + + try { + stream.close(); + } catch( java.io.IOException e ) { + }; + + return true; + }; + + private void initParent() + { + class Callback implements Runnable + { + public MainActivity Parent; + public void run() + { + Parent.initSDL(); + } + } + Callback cb = new Callback(); + synchronized(this) { + cb.Parent = Parent; + if(Parent != null) + Parent.runOnUiThread(cb); + } + } + + private String getOutFilePath(final String filename) + { + return outFilesDir + "/" + filename; + }; + + private static DefaultHttpClient HttpWithDisabledSslCertCheck() + { + return new DefaultHttpClient(); + // This code does not work + /* + HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER; + + DefaultHttpClient client = new DefaultHttpClient(); + + SchemeRegistry registry = new SchemeRegistry(); + SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory(); + socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier); + registry.register(new Scheme("https", socketFactory, 443)); + SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry); + DefaultHttpClient http = new DefaultHttpClient(mgr, client.getParams()); + + HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); + + return http; + */ + } + + + public class BackKeyListener implements MainActivity.KeyEventsListener + { + MainActivity p; + public BackKeyListener(MainActivity _p) + { + p = _p; + } + + public void onKeyEvent(final int keyCode) + { + if( DownloadFailed ) + System.exit(1); + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.cancel_download)); + builder.setMessage(p.getResources().getString(R.string.cancel_download) + (DownloadCanBeResumed ? " " + p.getResources().getString(R.string.cancel_download_resume) : "")); + + builder.setPositiveButton(p.getResources().getString(R.string.yes), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + System.exit(1); + dialog.dismiss(); + } + }); + builder.setNegativeButton(p.getResources().getString(R.string.no), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + public StatusWriter Status; + public boolean DownloadComplete = false; + public boolean DownloadFailed = false; + public boolean DownloadCanBeResumed = false; + private MainActivity Parent; + private String outFilesDir = null; +} + diff --git a/project/javaSDL2/GLSurfaceView_SDL.java b/project/javaSDL2/GLSurfaceView_SDL.java new file mode 100644 index 000000000..998bf1c5c --- /dev/null +++ b/project/javaSDL2/GLSurfaceView_SDL.java @@ -0,0 +1,1276 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This is GLSurfaceView class ripped out of Android 2.1 sources, + fixed with a hammer to work with libSDL port */ + +package net.sourceforge.clonekeenplus; + +import java.io.Writer; +import java.util.ArrayList; +import java.util.concurrent.Semaphore; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGL11; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; +import javax.microedition.khronos.opengles.GL; +import javax.microedition.khronos.opengles.GL10; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.app.KeyguardManager; + +/** + * An implementation of SurfaceView that uses the dedicated surface for + * displaying OpenGL rendering. + *

+ * A GLSurfaceView provides the following features: + *

+ *

+ * + *

Using GLSurfaceView

+ *

+ * Typically you use GLSurfaceView by subclassing it and overriding one or more of the + * View system input event methods. If your application does not need to override event + * methods then GLSurfaceView can be used as-is. For the most part + * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing. + * For example, unlike a regular View, drawing is delegated to a separate Renderer object which + * is registered with the GLSurfaceView + * using the {@link #setRenderer(Renderer)} call. + *

+ *

Initializing GLSurfaceView

+ * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}. + * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or + * more of these methods before calling setRenderer: + * + *

+ *

Choosing an EGL Configuration

+ * A given Android device may support multiple possible types of drawing surfaces. + * The available surfaces may differ in how may channels of data are present, as + * well as how many bits are allocated to each channel. Therefore, the first thing + * GLSurfaceView has to do when starting to render is choose what type of surface to use. + *

+ * By default GLSurfaceView chooses an available surface that's closest to a 16-bit R5G6B5 surface + * with a 16-bit depth buffer and no stencil. If you would prefer a different surface (for example, + * if you do not need a depth buffer) you can override the default behavior by calling one of the + * setEGLConfigChooser methods. + *

+ *

Debug Behavior

+ * You can optionally modify the behavior of GLSurfaceView by calling + * one or more of the debugging methods {@link #setDebugFlags(int)}, + * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but + * typically they are called before setRenderer so that they take effect immediately. + *

+ *

Setting a Renderer

+ * Finally, you must call {@link #setRenderer} to register a {@link Renderer}. + * The renderer is + * responsible for doing the actual OpenGL rendering. + *

+ *

Rendering Mode

+ * Once the renderer is set, you can control whether the renderer draws + * continuously or on-demand by calling + * {@link #setRenderMode}. The default is continuous rendering. + *

+ *

Activity Life-cycle

+ * A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients + * are required to call {@link #onPause()} when the activity pauses and + * {@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to + * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate + * the OpenGL display. + *

+ *

Handling events

+ *

+ * To handle an event you will typically subclass GLSurfaceView and override the + * appropriate method, just as you would with any other View. However, when handling + * the event, you may need to communicate with the Renderer object + * that's running in the rendering thread. You can do this using any + * standard Java cross-thread communication mechanism. In addition, + * one relatively easy way to communicate with your renderer is + * to call + * {@link #queueEvent(Runnable)}. For example: + *

+ * class MyGLSurfaceView extends GLSurfaceView {
+ *
+ *     private MyRenderer mMyRenderer;
+ *
+ *     public void start() {
+ *         mMyRenderer = ...;
+ *         setRenderer(mMyRenderer);
+ *     }
+ *
+ *     public boolean onKeyDown(int keyCode, KeyEvent event) {
+ *         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ *             queueEvent(new Runnable() {
+ *                 // This method will be called on the rendering
+ *                 // thread:
+ *                 public void run() {
+ *                     mMyRenderer.handleDpadCenter();
+ *                 }});
+ *             return true;
+ *         }
+ *         return super.onKeyDown(keyCode, event);
+ *     }
+ * }
+ * 
+ * + */ +public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Callback { + /** + * The renderer only renders + * when the surface is created, or when {@link #requestRender} is called. + * + * @see #getRenderMode() + * @see #setRenderMode(int) + */ + public final static int RENDERMODE_WHEN_DIRTY = 0; + /** + * The renderer is called + * continuously to re-render the scene. + * + * @see #getRenderMode() + * @see #setRenderMode(int) + * @see #requestRender() + */ + public final static int RENDERMODE_CONTINUOUSLY = 1; + + /** + * Check glError() after every GL call and throw an exception if glError indicates + * that an error has occurred. This can be used to help track down which OpenGL ES call + * is causing an error. + * + * @see #getDebugFlags + * @see #setDebugFlags + */ + public final static int DEBUG_CHECK_GL_ERROR = 1; + + /** + * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView". + * + * @see #getDebugFlags + * @see #setDebugFlags + */ + public final static int DEBUG_LOG_GL_CALLS = 2; + + /** + * Standard View constructor. In order to render something, you + * must call {@link #setRenderer} to register a renderer. + */ + public GLSurfaceView_SDL(Context context) { + super(context); + init(); + } + + /** + * Standard View constructor. In order to render something, you + * must call {@link #setRenderer} to register a renderer. + */ + public GLSurfaceView_SDL(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + // Install a SurfaceHolder.Callback so we get notified when the + // underlying surface is created and destroyed + SurfaceHolder holder = getHolder(); + holder.addCallback(this); + holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); + } + + /** + * Set the glWrapper. If the glWrapper is not null, its + * {@link GLWrapper#wrap(GL)} method is called + * whenever a surface is created. A GLWrapper can be used to wrap + * the GL object that's passed to the renderer. Wrapping a GL + * object enables examining and modifying the behavior of the + * GL calls made by the renderer. + *

+ * Wrapping is typically used for debugging purposes. + *

+ * The default value is null. + * @param glWrapper the new GLWrapper + */ + public void setGLWrapper(GLWrapper glWrapper) { + mGLWrapper = glWrapper; + } + + /** + * Set the debug flags to a new value. The value is + * constructed by OR-together zero or more + * of the DEBUG_CHECK_* constants. The debug flags take effect + * whenever a surface is created. The default value is zero. + * @param debugFlags the new debug flags + * @see #DEBUG_CHECK_GL_ERROR + * @see #DEBUG_LOG_GL_CALLS + */ + public void setDebugFlags(int debugFlags) { + mDebugFlags = debugFlags; + } + + /** + * Get the current value of the debug flags. + * @return the current value of the debug flags. + */ + public int getDebugFlags() { + return mDebugFlags; + } + + /** + * Set the renderer associated with this view. Also starts the thread that + * will call the renderer, which in turn causes the rendering to start. + *

This method should be called once and only once in the life-cycle of + * a GLSurfaceView. + *

The following GLSurfaceView methods can only be called before + * setRenderer is called: + *

+ *

+ * The following GLSurfaceView methods can only be called after + * setRenderer is called: + *

+ * + * @param renderer the renderer to use to perform OpenGL drawing. + */ + public void setRenderer(Renderer renderer) { + if (mGLThread != null) { + throw new IllegalStateException( + "setRenderer has already been called for this instance."); + } + if (mEGLConfigChooser == null) { + mEGLConfigChooser = getEglConfigChooser(16, false, false, false); + } + mGLThread = new GLThread(renderer); + mGLThread.start(); + } + + /** + * Install a custom EGLConfigChooser. + *

If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *

+ * If no setEGLConfigChooser method is called, then by default the + * view will choose a config as close to 16-bit RGB as possible, with + * a depth buffer as close to 16 bits as possible. + * @param configChooser + */ + public void setEGLConfigChooser(EGLConfigChooser configChooser) { + if (mGLThread != null) { + throw new IllegalStateException( + "setRenderer has already been called for this instance."); + } + mEGLConfigChooser = configChooser; + } + + /** + * Install a config chooser which will choose a config + * as close to 16-bit RGB as possible, with or without an optional depth + * buffer as close to 16-bits as possible. + *

If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *

+ * If no setEGLConfigChooser method is called, then by default the + * view will choose a config as close to 16-bit RGB as possible, with + * a depth buffer as close to 16 bits as possible. + * + * @param needDepth + */ + public void setEGLConfigChooser(int bpp, boolean needDepth, boolean stencil, boolean gles2) { + setEGLConfigChooser(getEglConfigChooser(bpp, needDepth, stencil, gles2)); + } + + /** + * Install a config chooser which will choose a config + * with at least the specified component sizes, and as close + * to the specified component sizes as possible. + *

If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *

+ * If no setEGLConfigChooser method is called, then by default the + * view will choose a config as close to 16-bit RGB as possible, with + * a depth buffer as close to 16 bits as possible. + * + */ + public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, + int alphaSize, int depthSize, int stencilSize, boolean gles2) { + setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, + blueSize, alphaSize, depthSize, stencilSize, gles2)); + } + /** + * Set the rendering mode. When renderMode is + * RENDERMODE_CONTINUOUSLY, the renderer is called + * repeatedly to re-render the scene. When renderMode + * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface + * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY. + *

+ * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance + * by allowing the GPU and CPU to idle when the view does not need to be updated. + *

+ * This method can only be called after {@link #setRenderer(Renderer)} + * + * @param renderMode one of the RENDERMODE_X constants + * @see #RENDERMODE_CONTINUOUSLY + * @see #RENDERMODE_WHEN_DIRTY + */ + public void setRenderMode(int renderMode) { + mGLThread.setRenderMode(renderMode); + } + + /** + * Get the current rendering mode. May be called + * from any thread. Must not be called before a renderer has been set. + * @return the current rendering mode. + * @see #RENDERMODE_CONTINUOUSLY + * @see #RENDERMODE_WHEN_DIRTY + */ + public int getRenderMode() { + return mGLThread.getRenderMode(); + } + + /** + * Request that the renderer render a frame. + * This method is typically used when the render mode has been set to + * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand. + * May be called + * from any thread. Must not be called before a renderer has been set. + */ + public void requestRender() { + mGLThread.requestRender(); + } + + /** + * This method is part of the SurfaceHolder.Callback interface, and is + * not normally called or subclassed by clients of GLSurfaceView. + */ + public void surfaceCreated(SurfaceHolder holder) { + mGLThread.surfaceCreated(); + } + + /** + * This method is part of the SurfaceHolder.Callback interface, and is + * not normally called or subclassed by clients of GLSurfaceView. + */ + public void surfaceDestroyed(SurfaceHolder holder) { + // Surface will be destroyed when we return + mGLThread.surfaceDestroyed(); + } + + /** + * This method is part of the SurfaceHolder.Callback interface, and is + * not normally called or subclassed by clients of GLSurfaceView. + */ + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + mGLThread.onWindowResize(w, h); + } + + /** + * Inform the view that the activity is paused. The owner of this view must + * call this method when the activity is paused. Calling this method will + * pause the rendering thread. + * Must not be called before a renderer has been set. + */ + public void onPause() { + mGLThread.onPause(); + } + + /** + * Inform the view that the activity is resumed. The owner of this view must + * call this method when the activity is resumed. Calling this method will + * recreate the OpenGL display and resume the rendering + * thread. + * Must not be called before a renderer has been set. + */ + public void onResume() { + mGLThread.onResume(); + } + + /** + * Queue a runnable to be run on the GL rendering thread. This can be used + * to communicate with the Renderer on the rendering thread. + * Must not be called before a renderer has been set. + * @param r the runnable to be run on the GL rendering thread. + */ + public void queueEvent(Runnable r) { + mGLThread.queueEvent(r); + } + + /** + * This method is used as part of the View class and is not normally + * called or subclassed by clients of GLSurfaceView. + * Must not be called before a renderer has been set. + */ + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mGLThread.requestExitAndWait(); + } + + // ---------------------------------------------------------------------- + + /** + * An interface used to wrap a GL interface. + *

Typically + * used for implementing debugging and tracing on top of the default + * GL interface. You would typically use this by creating your own class + * that implemented all the GL methods by delegating to another GL instance. + * Then you could add your own behavior before or after calling the + * delegate. All the GLWrapper would do was instantiate and return the + * wrapper GL instance: + *

+     * class MyGLWrapper implements GLWrapper {
+     *     GL wrap(GL gl) {
+     *         return new MyGLImplementation(gl);
+     *     }
+     *     static class MyGLImplementation implements GL,GL10,GL11,... {
+     *         ...
+     *     }
+     * }
+     * 
+ * @see #setGLWrapper(GLWrapper) + */ + public interface GLWrapper { + /** + * Wraps a gl interface in another gl interface. + * @param gl a GL interface that is to be wrapped. + * @return either the input argument or another GL object that wraps the input argument. + */ + GL wrap(GL gl); + } + + /** + * A generic renderer interface. + *

+ * The renderer is responsible for making OpenGL calls to render a frame. + *

+ * GLSurfaceView clients typically create their own classes that implement + * this interface, and then call {@link GLSurfaceView#setRenderer} to + * register the renderer with the GLSurfaceView. + *

+ *

Threading

+ * The renderer will be called on a separate thread, so that rendering + * performance is decoupled from the UI thread. Clients typically need to + * communicate with the renderer from the UI thread, because that's where + * input events are received. Clients can communicate using any of the + * standard Java techniques for cross-thread communication, or they can + * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method. + *

+ *

EGL Context Lost

+ * There are situations where the EGL rendering context will be lost. This + * typically happens when device wakes up after going to sleep. When + * the EGL context is lost, all OpenGL resources (such as textures) that are + * associated with that context will be automatically deleted. In order to + * keep rendering correctly, a renderer must recreate any lost resources + * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method + * is a convenient place to do this. + * + * + * @see #setRenderer(Renderer) + */ + public static interface SwapBuffersCallback { + public boolean SwapBuffers(); + } + + public static abstract class Renderer { + /** + * Called when the surface is created or recreated. + *

+ * Called when the rendering thread + * starts and whenever the EGL context is lost. The context will typically + * be lost when the Android device awakes after going to sleep. + *

+ * Since this method is called at the beginning of rendering, as well as + * every time the EGL context is lost, this method is a convenient place to put + * code to create resources that need to be created when the rendering + * starts, and that need to be recreated when the EGL context is lost. + * Textures are an example of a resource that you might want to create + * here. + *

+ * Note that when the EGL context is lost, all OpenGL resources associated + * with that context will be automatically deleted. You do not need to call + * the corresponding "glDelete" methods such as glDeleteTextures to + * manually delete these lost resources. + *

+ * @param gl the GL interface. Use instanceof to + * test if the interface supports GL11 or higher interfaces. + * @param config the EGLConfig of the created surface. Can be used + * to create matching pbuffers. + */ + public abstract void onSurfaceCreated(GL10 gl, EGLConfig config); + + public abstract void onSurfaceDestroyed(); + + /** + * Called when the surface changed size. + *

+ * Called after the surface is created and whenever + * the OpenGL ES surface size changes. + *

+ * Typically you will set your viewport here. If your camera + * is fixed then you could also set your projection matrix here: + *

+         * void onSurfaceChanged(GL10 gl, int width, int height) {
+         *     gl.glViewport(0, 0, width, height);
+         *     // for a fixed camera, set the projection too
+         *     float ratio = (float) width / height;
+         *     gl.glMatrixMode(GL10.GL_PROJECTION);
+         *     gl.glLoadIdentity();
+         *     gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+         * }
+         * 
+ * @param gl the GL interface. Use instanceof to + * test if the interface supports GL11 or higher interfaces. + * @param width + * @param height + */ + public abstract void onSurfaceChanged(GL10 gl, int width, int height); + + /** + * Called to draw the current frame. + *

+ * This method is responsible for drawing the current frame. + *

+ * The implementation of this method typically looks like this: + *

+         * void onDrawFrame(GL10 gl) {
+         *     gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+         *     //... other gl calls to render the scene ...
+         * }
+         * 
+ * @param gl the GL interface. Use instanceof to + * test if the interface supports GL11 or higher interfaces. + */ + public abstract void onDrawFrame(GL10 gl); + + public boolean SwapBuffers() { + if( mSwapBuffersCallback != null ) + return mSwapBuffersCallback.SwapBuffers(); + return false; + } + + public void setSwapBuffersCallback( SwapBuffersCallback c ) { + mSwapBuffersCallback = c; + } + + private SwapBuffersCallback mSwapBuffersCallback = null; + } + + /** + * An interface for choosing an EGLConfig configuration from a list of + * potential configurations. + *

+ * This interface must be implemented by clients wishing to call + * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)} + */ + public interface EGLConfigChooser { + /** + * Choose a configuration from the list. Implementors typically + * implement this method by calling + * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the + * EGL specification available from The Khronos Group to learn how to call eglChooseConfig. + * @param egl the EGL10 for the current display. + * @param display the current display. + * @return the chosen configuration. + */ + EGLConfig chooseConfig(EGL10 egl, EGLDisplay display); + public boolean isGles2Required(); + } + + private static abstract class BaseConfigChooser + implements EGLConfigChooser { + public BaseConfigChooser(int[] configSpec) { + mConfigSpec = configSpec; + } + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { + int[] num_config = new int[1]; + egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config); + + int numConfigs = num_config[0]; + + if (numConfigs <= 0) { + throw new IllegalArgumentException( + "No configs match configSpec"); + } + + EGLConfig[] configs = new EGLConfig[numConfigs]; + egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, + num_config); + EGLConfig config = chooseConfig(egl, display, configs); + if (config == null) { + throw new IllegalArgumentException("No config chosen"); + } + return config; + } + + abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, + EGLConfig[] configs); + + protected int[] mConfigSpec; + } + + private static class ComponentSizeChooser extends BaseConfigChooser { + public ComponentSizeChooser(int redSize, int greenSize, int blueSize, + int alphaSize, int depthSize, int stencilSize, boolean isGles2) { + super(new int[] {EGL10.EGL_NONE}); // Get all possible configs + mValue = new int[1]; + mRedSize = redSize; + mGreenSize = greenSize; + mBlueSize = blueSize; + mAlphaSize = alphaSize; + mDepthSize = depthSize; + mStencilSize = stencilSize; + mIsGles2 = isGles2; + } + + @Override + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, + EGLConfig[] configs) { + EGLConfig closestConfig = null; + int closestDistance = 1000; + String cfglog = ""; + int idx = 0; + int selectidx = -1; + + Log.v("SDL", "Desired GL config: " + "R" + mRedSize + "G" + mGreenSize + "B" + mBlueSize + "A" + mAlphaSize + " depth " + mDepthSize + " stencil " + mStencilSize + " type " + (mIsGles2 ? "GLES2" : "GLES")); + for(EGLConfig config : configs) { + if ( config == null ) + continue; + int r = findConfigAttrib(egl, display, config, + EGL10.EGL_RED_SIZE, 0); + int g = findConfigAttrib(egl, display, config, + EGL10.EGL_GREEN_SIZE, 0); + int b = findConfigAttrib(egl, display, config, + EGL10.EGL_BLUE_SIZE, 0); + int a = findConfigAttrib(egl, display, config, + EGL10.EGL_ALPHA_SIZE, 0); + int d = findConfigAttrib(egl, display, config, + EGL10.EGL_DEPTH_SIZE, 0); + int s = findConfigAttrib(egl, display, config, + EGL10.EGL_STENCIL_SIZE, 0); + int rendertype = findConfigAttrib(egl, display, config, + EGL10.EGL_RENDERABLE_TYPE, 0); + int desiredtype = mIsGles2 ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES_BIT; + int nativeRender = findConfigAttrib(egl, display, config, + EGL10.EGL_NATIVE_RENDERABLE, 0); + int caveat = findConfigAttrib(egl, display, config, + EGL10.EGL_CONFIG_CAVEAT, EGL10.EGL_NONE); + int distance = Math.abs(r - mRedSize) + Math.abs(g - mGreenSize) + Math.abs(b - mBlueSize); + int dist1 = distance; + if( mAlphaSize - a > 0 ) + distance += mAlphaSize - a; + else if( mAlphaSize - a < 0 ) + distance += 1; // Small penalty if we don't need alpha channel but it is present + int dist2 = distance; + if( (d > 0) != (mDepthSize > 0) ) + distance += (mDepthSize > 0) ? 5 : 1; // Small penalty if we don't need zbuffer but it is present + int dist3 = distance; + if( (s > 0) != (mStencilSize > 0) ) + distance += (mStencilSize > 0) ? 5 : 1; // Small penalty if we don't need stencil buffer but it is present + int dist4 = distance; + if( (rendertype & desiredtype) == 0 ) + distance += 5; + int dist5 = distance; + if( caveat == EGL10.EGL_SLOW_CONFIG ) + distance += 4; + if( caveat == EGL10.EGL_NON_CONFORMANT_CONFIG ) // dunno what that means, probably R and B channels swapped + distance += 1; + + String cfgcur = "R" + r + "G" + g + "B" + b + "A" + a + " depth " + d + " stencil " + s + + " type " + rendertype + " ("; + if((rendertype & EGL_OPENGL_ES_BIT) != 0) + cfgcur += "GLES"; + if((rendertype & EGL_OPENGL_ES2_BIT) != 0) + cfgcur += " GLES2"; + if((rendertype & EGL_OPENGL_BIT) != 0) + cfgcur += " OPENGL"; + if((rendertype & EGL_OPENVG_BIT) != 0) + cfgcur += " OPENVG"; + cfgcur += ")"; + cfgcur += " caveat " + (caveat == EGL10.EGL_NONE ? "none" : + (caveat == EGL10.EGL_SLOW_CONFIG ? "SLOW" : + caveat == EGL10.EGL_NON_CONFORMANT_CONFIG ? "non-conformant" : + String.valueOf(caveat))); + cfgcur += " nr " + nativeRender; + cfgcur += " pos " + distance + " (" + dist1 + "," + dist2 + "," + dist3 + "," + dist4 + "," + dist5 + ")"; + Log.v("SDL", "GL config " + idx + ": " + cfgcur); + if (distance < closestDistance) { + closestDistance = distance; + closestConfig = config; + cfglog = new String(cfgcur); + selectidx = idx; + } + idx += 1; + } + Log.v("SDL", "GLSurfaceView_SDL::EGLConfigChooser::chooseConfig(): selected " + selectidx + ": " + cfglog ); + return closestConfig; + } + + private int findConfigAttrib(EGL10 egl, EGLDisplay display, + EGLConfig config, int attribute, int defaultValue) { + mValue[0] = -1; + if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { + return mValue[0]; + } + Log.w("SDL", "GLSurfaceView_SDL::EGLConfigChooser::findConfigAttrib(): attribute doesn't exist: " + attribute); + return defaultValue; + } + + public boolean isGles2Required() + { + return mIsGles2; + } + + private int[] mValue; + // Subclasses can adjust these values: + protected int mRedSize; + protected int mGreenSize; + protected int mBlueSize; + protected int mAlphaSize; + protected int mDepthSize; + protected int mStencilSize; + protected boolean mIsGles2 = false; + + public static final int EGL_OPENGL_ES_BIT = 1; + public static final int EGL_OPENVG_BIT = 2; + public static final int EGL_OPENGL_ES2_BIT = 4; + public static final int EGL_OPENGL_BIT = 8; + } + + /** + * This class will choose a supported surface as close to + * RGB565 as possible, with or without a depth buffer. + * + */ + private static class SimpleEGLConfigChooser16 extends ComponentSizeChooser { + public SimpleEGLConfigChooser16(boolean withDepthBuffer, boolean stencil, boolean gles2) { + super(4, 4, 4, 0, withDepthBuffer ? 16 : 0, stencil ? 8 : 0, gles2); + // Adjust target values. This way we'll accept a 4444 or + // 555 buffer if there's no 565 buffer available. + mRedSize = 5; + mGreenSize = 6; + mBlueSize = 5; + } + } + + private static class SimpleEGLConfigChooser24 extends ComponentSizeChooser { + public SimpleEGLConfigChooser24(boolean withDepthBuffer, boolean stencil, boolean gles2) { + super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, stencil ? 8 : 0, gles2); + mRedSize = 8; + mGreenSize = 8; + mBlueSize = 8; + } + } + + private static class SimpleEGLConfigChooser32 extends ComponentSizeChooser { + public SimpleEGLConfigChooser32(boolean withDepthBuffer, boolean stencil, boolean gles2) { + super(8, 8, 8, 8, withDepthBuffer ? 16 : 0, stencil ? 8 : 0, gles2); + mRedSize = 8; + mGreenSize = 8; + mBlueSize = 8; + mAlphaSize = 8; + } + } + private static ComponentSizeChooser getEglConfigChooser(int videoDepthBpp, boolean withDepthBuffer, boolean stencil, boolean gles2) { + if(videoDepthBpp == 16) + return new SimpleEGLConfigChooser16(withDepthBuffer, stencil, gles2); + if(videoDepthBpp == 24) + return new SimpleEGLConfigChooser24(withDepthBuffer, stencil, gles2); + if(videoDepthBpp == 32) + return new SimpleEGLConfigChooser32(withDepthBuffer, stencil, gles2); + return null; + }; + + /** + * An EGL helper class. + */ + + private class EglHelper { + public EglHelper() { + + } + + /** + * Initialize EGL for a given configuration spec. + * @param configSpec + */ + public void start(){ + + Log.v("SDL", "GLSurfaceView_SDL::EglHelper::start(): creating GL context"); + /* + * Get an EGL instance + */ + mEgl = (EGL10) EGLContext.getEGL(); + + /* + * Get to the default display. + */ + mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + + /* + * We can now initialize EGL for that display + */ + int[] version = new int[2]; + mEgl.eglInitialize(mEglDisplay, version); + mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); + if( mEglConfig == null ) + Log.e("SDL", "GLSurfaceView_SDL::EglHelper::start(): mEglConfig is NULL"); + + /* + * Create an OpenGL ES context. This must be done only once, an + * OpenGL context is a somewhat heavy object. + */ + final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + final int[] gles2_attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; + + mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig, + EGL10.EGL_NO_CONTEXT, mEGLConfigChooser.isGles2Required() ? gles2_attrib_list : null ); + + if( mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT ) + Log.e("SDL", "GLSurfaceView_SDL::EglHelper::start(): mEglContext is EGL_NO_CONTEXT, error: " + mEgl.eglGetError()); + + mEglSurface = null; + } + + /* + * React to the creation of a new surface by creating and returning an + * OpenGL interface that renders to that surface. + */ + public GL createSurface(SurfaceHolder holder) { + Log.v("SDL", "GLSurfaceView_SDL::EglHelper::createSurface(): creating GL context"); + /* + * The window size has changed, so we need to create a new + * surface. + */ + if (mEglSurface != null) { + + /* + * Unbind and destroy the old EGL surface, if + * there is one. + */ + mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, + EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); + mEgl.eglDestroySurface(mEglDisplay, mEglSurface); + } + + /* + * Create an EGL surface we can render into. + */ + /* + // This does not have any effect on Galaxy Note + int [] attribList = new int[4]; + attribList[0] = mEgl.EGL_RENDER_BUFFER; + attribList[1] = mEgl.EGL_SINGLE_BUFFER; + attribList[2] = mEgl.EGL_NONE; + attribList[3] = mEgl.EGL_NONE; + */ + mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, + mEglConfig, holder, null); + + /* + * Before we can issue GL commands, we need to make sure + * the context is current and bound to a surface. + */ + mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext); + + + GL gl = mEglContext.getGL(); + if (mGLWrapper != null) { + gl = mGLWrapper.wrap(gl); + } + + return gl; + } + + /** + * Display the current render surface. + * @return false if the context has been lost. + */ + public boolean swap() { + mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); + + /* + * Always check for EGL_CONTEXT_LOST, which means the context + * and all associated data were lost (For instance because + * the device went to sleep). We need to sleep until we + * get a new surface. + */ + return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST; + } + + public void finish() { + Log.v("SDL", "GLSurfaceView_SDL::EglHelper::finish(): destroying GL context"); + if (mEglSurface != null) { + mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, + EGL10.EGL_NO_SURFACE, + EGL10.EGL_NO_CONTEXT); + mEgl.eglDestroySurface(mEglDisplay, mEglSurface); + mEglSurface = null; + } + if (mEglContext != null) { + mEgl.eglDestroyContext(mEglDisplay, mEglContext); + mEglContext = null; + } + if (mEglDisplay != null) { + mEgl.eglTerminate(mEglDisplay); + mEglDisplay = null; + } + } + + EGL10 mEgl; + EGLDisplay mEglDisplay; + EGLSurface mEglSurface; + EGLConfig mEglConfig; + EGLContext mEglContext; + } + + /** + * A generic GL Thread. Takes care of initializing EGL and GL. Delegates + * to a Renderer instance to do the actual drawing. Can be configured to + * render continuously or on request. + * + */ + class GLThread extends Thread implements SwapBuffersCallback { + GLThread(Renderer renderer) { + super(); + mDone = false; + mWidth = 0; + mHeight = 0; + mRequestRender = true; + mRenderMode = RENDERMODE_CONTINUOUSLY; + mRenderer = renderer; + mRenderer.setSwapBuffersCallback(this); + setName("GLThread"); + } + + @Override + public void run() { + /* + * When the android framework launches a second instance of + * an activity, the new instance's onCreate() method may be + * called before the first instance returns from onDestroy(). + * + * This semaphore ensures that only one instance at a time + * accesses EGL. + */ + try { + sEglSemaphore.acquire(); + } catch (InterruptedException e) { + return; + } + + mEglHelper = new EglHelper(); + // mEglHelper.start(); + mNeedStart = true; + mSizeChanged = true; + SwapBuffers(); + + mRenderer.onDrawFrame(mGL); + + mEglHelper.finish(); + + /* + synchronized (sGLThreadManager) { + stopEglLocked(); + } + sGLThreadManager.threadExiting(this); + */ + + sEglSemaphore.release(); + } + + public boolean SwapBuffers() { + + boolean tellRendererSurfaceCreated = false; + boolean tellRendererSurfaceChanged = false; + + /* + * This is our main activity thread's loop, we go until + * asked to quit. + */ + + /* + * Update the asynchronous state (window size) + */ + while(true) { // Loop until we're re-created GL context and successfully called swap() + + int w, h; + boolean changed = false; + synchronized (this) { + /* + Runnable r; + while ((r = getEvent()) != null) { + r.run(); + } + */ + if (mPaused) { + mRenderer.onSurfaceDestroyed(); + mEglHelper.finish(); + mNeedStart = true; + if( Globals.NonBlockingSwapBuffers ) + return false; + } + while (needToWait()) { + //Log.v("SDL", "GLSurfaceView_SDL::run(): paused"); + try { + wait(500); + } catch(Exception e) { } + } + if (mDone) { + return false; + } + // changed = mSizeChanged; + w = mWidth; + h = mHeight; + mSizeChanged = false; + mRequestRender = false; + } + if (mNeedStart) { + mEglHelper.start(); + tellRendererSurfaceCreated = true; + changed = true; + mNeedStart = false; + } + if (changed) { + mGL = (GL10) mEglHelper.createSurface(getHolder()); + tellRendererSurfaceChanged = true; + } + if (tellRendererSurfaceCreated) { + mRenderer.onSurfaceCreated(mGL, mEglHelper.mEglConfig); + tellRendererSurfaceCreated = false; + } + if (tellRendererSurfaceChanged) { + mRenderer.onSurfaceChanged(mGL, w, h); + tellRendererSurfaceChanged = false; + } + /* + * Once we're done with GL, we need to call swapBuffers() + * to instruct the system to display the rendered frame + */ + if( mEglHelper.swap() ) + return true; + // We've lost GL context - recreate it + mRenderer.onSurfaceDestroyed(); + mEglHelper.finish(); + mNeedStart = true; + if( Globals.NonBlockingSwapBuffers ) + return false; + } + } + + private boolean needToWait() { + if (((KeyguardManager)getContext().getSystemService(Context.KEYGUARD_SERVICE)).inKeyguardRestrictedInputMode()) { + return true; // We're in lockscreen - sleep until user unlocks the device + } + + if (mDone) { + return false; + } + + if (mPaused || (! mHasSurface)) { + return true; + } + + if ((mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) { + return false; + } + + return true; + } + + public void setRenderMode(int renderMode) { + if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) { + throw new IllegalArgumentException("renderMode"); + } + synchronized(this) { + mRenderMode = renderMode; + if (renderMode == RENDERMODE_CONTINUOUSLY) { + notify(); + } + } + } + + public int getRenderMode() { + synchronized(this) { + return mRenderMode; + } + } + + public void requestRender() { + synchronized(this) { + mRequestRender = true; + notify(); + } + } + + public void surfaceCreated() { + synchronized(this) { + mHasSurface = true; + notify(); + } + } + + public void surfaceDestroyed() { + synchronized(this) { + mHasSurface = false; + notify(); + } + } + + public void onPause() { + Log.v("SDL", "GLSurfaceView_SDL::onPause()"); + synchronized (this) { + mPaused = true; + } + } + + public void onResume() { + Log.v("SDL", "GLSurfaceView_SDL::onResume()"); + synchronized (this) { + mPaused = false; + notify(); + } + } + + public void onWindowResize(int w, int h) { + synchronized (this) { + mWidth = w; + mHeight = h; + mSizeChanged = true; + notify(); + } + } + + public void requestExitAndWait() { + // don't call this from GLThread thread or it is a guaranteed + // deadlock! + synchronized(this) { + mDone = true; + notify(); + } + try { + join(); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + + /** + * Queue an "event" to be run on the GL rendering thread. + * @param r the runnable to be run on the GL rendering thread. + */ + public void queueEvent(Runnable r) { + synchronized(this) { + mEventQueue.add(r); + } + } + + private Runnable getEvent() { + synchronized(this) { + if (mEventQueue.size() > 0) { + return mEventQueue.remove(0); + } + + } + return null; + } + + private boolean mDone; + private boolean mPaused; + private boolean mHasSurface; + private int mWidth; + private int mHeight; + private int mRenderMode; + private boolean mRequestRender; + private Renderer mRenderer; + private ArrayList mEventQueue = new ArrayList(); + private EglHelper mEglHelper; + private GL10 mGL = null; + private boolean mNeedStart = false; + } + + static class LogWriter extends Writer { + + @Override public void close() { + flushBuilder(); + } + + @Override public void flush() { + flushBuilder(); + } + + @Override public void write(char[] buf, int offset, int count) { + for(int i = 0; i < count; i++) { + char c = buf[offset + i]; + if ( c == '\n') { + flushBuilder(); + } + else { + mBuilder.append(c); + } + } + } + + private void flushBuilder() { + if (mBuilder.length() > 0) { + Log.v("GLSurfaceView", mBuilder.toString()); + mBuilder.delete(0, mBuilder.length()); + } + } + + private StringBuilder mBuilder = new StringBuilder(); + } + + private static final Semaphore sEglSemaphore = new Semaphore(1); + private boolean mSizeChanged = true; + + private GLThread mGLThread; + private EGLConfigChooser mEGLConfigChooser; + private GLWrapper mGLWrapper; + private int mDebugFlags; +} diff --git a/project/javaSDL2/Keycodes.java b/project/javaSDL2/Keycodes.java new file mode 100644 index 000000000..15aa6b6e9 --- /dev/null +++ b/project/javaSDL2/Keycodes.java @@ -0,0 +1,592 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import java.lang.String; +import java.util.ArrayList; +import java.util.Arrays; +import java.lang.reflect.Field; + + +// Autogenerated by hand with a command: +// grep 'SDLK_' SDL_keysym.h | sed 's/SDLK_\([a-zA-Z0-9_]\+\).*[=] \([0-9]\+\).*/public static final int SDLK_\1 = \2;/' >> Keycodes.java +class SDL_1_2_Keycodes +{ + public static final int SDLK_UNKNOWN = 0; + public static final int SDLK_BACKSPACE = 8; + public static final int SDLK_TAB = 9; + public static final int SDLK_CLEAR = 12; + public static final int SDLK_RETURN = 13; + public static final int SDLK_PAUSE = 19; + public static final int SDLK_ESCAPE = 27; + public static final int SDLK_SPACE = 32; + public static final int SDLK_EXCLAIM = 33; + public static final int SDLK_QUOTEDBL = 34; + public static final int SDLK_HASH = 35; + public static final int SDLK_DOLLAR = 36; + public static final int SDLK_AMPERSAND = 38; + public static final int SDLK_QUOTE = 39; + public static final int SDLK_LEFTPAREN = 40; + public static final int SDLK_RIGHTPAREN = 41; + public static final int SDLK_ASTERISK = 42; + public static final int SDLK_PLUS = 43; + public static final int SDLK_COMMA = 44; + public static final int SDLK_MINUS = 45; + public static final int SDLK_PERIOD = 46; + public static final int SDLK_SLASH = 47; + public static final int SDLK_0 = 48; + public static final int SDLK_1 = 49; + public static final int SDLK_2 = 50; + public static final int SDLK_3 = 51; + public static final int SDLK_4 = 52; + public static final int SDLK_5 = 53; + public static final int SDLK_6 = 54; + public static final int SDLK_7 = 55; + public static final int SDLK_8 = 56; + public static final int SDLK_9 = 57; + public static final int SDLK_COLON = 58; + public static final int SDLK_SEMICOLON = 59; + public static final int SDLK_LESS = 60; + public static final int SDLK_EQUALS = 61; + public static final int SDLK_GREATER = 62; + public static final int SDLK_QUESTION = 63; + public static final int SDLK_AT = 64; + public static final int SDLK_LEFTBRACKET = 91; + public static final int SDLK_BACKSLASH = 92; + public static final int SDLK_RIGHTBRACKET = 93; + public static final int SDLK_CARET = 94; + public static final int SDLK_UNDERSCORE = 95; + public static final int SDLK_BACKQUOTE = 96; + public static final int SDLK_a = 97; + public static final int SDLK_b = 98; + public static final int SDLK_c = 99; + public static final int SDLK_d = 100; + public static final int SDLK_e = 101; + public static final int SDLK_f = 102; + public static final int SDLK_g = 103; + public static final int SDLK_h = 104; + public static final int SDLK_i = 105; + public static final int SDLK_j = 106; + public static final int SDLK_k = 107; + public static final int SDLK_l = 108; + public static final int SDLK_m = 109; + public static final int SDLK_n = 110; + public static final int SDLK_o = 111; + public static final int SDLK_p = 112; + public static final int SDLK_q = 113; + public static final int SDLK_r = 114; + public static final int SDLK_s = 115; + public static final int SDLK_t = 116; + public static final int SDLK_u = 117; + public static final int SDLK_v = 118; + public static final int SDLK_w = 119; + public static final int SDLK_x = 120; + public static final int SDLK_y = 121; + public static final int SDLK_z = 122; + public static final int SDLK_DELETE = 127; + public static final int SDLK_WORLD_0 = 160; + public static final int SDLK_WORLD_1 = 161; + public static final int SDLK_WORLD_2 = 162; + public static final int SDLK_WORLD_3 = 163; + public static final int SDLK_WORLD_4 = 164; + public static final int SDLK_WORLD_5 = 165; + public static final int SDLK_WORLD_6 = 166; + public static final int SDLK_WORLD_7 = 167; + public static final int SDLK_WORLD_8 = 168; + public static final int SDLK_WORLD_9 = 169; + public static final int SDLK_WORLD_10 = 170; + public static final int SDLK_WORLD_11 = 171; + public static final int SDLK_WORLD_12 = 172; + public static final int SDLK_WORLD_13 = 173; + public static final int SDLK_WORLD_14 = 174; + public static final int SDLK_WORLD_15 = 175; + public static final int SDLK_WORLD_16 = 176; + public static final int SDLK_WORLD_17 = 177; + public static final int SDLK_WORLD_18 = 178; + public static final int SDLK_WORLD_19 = 179; + public static final int SDLK_WORLD_20 = 180; + public static final int SDLK_WORLD_21 = 181; + public static final int SDLK_WORLD_22 = 182; + public static final int SDLK_WORLD_23 = 183; + public static final int SDLK_WORLD_24 = 184; + public static final int SDLK_WORLD_25 = 185; + public static final int SDLK_WORLD_26 = 186; + public static final int SDLK_WORLD_27 = 187; + public static final int SDLK_WORLD_28 = 188; + public static final int SDLK_WORLD_29 = 189; + public static final int SDLK_WORLD_30 = 190; + public static final int SDLK_WORLD_31 = 191; + public static final int SDLK_WORLD_32 = 192; + public static final int SDLK_WORLD_33 = 193; + public static final int SDLK_WORLD_34 = 194; + public static final int SDLK_WORLD_35 = 195; + public static final int SDLK_WORLD_36 = 196; + public static final int SDLK_WORLD_37 = 197; + public static final int SDLK_WORLD_38 = 198; + public static final int SDLK_WORLD_39 = 199; + public static final int SDLK_WORLD_40 = 200; + public static final int SDLK_WORLD_41 = 201; + public static final int SDLK_WORLD_42 = 202; + public static final int SDLK_WORLD_43 = 203; + public static final int SDLK_WORLD_44 = 204; + public static final int SDLK_WORLD_45 = 205; + public static final int SDLK_WORLD_46 = 206; + public static final int SDLK_WORLD_47 = 207; + public static final int SDLK_WORLD_48 = 208; + public static final int SDLK_WORLD_49 = 209; + public static final int SDLK_WORLD_50 = 210; + public static final int SDLK_WORLD_51 = 211; + public static final int SDLK_WORLD_52 = 212; + public static final int SDLK_WORLD_53 = 213; + public static final int SDLK_WORLD_54 = 214; + public static final int SDLK_WORLD_55 = 215; + public static final int SDLK_WORLD_56 = 216; + public static final int SDLK_WORLD_57 = 217; + public static final int SDLK_WORLD_58 = 218; + public static final int SDLK_WORLD_59 = 219; + public static final int SDLK_WORLD_60 = 220; + public static final int SDLK_WORLD_61 = 221; + public static final int SDLK_WORLD_62 = 222; + public static final int SDLK_WORLD_63 = 223; + public static final int SDLK_WORLD_64 = 224; + public static final int SDLK_WORLD_65 = 225; + public static final int SDLK_WORLD_66 = 226; + public static final int SDLK_WORLD_67 = 227; + public static final int SDLK_WORLD_68 = 228; + public static final int SDLK_WORLD_69 = 229; + public static final int SDLK_WORLD_70 = 230; + public static final int SDLK_WORLD_71 = 231; + public static final int SDLK_WORLD_72 = 232; + public static final int SDLK_WORLD_73 = 233; + public static final int SDLK_WORLD_74 = 234; + public static final int SDLK_WORLD_75 = 235; + public static final int SDLK_WORLD_76 = 236; + public static final int SDLK_WORLD_77 = 237; + public static final int SDLK_WORLD_78 = 238; + public static final int SDLK_WORLD_79 = 239; + public static final int SDLK_WORLD_80 = 240; + public static final int SDLK_WORLD_81 = 241; + public static final int SDLK_WORLD_82 = 242; + public static final int SDLK_WORLD_83 = 243; + public static final int SDLK_WORLD_84 = 244; + public static final int SDLK_WORLD_85 = 245; + public static final int SDLK_WORLD_86 = 246; + public static final int SDLK_WORLD_87 = 247; + public static final int SDLK_WORLD_88 = 248; + public static final int SDLK_WORLD_89 = 249; + public static final int SDLK_WORLD_90 = 250; + public static final int SDLK_WORLD_91 = 251; + public static final int SDLK_WORLD_92 = 252; + public static final int SDLK_WORLD_93 = 253; + public static final int SDLK_WORLD_94 = 254; + public static final int SDLK_WORLD_95 = 255; + public static final int SDLK_KP0 = 256; + public static final int SDLK_KP1 = 257; + public static final int SDLK_KP2 = 258; + public static final int SDLK_KP3 = 259; + public static final int SDLK_KP4 = 260; + public static final int SDLK_KP5 = 261; + public static final int SDLK_KP6 = 262; + public static final int SDLK_KP7 = 263; + public static final int SDLK_KP8 = 264; + public static final int SDLK_KP9 = 265; + public static final int SDLK_KP_PERIOD = 266; + public static final int SDLK_KP_DIVIDE = 267; + public static final int SDLK_KP_MULTIPLY = 268; + public static final int SDLK_KP_MINUS = 269; + public static final int SDLK_KP_PLUS = 270; + public static final int SDLK_KP_ENTER = 271; + public static final int SDLK_KP_EQUALS = 272; + public static final int SDLK_UP = 273; + public static final int SDLK_DOWN = 274; + public static final int SDLK_RIGHT = 275; + public static final int SDLK_LEFT = 276; + public static final int SDLK_INSERT = 277; + public static final int SDLK_HOME = 278; + public static final int SDLK_END = 279; + public static final int SDLK_PAGEUP = 280; + public static final int SDLK_PAGEDOWN = 281; + public static final int SDLK_F1 = 282; + public static final int SDLK_F2 = 283; + public static final int SDLK_F3 = 284; + public static final int SDLK_F4 = 285; + public static final int SDLK_F5 = 286; + public static final int SDLK_F6 = 287; + public static final int SDLK_F7 = 288; + public static final int SDLK_F8 = 289; + public static final int SDLK_F9 = 290; + public static final int SDLK_F10 = 291; + public static final int SDLK_F11 = 292; + public static final int SDLK_F12 = 293; + public static final int SDLK_F13 = 294; + public static final int SDLK_F14 = 295; + public static final int SDLK_F15 = 296; + public static final int SDLK_NUMLOCK = 300; + public static final int SDLK_CAPSLOCK = 301; + public static final int SDLK_SCROLLOCK = 302; + public static final int SDLK_RSHIFT = 303; + public static final int SDLK_LSHIFT = 304; + public static final int SDLK_RCTRL = 305; + public static final int SDLK_LCTRL = 306; + public static final int SDLK_RALT = 307; + public static final int SDLK_LALT = 308; + public static final int SDLK_RMETA = 309; + public static final int SDLK_LMETA = 310; + public static final int SDLK_LSUPER = 311; + public static final int SDLK_RSUPER = 312; + public static final int SDLK_MODE = 313; + public static final int SDLK_COMPOSE = 314; + public static final int SDLK_HELP = 315; + public static final int SDLK_PRINT = 316; + public static final int SDLK_SYSREQ = 317; + public static final int SDLK_BREAK = 318; + public static final int SDLK_MENU = 319; + public static final int SDLK_POWER = 320; + public static final int SDLK_EURO = 321; + public static final int SDLK_UNDO = 322; + + public static final int SDLK_NO_REMAP = 512; +} + +// Autogenerated by hand with a command: +// grep 'SDL_SCANCODE_' SDL_scancode.h | sed 's/SDL_SCANCODE_\([a-zA-Z0-9_]\+\).*[=] \([0-9]\+\).*/public static final int SDLK_\1 = \2;/' >> Keycodes.java +class SDL_1_3_Keycodes +{ + public static final int SDLK_UNKNOWN = 0; + public static final int SDLK_A = 4; + public static final int SDLK_B = 5; + public static final int SDLK_C = 6; + public static final int SDLK_D = 7; + public static final int SDLK_E = 8; + public static final int SDLK_F = 9; + public static final int SDLK_G = 10; + public static final int SDLK_H = 11; + public static final int SDLK_I = 12; + public static final int SDLK_J = 13; + public static final int SDLK_K = 14; + public static final int SDLK_L = 15; + public static final int SDLK_M = 16; + public static final int SDLK_N = 17; + public static final int SDLK_O = 18; + public static final int SDLK_P = 19; + public static final int SDLK_Q = 20; + public static final int SDLK_R = 21; + public static final int SDLK_S = 22; + public static final int SDLK_T = 23; + public static final int SDLK_U = 24; + public static final int SDLK_V = 25; + public static final int SDLK_W = 26; + public static final int SDLK_X = 27; + public static final int SDLK_Y = 28; + public static final int SDLK_Z = 29; + public static final int SDLK_1 = 30; + public static final int SDLK_2 = 31; + public static final int SDLK_3 = 32; + public static final int SDLK_4 = 33; + public static final int SDLK_5 = 34; + public static final int SDLK_6 = 35; + public static final int SDLK_7 = 36; + public static final int SDLK_8 = 37; + public static final int SDLK_9 = 38; + public static final int SDLK_0 = 39; + public static final int SDLK_RETURN = 40; + public static final int SDLK_ESCAPE = 41; + public static final int SDLK_BACKSPACE = 42; + public static final int SDLK_TAB = 43; + public static final int SDLK_SPACE = 44; + public static final int SDLK_MINUS = 45; + public static final int SDLK_EQUALS = 46; + public static final int SDLK_LEFTBRACKET = 47; + public static final int SDLK_RIGHTBRACKET = 48; + public static final int SDLK_BACKSLASH = 49; + public static final int SDLK_NONUSHASH = 50; + public static final int SDLK_SEMICOLON = 51; + public static final int SDLK_APOSTROPHE = 52; + public static final int SDLK_GRAVE = 53; + public static final int SDLK_COMMA = 54; + public static final int SDLK_PERIOD = 55; + public static final int SDLK_SLASH = 56; + public static final int SDLK_CAPSLOCK = 57; + public static final int SDLK_F1 = 58; + public static final int SDLK_F2 = 59; + public static final int SDLK_F3 = 60; + public static final int SDLK_F4 = 61; + public static final int SDLK_F5 = 62; + public static final int SDLK_F6 = 63; + public static final int SDLK_F7 = 64; + public static final int SDLK_F8 = 65; + public static final int SDLK_F9 = 66; + public static final int SDLK_F10 = 67; + public static final int SDLK_F11 = 68; + public static final int SDLK_F12 = 69; + public static final int SDLK_PRINTSCREEN = 70; + public static final int SDLK_SCROLLLOCK = 71; + public static final int SDLK_PAUSE = 72; + public static final int SDLK_INSERT = 73; + public static final int SDLK_HOME = 74; + public static final int SDLK_PAGEUP = 75; + public static final int SDLK_DELETE = 76; + public static final int SDLK_END = 77; + public static final int SDLK_PAGEDOWN = 78; + public static final int SDLK_RIGHT = 79; + public static final int SDLK_LEFT = 80; + public static final int SDLK_DOWN = 81; + public static final int SDLK_UP = 82; + public static final int SDLK_NUMLOCKCLEAR = 83; + public static final int SDLK_KP_DIVIDE = 84; + public static final int SDLK_KP_MULTIPLY = 85; + public static final int SDLK_KP_MINUS = 86; + public static final int SDLK_KP_PLUS = 87; + public static final int SDLK_KP_ENTER = 88; + public static final int SDLK_KP_1 = 89; + public static final int SDLK_KP_2 = 90; + public static final int SDLK_KP_3 = 91; + public static final int SDLK_KP_4 = 92; + public static final int SDLK_KP_5 = 93; + public static final int SDLK_KP_6 = 94; + public static final int SDLK_KP_7 = 95; + public static final int SDLK_KP_8 = 96; + public static final int SDLK_KP_9 = 97; + public static final int SDLK_KP_0 = 98; + public static final int SDLK_KP_PERIOD = 99; + public static final int SDLK_NONUSBACKSLASH = 100; + public static final int SDLK_APPLICATION = 101; + public static final int SDLK_POWER = 102; + public static final int SDLK_KP_EQUALS = 103; + public static final int SDLK_F13 = 104; + public static final int SDLK_F14 = 105; + public static final int SDLK_F15 = 106; + public static final int SDLK_F16 = 107; + public static final int SDLK_F17 = 108; + public static final int SDLK_F18 = 109; + public static final int SDLK_F19 = 110; + public static final int SDLK_F20 = 111; + public static final int SDLK_F21 = 112; + public static final int SDLK_F22 = 113; + public static final int SDLK_F23 = 114; + public static final int SDLK_F24 = 115; + public static final int SDLK_EXECUTE = 116; + public static final int SDLK_HELP = 117; + public static final int SDLK_MENU = 118; + public static final int SDLK_SELECT = 119; + public static final int SDLK_STOP = 120; + public static final int SDLK_AGAIN = 121; + public static final int SDLK_UNDO = 122; + public static final int SDLK_CUT = 123; + public static final int SDLK_COPY = 124; + public static final int SDLK_PASTE = 125; + public static final int SDLK_FIND = 126; + public static final int SDLK_MUTE = 127; + public static final int SDLK_VOLUMEUP = 128; + public static final int SDLK_VOLUMEDOWN = 129; + public static final int SDLK_KP_COMMA = 133; + public static final int SDLK_KP_EQUALSAS400 = 134; + public static final int SDLK_INTERNATIONAL1 = 135; + public static final int SDLK_INTERNATIONAL2 = 136; + public static final int SDLK_INTERNATIONAL3 = 137; + public static final int SDLK_INTERNATIONAL4 = 138; + public static final int SDLK_INTERNATIONAL5 = 139; + public static final int SDLK_INTERNATIONAL6 = 140; + public static final int SDLK_INTERNATIONAL7 = 141; + public static final int SDLK_INTERNATIONAL8 = 142; + public static final int SDLK_INTERNATIONAL9 = 143; + public static final int SDLK_LANG1 = 144; + public static final int SDLK_LANG2 = 145; + public static final int SDLK_LANG3 = 146; + public static final int SDLK_LANG4 = 147; + public static final int SDLK_LANG5 = 148; + public static final int SDLK_LANG6 = 149; + public static final int SDLK_LANG7 = 150; + public static final int SDLK_LANG8 = 151; + public static final int SDLK_LANG9 = 152; + public static final int SDLK_ALTERASE = 153; + public static final int SDLK_SYSREQ = 154; + public static final int SDLK_CANCEL = 155; + public static final int SDLK_CLEAR = 156; + public static final int SDLK_PRIOR = 157; + public static final int SDLK_RETURN2 = 158; + public static final int SDLK_SEPARATOR = 159; + public static final int SDLK_OUT = 160; + public static final int SDLK_OPER = 161; + public static final int SDLK_CLEARAGAIN = 162; + public static final int SDLK_CRSEL = 163; + public static final int SDLK_EXSEL = 164; + public static final int SDLK_KP_00 = 176; + public static final int SDLK_KP_000 = 177; + public static final int SDLK_THOUSANDSSEPARATOR = 178; + public static final int SDLK_DECIMALSEPARATOR = 179; + public static final int SDLK_CURRENCYUNIT = 180; + public static final int SDLK_CURRENCYSUBUNIT = 181; + public static final int SDLK_KP_LEFTPAREN = 182; + public static final int SDLK_KP_RIGHTPAREN = 183; + public static final int SDLK_KP_LEFTBRACE = 184; + public static final int SDLK_KP_RIGHTBRACE = 185; + public static final int SDLK_KP_TAB = 186; + public static final int SDLK_KP_BACKSPACE = 187; + public static final int SDLK_KP_A = 188; + public static final int SDLK_KP_B = 189; + public static final int SDLK_KP_C = 190; + public static final int SDLK_KP_D = 191; + public static final int SDLK_KP_E = 192; + public static final int SDLK_KP_F = 193; + public static final int SDLK_KP_XOR = 194; + public static final int SDLK_KP_POWER = 195; + public static final int SDLK_KP_PERCENT = 196; + public static final int SDLK_KP_LESS = 197; + public static final int SDLK_KP_GREATER = 198; + public static final int SDLK_KP_AMPERSAND = 199; + public static final int SDLK_KP_DBLAMPERSAND = 200; + public static final int SDLK_KP_VERTICALBAR = 201; + public static final int SDLK_KP_DBLVERTICALBAR = 202; + public static final int SDLK_KP_COLON = 203; + public static final int SDLK_KP_HASH = 204; + public static final int SDLK_KP_SPACE = 205; + public static final int SDLK_KP_AT = 206; + public static final int SDLK_KP_EXCLAM = 207; + public static final int SDLK_KP_MEMSTORE = 208; + public static final int SDLK_KP_MEMRECALL = 209; + public static final int SDLK_KP_MEMCLEAR = 210; + public static final int SDLK_KP_MEMADD = 211; + public static final int SDLK_KP_MEMSUBTRACT = 212; + public static final int SDLK_KP_MEMMULTIPLY = 213; + public static final int SDLK_KP_MEMDIVIDE = 214; + public static final int SDLK_KP_PLUSMINUS = 215; + public static final int SDLK_KP_CLEAR = 216; + public static final int SDLK_KP_CLEARENTRY = 217; + public static final int SDLK_KP_BINARY = 218; + public static final int SDLK_KP_OCTAL = 219; + public static final int SDLK_KP_DECIMAL = 220; + public static final int SDLK_KP_HEXADECIMAL = 221; + public static final int SDLK_LCTRL = 224; + public static final int SDLK_LSHIFT = 225; + public static final int SDLK_LALT = 226; + public static final int SDLK_LGUI = 227; + public static final int SDLK_RCTRL = 228; + public static final int SDLK_RSHIFT = 229; + public static final int SDLK_RALT = 230; + public static final int SDLK_RGUI = 231; + public static final int SDLK_MODE = 257; + public static final int SDLK_AUDIONEXT = 258; + public static final int SDLK_AUDIOPREV = 259; + public static final int SDLK_AUDIOSTOP = 260; + public static final int SDLK_AUDIOPLAY = 261; + public static final int SDLK_AUDIOMUTE = 262; + public static final int SDLK_MEDIASELECT = 263; + public static final int SDLK_WWW = 264; + public static final int SDLK_MAIL = 265; + public static final int SDLK_CALCULATOR = 266; + public static final int SDLK_COMPUTER = 267; + public static final int SDLK_AC_SEARCH = 268; + public static final int SDLK_AC_HOME = 269; + public static final int SDLK_AC_BACK = 270; + public static final int SDLK_AC_FORWARD = 271; + public static final int SDLK_AC_STOP = 272; + public static final int SDLK_AC_REFRESH = 273; + public static final int SDLK_AC_BOOKMARKS = 274; + public static final int SDLK_BRIGHTNESSDOWN = 275; + public static final int SDLK_BRIGHTNESSUP = 276; + public static final int SDLK_DISPLAYSWITCH = 277; + public static final int SDLK_KBDILLUMTOGGLE = 278; + public static final int SDLK_KBDILLUMDOWN = 279; + public static final int SDLK_KBDILLUMUP = 280; + public static final int SDLK_EJECT = 281; + public static final int SDLK_SLEEP = 282; + + public static final int SDLK_NO_REMAP = 512; +} + +class SDL_Keys +{ + public static String [] names = null; + public static Integer [] values = null; + + public static String [] namesSorted = null; + public static Integer [] namesSortedIdx = null; + public static Integer [] namesSortedBackIdx = null; + + static final int JAVA_KEYCODE_LAST = 255; // Android 2.3 added several new gaming keys, Android 3.1 added even more - keep in sync with javakeycodes.h + + static String getName(int v) + { + for( int f = 0; f < values.length; f++ ) + { + if( values[f] == v ) + return names[f]; + } + return names[0]; + } + + static + { + ArrayList Names = new ArrayList (); + ArrayList Values = new ArrayList (); + Field [] fields = SDL_1_2_Keycodes.class.getDeclaredFields(); + if( Globals.Using_SDL_1_3 ) + { + fields = SDL_1_3_Keycodes.class.getDeclaredFields(); + } + + try { + for(Field f: fields) + { + Values.add(f.getInt(null)); + Names.add(f.getName().substring(5).toUpperCase()); + } + } catch(IllegalAccessException e) {}; + + // Sort by value + for( int i = 0; i < Values.size(); i++ ) + { + for( int j = i; j < Values.size(); j++ ) + { + if( Values.get(i) > Values.get(j) ) + { + int x = Values.get(i); + Values.set(i, Values.get(j)); + Values.set(j, x); + String s = Names.get(i); + Names.set(i, Names.get(j)); + Names.set(j, s); + } + } + } + + names = Names.toArray(new String[0]); + values = Values.toArray(new Integer[0]); + namesSorted = Names.toArray(new String[0]); + namesSortedIdx = new Integer[values.length]; + namesSortedBackIdx = new Integer[values.length]; + Arrays.sort(namesSorted); + for( int i = 0; i < namesSorted.length; i++ ) + { + for( int j = 0; j < namesSorted.length; j++ ) + { + if( namesSorted[i].equals( names[j] ) ) + { + namesSortedIdx[i] = j; + namesSortedBackIdx[j] = i; + break; + } + } + } + } +} diff --git a/project/javaSDL2/Settings.java b/project/javaSDL2/Settings.java new file mode 100644 index 000000000..5fa4a6994 --- /dev/null +++ b/project/javaSDL2/Settings.java @@ -0,0 +1,782 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import android.widget.TextView; +import android.util.Log; +import java.io.*; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Environment; +import android.os.StatFs; +import java.util.Locale; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.GZIPInputStream; +import java.util.Collections; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import java.lang.String; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.FrameLayout; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap; +import android.widget.TextView; +import android.widget.EditText; +import android.widget.ScrollView; +import android.widget.Button; +import android.view.View; +import android.widget.LinearLayout; +import android.text.Editable; +import android.text.SpannedString; +import android.content.Intent; +import android.app.PendingIntent; +import android.app.AlarmManager; +import android.util.DisplayMetrics; +import android.net.Uri; +import java.util.concurrent.Semaphore; +import java.util.Arrays; +import android.graphics.Color; +import android.hardware.SensorEventListener; +import android.hardware.SensorEvent; +import android.hardware.Sensor; +import android.widget.Toast; + + +// TODO: too much code here, split into multiple files, possibly auto-generated menus? +class Settings +{ + static String SettingsFileName = "libsdl-settings.cfg"; + + static boolean settingsLoaded = false; + static boolean settingsChanged = false; + static final int SETTINGS_FILE_VERSION = 5; + + static void Save(final MainActivity p) + { + try { + ObjectOutputStream out = new ObjectOutputStream(p.openFileOutput( SettingsFileName, p.MODE_WORLD_READABLE )); + out.writeInt(SETTINGS_FILE_VERSION); + out.writeBoolean(Globals.DownloadToSdcard); + out.writeBoolean(Globals.PhoneHasArrowKeys); + out.writeBoolean(Globals.PhoneHasTrackball); + out.writeBoolean(Globals.UseAccelerometerAsArrowKeys); + out.writeBoolean(Globals.UseTouchscreenKeyboard); + out.writeInt(Globals.TouchscreenKeyboardSize); + out.writeInt(Globals.AccelerometerSensitivity); + out.writeInt(Globals.AccelerometerCenterPos); + out.writeInt(Globals.TrackballDampening); + out.writeInt(Globals.AudioBufferConfig); + out.writeInt(Globals.TouchscreenKeyboardTheme); + out.writeInt(Globals.RightClickMethod); + out.writeInt(Globals.ShowScreenUnderFinger); + out.writeInt(Globals.LeftClickMethod); + out.writeBoolean(Globals.MoveMouseWithJoystick); + out.writeBoolean(Globals.ClickMouseWithDpad); + out.writeInt(Globals.ClickScreenPressure); + out.writeInt(Globals.ClickScreenTouchspotSize); + out.writeBoolean(Globals.KeepAspectRatio); + out.writeInt(Globals.MoveMouseWithJoystickSpeed); + out.writeInt(Globals.MoveMouseWithJoystickAccel); + out.writeInt(SDL_Keys.JAVA_KEYCODE_LAST); + for( int i = 0; i < SDL_Keys.JAVA_KEYCODE_LAST; i++ ) + { + out.writeInt(Globals.RemapHwKeycode[i]); + } + out.writeInt(Globals.RemapScreenKbKeycode.length); + for( int i = 0; i < Globals.RemapScreenKbKeycode.length; i++ ) + { + out.writeInt(Globals.RemapScreenKbKeycode[i]); + } + out.writeInt(Globals.ScreenKbControlsShown.length); + for( int i = 0; i < Globals.ScreenKbControlsShown.length; i++ ) + { + out.writeBoolean(Globals.ScreenKbControlsShown[i]); + } + out.writeInt(Globals.TouchscreenKeyboardTransparency); + out.writeInt(Globals.RemapMultitouchGestureKeycode.length); + for( int i = 0; i < Globals.RemapMultitouchGestureKeycode.length; i++ ) + { + out.writeInt(Globals.RemapMultitouchGestureKeycode[i]); + out.writeBoolean(Globals.MultitouchGesturesUsed[i]); + } + out.writeInt(Globals.MultitouchGestureSensitivity); + for( int i = 0; i < Globals.TouchscreenCalibration.length; i++ ) + out.writeInt(Globals.TouchscreenCalibration[i]); + out.writeInt(Globals.DataDir.length()); + for( int i = 0; i < Globals.DataDir.length(); i++ ) + out.writeChar(Globals.DataDir.charAt(i)); + out.writeInt(Globals.CommandLine.length()); + for( int i = 0; i < Globals.CommandLine.length(); i++ ) + out.writeChar(Globals.CommandLine.charAt(i)); + out.writeInt(Globals.ScreenKbControlsLayout.length); + for( int i = 0; i < Globals.ScreenKbControlsLayout.length; i++ ) + for( int ii = 0; ii < 4; ii++ ) + out.writeInt(Globals.ScreenKbControlsLayout[i][ii]); + out.writeInt(Globals.LeftClickKey); + out.writeInt(Globals.RightClickKey); + out.writeBoolean(Globals.VideoLinearFilter); + out.writeInt(Globals.LeftClickTimeout); + out.writeInt(Globals.RightClickTimeout); + out.writeBoolean(Globals.RelativeMouseMovement); + out.writeInt(Globals.RelativeMouseMovementSpeed); + out.writeInt(Globals.RelativeMouseMovementAccel); + out.writeBoolean(Globals.MultiThreadedVideo); + + out.writeInt(Globals.OptionalDataDownload.length); + for(int i = 0; i < Globals.OptionalDataDownload.length; i++) + out.writeBoolean(Globals.OptionalDataDownload[i]); + out.writeBoolean(Globals.BrokenLibCMessageShown); + out.writeInt(Globals.TouchscreenKeyboardDrawSize); + out.writeInt(p.getApplicationVersion()); + out.writeFloat(Globals.gyro_x1); + out.writeFloat(Globals.gyro_x2); + out.writeFloat(Globals.gyro_xc); + out.writeFloat(Globals.gyro_y1); + out.writeFloat(Globals.gyro_y2); + out.writeFloat(Globals.gyro_yc); + out.writeFloat(Globals.gyro_z1); + out.writeFloat(Globals.gyro_z2); + out.writeFloat(Globals.gyro_zc); + + out.writeBoolean(Globals.OuyaEmulation); + + out.close(); + settingsLoaded = true; + + } catch( FileNotFoundException e ) { + } catch( SecurityException e ) { + } catch ( IOException e ) {}; + } + + static void Load( final MainActivity p ) + { + if(settingsLoaded) // Prevent starting twice + { + return; + } + Log.i("SDL", "libSDL: Settings.Load(): enter"); + /*nativeInitKeymap(); // TODO: Disabled in SDL2 + if( p.isRunningOnOUYA() ) + nativeSetKeymapKey(KeyEvent.KEYCODE_MENU, nativeGetKeymapKey(KeyEvent.KEYCODE_BACK)); // Ouya does not have Back key, only Menu, so remap Back keycode to Menu + for( int i = 0; i < SDL_Keys.JAVA_KEYCODE_LAST; i++ ) + { + int sdlKey = nativeGetKeymapKey(i); + int idx = 0; + for(int ii = 0; ii < SDL_Keys.values.length; ii++) + if(SDL_Keys.values[ii] == sdlKey) + idx = ii; + Globals.RemapHwKeycode[i] = idx; + } + for( int i = 0; i < Globals.RemapScreenKbKeycode.length; i++ ) + { + int sdlKey = nativeGetKeymapKeyScreenKb(i); + int idx = 0; + for(int ii = 0; ii < SDL_Keys.values.length; ii++) + if(SDL_Keys.values[ii] == sdlKey) + idx = ii; + Globals.RemapScreenKbKeycode[i] = idx; + }*/ + Globals.ScreenKbControlsShown[0] = (Globals.AppNeedsArrowKeys || Globals.AppUsesJoystick); + Globals.ScreenKbControlsShown[1] = Globals.AppNeedsTextInput; + for( int i = 2; i < Globals.ScreenKbControlsShown.length; i++ ) + Globals.ScreenKbControlsShown[i] = ( i - 2 < Globals.AppTouchscreenKeyboardKeysAmount ); + if( Globals.AppUsesSecondJoystick ) + Globals.ScreenKbControlsShown[8] = true; + /*for( int i = 0; i < Globals.RemapMultitouchGestureKeycode.length; i++ ) + { + int sdlKey = nativeGetKeymapKeyMultitouchGesture(i); + int idx = 0; + for(int ii = 0; ii < SDL_Keys.values.length; ii++) + if(SDL_Keys.values[ii] == sdlKey) + idx = ii; + Globals.RemapMultitouchGestureKeycode[i] = idx; + }*/ + for( int i = 0; i < Globals.MultitouchGesturesUsed.length; i++ ) + Globals.MultitouchGesturesUsed[i] = true; + // Adjust coordinates of on-screen buttons from 800x480 + int displayX = 800; + int displayY = 480; + try { + DisplayMetrics dm = new DisplayMetrics(); + p.getWindowManager().getDefaultDisplay().getMetrics(dm); + displayX = dm.widthPixels; + displayY = dm.heightPixels; + } catch (Exception eeeee) {} + for( int i = 0; i < Globals.ScreenKbControlsLayout.length; i++ ) + { + Globals.ScreenKbControlsLayout[i][0] *= (float)displayX / 800.0f; + Globals.ScreenKbControlsLayout[i][2] *= (float)displayX / 800.0f; + Globals.ScreenKbControlsLayout[i][1] *= (float)displayY / 480.0f; + Globals.ScreenKbControlsLayout[i][3] *= (float)displayY / 480.0f; + // Make them square + int wh = Math.min( Globals.ScreenKbControlsLayout[i][2] - Globals.ScreenKbControlsLayout[i][0], Globals.ScreenKbControlsLayout[i][3] - Globals.ScreenKbControlsLayout[i][1] ); + Globals.ScreenKbControlsLayout[i][2] = Globals.ScreenKbControlsLayout[i][0] + wh; + Globals.ScreenKbControlsLayout[i][3] = Globals.ScreenKbControlsLayout[i][1] + wh; + } + + Log.i("SDL", "android.os.Build.MODEL: " + android.os.Build.MODEL); + if( (android.os.Build.MODEL.equals("GT-N7000") || android.os.Build.MODEL.equals("SGH-I717")) + && android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.GINGERBREAD_MR1 ) + { + // Samsung Galaxy Note generates a keypress when you hover a stylus over the screen, and that messes up OpenTTD dialogs + // ICS update sends events in a proper way + Globals.RemapHwKeycode[112] = SDL_1_2_Keycodes.SDLK_UNKNOWN; + } + + try { + ObjectInputStream settingsFile = new ObjectInputStream(new FileInputStream( p.getFilesDir().getAbsolutePath() + "/" + SettingsFileName )); + if( settingsFile.readInt() != SETTINGS_FILE_VERSION ) + throw new IOException(); + Globals.DownloadToSdcard = settingsFile.readBoolean(); + Globals.PhoneHasArrowKeys = settingsFile.readBoolean(); + Globals.PhoneHasTrackball = settingsFile.readBoolean(); + Globals.UseAccelerometerAsArrowKeys = settingsFile.readBoolean(); + Globals.UseTouchscreenKeyboard = settingsFile.readBoolean(); + Globals.TouchscreenKeyboardSize = settingsFile.readInt(); + Globals.AccelerometerSensitivity = settingsFile.readInt(); + Globals.AccelerometerCenterPos = settingsFile.readInt(); + Globals.TrackballDampening = settingsFile.readInt(); + Globals.AudioBufferConfig = settingsFile.readInt(); + Globals.TouchscreenKeyboardTheme = settingsFile.readInt(); + Globals.RightClickMethod = settingsFile.readInt(); + Globals.ShowScreenUnderFinger = settingsFile.readInt(); + Globals.LeftClickMethod = settingsFile.readInt(); + Globals.MoveMouseWithJoystick = settingsFile.readBoolean(); + Globals.ClickMouseWithDpad = settingsFile.readBoolean(); + Globals.ClickScreenPressure = settingsFile.readInt(); + Globals.ClickScreenTouchspotSize = settingsFile.readInt(); + Globals.KeepAspectRatio = settingsFile.readBoolean(); + Globals.MoveMouseWithJoystickSpeed = settingsFile.readInt(); + Globals.MoveMouseWithJoystickAccel = settingsFile.readInt(); + int readKeys = settingsFile.readInt(); + for( int i = 0; i < readKeys; i++ ) + { + Globals.RemapHwKeycode[i] = settingsFile.readInt(); + } + if( settingsFile.readInt() != Globals.RemapScreenKbKeycode.length ) + throw new IOException(); + for( int i = 0; i < Globals.RemapScreenKbKeycode.length; i++ ) + { + Globals.RemapScreenKbKeycode[i] = settingsFile.readInt(); + } + if( settingsFile.readInt() != Globals.ScreenKbControlsShown.length ) + throw new IOException(); + for( int i = 0; i < Globals.ScreenKbControlsShown.length; i++ ) + { + Globals.ScreenKbControlsShown[i] = settingsFile.readBoolean(); + } + Globals.TouchscreenKeyboardTransparency = settingsFile.readInt(); + if( settingsFile.readInt() != Globals.RemapMultitouchGestureKeycode.length ) + throw new IOException(); + for( int i = 0; i < Globals.RemapMultitouchGestureKeycode.length; i++ ) + { + Globals.RemapMultitouchGestureKeycode[i] = settingsFile.readInt(); + Globals.MultitouchGesturesUsed[i] = settingsFile.readBoolean(); + } + Globals.MultitouchGestureSensitivity = settingsFile.readInt(); + for( int i = 0; i < Globals.TouchscreenCalibration.length; i++ ) + Globals.TouchscreenCalibration[i] = settingsFile.readInt(); + StringBuilder b = new StringBuilder(); + int len = settingsFile.readInt(); + for( int i = 0; i < len; i++ ) + b.append( settingsFile.readChar() ); + Globals.DataDir = b.toString(); + + b = new StringBuilder(); + len = settingsFile.readInt(); + for( int i = 0; i < len; i++ ) + b.append( settingsFile.readChar() ); + Globals.CommandLine = b.toString(); + + if( settingsFile.readInt() != Globals.ScreenKbControlsLayout.length ) + throw new IOException(); + for( int i = 0; i < Globals.ScreenKbControlsLayout.length; i++ ) + for( int ii = 0; ii < 4; ii++ ) + Globals.ScreenKbControlsLayout[i][ii] = settingsFile.readInt(); + Globals.LeftClickKey = settingsFile.readInt(); + Globals.RightClickKey = settingsFile.readInt(); + Globals.VideoLinearFilter = settingsFile.readBoolean(); + Globals.LeftClickTimeout = settingsFile.readInt(); + Globals.RightClickTimeout = settingsFile.readInt(); + Globals.RelativeMouseMovement = settingsFile.readBoolean(); + Globals.RelativeMouseMovementSpeed = settingsFile.readInt(); + Globals.RelativeMouseMovementAccel = settingsFile.readInt(); + Globals.MultiThreadedVideo = settingsFile.readBoolean(); + + Globals.OptionalDataDownload = new boolean[settingsFile.readInt()]; + for(int i = 0; i < Globals.OptionalDataDownload.length; i++) + Globals.OptionalDataDownload[i] = settingsFile.readBoolean(); + Globals.BrokenLibCMessageShown = settingsFile.readBoolean(); + Globals.TouchscreenKeyboardDrawSize = settingsFile.readInt(); + int cfgVersion = settingsFile.readInt(); + Globals.gyro_x1 = settingsFile.readFloat(); + Globals.gyro_x2 = settingsFile.readFloat(); + Globals.gyro_xc = settingsFile.readFloat(); + Globals.gyro_y1 = settingsFile.readFloat(); + Globals.gyro_y2 = settingsFile.readFloat(); + Globals.gyro_yc = settingsFile.readFloat(); + Globals.gyro_z1 = settingsFile.readFloat(); + Globals.gyro_z2 = settingsFile.readFloat(); + Globals.gyro_zc = settingsFile.readFloat(); + + Globals.OuyaEmulation = settingsFile.readBoolean(); + + settingsLoaded = true; + + Log.i("SDL", "libSDL: Settings.Load(): loaded settings successfully"); + settingsFile.close(); + + Log.i("SDL", "libSDL: old cfg version " + cfgVersion + ", our version " + p.getApplicationVersion()); + if( cfgVersion != p.getApplicationVersion() ) + { + DeleteFilesOnUpgrade(p); + if( Globals.ResetSdlConfigForThisVersion ) + { + Log.i("SDL", "libSDL: old cfg version " + cfgVersion + ", our version " + p.getApplicationVersion() + " and we need to clean up config file"); + // Delete settings file, and restart the application + DeleteSdlConfigOnUpgradeAndRestart(p); + } + Save(p); + } + + return; + + } catch( FileNotFoundException e ) { + } catch( SecurityException e ) { + } catch ( IOException e ) { + DeleteFilesOnUpgrade(p); + if( Globals.ResetSdlConfigForThisVersion ) + { + Log.i("SDL", "libSDL: old cfg version unknown or too old, our version " + p.getApplicationVersion() + " and we need to clean up config file"); + DeleteSdlConfigOnUpgradeAndRestart(p); + } + }; + + if( Globals.DataDir.length() == 0 ) + { + if( !Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) ) + { + Log.i("SDL", "libSDL: SD card or external storage is not mounted (state " + Environment.getExternalStorageState() + "), switching to the internal storage."); + Globals.DownloadToSdcard = false; + } + Globals.DataDir = Globals.DownloadToSdcard ? + SdcardAppPath.getPath(p) : + p.getFilesDir().getAbsolutePath(); + if( Globals.DownloadToSdcard ) + { + // Check if data already installed into deprecated location at /sdcard/app-data/ + String[] fileList = new File(SdcardAppPath.deprecatedPath(p)).list(); + if( fileList != null ) + for( String s: fileList ) + if( s.toUpperCase().startsWith(DataDownloader.DOWNLOAD_FLAG_FILENAME.toUpperCase()) ) + Globals.DataDir = SdcardAppPath.deprecatedPath(p); + } + } + + Log.i("SDL", "libSDL: Settings.Load(): loading settings failed, running config dialog"); + p.setUpStatusLabel(); + if( checkRamSize(p) ) + SettingsMenu.showConfig(p, true); + } + + // =============================================================================================== + + public static boolean deleteRecursively(File dir) + { + if (dir.isDirectory()) { + String[] children = dir.list(); + for (int i=0; i Globals.TouchscreenCalibration[0] ) + nativeSetTouchscreenCalibration(Globals.TouchscreenCalibration[0], Globals.TouchscreenCalibration[1], + Globals.TouchscreenCalibration[2], Globals.TouchscreenCalibration[3]); + + String lang = new String(Locale.getDefault().getLanguage()); + if( Locale.getDefault().getCountry().length() > 0 ) + lang = lang + "_" + Locale.getDefault().getCountry(); + Log.i("SDL", "libSDL: setting envvar LANGUAGE to '" + lang + "'"); + nativeSetEnv( "LANG", lang ); + nativeSetEnv( "LANGUAGE", lang ); + // TODO: get current user name and set envvar USER, the API is not availalbe on Android 1.6 so I don't bother with this + nativeSetEnv( "APPDIR", p.getFilesDir().getAbsolutePath() ); + nativeSetEnv( "SECURE_STORAGE_DIR", p.getFilesDir().getAbsolutePath() ); + nativeSetEnv( "DATADIR", Globals.DataDir ); + nativeSetEnv( "UNSECURE_STORAGE_DIR", Globals.DataDir ); + nativeSetEnv( "HOME", Globals.DataDir ); + nativeSetEnv( "ANDROID_VERSION", String.valueOf(android.os.Build.VERSION.SDK_INT) ); + Log.d("SDL", "libSDL: Is running on OUYA: " + p.isRunningOnOUYA()); + if( p.isRunningOnOUYA() ) + nativeSetEnv( "OUYA", "1" ); + try { + DisplayMetrics dm = new DisplayMetrics(); + p.getWindowManager().getDefaultDisplay().getMetrics(dm); + float xx = dm.widthPixels/dm.xdpi; + float yy = dm.heightPixels/dm.ydpi; + float x = Math.max(xx, yy); + float y = Math.min(xx, yy); + float displayInches = (float)Math.sqrt( x*x + y*y ); + nativeSetEnv( "DISPLAY_SIZE", String.valueOf(displayInches) ); + nativeSetEnv( "DISPLAY_SIZE_MM", String.valueOf((int)(displayInches*25.4f)) ); + nativeSetEnv( "DISPLAY_WIDTH", String.valueOf(x) ); + nativeSetEnv( "DISPLAY_HEIGHT", String.valueOf(y) ); + nativeSetEnv( "DISPLAY_WIDTH_MM", String.valueOf((int)(x*25.4f)) ); + nativeSetEnv( "DISPLAY_HEIGHT_MM", String.valueOf((int)(y*25.4f)) ); + nativeSetEnv( "DISPLAY_RESOLUTION_WIDTH", String.valueOf(Math.max(dm.widthPixels, dm.heightPixels)) ); + nativeSetEnv( "DISPLAY_RESOLUTION_HEIGHT", String.valueOf(Math.min(dm.widthPixels, dm.heightPixels)) ); + } catch (Exception eeeee) {} + } + + static byte [] loadRaw(Activity p, int res) + { + byte [] buf = new byte[65536 * 2]; + byte [] a = new byte[65536 * 4 * 10]; // We need 2363516 bytes for the Sun theme + int written = 0; + try{ + InputStream is = new GZIPInputStream(p.getResources().openRawResource(res)); + int readed = 0; + while( (readed = is.read(buf)) >= 0 ) + { + if( written + readed > a.length ) + { + byte [] b = new byte [written + readed]; + System.arraycopy(a, 0, b, 0, written); + a = b; + } + System.arraycopy(buf, 0, a, written, readed); + written += readed; + } + } catch(Exception e) {}; + byte [] b = new byte [written]; + System.arraycopy(a, 0, b, 0, written); + return b; + } + + static void SetupTouchscreenKeyboardGraphics(Activity p) + { + if( Globals.UseTouchscreenKeyboard ) + { + if(Globals.TouchscreenKeyboardTheme < 0) + Globals.TouchscreenKeyboardTheme = 0; + if(Globals.TouchscreenKeyboardTheme > 3) + Globals.TouchscreenKeyboardTheme = 3; + + if( Globals.TouchscreenKeyboardTheme == 0 ) + { + nativeSetupScreenKeyboardButtons(loadRaw(p, R.raw.ultimatedroid)); + } + if( Globals.TouchscreenKeyboardTheme == 1 ) + { + nativeSetupScreenKeyboardButtons(loadRaw(p, R.raw.simpletheme)); + } + if( Globals.TouchscreenKeyboardTheme == 2 ) + { + nativeSetupScreenKeyboardButtons(loadRaw(p, R.raw.sun)); + } + if( Globals.TouchscreenKeyboardTheme == 3 ) + { + nativeSetupScreenKeyboardButtons(loadRaw(p, R.raw.keen)); + } + } + } + + abstract static class SdcardAppPath + { + private static SdcardAppPath get() + { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.FROYO) + return Froyo.Holder.sInstance; + else + return Dummy.Holder.sInstance; + } + public abstract String path(final Context p); + public static String deprecatedPath(final Context p) + { + return Environment.getExternalStorageDirectory().getAbsolutePath() + "/app-data/" + p.getPackageName(); + } + public static String getPath(final Context p) + { + try { + return get().path(p); + } catch(Exception e) { } + return Dummy.Holder.sInstance.path(p); + } + + private static class Froyo extends SdcardAppPath + { + private static class Holder + { + private static final Froyo sInstance = new Froyo(); + } + public String path(final Context p) + { + return p.getExternalFilesDir(null).getAbsolutePath(); + } + } + private static class Dummy extends SdcardAppPath + { + private static class Holder + { + private static final Dummy sInstance = new Dummy(); + } + public String path(final Context p) + { + return Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + p.getPackageName() + "/files"; + } + } + } + + static boolean checkRamSize(final MainActivity p) + { + try { + BufferedReader reader = new BufferedReader(new FileReader("/proc/meminfo")); + String line = null; + while( ( line = reader.readLine() ) != null ) + { + if( line.indexOf("MemTotal:") == 0 ) + { + String[] fields = line.split("[ \t]+"); + Long size = Long.parseLong(fields[1]); + Log.i("SDL", "Device RAM size: " + size / 1024 + " Mb, required minimum RAM: " + Globals.AppMinimumRAM + " Mb" ); + if( size / 1024 < Globals.AppMinimumRAM ) + { + settingsChanged = true; + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.not_enough_ram); + builder.setMessage(p.getResources().getString( R.string.not_enough_ram_size, Globals.AppMinimumRAM, (int)(size / 1024)) ); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + p.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + p.getPackageName()))); + System.exit(0); + } + }); + builder.setNegativeButton(p.getResources().getString(R.string.ignore), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + SettingsMenu.showConfig(p, true); + return; + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + p.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + p.getPackageName()))); + System.exit(0); + } + }); + final AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + return false; + } + } + } + } catch ( Exception e ) { + Log.i("SDL", "Error: cannot parse /proc/meminfo: " + e.toString()); + } + return true; + } + + private static native void nativeSetTrackballUsed(); + private static native void nativeSetTrackballDampening(int value); + private static native void nativeSetAccelerometerSettings(int sensitivity, int centerPos); + private static native void nativeSetMouseUsed(int RightClickMethod, int ShowScreenUnderFinger, int LeftClickMethod, + int MoveMouseWithJoystick, int ClickMouseWithDpad, int MaxForce, int MaxRadius, + int MoveMouseWithJoystickSpeed, int MoveMouseWithJoystickAccel, + int leftClickKeycode, int rightClickKeycode, + int leftClickTimeout, int rightClickTimeout, + int relativeMovement, int relativeMovementSpeed, + int relativeMovementAccel, int showMouseCursor); + private static native void nativeSetJoystickUsed(int firstJoystickUsed, int secondJoystickUsed); + private static native void nativeSetAccelerometerUsed(); + private static native void nativeSetMultitouchUsed(); + private static native void nativeSetTouchscreenKeyboardUsed(); + private static native void nativeSetVideoLinearFilter(); + private static native void nativeSetVideoDepth(int bpp, int gles2); + private static native void nativeSetCompatibilityHacks(); + private static native void nativeSetVideoMultithreaded(); + private static native void nativeSetVideoForceSoftwareMode(); + private static native void nativeSetupScreenKeyboard(int size, int drawsize, int theme, int nbuttonsAutoFire, int transparency); + private static native void nativeSetupScreenKeyboardButtons(byte[] img); + private static native void nativeInitKeymap(); + private static native int nativeGetKeymapKey(int key); + private static native void nativeSetKeymapKey(int javakey, int key); + private static native int nativeGetKeymapKeyScreenKb(int keynum); + private static native void nativeSetKeymapKeyScreenKb(int keynum, int key); + private static native void nativeSetScreenKbKeyUsed(int keynum, int used); + private static native void nativeSetScreenKbKeyLayout(int keynum, int x1, int y1, int x2, int y2); + private static native int nativeGetKeymapKeyMultitouchGesture(int keynum); + private static native void nativeSetKeymapKeyMultitouchGesture(int keynum, int key); + private static native void nativeSetMultitouchGestureSensitivity(int sensitivity); + private static native void nativeSetTouchscreenCalibration(int x1, int y1, int x2, int y2); + public static native void nativeSetEnv(final String name, final String value); + public static native int nativeChmod(final String name, int mode); + public static native void nativeChdir(final String dir); +} + diff --git a/project/javaSDL2/SettingsMenu.java b/project/javaSDL2/SettingsMenu.java new file mode 100644 index 000000000..544eb0977 --- /dev/null +++ b/project/javaSDL2/SettingsMenu.java @@ -0,0 +1,257 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import android.widget.TextView; +import android.util.Log; +import java.io.*; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Environment; +import android.os.StatFs; +import java.util.Locale; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.GZIPInputStream; +import java.util.Collections; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import java.lang.String; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.FrameLayout; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap; +import android.widget.TextView; +import android.widget.EditText; +import android.widget.ScrollView; +import android.widget.Button; +import android.view.View; +import android.widget.LinearLayout; +import android.text.Editable; +import android.text.SpannedString; +import android.content.Intent; +import android.app.PendingIntent; +import android.app.AlarmManager; +import android.util.DisplayMetrics; +import android.net.Uri; +import java.util.concurrent.Semaphore; +import java.util.Arrays; +import android.graphics.Color; +import android.hardware.SensorEventListener; +import android.hardware.SensorEvent; +import android.hardware.Sensor; +import android.widget.Toast; + + +class SettingsMenu +{ + public static abstract class Menu + { + // Should be overridden by children + abstract void run(final MainActivity p); + abstract String title(final MainActivity p); + boolean enabled() + { + return true; + } + // Should not be overridden + boolean enabledOrHidden() + { + for( Menu m: Globals.HiddenMenuOptions ) + { + if( m.getClass().getName().equals( this.getClass().getName() ) ) + return false; + } + return enabled(); + } + void showMenuOptionsList(final MainActivity p, final Menu[] list) + { + menuStack.add(this); + ArrayList items = new ArrayList (); + for( Menu m: list ) + { + if(m.enabledOrHidden()) + items.add(m.title(p)); + } + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(title(p)); + builder.setItems(items.toArray(new CharSequence[0]), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + int selected = 0; + + for( Menu m: list ) + { + if(m.enabledOrHidden()) + { + if( selected == item ) + { + m.run(p); + return; + } + selected ++; + } + } + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBackOuterMenu(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static ArrayList

menuStack = new ArrayList (); + + public static void showConfig(final MainActivity p, final boolean firstStart) + { + Settings.settingsChanged = true; + if( Globals.OptionalDataDownload == null ) + { + String downloads[] = Globals.DataDownloadUrl; + Globals.OptionalDataDownload = new boolean[downloads.length]; + boolean oldFormat = true; + for( int i = 0; i < downloads.length; i++ ) + { + if( downloads[i].indexOf("!") == 0 ) + { + Globals.OptionalDataDownload[i] = true; + oldFormat = false; + } + } + if( oldFormat ) + Globals.OptionalDataDownload[0] = true; + } + + if(!firstStart) + new MainMenu().run(p); + else + { + if( Globals.StartupMenuButtonTimeout > 0 ) // If we did not disable startup menu altogether + { + for( Menu m: Globals.FirstStartMenuOptions ) + { + boolean hidden = false; + for( Menu m1: Globals.HiddenMenuOptions ) + { + if( m1.getClass().getName().equals( m.getClass().getName() ) ) + hidden = true; + } + if( ! hidden ) + menuStack.add(0, m); + } + } + goBack(p); + } + } + + static void goBack(final MainActivity p) + { + if(menuStack.isEmpty()) + { + Settings.Save(p); + p.startDownloader(); + } + else + { + Menu c = menuStack.remove(menuStack.size() - 1); + c.run(p); + } + } + + static void goBackOuterMenu(final MainActivity p) + { + if(!menuStack.isEmpty()) + menuStack.remove(menuStack.size() - 1); + goBack(p); + } + + static class OkButton extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.ok); + } + void run (final MainActivity p) + { + goBackOuterMenu(p); + } + } + + static class DummyMenu extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.ok); + } + void run (final MainActivity p) + { + goBack(p); + } + } + + static class MainMenu extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.device_config); + } + void run (final MainActivity p) + { + Menu options[] = + { + new SettingsMenuMisc.DownloadConfig(), + new SettingsMenuMisc.OptionalDownloadConfig(false), + new SettingsMenuKeyboard.KeyboardConfigMainMenu(), + new SettingsMenuMouse.MouseConfigMainMenu(), + new SettingsMenuMisc.GyroscopeCalibration(), + new SettingsMenuMisc.AudioConfig(), + new SettingsMenuKeyboard.RemapHwKeysConfig(), + new SettingsMenuKeyboard.ScreenGesturesConfig(), + new SettingsMenuMisc.VideoSettingsConfig(), + new SettingsMenuMisc.ResetToDefaultsConfig(), + new OkButton(), + }; + showMenuOptionsList(p, options); + } + } +} diff --git a/project/javaSDL2/SettingsMenuKeyboard.java b/project/javaSDL2/SettingsMenuKeyboard.java new file mode 100644 index 000000000..d0d67148e --- /dev/null +++ b/project/javaSDL2/SettingsMenuKeyboard.java @@ -0,0 +1,843 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import android.widget.TextView; +import android.util.Log; +import java.io.*; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Environment; +import android.os.StatFs; +import java.util.Locale; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.GZIPInputStream; +import java.util.Collections; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import java.lang.String; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.FrameLayout; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap; +import android.widget.TextView; +import android.widget.EditText; +import android.widget.ScrollView; +import android.widget.Button; +import android.view.View; +import android.widget.LinearLayout; +import android.text.Editable; +import android.text.SpannedString; +import android.content.Intent; +import android.app.PendingIntent; +import android.app.AlarmManager; +import android.util.DisplayMetrics; +import android.net.Uri; +import java.util.concurrent.Semaphore; +import java.util.Arrays; +import android.graphics.Color; +import android.hardware.SensorEventListener; +import android.hardware.SensorEvent; +import android.hardware.Sensor; +import android.widget.Toast; + + +class SettingsMenuKeyboard extends SettingsMenu +{ + static class KeyboardConfigMainMenu extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.controls_screenkb); + } + boolean enabled() + { + return Globals.UseTouchscreenKeyboard; + } + void run (final MainActivity p) + { + Menu options[] = + { + new ScreenKeyboardThemeConfig(), + new ScreenKeyboardSizeConfig(), + new ScreenKeyboardDrawSizeConfig(), + new ScreenKeyboardTransparencyConfig(), + new RemapScreenKbConfig(), + new CustomizeScreenKbLayout(), + new OkButton(), + }; + showMenuOptionsList(p, options); + } + } + + static class ScreenKeyboardSizeConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.controls_screenkb_size); + } + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.controls_screenkb_large), + p.getResources().getString(R.string.controls_screenkb_medium), + p.getResources().getString(R.string.controls_screenkb_small), + p.getResources().getString(R.string.controls_screenkb_tiny), + p.getResources().getString(R.string.controls_screenkb_custom) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.controls_screenkb_size)); + builder.setSingleChoiceItems(items, Globals.TouchscreenKeyboardSize, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.TouchscreenKeyboardSize = item; + dialog.dismiss(); + if( Globals.TouchscreenKeyboardSize == Globals.TOUCHSCREEN_KEYBOARD_CUSTOM ) + new CustomizeScreenKbLayout().run(p); + else + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class ScreenKeyboardDrawSizeConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.controls_screenkb_drawsize); + } + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.controls_screenkb_large), + p.getResources().getString(R.string.controls_screenkb_medium), + p.getResources().getString(R.string.controls_screenkb_small), + p.getResources().getString(R.string.controls_screenkb_tiny) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.controls_screenkb_drawsize)); + builder.setSingleChoiceItems(items, Globals.TouchscreenKeyboardDrawSize, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.TouchscreenKeyboardDrawSize = item; + + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class ScreenKeyboardThemeConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.controls_screenkb_theme); + } + void run (final MainActivity p) + { + final CharSequence[] items = { + p.getResources().getString(R.string.controls_screenkb_by, "Ultimate Droid", "Sean Stieber"), + p.getResources().getString(R.string.controls_screenkb_by, "Simple Theme", "Beholder"), + p.getResources().getString(R.string.controls_screenkb_by, "Sun", "Sirea"), + p.getResources().getString(R.string.controls_screenkb_by, "Keen", "Gerstrong") + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.controls_screenkb_theme)); + builder.setSingleChoiceItems(items, Globals.TouchscreenKeyboardTheme, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.TouchscreenKeyboardTheme = item; + + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class ScreenKeyboardTransparencyConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.controls_screenkb_transparency); + } + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.controls_screenkb_trans_0), + p.getResources().getString(R.string.controls_screenkb_trans_1), + p.getResources().getString(R.string.controls_screenkb_trans_2), + p.getResources().getString(R.string.controls_screenkb_trans_3), + p.getResources().getString(R.string.controls_screenkb_trans_4) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.controls_screenkb_transparency)); + builder.setSingleChoiceItems(items, Globals.TouchscreenKeyboardTransparency, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.TouchscreenKeyboardTransparency = item; + + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class RemapHwKeysConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.remap_hwkeys); + } + void run (final MainActivity p) + { + p.setText(p.getResources().getString(R.string.remap_hwkeys_press)); + p.keyListener = new KeyRemapTool(p); + } + + public static class KeyRemapTool implements MainActivity.KeyEventsListener + { + MainActivity p; + public KeyRemapTool(MainActivity _p) + { + p = _p; + } + + public void onKeyEvent(final int keyCode) + { + p.keyListener = null; + int keyIndex = keyCode; + if( keyIndex < 0 ) + keyIndex = 0; + if( keyIndex > SDL_Keys.JAVA_KEYCODE_LAST ) + keyIndex = 0; + + final int KeyIndexFinal = keyIndex; + CharSequence[] items = { + SDL_Keys.names[Globals.RemapScreenKbKeycode[0]], + SDL_Keys.names[Globals.RemapScreenKbKeycode[1]], + SDL_Keys.names[Globals.RemapScreenKbKeycode[2]], + SDL_Keys.names[Globals.RemapScreenKbKeycode[3]], + SDL_Keys.names[Globals.RemapScreenKbKeycode[4]], + SDL_Keys.names[Globals.RemapScreenKbKeycode[5]], + p.getResources().getString(R.string.remap_hwkeys_select_more_keys), + }; + + for( int i = 0; i < Math.min(6, Globals.AppTouchscreenKeyboardKeysNames.length); i++ ) + items[i] = Globals.AppTouchscreenKeyboardKeysNames[i].replace("_", " "); + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.remap_hwkeys_select_simple); + builder.setItems(items, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + if( item >= 6 ) + ShowAllKeys(KeyIndexFinal); + else + { + Globals.RemapHwKeycode[KeyIndexFinal] = Globals.RemapScreenKbKeycode[item]; + goBack(p); + } + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + public void ShowAllKeys(final int KeyIndex) + { + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.remap_hwkeys_select); + builder.setSingleChoiceItems(SDL_Keys.namesSorted, SDL_Keys.namesSortedBackIdx[Globals.RemapHwKeycode[KeyIndex]], new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RemapHwKeycode[KeyIndex] = SDL_Keys.namesSortedIdx[item]; + + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + } + + static class RemapScreenKbConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.remap_screenkb); + } + //boolean enabled() { return true; }; + void run (final MainActivity p) + { + CharSequence[] items = { + p.getResources().getString(R.string.remap_screenkb_joystick), + p.getResources().getString(R.string.remap_screenkb_button_text), + p.getResources().getString(R.string.remap_screenkb_button) + " 1", + p.getResources().getString(R.string.remap_screenkb_button) + " 2", + p.getResources().getString(R.string.remap_screenkb_button) + " 3", + p.getResources().getString(R.string.remap_screenkb_button) + " 4", + p.getResources().getString(R.string.remap_screenkb_button) + " 5", + p.getResources().getString(R.string.remap_screenkb_button) + " 6", + }; + + boolean defaults[] = Arrays.copyOf(Globals.ScreenKbControlsShown, Globals.ScreenKbControlsShown.length); + if( Globals.AppUsesSecondJoystick ) + { + items = Arrays.copyOf(items, items.length + 1); + items[items.length - 1] = p.getResources().getString(R.string.remap_screenkb_joystick) + " 2"; + defaults = Arrays.copyOf(defaults, defaults.length + 1); + defaults[defaults.length - 1] = true; + } + + for( int i = 0; i < Math.min(6, Globals.AppTouchscreenKeyboardKeysNames.length); i++ ) + items[i+2] = items[i+2] + " - " + Globals.AppTouchscreenKeyboardKeysNames[i].replace("_", " "); + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.remap_screenkb)); + builder.setMultiChoiceItems(items, defaults, new DialogInterface.OnMultiChoiceClickListener() + { + public void onClick(DialogInterface dialog, int item, boolean isChecked) + { + Globals.ScreenKbControlsShown[item] = isChecked; + } + }); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + showRemapScreenKbConfig2(p, 0); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showRemapScreenKbConfig2(final MainActivity p, final int currentButton) + { + CharSequence[] items = { + p.getResources().getString(R.string.remap_screenkb_button) + " 1", + p.getResources().getString(R.string.remap_screenkb_button) + " 2", + p.getResources().getString(R.string.remap_screenkb_button) + " 3", + p.getResources().getString(R.string.remap_screenkb_button) + " 4", + p.getResources().getString(R.string.remap_screenkb_button) + " 5", + p.getResources().getString(R.string.remap_screenkb_button) + " 6", + }; + + for( int i = 0; i < Math.min(6, Globals.AppTouchscreenKeyboardKeysNames.length); i++ ) + items[i] = items[i] + " - " + Globals.AppTouchscreenKeyboardKeysNames[i].replace("_", " "); + + if( currentButton >= Globals.RemapScreenKbKeycode.length ) + { + goBack(p); + return; + } + if( ! Globals.ScreenKbControlsShown[currentButton + 2] ) + { + showRemapScreenKbConfig2(p, currentButton + 1); + return; + } + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(items[currentButton]); + builder.setSingleChoiceItems(SDL_Keys.namesSorted, SDL_Keys.namesSortedBackIdx[Globals.RemapScreenKbKeycode[currentButton]], new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RemapScreenKbKeycode[currentButton] = SDL_Keys.namesSortedIdx[item]; + + dialog.dismiss(); + showRemapScreenKbConfig2(p, currentButton + 1); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class ScreenGesturesConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.remap_screenkb_button_gestures); + } + //boolean enabled() { return true; }; + void run (final MainActivity p) + { + CharSequence[] items = { + p.getResources().getString(R.string.remap_screenkb_button_zoomin), + p.getResources().getString(R.string.remap_screenkb_button_zoomout), + p.getResources().getString(R.string.remap_screenkb_button_rotateleft), + p.getResources().getString(R.string.remap_screenkb_button_rotateright), + }; + + boolean defaults[] = { + Globals.MultitouchGesturesUsed[0], + Globals.MultitouchGesturesUsed[1], + Globals.MultitouchGesturesUsed[2], + Globals.MultitouchGesturesUsed[3], + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.remap_screenkb_button_gestures)); + builder.setMultiChoiceItems(items, defaults, new DialogInterface.OnMultiChoiceClickListener() + { + public void onClick(DialogInterface dialog, int item, boolean isChecked) + { + Globals.MultitouchGesturesUsed[item] = isChecked; + } + }); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + showScreenGesturesConfig2(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showScreenGesturesConfig2(final MainActivity p) + { + final CharSequence[] items = { + p.getResources().getString(R.string.accel_slow), + p.getResources().getString(R.string.accel_medium), + p.getResources().getString(R.string.accel_fast), + p.getResources().getString(R.string.accel_veryfast) + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.remap_screenkb_button_gestures_sensitivity); + builder.setSingleChoiceItems(items, Globals.MultitouchGestureSensitivity, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.MultitouchGestureSensitivity = item; + + dialog.dismiss(); + showScreenGesturesConfig3(p, 0); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showScreenGesturesConfig3(final MainActivity p, final int currentButton) + { + CharSequence[] items = { + p.getResources().getString(R.string.remap_screenkb_button_zoomin), + p.getResources().getString(R.string.remap_screenkb_button_zoomout), + p.getResources().getString(R.string.remap_screenkb_button_rotateleft), + p.getResources().getString(R.string.remap_screenkb_button_rotateright), + }; + + if( currentButton >= Globals.RemapMultitouchGestureKeycode.length ) + { + goBack(p); + return; + } + if( ! Globals.MultitouchGesturesUsed[currentButton] ) + { + showScreenGesturesConfig3(p, currentButton + 1); + return; + } + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(items[currentButton]); + builder.setSingleChoiceItems(SDL_Keys.namesSorted, SDL_Keys.namesSortedBackIdx[Globals.RemapMultitouchGestureKeycode[currentButton]], new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RemapMultitouchGestureKeycode[currentButton] = SDL_Keys.namesSortedIdx[item]; + + dialog.dismiss(); + showScreenGesturesConfig3(p, currentButton + 1); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class CustomizeScreenKbLayout extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.screenkb_custom_layout); + } + //boolean enabled() { return true; }; + void run (final MainActivity p) + { + p.setText(p.getResources().getString(R.string.screenkb_custom_layout_help)); + CustomizeScreenKbLayoutTool tool = new CustomizeScreenKbLayoutTool(p); + p.touchListener = tool; + p.keyListener = tool; + Globals.TouchscreenKeyboardSize = Globals.TOUCHSCREEN_KEYBOARD_CUSTOM; + } + + static class CustomizeScreenKbLayoutTool implements MainActivity.TouchEventsListener, MainActivity.KeyEventsListener + { + MainActivity p; + FrameLayout layout = null; + ImageView imgs[] = new ImageView[Globals.ScreenKbControlsLayout.length]; + Bitmap bmps[] = new Bitmap[Globals.ScreenKbControlsLayout.length]; + ImageView boundary = null; + Bitmap boundaryBmp = null; + int currentButton = 0; + int buttons[] = { + R.drawable.dpad, + R.drawable.keyboard, + R.drawable.b1, + R.drawable.b2, + R.drawable.b3, + R.drawable.b4, + R.drawable.b5, + R.drawable.b6, + R.drawable.dpad + }; + int oldX = 0, oldY = 0; + boolean resizing = false; + + public CustomizeScreenKbLayoutTool(MainActivity _p) + { + p = _p; + layout = new FrameLayout(p); + p.getVideoLayout().addView(layout); + boundary = new ImageView(p); + boundary.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + boundary.setScaleType(ImageView.ScaleType.MATRIX); + boundaryBmp = BitmapFactory.decodeResource( p.getResources(), R.drawable.rectangle ); + boundary.setImageBitmap(boundaryBmp); + layout.addView(boundary); + currentButton = -1; + if( Globals.TouchscreenKeyboardTheme == 2 ) + { + buttons = new int[] { + R.drawable.sun_dpad, + R.drawable.sun_keyboard, + R.drawable.sun_b1, + R.drawable.sun_b2, + R.drawable.sun_b3, + R.drawable.sun_b4, + R.drawable.sun_b5, + R.drawable.sun_b6, + R.drawable.sun_dpad + }; + } + + int displayX = 800; + int displayY = 480; + try { + DisplayMetrics dm = new DisplayMetrics(); + p.getWindowManager().getDefaultDisplay().getMetrics(dm); + displayX = dm.widthPixels; + displayY = dm.heightPixels; + } catch (Exception eeeee) {} + + for( int i = 0; i < Globals.ScreenKbControlsLayout.length; i++ ) + { + if( ! Globals.ScreenKbControlsShown[i] ) + continue; + if( currentButton == -1 ) + currentButton = i; + //Log.i("SDL", "Screen kb button " + i + " coords " + Globals.ScreenKbControlsLayout[i][0] + ":" + Globals.ScreenKbControlsLayout[i][1] + ":" + Globals.ScreenKbControlsLayout[i][2] + ":" + Globals.ScreenKbControlsLayout[i][3] ); + // Check if the button is off screen edge or shrunk to zero + if( Globals.ScreenKbControlsLayout[i][0] > Globals.ScreenKbControlsLayout[i][2] - displayY/12 ) + Globals.ScreenKbControlsLayout[i][0] = Globals.ScreenKbControlsLayout[i][2] - displayY/12; + if( Globals.ScreenKbControlsLayout[i][1] > Globals.ScreenKbControlsLayout[i][3] - displayY/12 ) + Globals.ScreenKbControlsLayout[i][1] = Globals.ScreenKbControlsLayout[i][3] - displayY/12; + if( Globals.ScreenKbControlsLayout[i][0] < Globals.ScreenKbControlsLayout[i][2] - displayY*2/3 ) + Globals.ScreenKbControlsLayout[i][0] = Globals.ScreenKbControlsLayout[i][2] - displayY*2/3; + if( Globals.ScreenKbControlsLayout[i][1] < Globals.ScreenKbControlsLayout[i][3] - displayY*2/3 ) + Globals.ScreenKbControlsLayout[i][1] = Globals.ScreenKbControlsLayout[i][3] - displayY*2/3; + if( Globals.ScreenKbControlsLayout[i][0] < 0 ) + { + Globals.ScreenKbControlsLayout[i][2] += -Globals.ScreenKbControlsLayout[i][0]; + Globals.ScreenKbControlsLayout[i][0] = 0; + } + if( Globals.ScreenKbControlsLayout[i][2] > displayX ) + { + Globals.ScreenKbControlsLayout[i][0] -= Globals.ScreenKbControlsLayout[i][2] - displayX; + Globals.ScreenKbControlsLayout[i][2] = displayX; + } + if( Globals.ScreenKbControlsLayout[i][1] < 0 ) + { + Globals.ScreenKbControlsLayout[i][3] += -Globals.ScreenKbControlsLayout[i][1]; + Globals.ScreenKbControlsLayout[i][1] = 0; + } + if( Globals.ScreenKbControlsLayout[i][3] > displayY ) + { + Globals.ScreenKbControlsLayout[i][1] -= Globals.ScreenKbControlsLayout[i][3] - displayY; + Globals.ScreenKbControlsLayout[i][3] = displayY; + } + //Log.i("SDL", "After bounds check coords " + Globals.ScreenKbControlsLayout[i][0] + ":" + Globals.ScreenKbControlsLayout[i][1] + ":" + Globals.ScreenKbControlsLayout[i][2] + ":" + Globals.ScreenKbControlsLayout[i][3] ); + + imgs[i] = new ImageView(p); + imgs[i].setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + imgs[i].setScaleType(ImageView.ScaleType.MATRIX); + bmps[i] = BitmapFactory.decodeResource( p.getResources(), buttons[i] ); + imgs[i].setImageBitmap(bmps[i]); + imgs[i].setAlpha(128); + layout.addView(imgs[i]); + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmps[i].getWidth(), bmps[i].getHeight()); + RectF dst = new RectF(Globals.ScreenKbControlsLayout[i][0], Globals.ScreenKbControlsLayout[i][1], + Globals.ScreenKbControlsLayout[i][2], Globals.ScreenKbControlsLayout[i][3]); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + imgs[i].setImageMatrix(m); + } + boundary.bringToFront(); + if( currentButton == -1 ) + onKeyEvent( KeyEvent.KEYCODE_BACK ); // All buttons disabled - do not show anything + else + setupButton(currentButton); + } + + void setupButton(int i) + { + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmps[i].getWidth(), bmps[i].getHeight()); + RectF dst = new RectF(Globals.ScreenKbControlsLayout[i][0], Globals.ScreenKbControlsLayout[i][1], + Globals.ScreenKbControlsLayout[i][2], Globals.ScreenKbControlsLayout[i][3]); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + imgs[i].setImageMatrix(m); + m = new Matrix(); + src = new RectF(0, 0, boundaryBmp.getWidth(), boundaryBmp.getHeight()); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + boundary.setImageMatrix(m); + String buttonText = ""; + if( i >= 2 && i <= 7 ) + buttonText = p.getResources().getString(R.string.remap_screenkb_button) + (i - 2); + if( i >= 2 && i - 2 < Globals.AppTouchscreenKeyboardKeysNames.length ) + buttonText = Globals.AppTouchscreenKeyboardKeysNames[i - 2].replace("_", " "); + if( i == 0 ) + buttonText = "Joystick"; + if( i == 1 ) + buttonText = "Text input"; + if( i == 8 ) + buttonText = "Joystick 2"; + p.setText(p.getResources().getString(R.string.screenkb_custom_layout_help) + "\n" + buttonText); + } + + public void onTouchEvent(final MotionEvent ev) + { + if( ev.getAction() == MotionEvent.ACTION_DOWN ) + { + oldX = (int)ev.getX(); + oldY = (int)ev.getY(); + resizing = true; + for( int i = 0; i < Globals.ScreenKbControlsLayout.length; i++ ) + { + if( ! Globals.ScreenKbControlsShown[i] ) + continue; + if( Globals.ScreenKbControlsLayout[i][0] <= oldX && + Globals.ScreenKbControlsLayout[i][2] >= oldX && + Globals.ScreenKbControlsLayout[i][1] <= oldY && + Globals.ScreenKbControlsLayout[i][3] >= oldY ) + { + currentButton = i; + setupButton(currentButton); + resizing = false; + break; + } + } + } + if( ev.getAction() == MotionEvent.ACTION_MOVE ) + { + int dx = (int)ev.getX() - oldX; + int dy = (int)ev.getY() - oldY; + if( resizing ) + { + // Resize slowly, with 1/3 of movement speed + dx /= 6; + dy /= 6; + if( Globals.ScreenKbControlsLayout[currentButton][0] <= Globals.ScreenKbControlsLayout[currentButton][2] + dx*2 ) + { + Globals.ScreenKbControlsLayout[currentButton][0] -= dx; + Globals.ScreenKbControlsLayout[currentButton][2] += dx; + } + if( Globals.ScreenKbControlsLayout[currentButton][1] <= Globals.ScreenKbControlsLayout[currentButton][3] + dy*2 ) + { + Globals.ScreenKbControlsLayout[currentButton][1] += dy; + Globals.ScreenKbControlsLayout[currentButton][3] -= dy; + } + dx *= 6; + dy *= 6; + } + else + { + Globals.ScreenKbControlsLayout[currentButton][0] += dx; + Globals.ScreenKbControlsLayout[currentButton][2] += dx; + Globals.ScreenKbControlsLayout[currentButton][1] += dy; + Globals.ScreenKbControlsLayout[currentButton][3] += dy; + } + oldX += dx; + oldY += dy; + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmps[currentButton].getWidth(), bmps[currentButton].getHeight()); + RectF dst = new RectF(Globals.ScreenKbControlsLayout[currentButton][0], Globals.ScreenKbControlsLayout[currentButton][1], + Globals.ScreenKbControlsLayout[currentButton][2], Globals.ScreenKbControlsLayout[currentButton][3]); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + imgs[currentButton].setImageMatrix(m); + m = new Matrix(); + src = new RectF(0, 0, boundaryBmp.getWidth(), boundaryBmp.getHeight()); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + boundary.setImageMatrix(m); + } + } + + public void onKeyEvent(final int keyCode) + { + if( keyCode == KeyEvent.KEYCODE_BACK ) + { + p.getVideoLayout().removeView(layout); + layout = null; + p.touchListener = null; + p.keyListener = null; + goBack(p); + } + } + } + } +} + diff --git a/project/javaSDL2/SettingsMenuMisc.java b/project/javaSDL2/SettingsMenuMisc.java new file mode 100644 index 000000000..2e9e30fd2 --- /dev/null +++ b/project/javaSDL2/SettingsMenuMisc.java @@ -0,0 +1,755 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import android.widget.TextView; +import android.util.Log; +import java.io.*; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Environment; +import android.os.StatFs; +import java.util.Locale; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.GZIPInputStream; +import java.util.Collections; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import java.lang.String; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.FrameLayout; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap; +import android.widget.TextView; +import android.widget.EditText; +import android.widget.ScrollView; +import android.widget.Button; +import android.widget.Scroller; +import android.view.View; +import android.view.Gravity; +import android.widget.LinearLayout; +import android.text.Editable; +import android.text.SpannedString; +import android.content.Intent; +import android.app.PendingIntent; +import android.app.AlarmManager; +import android.util.DisplayMetrics; +import android.net.Uri; +import java.util.concurrent.Semaphore; +import java.util.Arrays; +import android.graphics.Color; +import android.hardware.SensorEventListener; +import android.hardware.SensorEvent; +import android.hardware.Sensor; +import android.widget.Toast; + + +class SettingsMenuMisc extends SettingsMenu +{ + static class DownloadConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.storage_question); + } + void run (final MainActivity p) + { + long freeSdcard = 0; + long freePhone = 0; + try + { + StatFs sdcard = new StatFs(Environment.getExternalStorageDirectory().getPath()); + StatFs phone = new StatFs(Environment.getDataDirectory().getPath()); + freeSdcard = (long)sdcard.getAvailableBlocks() * sdcard.getBlockSize() / 1024 / 1024; + freePhone = (long)phone.getAvailableBlocks() * phone.getBlockSize() / 1024 / 1024; + } + catch(Exception e) {} + + final CharSequence[] items = { p.getResources().getString(R.string.storage_phone, freePhone), + p.getResources().getString(R.string.storage_sd, freeSdcard), + p.getResources().getString(R.string.storage_custom) }; + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.storage_question)); + builder.setSingleChoiceItems(items, Globals.DownloadToSdcard ? 1 : 0, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + + if( item == 2 ) + showCustomDownloadDirConfig(p); + else + { + Globals.DownloadToSdcard = (item != 0); + Globals.DataDir = Globals.DownloadToSdcard ? + Settings.SdcardAppPath.getPath(p) : + p.getFilesDir().getAbsolutePath(); + goBack(p); + } + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + static void showCustomDownloadDirConfig(final MainActivity p) + { + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.storage_custom)); + + final EditText edit = new EditText(p); + edit.setFocusableInTouchMode(true); + edit.setFocusable(true); + edit.setText(Globals.DataDir); + builder.setView(edit); + + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.DataDir = edit.getText().toString(); + dialog.dismiss(); + showCommandLineConfig(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + static void showCommandLineConfig(final MainActivity p) + { + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.storage_commandline)); + + final EditText edit = new EditText(p); + edit.setFocusableInTouchMode(true); + edit.setFocusable(true); + edit.setText(Globals.CommandLine); + builder.setView(edit); + + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.CommandLine = edit.getText().toString(); + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class OptionalDownloadConfig extends Menu + { + boolean firstStart = false; + OptionalDownloadConfig() + { + firstStart = true; + } + OptionalDownloadConfig(boolean firstStart) + { + this.firstStart = firstStart; + } + String title(final MainActivity p) + { + return p.getResources().getString(R.string.downloads); + } + void run (final MainActivity p) + { + String [] downloadFiles = Globals.DataDownloadUrl; + final boolean [] mandatory = new boolean[downloadFiles.length]; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.downloads)); + + CharSequence[] items = new CharSequence[downloadFiles.length]; + for(int i = 0; i < downloadFiles.length; i++ ) + { + items[i] = new String(downloadFiles[i].split("[|]")[0]); + if( items[i].toString().indexOf("!") == 0 ) + items[i] = items[i].toString().substring(1); + if( items[i].toString().indexOf("!") == 0 ) + { + items[i] = items[i].toString().substring(1); + mandatory[i] = true; + } + } + + if( Globals.OptionalDataDownload == null || Globals.OptionalDataDownload.length != items.length ) + { + Globals.OptionalDataDownload = new boolean[downloadFiles.length]; + boolean oldFormat = true; + for( int i = 0; i < downloadFiles.length; i++ ) + { + if( downloadFiles[i].indexOf("!") == 0 ) + { + Globals.OptionalDataDownload[i] = true; + oldFormat = false; + } + } + if( oldFormat ) + { + Globals.OptionalDataDownload[0] = true; + mandatory[0] = true; + } + } + + builder.setMultiChoiceItems(items, Globals.OptionalDataDownload, new DialogInterface.OnMultiChoiceClickListener() + { + public void onClick(DialogInterface dialog, int item, boolean isChecked) + { + Globals.OptionalDataDownload[item] = isChecked; + if( mandatory[item] && !isChecked ) + { + Globals.OptionalDataDownload[item] = true; + ((AlertDialog)dialog).getListView().setItemChecked(item, true); + } + } + }); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + goBack(p); + } + }); + if( firstStart ) + { + builder.setNegativeButton(p.getResources().getString(R.string.show_more_options), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + menuStack.clear(); + new MainMenu().run(p); + } + }); + } + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class AudioConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.audiobuf_question); + } + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.audiobuf_verysmall), + p.getResources().getString(R.string.audiobuf_small), + p.getResources().getString(R.string.audiobuf_medium), + p.getResources().getString(R.string.audiobuf_large) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.audiobuf_question); + builder.setSingleChoiceItems(items, Globals.AudioBufferConfig, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.AudioBufferConfig = item; + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class VideoSettingsConfig extends Menu + { + static int debugMenuShowCount = 0; + String title(final MainActivity p) + { + return p.getResources().getString(R.string.video); + } + //boolean enabled() { return true; }; + void run (final MainActivity p) + { + debugMenuShowCount++; + CharSequence[] items = { + p.getResources().getString(R.string.pointandclick_keepaspectratio), + p.getResources().getString(R.string.video_smooth) + }; + boolean defaults[] = { + Globals.KeepAspectRatio, + Globals.VideoLinearFilter + }; + + if(Globals.SwVideoMode && !Globals.CompatibilityHacksVideo) + { + CharSequence[] items2 = { + p.getResources().getString(R.string.pointandclick_keepaspectratio), + p.getResources().getString(R.string.video_smooth), + p.getResources().getString(R.string.video_separatethread), + }; + boolean defaults2[] = { + Globals.KeepAspectRatio, + Globals.VideoLinearFilter, + Globals.MultiThreadedVideo + }; + items = items2; + defaults = defaults2; + } + + if(Globals.Using_SDL_1_3) + { + CharSequence[] items2 = { + p.getResources().getString(R.string.pointandclick_keepaspectratio), + }; + boolean defaults2[] = { + Globals.KeepAspectRatio, + }; + items = items2; + defaults = defaults2; + } + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.video)); + builder.setMultiChoiceItems(items, defaults, new DialogInterface.OnMultiChoiceClickListener() + { + public void onClick(DialogInterface dialog, int item, boolean isChecked) + { + if( item == 0 ) + Globals.KeepAspectRatio = isChecked; + if( item == 1 ) + Globals.VideoLinearFilter = isChecked; + if( item == 2 ) + Globals.MultiThreadedVideo = isChecked; + } + }); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + if( debugMenuShowCount > 5 || Globals.OuyaEmulation ) + showDebugMenu(p); + else + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + void showDebugMenu (final MainActivity p) + { + CharSequence[] items = { + "OUYA emulation" + }; + boolean defaults[] = { + Globals.OuyaEmulation, + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle("Debug settings"); + builder.setMultiChoiceItems(items, defaults, new DialogInterface.OnMultiChoiceClickListener() + { + public void onClick(DialogInterface dialog, int item, boolean isChecked) + { + if( item == 0 ) + Globals.OuyaEmulation = isChecked; + } + }); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class ShowReadme extends Menu + { + String title(final MainActivity p) + { + return "Readme"; + } + boolean enabled() + { + return true; + } + void run (final MainActivity p) + { + String readmes[] = Globals.ReadmeText.split("\\^"); + String lang = new String(Locale.getDefault().getLanguage()) + ":"; + if( p.isRunningOnOUYA() ) + lang = "ouya:"; + String readme = readmes[0]; + String buttonName = "", buttonUrl = ""; + for( String r: readmes ) + { + if( r.startsWith(lang) ) + readme = r.substring(lang.length()); + if( r.startsWith("button:") ) + { + buttonName = r.substring("button:".length()); + if( buttonName.indexOf(":") != -1 ) + { + buttonUrl = buttonName.substring(buttonName.indexOf(":") + 1); + buttonName = buttonName.substring(0, buttonName.indexOf(":")); + } + } + } + readme = readme.trim(); + if( readme.length() <= 2 ) + { + goBack(p); + return; + } + TextView text = new TextView(p); + text.setMaxLines(100); + //text.setScroller(new Scroller(p)); + //text.setVerticalScrollBarEnabled(true); + text.setText(readme); + text.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + text.setPadding(0, 5, 0, 20); + text.setTextSize(20.0f); + text.setGravity(Gravity.CENTER); + AlertDialog.Builder builder = new AlertDialog.Builder(p); + ScrollView scroll = new ScrollView(p); + scroll.addView(text, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + Button ok = new Button(p); + final AlertDialog alertDismiss[] = new AlertDialog[1]; + ok.setOnClickListener(new View.OnClickListener() + { + public void onClick(View v) + { + alertDismiss[0].cancel(); + } + }); + ok.setText(R.string.ok); + LinearLayout layout = new LinearLayout(p); + layout.setOrientation(LinearLayout.VERTICAL); + layout.addView(scroll); + //layout.addView(text); + layout.addView(ok); + if( buttonName.length() > 0 ) + { + Button cancel = new Button(p); + cancel.setText(buttonName); + final String url = buttonUrl; + cancel.setOnClickListener(new View.OnClickListener() + { + public void onClick(View v) + { + if( url.length() > 0 ) + { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + p.startActivity(i); + } + alertDismiss[0].cancel(); + System.exit(0); + } + }); + layout.addView(cancel); + } + builder.setView(layout); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alertDismiss[0] = alert; + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class GyroscopeCalibration extends Menu implements SensorEventListener + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.calibrate_gyroscope); + } + boolean enabled() + { + return Globals.AppUsesGyroscope; + } + void run (final MainActivity p) + { + if( !Globals.AppUsesGyroscope || !AccelerometerReader.gyro.available(p) ) + { + if( Globals.AppUsesGyroscope ) + { + Toast toast = Toast.makeText(p, p.getResources().getString(R.string.calibrate_gyroscope_not_supported), Toast.LENGTH_LONG); + toast.show(); + } + goBack(p); + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.calibrate_gyroscope)); + builder.setMessage(p.getResources().getString(R.string.calibrate_gyroscope_text)); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + startCalibration(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + ImageView img; + Bitmap bmp; + int numEvents; + MainActivity p; + + void startCalibration(final MainActivity _p) + { + p = _p; + img = new ImageView(p); + img.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + img.setScaleType(ImageView.ScaleType.MATRIX); + bmp = BitmapFactory.decodeResource( p.getResources(), R.drawable.calibrate ); + img.setImageBitmap(bmp); + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); + RectF dst = new RectF( p.getVideoLayout().getWidth()/2 - 50, p.getVideoLayout().getHeight()/2 - 50, + p.getVideoLayout().getWidth()/2 + 50, p.getVideoLayout().getHeight()/2 + 50); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + img.setImageMatrix(m); + p.getVideoLayout().addView(img); + numEvents = 0; + AccelerometerReader.gyro.x1 = 100; + AccelerometerReader.gyro.x2 = -100; + AccelerometerReader.gyro.xc = 0; + AccelerometerReader.gyro.y1 = 100; + AccelerometerReader.gyro.y2 = -100; + AccelerometerReader.gyro.yc = 0; + AccelerometerReader.gyro.z1 = 100; + AccelerometerReader.gyro.z2 = -100; + AccelerometerReader.gyro.zc = 0; + AccelerometerReader.gyro.registerListener(p, this); + (new Thread(new Runnable() + { + public void run() + { + for(int count = 1; count < 10; count++) + { + p.setText("" + count + "0% ..."); + try { + Thread.sleep(500); + } catch( Exception e ) {} + } + finishCalibration(p); + } + } + )).start(); + } + + public void onSensorChanged(SensorEvent event) + { + gyroscopeEvent(event.values[0], event.values[1], event.values[2]); + } + public void onAccuracyChanged(Sensor s, int a) + { + } + void gyroscopeEvent(float x, float y, float z) + { + numEvents++; + AccelerometerReader.gyro.xc += x; + AccelerometerReader.gyro.yc += y; + AccelerometerReader.gyro.zc += z; + AccelerometerReader.gyro.x1 = Math.min(AccelerometerReader.gyro.x1, x * 1.1f); // Small safety bound coefficient + AccelerometerReader.gyro.x2 = Math.max(AccelerometerReader.gyro.x2, x * 1.1f); + AccelerometerReader.gyro.y1 = Math.min(AccelerometerReader.gyro.y1, y * 1.1f); + AccelerometerReader.gyro.y2 = Math.max(AccelerometerReader.gyro.y2, y * 1.1f); + AccelerometerReader.gyro.z1 = Math.min(AccelerometerReader.gyro.z1, z * 1.1f); + AccelerometerReader.gyro.z2 = Math.max(AccelerometerReader.gyro.z2, z * 1.1f); + final Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); + RectF dst = new RectF( x * 5000 + p.getVideoLayout().getWidth()/2 - 50, y * 5000 + p.getVideoLayout().getHeight()/2 - 50, + x * 5000 + p.getVideoLayout().getWidth()/2 + 50, y * 5000 + p.getVideoLayout().getHeight()/2 + 50); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + p.runOnUiThread(new Runnable() + { + public void run() + { + img.setImageMatrix(m); + } + }); + } + void finishCalibration(final MainActivity p) + { + AccelerometerReader.gyro.unregisterListener(p, this); + try { + Thread.sleep(200); // Just in case we have pending events + } catch( Exception e ) {} + if( numEvents > 5 ) + { + AccelerometerReader.gyro.xc /= (float)numEvents; + AccelerometerReader.gyro.yc /= (float)numEvents; + AccelerometerReader.gyro.zc /= (float)numEvents; + } + p.runOnUiThread(new Runnable() + { + public void run() + { + p.getVideoLayout().removeView(img); + goBack(p); + } + }); + } + } + + static class ResetToDefaultsConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.reset_config); + } + boolean enabled() + { + return true; + } + void run (final MainActivity p) + { + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.reset_config_ask)); + builder.setMessage(p.getResources().getString(R.string.reset_config_ask)); + + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Settings.DeleteSdlConfigOnUpgradeAndRestart(p); // Never returns + dialog.dismiss(); + goBack(p); + } + }); + builder.setNegativeButton(p.getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } +} + diff --git a/project/javaSDL2/SettingsMenuMouse.java b/project/javaSDL2/SettingsMenuMouse.java new file mode 100644 index 000000000..c4495874d --- /dev/null +++ b/project/javaSDL2/SettingsMenuMouse.java @@ -0,0 +1,771 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import android.widget.TextView; +import android.util.Log; +import java.io.*; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Environment; +import android.os.StatFs; +import java.util.Locale; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.GZIPInputStream; +import java.util.Collections; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import java.lang.String; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.FrameLayout; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap; +import android.widget.TextView; +import android.widget.EditText; +import android.widget.ScrollView; +import android.widget.Button; +import android.view.View; +import android.widget.LinearLayout; +import android.text.Editable; +import android.text.SpannedString; +import android.content.Intent; +import android.app.PendingIntent; +import android.app.AlarmManager; +import android.util.DisplayMetrics; +import android.net.Uri; +import java.util.concurrent.Semaphore; +import java.util.Arrays; +import android.graphics.Color; +import android.hardware.SensorEventListener; +import android.hardware.SensorEvent; +import android.hardware.Sensor; +import android.widget.Toast; + + +class SettingsMenuMouse extends SettingsMenu +{ + static class MouseConfigMainMenu extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.mouse_emulation); + } + boolean enabled() + { + return Globals.AppUsesMouse; + } + void run (final MainActivity p) + { + Menu options[] = + { + new DisplaySizeConfig(false), + new LeftClickConfig(), + new RightClickConfig(), + new AdditionalMouseConfig(), + new JoystickMouseConfig(), + new TouchPressureMeasurementTool(), + new CalibrateTouchscreenMenu(), + new OkButton(), + }; + showMenuOptionsList(p, options); + } + } + + static class DisplaySizeConfig extends Menu + { + boolean firstStart = false; + DisplaySizeConfig() + { + this.firstStart = true; + } + DisplaySizeConfig(boolean firstStart) + { + this.firstStart = firstStart; + } + String title(final MainActivity p) + { + return p.getResources().getString(R.string.display_size_mouse); + } + void run (final MainActivity p) + { + CharSequence[] items = { + p.getResources().getString(R.string.display_size_tiny_touchpad), + p.getResources().getString(R.string.display_size_tiny), + p.getResources().getString(R.string.display_size_small), + p.getResources().getString(R.string.display_size_small_touchpad), + p.getResources().getString(R.string.display_size_large), + }; + int _size_tiny_touchpad = 0; + int _size_tiny = 1; + int _size_small = 2; + int _size_small_touchpad = 3; + int _size_large = 4; + int _more_options = 5; + + if( ! Globals.SwVideoMode ) + { + CharSequence[] items2 = { + p.getResources().getString(R.string.display_size_small_touchpad), + p.getResources().getString(R.string.display_size_large), + }; + items = items2; + _size_small_touchpad = 0; + _size_large = 1; + _size_tiny_touchpad = _size_tiny = _size_small = 1000; + + } + if( firstStart ) + { + CharSequence[] items2 = { + p.getResources().getString(R.string.display_size_tiny_touchpad), + p.getResources().getString(R.string.display_size_tiny), + p.getResources().getString(R.string.display_size_small), + p.getResources().getString(R.string.display_size_small_touchpad), + p.getResources().getString(R.string.display_size_large), + p.getResources().getString(R.string.show_more_options), + }; + items = items2; + if( ! Globals.SwVideoMode ) + { + CharSequence[] items3 = { + p.getResources().getString(R.string.display_size_small_touchpad), + p.getResources().getString(R.string.display_size_large), + p.getResources().getString(R.string.show_more_options), + }; + items = items3; + _more_options = 3; + } + } + // Java is so damn worse than C++11 + final int size_tiny_touchpad = _size_tiny_touchpad; + final int size_tiny = _size_tiny; + final int size_small = _size_small; + final int size_small_touchpad = _size_small_touchpad; + final int size_large = _size_large; + final int more_options = _more_options; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.display_size); + class ClickListener implements DialogInterface.OnClickListener + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + if( item == size_large ) + { + Globals.LeftClickMethod = Mouse.LEFT_CLICK_NORMAL; + Globals.RelativeMouseMovement = false; + Globals.ShowScreenUnderFinger = Mouse.ZOOM_NONE; + } + if( item == size_small ) + { + Globals.LeftClickMethod = Mouse.LEFT_CLICK_NEAR_CURSOR; + Globals.RelativeMouseMovement = false; + Globals.ShowScreenUnderFinger = Mouse.ZOOM_MAGNIFIER; + } + if( item == size_small_touchpad ) + { + Globals.LeftClickMethod = Mouse.LEFT_CLICK_WITH_TAP_OR_TIMEOUT; + Globals.RelativeMouseMovement = true; + Globals.ShowScreenUnderFinger = Mouse.ZOOM_NONE; + } + if( item == size_tiny ) + { + Globals.LeftClickMethod = Mouse.LEFT_CLICK_NEAR_CURSOR; + Globals.RelativeMouseMovement = false; + Globals.ShowScreenUnderFinger = Mouse.ZOOM_SCREEN_TRANSFORM; + } + if( item == size_tiny_touchpad ) + { + Globals.LeftClickMethod = Mouse.LEFT_CLICK_WITH_TAP_OR_TIMEOUT; + Globals.RelativeMouseMovement = true; + Globals.ShowScreenUnderFinger = Mouse.ZOOM_FULLSCREEN_MAGNIFIER; + } + if( item == more_options ) + { + menuStack.clear(); + new MainMenu().run(p); + return; + } + goBack(p); + } + } + builder.setItems(items, new ClickListener()); + /* + else + builder.setSingleChoiceItems(items, + Globals.ShowScreenUnderFinger == Mouse.ZOOM_NONE ? + ( Globals.RelativeMouseMovement ? Globals.SwVideoMode ? 2 : 1 : 0 ) : + ( Globals.ShowScreenUnderFinger == Mouse.ZOOM_MAGNIFIER && Globals.SwVideoMode ) ? 1 : + Globals.ShowScreenUnderFinger + 1, + new ClickListener()); + */ + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class LeftClickConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.leftclick_question); + } + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.leftclick_normal), + p.getResources().getString(R.string.leftclick_near_cursor), + p.getResources().getString(R.string.leftclick_multitouch), + p.getResources().getString(R.string.leftclick_pressure), + p.getResources().getString(R.string.rightclick_key), + p.getResources().getString(R.string.leftclick_timeout), + p.getResources().getString(R.string.leftclick_tap), + p.getResources().getString(R.string.leftclick_tap_or_timeout) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.leftclick_question); + builder.setSingleChoiceItems(items, Globals.LeftClickMethod, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + Globals.LeftClickMethod = item; + if( item == Mouse.LEFT_CLICK_WITH_KEY ) + p.keyListener = new KeyRemapToolMouseClick(p, true); + else if( item == Mouse.LEFT_CLICK_WITH_TIMEOUT || item == Mouse.LEFT_CLICK_WITH_TAP_OR_TIMEOUT ) + showLeftClickTimeoutConfig(p); + else + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + static void showLeftClickTimeoutConfig(final MainActivity p) { + final CharSequence[] items = { p.getResources().getString(R.string.leftclick_timeout_time_0), + p.getResources().getString(R.string.leftclick_timeout_time_1), + p.getResources().getString(R.string.leftclick_timeout_time_2), + p.getResources().getString(R.string.leftclick_timeout_time_3), + p.getResources().getString(R.string.leftclick_timeout_time_4) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.leftclick_timeout_time); + builder.setSingleChoiceItems(items, Globals.LeftClickTimeout, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.LeftClickTimeout = item; + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class RightClickConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.rightclick_question); + } + boolean enabled() + { + return Globals.AppNeedsTwoButtonMouse; + } + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.rightclick_none), + p.getResources().getString(R.string.rightclick_multitouch), + p.getResources().getString(R.string.rightclick_pressure), + p.getResources().getString(R.string.rightclick_key), + p.getResources().getString(R.string.leftclick_timeout) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.rightclick_question); + builder.setSingleChoiceItems(items, Globals.RightClickMethod, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RightClickMethod = item; + dialog.dismiss(); + if( item == Mouse.RIGHT_CLICK_WITH_KEY ) + p.keyListener = new KeyRemapToolMouseClick(p, false); + else if( item == Mouse.RIGHT_CLICK_WITH_TIMEOUT ) + showRightClickTimeoutConfig(p); + else + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showRightClickTimeoutConfig(final MainActivity p) { + final CharSequence[] items = { p.getResources().getString(R.string.leftclick_timeout_time_0), + p.getResources().getString(R.string.leftclick_timeout_time_1), + p.getResources().getString(R.string.leftclick_timeout_time_2), + p.getResources().getString(R.string.leftclick_timeout_time_3), + p.getResources().getString(R.string.leftclick_timeout_time_4) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.leftclick_timeout_time); + builder.setSingleChoiceItems(items, Globals.RightClickTimeout, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RightClickTimeout = item; + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + public static class KeyRemapToolMouseClick implements MainActivity.KeyEventsListener + { + MainActivity p; + boolean leftClick; + public KeyRemapToolMouseClick(MainActivity _p, boolean leftClick) + { + p = _p; + p.setText(p.getResources().getString(R.string.remap_hwkeys_press)); + this.leftClick = leftClick; + } + + public void onKeyEvent(final int keyCode) + { + p.keyListener = null; + int keyIndex = keyCode; + if( keyIndex < 0 ) + keyIndex = 0; + if( keyIndex > SDL_Keys.JAVA_KEYCODE_LAST ) + keyIndex = 0; + + if( leftClick ) + Globals.LeftClickKey = keyIndex; + else + Globals.RightClickKey = keyIndex; + + goBack(p); + } + } + + static class AdditionalMouseConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.pointandclick_question); + } + void run (final MainActivity p) + { + CharSequence[] items = { + p.getResources().getString(R.string.pointandclick_joystickmouse), + p.getResources().getString(R.string.click_with_dpadcenter), + p.getResources().getString(R.string.pointandclick_relative) + }; + + boolean defaults[] = { + Globals.MoveMouseWithJoystick, + Globals.ClickMouseWithDpad, + Globals.RelativeMouseMovement + }; + + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.pointandclick_question)); + builder.setMultiChoiceItems(items, defaults, new DialogInterface.OnMultiChoiceClickListener() + { + public void onClick(DialogInterface dialog, int item, boolean isChecked) + { + if( item == 0 ) + Globals.MoveMouseWithJoystick = isChecked; + if( item == 1 ) + Globals.ClickMouseWithDpad = isChecked; + if( item == 2 ) + Globals.RelativeMouseMovement = isChecked; + } + }); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + if( Globals.RelativeMouseMovement ) + showRelativeMouseMovementConfig(p); + else + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showRelativeMouseMovementConfig(final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.accel_veryslow), + p.getResources().getString(R.string.accel_slow), + p.getResources().getString(R.string.accel_medium), + p.getResources().getString(R.string.accel_fast), + p.getResources().getString(R.string.accel_veryfast) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.pointandclick_relative_speed); + builder.setSingleChoiceItems(items, Globals.RelativeMouseMovementSpeed, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RelativeMouseMovementSpeed = item; + + dialog.dismiss(); + showRelativeMouseMovementConfig1(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showRelativeMouseMovementConfig1(final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.none), + p.getResources().getString(R.string.accel_slow), + p.getResources().getString(R.string.accel_medium), + p.getResources().getString(R.string.accel_fast) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.pointandclick_relative_accel); + builder.setSingleChoiceItems(items, Globals.RelativeMouseMovementAccel, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RelativeMouseMovementAccel = item; + + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class JoystickMouseConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.pointandclick_joystickmousespeed); + } + boolean enabled() + { + return Globals.MoveMouseWithJoystick; + }; + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.accel_slow), + p.getResources().getString(R.string.accel_medium), + p.getResources().getString(R.string.accel_fast) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.pointandclick_joystickmousespeed); + builder.setSingleChoiceItems(items, Globals.MoveMouseWithJoystickSpeed, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.MoveMouseWithJoystickSpeed = item; + + dialog.dismiss(); + showJoystickMouseAccelConfig(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showJoystickMouseAccelConfig(final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.none), + p.getResources().getString(R.string.accel_slow), + p.getResources().getString(R.string.accel_medium), + p.getResources().getString(R.string.accel_fast) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.pointandclick_joystickmouseaccel); + builder.setSingleChoiceItems(items, Globals.MoveMouseWithJoystickAccel, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.MoveMouseWithJoystickAccel = item; + + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class TouchPressureMeasurementTool extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.measurepressure); + } + boolean enabled() + { + return Globals.RightClickMethod == Mouse.RIGHT_CLICK_WITH_PRESSURE || + Globals.LeftClickMethod == Mouse.LEFT_CLICK_WITH_PRESSURE; + }; + void run (final MainActivity p) + { + p.setText(p.getResources().getString(R.string.measurepressure_touchplease)); + p.touchListener = new TouchMeasurementTool(p); + } + + public static class TouchMeasurementTool implements MainActivity.TouchEventsListener + { + MainActivity p; + ArrayList force = new ArrayList(); + ArrayList radius = new ArrayList(); + static final int maxEventAmount = 100; + + public TouchMeasurementTool(MainActivity _p) + { + p = _p; + } + + public void onTouchEvent(final MotionEvent ev) + { + force.add(new Integer((int)(ev.getPressure() * 1000.0))); + radius.add(new Integer((int)(ev.getSize() * 1000.0))); + p.setText(p.getResources().getString(R.string.measurepressure_response, force.get(force.size()-1), radius.get(radius.size()-1))); + try { + Thread.sleep(10L); + } catch (InterruptedException e) { } + + if( force.size() >= maxEventAmount ) + { + p.touchListener = null; + Globals.ClickScreenPressure = getAverageForce(); + Globals.ClickScreenTouchspotSize = getAverageRadius(); + Log.i("SDL", "SDL: measured average force " + Globals.ClickScreenPressure + " radius " + Globals.ClickScreenTouchspotSize); + goBack(p); + } + } + + int getAverageForce() + { + int avg = 0; + for(Integer f: force) + { + avg += f; + } + return avg / force.size(); + } + int getAverageRadius() + { + int avg = 0; + for(Integer r: radius) + { + avg += r; + } + return avg / radius.size(); + } + } + } + + static class CalibrateTouchscreenMenu extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.calibrate_touchscreen); + } + //boolean enabled() { return true; }; + void run (final MainActivity p) + { + p.setText(p.getResources().getString(R.string.calibrate_touchscreen_touch)); + Globals.TouchscreenCalibration[0] = 0; + Globals.TouchscreenCalibration[1] = 0; + Globals.TouchscreenCalibration[2] = 0; + Globals.TouchscreenCalibration[3] = 0; + ScreenEdgesCalibrationTool tool = new ScreenEdgesCalibrationTool(p); + p.touchListener = tool; + p.keyListener = tool; + } + + static class ScreenEdgesCalibrationTool implements MainActivity.TouchEventsListener, MainActivity.KeyEventsListener + { + MainActivity p; + ImageView img; + Bitmap bmp; + + public ScreenEdgesCalibrationTool(MainActivity _p) + { + p = _p; + img = new ImageView(p); + img.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + img.setScaleType(ImageView.ScaleType.MATRIX); + bmp = BitmapFactory.decodeResource( p.getResources(), R.drawable.calibrate ); + img.setImageBitmap(bmp); + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); + RectF dst = new RectF(Globals.TouchscreenCalibration[0], Globals.TouchscreenCalibration[1], + Globals.TouchscreenCalibration[2], Globals.TouchscreenCalibration[3]); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + img.setImageMatrix(m); + p.getVideoLayout().addView(img); + } + + public void onTouchEvent(final MotionEvent ev) + { + if( Globals.TouchscreenCalibration[0] == Globals.TouchscreenCalibration[1] && + Globals.TouchscreenCalibration[1] == Globals.TouchscreenCalibration[2] && + Globals.TouchscreenCalibration[2] == Globals.TouchscreenCalibration[3] ) + { + Globals.TouchscreenCalibration[0] = (int)ev.getX(); + Globals.TouchscreenCalibration[1] = (int)ev.getY(); + Globals.TouchscreenCalibration[2] = (int)ev.getX(); + Globals.TouchscreenCalibration[3] = (int)ev.getY(); + } + if( ev.getX() < Globals.TouchscreenCalibration[0] ) + Globals.TouchscreenCalibration[0] = (int)ev.getX(); + if( ev.getY() < Globals.TouchscreenCalibration[1] ) + Globals.TouchscreenCalibration[1] = (int)ev.getY(); + if( ev.getX() > Globals.TouchscreenCalibration[2] ) + Globals.TouchscreenCalibration[2] = (int)ev.getX(); + if( ev.getY() > Globals.TouchscreenCalibration[3] ) + Globals.TouchscreenCalibration[3] = (int)ev.getY(); + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); + RectF dst = new RectF(Globals.TouchscreenCalibration[0], Globals.TouchscreenCalibration[1], + Globals.TouchscreenCalibration[2], Globals.TouchscreenCalibration[3]); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + img.setImageMatrix(m); + } + + public void onKeyEvent(final int keyCode) + { + p.touchListener = null; + p.keyListener = null; + p.getVideoLayout().removeView(img); + goBack(p); + } + } + } +} + diff --git a/project/javaSDL2/Video.java b/project/javaSDL2/Video.java new file mode 100644 index 000000000..8fcf0eb4c --- /dev/null +++ b/project/javaSDL2/Video.java @@ -0,0 +1,845 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import javax.microedition.khronos.opengles.GL10; +import javax.microedition.khronos.opengles.GL11; +import javax.microedition.khronos.opengles.GL11Ext; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGL11; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.InputDevice; +import android.view.Window; +import android.view.WindowManager; +import android.os.Environment; +import java.io.File; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.content.res.Resources; +import android.content.res.AssetManager; +import android.widget.Toast; +import android.util.Log; + +import android.widget.TextView; +import java.lang.Thread; +import java.util.concurrent.locks.ReentrantLock; +import android.os.Build; +import java.lang.reflect.Method; +import java.util.LinkedList; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + + +class Mouse +{ + public static final int LEFT_CLICK_NORMAL = 0; + public static final int LEFT_CLICK_NEAR_CURSOR = 1; + public static final int LEFT_CLICK_WITH_MULTITOUCH = 2; + public static final int LEFT_CLICK_WITH_PRESSURE = 3; + public static final int LEFT_CLICK_WITH_KEY = 4; + public static final int LEFT_CLICK_WITH_TIMEOUT = 5; + public static final int LEFT_CLICK_WITH_TAP = 6; + public static final int LEFT_CLICK_WITH_TAP_OR_TIMEOUT = 7; + + public static final int RIGHT_CLICK_NONE = 0; + public static final int RIGHT_CLICK_WITH_MULTITOUCH = 1; + public static final int RIGHT_CLICK_WITH_PRESSURE = 2; + public static final int RIGHT_CLICK_WITH_KEY = 3; + public static final int RIGHT_CLICK_WITH_TIMEOUT = 4; + + public static final int SDL_FINGER_DOWN = 0; + public static final int SDL_FINGER_UP = 1; + public static final int SDL_FINGER_MOVE = 2; + public static final int SDL_FINGER_HOVER = 3; + + public static final int ZOOM_NONE = 0; + public static final int ZOOM_MAGNIFIER = 1; + public static final int ZOOM_SCREEN_TRANSFORM = 2; + public static final int ZOOM_FULLSCREEN_MAGNIFIER = 3; +} + +abstract class DifferentTouchInput +{ + public abstract void process(final MotionEvent event); + public abstract void processGenericEvent(final MotionEvent event); + + public static boolean ExternalMouseDetected = false; + + public static DifferentTouchInput getInstance() + { + boolean multiTouchAvailable1 = false; + boolean multiTouchAvailable2 = false; + // Not checking for getX(int), getY(int) etc 'cause I'm lazy + Method methods [] = MotionEvent.class.getDeclaredMethods(); + for(Method m: methods) + { + if( m.getName().equals("getPointerCount") ) + multiTouchAvailable1 = true; + if( m.getName().equals("getPointerId") ) + multiTouchAvailable2 = true; + } + try { + Log.i("SDL", "Device model: " + android.os.Build.MODEL); + if( android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH ) + { + if( DetectCrappyDragonRiseDatexGamepad() ) + return CrappyDragonRiseDatexGamepadInputWhichNeedsItsOwnHandlerBecauseImTooCheapAndStupidToBuyProperGamepad.Holder.sInstance; + return IcsTouchInput.Holder.sInstance; + } + if( android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD ) + return GingerbreadTouchInput.Holder.sInstance; + if (multiTouchAvailable1 && multiTouchAvailable2) + return MultiTouchInput.Holder.sInstance; + else + return SingleTouchInput.Holder.sInstance; + } catch( Exception e ) { + try { + if (multiTouchAvailable1 && multiTouchAvailable2) + return MultiTouchInput.Holder.sInstance; + else + return SingleTouchInput.Holder.sInstance; + } catch( Exception ee ) { + return SingleTouchInput.Holder.sInstance; + } + } + } + private static boolean DetectCrappyDragonRiseDatexGamepad() + { + if( CrappyDragonRiseDatexGamepadInputWhichNeedsItsOwnHandlerBecauseImTooCheapAndStupidToBuyProperGamepad.Holder.sInstance == null ) + return false; + return CrappyDragonRiseDatexGamepadInputWhichNeedsItsOwnHandlerBecauseImTooCheapAndStupidToBuyProperGamepad.Holder.sInstance.detect(); + } + + private static class SingleTouchInput extends DifferentTouchInput + { + private static class Holder + { + private static final SingleTouchInput sInstance = new SingleTouchInput(); + } + @Override + public void processGenericEvent(final MotionEvent event) + { + process(event); + } + public void process(final MotionEvent event) + { + int action = -1; + if( event.getAction() == MotionEvent.ACTION_DOWN ) + action = Mouse.SDL_FINGER_DOWN; + if( event.getAction() == MotionEvent.ACTION_UP ) + action = Mouse.SDL_FINGER_UP; + if( event.getAction() == MotionEvent.ACTION_MOVE ) + action = Mouse.SDL_FINGER_MOVE; + if ( action >= 0 ) + DemoGLSurfaceView.nativeMotionEvent( (int)event.getX(), (int)event.getY(), action, 0, + (int)(event.getPressure() * 1000.0), + (int)(event.getSize() * 1000.0) ); + } + } + private static class MultiTouchInput extends DifferentTouchInput + { + public static final int TOUCH_EVENTS_MAX = 16; // Max multitouch pointers + + private class touchEvent + { + public boolean down = false; + public int x = 0; + public int y = 0; + public int pressure = 0; + public int size = 0; + } + + protected touchEvent touchEvents[]; + + MultiTouchInput() + { + touchEvents = new touchEvent[TOUCH_EVENTS_MAX]; + for( int i = 0; i < TOUCH_EVENTS_MAX; i++ ) + touchEvents[i] = new touchEvent(); + } + + private static class Holder + { + private static final MultiTouchInput sInstance = new MultiTouchInput(); + } + + public void processGenericEvent(final MotionEvent event) + { + process(event); + } + public void process(final MotionEvent event) + { + int action = -1; + + //Log.i("SDL", "Got motion event, type " + (int)(event.getAction()) + " X " + (int)event.getX() + " Y " + (int)event.getY()); + if( (event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP || + (event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_CANCEL ) + { + action = Mouse.SDL_FINGER_UP; + for( int i = 0; i < TOUCH_EVENTS_MAX; i++ ) + { + if( touchEvents[i].down ) + { + touchEvents[i].down = false; + DemoGLSurfaceView.nativeMotionEvent( touchEvents[i].x, touchEvents[i].y, action, i, touchEvents[i].pressure, touchEvents[i].size ); + } + } + } + if( (event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN ) + { + action = Mouse.SDL_FINGER_DOWN; + for( int i = 0; i < event.getPointerCount(); i++ ) + { + int id = event.getPointerId(i); + if( id >= TOUCH_EVENTS_MAX ) + id = TOUCH_EVENTS_MAX - 1; + touchEvents[id].down = true; + touchEvents[id].x = (int)event.getX(i); + touchEvents[id].y = (int)event.getY(i); + touchEvents[id].pressure = (int)(event.getPressure(i) * 1000.0); + touchEvents[id].size = (int)(event.getSize(i) * 1000.0); + DemoGLSurfaceView.nativeMotionEvent( touchEvents[id].x, touchEvents[id].y, action, id, touchEvents[id].pressure, touchEvents[id].size ); + } + } + if( (event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_MOVE || + (event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN || + (event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP ) + { + /* + String s = "MOVE: ptrs " + event.getPointerCount(); + for( int i = 0 ; i < event.getPointerCount(); i++ ) + { + s += " p" + event.getPointerId(i) + "=" + (int)event.getX(i) + ":" + (int)event.getY(i); + } + Log.i("SDL", s); + */ + int pointerReleased = -1; + if( (event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP ) + pointerReleased = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + + for( int id = 0; id < TOUCH_EVENTS_MAX; id++ ) + { + int ii; + for( ii = 0; ii < event.getPointerCount(); ii++ ) + { + if( id == event.getPointerId(ii) ) + break; + } + if( ii >= event.getPointerCount() ) + { + // Up event + if( touchEvents[id].down ) + { + action = Mouse.SDL_FINGER_UP; + touchEvents[id].down = false; + DemoGLSurfaceView.nativeMotionEvent( touchEvents[id].x, touchEvents[id].y, action, id, touchEvents[id].pressure, touchEvents[id].size ); + } + } + else + { + if( pointerReleased == id && touchEvents[pointerReleased].down ) + { + action = Mouse.SDL_FINGER_UP; + touchEvents[id].down = false; + } + else if( touchEvents[id].down ) + { + action = Mouse.SDL_FINGER_MOVE; + } + else + { + action = Mouse.SDL_FINGER_DOWN; + touchEvents[id].down = true; + } + touchEvents[id].x = (int)event.getX(ii); + touchEvents[id].y = (int)event.getY(ii); + touchEvents[id].pressure = (int)(event.getPressure(ii) * 1000.0); + touchEvents[id].size = (int)(event.getSize(ii) * 1000.0); + DemoGLSurfaceView.nativeMotionEvent( touchEvents[id].x, touchEvents[id].y, action, id, touchEvents[id].pressure, touchEvents[id].size ); + } + } + } + if( (event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_HOVER_MOVE ) // Support bluetooth/USB mouse - available since Android 3.1 + { + // TODO: it is possible that multiple pointers return that event, but we're handling only pointer #0 + if( touchEvents[0].down ) + action = Mouse.SDL_FINGER_UP; + else + action = Mouse.SDL_FINGER_HOVER; + touchEvents[0].down = false; + touchEvents[0].x = (int)event.getX(); + touchEvents[0].y = (int)event.getY(); + touchEvents[0].pressure = 0; + touchEvents[0].size = 0; + DemoGLSurfaceView.nativeMotionEvent( touchEvents[0].x, touchEvents[0].y, action, 0, touchEvents[0].pressure, touchEvents[0].size ); + } + } + } + private static class GingerbreadTouchInput extends MultiTouchInput + { + private static class Holder + { + private static final GingerbreadTouchInput sInstance = new GingerbreadTouchInput(); + } + + GingerbreadTouchInput() + { + super(); + } + public void process(final MotionEvent event) + { + boolean hwMouseEvent = ( (event.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE || + (event.getSource() & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS || + (event.getMetaState() & KeyEvent.FLAG_TRACKING) != 0 ); // Hack to recognize Galaxy Note Gingerbread stylus + if( ExternalMouseDetected != hwMouseEvent ) + { + ExternalMouseDetected = hwMouseEvent; + DemoGLSurfaceView.nativeHardwareMouseDetected(hwMouseEvent ? 1 : 0); + } + super.process(event); + } + public void processGenericEvent(final MotionEvent event) + { + process(event); + } + } + private static class IcsTouchInput extends GingerbreadTouchInput + { + private static class Holder + { + private static final IcsTouchInput sInstance = new IcsTouchInput(); + } + private int buttonState = 0; + public void process(final MotionEvent event) + { + //Log.i("SDL", "Got motion event, type " + (int)(event.getAction()) + " X " + (int)event.getX() + " Y " + (int)event.getY() + " buttons " + buttonState + " source " + event.getSource()); + int buttonStateNew = event.getButtonState(); + if( buttonStateNew != buttonState ) + { + for( int i = 1; i <= MotionEvent.BUTTON_FORWARD; i *= 2 ) + { + if( (buttonStateNew & i) != (buttonState & i) ) + DemoGLSurfaceView.nativeMouseButtonsPressed(i, ((buttonStateNew & i) == 0) ? 0 : 1); + } + buttonState = buttonStateNew; + } + super.process(event); // Push mouse coordinate first + } + public void processGenericEvent(final MotionEvent event) + { + // Joysticks are supported since Honeycomb, but I don't care about it, because very little devices have it + if( (event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK ) + { + DemoGLSurfaceView.nativeGamepadAnalogJoystickInput( + event.getAxisValue(MotionEvent.AXIS_X), event.getAxisValue(MotionEvent.AXIS_Y), + event.getAxisValue(MotionEvent.AXIS_Z), event.getAxisValue(MotionEvent.AXIS_RZ), + event.getAxisValue(MotionEvent.AXIS_RTRIGGER), event.getAxisValue(MotionEvent.AXIS_LTRIGGER) ); + return; + } + // Process mousewheel + if( event.getAction() == MotionEvent.ACTION_SCROLL ) + { + int scrollX = Math.round(event.getAxisValue(MotionEvent.AXIS_HSCROLL)); + int scrollY = Math.round(event.getAxisValue(MotionEvent.AXIS_VSCROLL)); + DemoGLSurfaceView.nativeMouseWheel(scrollX, scrollY); + return; + } + super.processGenericEvent(event); + } + } + private static class CrappyDragonRiseDatexGamepadInputWhichNeedsItsOwnHandlerBecauseImTooCheapAndStupidToBuyProperGamepad extends IcsTouchInput + { + private static class Holder + { + private static final CrappyDragonRiseDatexGamepadInputWhichNeedsItsOwnHandlerBecauseImTooCheapAndStupidToBuyProperGamepad sInstance = new CrappyDragonRiseDatexGamepadInputWhichNeedsItsOwnHandlerBecauseImTooCheapAndStupidToBuyProperGamepad(); + } + float hatX = 0.0f, hatY = 0.0f; + public void processGenericEvent(final MotionEvent event) + { + // Joysticks are supported since Honeycomb, but I don't care about it, because very little devices have it + if( (event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK ) + { + // event.getAxisValue(AXIS_HAT_X) and event.getAxisValue(AXIS_HAT_Y) are joystick arrow keys, they also send keyboard events + DemoGLSurfaceView.nativeGamepadAnalogJoystickInput( + event.getAxisValue(MotionEvent.AXIS_X), event.getAxisValue(MotionEvent.AXIS_Y), + event.getAxisValue(MotionEvent.AXIS_RX), event.getAxisValue(MotionEvent.AXIS_RZ), + 0, 0); + if( event.getAxisValue(MotionEvent.AXIS_HAT_X) != hatX ) + { + hatX = event.getAxisValue(MotionEvent.AXIS_HAT_X); + if( hatX == 0.0f ) + { + DemoGLSurfaceView.nativeKey(KeyEvent.KEYCODE_DPAD_LEFT, 0); + DemoGLSurfaceView.nativeKey(KeyEvent.KEYCODE_DPAD_RIGHT, 0); + } + else + DemoGLSurfaceView.nativeKey(hatX < 0.0f ? KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT, 1); + } + if( event.getAxisValue(MotionEvent.AXIS_HAT_Y) != hatY ) + { + hatY = event.getAxisValue(MotionEvent.AXIS_HAT_Y); + if( hatY == 0.0f ) + { + DemoGLSurfaceView.nativeKey(KeyEvent.KEYCODE_DPAD_UP, 0); + DemoGLSurfaceView.nativeKey(KeyEvent.KEYCODE_DPAD_DOWN, 0); + } + else + DemoGLSurfaceView.nativeKey(hatY < 0.0f ? KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN, 1); + } + return; + } + super.processGenericEvent(event); + } + public boolean detect() + { + int[] devIds = InputDevice.getDeviceIds(); + for( int id : devIds ) + { + InputDevice device = InputDevice.getDevice(id); + if( device == null ) + continue; + System.out.println("libSDL: input device ID " + id + " type " + device.getSources() + " name " + device.getName() ); + if( (device.getSources() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD && + (device.getSources() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && + device.getName().indexOf("DragonRise Inc") == 0 ) + { + System.out.println("libSDL: Detected crappy DragonRise gamepad, enabling special hack for it. Please press button labeled 'Analog', otherwise it won't work, because it's cheap and crappy"); + return true; + } + } + return false; + } + } +} + + +class DemoRenderer extends GLSurfaceView_SDL.Renderer +{ + public DemoRenderer(MainActivity _context) + { + context = _context; + } + + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + Log.i("SDL", "libSDL: DemoRenderer.onSurfaceCreated(): paused " + mPaused + " mFirstTimeStart " + mFirstTimeStart ); + mGlSurfaceCreated = true; + mGl = gl; + if( ! mPaused && ! mFirstTimeStart ) + nativeGlContextRecreated(); + mFirstTimeStart = false; + } + + public void onSurfaceChanged(GL10 gl, int w, int h) { + Log.i("SDL", "libSDL: DemoRenderer.onSurfaceChanged(): paused " + mPaused + " mFirstTimeStart " + mFirstTimeStart + " w " + w + " h " + h); + if( w < h && Globals.HorizontalOrientation ) + { + // Sometimes when Android awakes from lockscreen, portrait orientation is kept + int x = w; + w = h; + h = x; + } + mWidth = w; + mHeight = h; + mGl = gl; + nativeResize(w, h, Globals.KeepAspectRatio ? 1 : 0); + } + + public void onSurfaceDestroyed() { + Log.i("SDL", "libSDL: DemoRenderer.onSurfaceDestroyed(): paused " + mPaused + " mFirstTimeStart " + mFirstTimeStart ); + mGlSurfaceCreated = false; + mGlContextLost = true; + nativeGlContextLost(); + }; + + public void onDrawFrame(GL10 gl) { + + mGl = gl; + DrawLogo(mGl); + SwapBuffers(); + + nativeInitJavaCallbacks(); + + // Make main thread priority lower so audio thread won't get underrun + // Thread.currentThread().setPriority((Thread.currentThread().getPriority() + Thread.MIN_PRIORITY)/2); + + mGlContextLost = false; + + if(Globals.CompatibilityHacksStaticInit) + MainActivity.LoadApplicationLibrary(context); + + Settings.Apply(context); + accelerometer = new AccelerometerReader(context); + // Tweak video thread priority, if user selected big audio buffer + if(Globals.AudioBufferConfig >= 2) + Thread.currentThread().setPriority( (Thread.NORM_PRIORITY + Thread.MIN_PRIORITY) / 2 ); // Lower than normal + // Calls main() and never returns, hehe - we'll call eglSwapBuffers() from native code + nativeInit( Globals.DataDir, + Globals.CommandLine, + ( (Globals.SwVideoMode && Globals.MultiThreadedVideo) || Globals.CompatibilityHacksVideo ) ? 1 : 0, + android.os.Debug.isDebuggerConnected() ? 1 : 0 ); + System.exit(0); // The main() returns here - I don't bother with deinit stuff, just terminate process + } + + public int swapBuffers() // Called from native code + { + if( ! super.SwapBuffers() && Globals.NonBlockingSwapBuffers ) + { + if(mRatelimitTouchEvents) + { + synchronized(this) + { + this.notify(); + } + } + return 0; + } + + if(mGlContextLost) { + mGlContextLost = false; + Settings.SetupTouchscreenKeyboardGraphics(context); // Reload on-screen buttons graphics + DrawLogo(mGl); + super.SwapBuffers(); + } + + // Unblock event processing thread only after we've finished rendering + if(mRatelimitTouchEvents) + { + synchronized(this) + { + this.notify(); + } + } + if( context.isScreenKeyboardShown() ) + { + try { + Thread.sleep(50); // Give some time to the keyboard input thread + } catch(Exception e) { }; + } + return 1; + } + + public void showScreenKeyboardWithoutTextInputField() // Called from native code + { + class Callback implements Runnable + { + public MainActivity parent; + public void run() + { + parent.showScreenKeyboardWithoutTextInputField(); + } + } + Callback cb = new Callback(); + cb.parent = context; + context.runOnUiThread(cb); + } + + public void showScreenKeyboard(final String oldText, int sendBackspace) // Called from native code + { + class Callback implements Runnable + { + public MainActivity parent; + public String oldText; + public boolean sendBackspace; + public void run() + { + parent.showScreenKeyboard(oldText, sendBackspace); + } + } + Callback cb = new Callback(); + cb.parent = context; + cb.oldText = oldText; + cb.sendBackspace = (sendBackspace != 0); + context.runOnUiThread(cb); + } + + public void hideScreenKeyboard() // Called from native code + { + class Callback implements Runnable + { + public MainActivity parent; + public void run() + { + parent.hideScreenKeyboard(); + } + } + Callback cb = new Callback(); + cb.parent = context; + context.runOnUiThread(cb); + } + + public int isScreenKeyboardShown() // Called from native code + { + return context.isScreenKeyboardShown() ? 1 : 0; + } + + public void setScreenKeyboardHintMessage(String s) + { + context.setScreenKeyboardHintMessage(s); + } + + public void startAccelerometerGyroscope(int started) + { + accelerometer.openedBySDL = (started != 0); + if( accelerometer.openedBySDL && !mPaused ) + accelerometer.start(); + else + accelerometer.stop(); + } + + public void exitApp() + { + nativeDone(); + } + + public void getAdvertisementParams(int params[]) + { + context.getAdvertisementParams(params); + } + public void setAdvertisementVisible(int visible) + { + context.setAdvertisementVisible(visible); + } + public void setAdvertisementPosition(int left, int top) + { + context.setAdvertisementPosition(left, top); + } + public void requestNewAdvertisement() + { + context.requestNewAdvertisement(); + } + + private int PowerOf2(int i) + { + int value = 1; + while (value < i) + value <<= 1; + return value; + } + public void DrawLogo(GL10 gl) + { + /* + // TODO: this not quite works, as it seems + BitmapDrawable bmp = null; + try + { + bmp = new BitmapDrawable(context.getAssets().open("logo.png")); + } + catch(Exception e) + { + bmp = new BitmapDrawable(context.getResources().openRawResource(R.drawable.publisherlogo)); + } + int width = bmp.getBitmap().getWidth(); + int height = bmp.getBitmap().getHeight(); + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4 * width * height); + //byteBuffer.order(ByteOrder.BIG_ENDIAN); + bmp.getBitmap().copyPixelsToBuffer(byteBuffer); + byteBuffer.position(0); + + gl.glViewport(0, 0, mWidth, mHeight); + gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT); + gl.glColor4x(0x10000, 0x10000, 0x10000, 0x10000); + gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1); + gl.glEnable(GL10.GL_TEXTURE_2D); + int textureName = -1; + int mTextureNameWorkspace[] = new int[1]; + int mCropWorkspace[] = new int[4]; + gl.glGenTextures(1, mTextureNameWorkspace, 0); + textureName = mTextureNameWorkspace[0]; + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName); + gl.glActiveTexture(textureName); + gl.glClientActiveTexture(textureName); + gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA, + PowerOf2(width), PowerOf2(height), 0, + GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, null); + gl.glTexSubImage2D(GL10.GL_TEXTURE_2D, 0, 0, 0, + width, height, + GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, byteBuffer); + mCropWorkspace[0] = 0; // u + mCropWorkspace[1] = height; // v + mCropWorkspace[2] = width; + mCropWorkspace[3] = -height; + ((GL11) gl).glTexParameteriv(GL10.GL_TEXTURE_2D, + GL11Ext.GL_TEXTURE_CROP_RECT_OES, mCropWorkspace, 0); + ((GL11Ext) gl).glDrawTexiOES(0, -mHeight, 0, mWidth, mHeight); + gl.glActiveTexture(0); + gl.glClientActiveTexture(0); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + gl.glDeleteTextures(1, mTextureNameWorkspace, 0); + + gl.glFlush(); + */ + } + + + private native void nativeInitJavaCallbacks(); + private native void nativeInit(String CurrentPath, String CommandLine, int multiThreadedVideo, int isDebuggerConnected); + private native void nativeResize(int w, int h, int keepAspectRatio); + private native void nativeDone(); + private native void nativeGlContextLost(); + public native void nativeGlContextRecreated(); + public native void nativeGlContextLostAsyncEvent(); + public static native void nativeTextInput( int ascii, int unicode ); + public static native void nativeTextInputFinished(); + + private MainActivity context = null; + public AccelerometerReader accelerometer = null; + + private GL10 mGl = null; + private EGL10 mEgl = null; + private EGLDisplay mEglDisplay = null; + private EGLSurface mEglSurface = null; + private EGLContext mEglContext = null; + private boolean mGlContextLost = false; + public boolean mGlSurfaceCreated = false; + public boolean mPaused = false; + private boolean mFirstTimeStart = true; + public int mWidth = 0; + public int mHeight = 0; + + public static final boolean mRatelimitTouchEvents = true; //(Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO); +} + +class DemoGLSurfaceView extends GLSurfaceView_SDL { + public DemoGLSurfaceView(MainActivity context) { + super(context); + mParent = context; + touchInput = DifferentTouchInput.getInstance(); + setEGLConfigChooser(Globals.VideoDepthBpp, Globals.NeedDepthBuffer, Globals.NeedStencilBuffer, Globals.NeedGles2); + mRenderer = new DemoRenderer(context); + setRenderer(mRenderer); + } + + @Override + public boolean onTouchEvent(final MotionEvent event) + { + touchInput.process(event); + if( DemoRenderer.mRatelimitTouchEvents ) + { + limitEventRate(event); + } + return true; + }; + + @Override + public boolean onGenericMotionEvent (final MotionEvent event) + { + touchInput.processGenericEvent(event); + if( DemoRenderer.mRatelimitTouchEvents ) + { + limitEventRate(event); + } + return true; + } + + public void limitEventRate(final MotionEvent event) + { + // Wait a bit, and try to synchronize to app framerate, or event thread will eat all CPU and we'll lose FPS + // With Froyo the rate of touch events seems to be limited by OS, but they are arriving faster then we're redrawing anyway + if((event.getAction() == MotionEvent.ACTION_MOVE || + event.getAction() == MotionEvent.ACTION_HOVER_MOVE)) + { + synchronized(mRenderer) + { + try + { + mRenderer.wait(300L); // And sometimes the app decides not to render at all, so this timeout should not be big. + } catch (InterruptedException e) { } + } + } + } + + public void exitApp() { + mRenderer.exitApp(); + }; + + @Override + public void onPause() { + if(mRenderer.mPaused) + return; + mRenderer.mPaused = true; + mRenderer.nativeGlContextLostAsyncEvent(); + if( mRenderer.accelerometer != null ) // For some reason it crashes here often - are we getting this event before initialization? + mRenderer.accelerometer.stop(); + super.onPause(); + }; + + public boolean isPaused() { + return mRenderer.mPaused; + } + + @Override + public void onResume() { + if(!mRenderer.mPaused) + return; + mRenderer.mPaused = false; + super.onResume(); + Log.i("SDL", "libSDL: DemoGLSurfaceView.onResume(): mRenderer.mGlSurfaceCreated " + mRenderer.mGlSurfaceCreated + " mRenderer.mPaused " + mRenderer.mPaused); + if( mRenderer.mGlSurfaceCreated && ! mRenderer.mPaused || Globals.NonBlockingSwapBuffers ) + mRenderer.nativeGlContextRecreated(); + if( mRenderer.accelerometer != null && mRenderer.accelerometer.openedBySDL ) // For some reason it crashes here often - are we getting this event before initialization? + mRenderer.accelerometer.start(); + }; + + // This seems like redundant code - it handled in MainActivity.java + @Override + public boolean onKeyDown(int keyCode, final KeyEvent event) { + //Log.i("SDL", "Got key down event, id " + keyCode + " meta " + event.getMetaState() + " event " + event.toString()); + if( nativeKey( keyCode, 1 ) == 0 ) + return super.onKeyDown(keyCode, event); + return true; + } + + @Override + public boolean onKeyUp(int keyCode, final KeyEvent event) { + //Log.i("SDL", "Got key up event, id " + keyCode + " meta " + event.getMetaState()); + if( nativeKey( keyCode, 0 ) == 0 ) + return super.onKeyUp(keyCode, event); + return true; + } + + DemoRenderer mRenderer; + MainActivity mParent; + DifferentTouchInput touchInput = null; + + public static native void nativeMotionEvent( int x, int y, int action, int pointerId, int pressure, int radius ); + public static native int nativeKey( int keyCode, int down ); + public static native void nativeTouchpad( int x, int y, int down, int multitouch ); + public static native void initJavaCallbacks(); + public static native void nativeHardwareMouseDetected( int detected ); + public static native void nativeMouseButtonsPressed( int buttonId, int pressedState ); + public static native void nativeMouseWheel( int scrollX, int scrollY ); + public static native void nativeGamepadAnalogJoystickInput( float stick1x, float stick1y, float stick2x, float stick2y, float rtrigger, float ltrigger ); +} + +