project_files/Android-build/SDL-android-project/src/org/libsdl/app/SDLActivity.java
branchhedgeroid
changeset 5331 3f93e036ca94
parent 5329 abfc41832374
child 5333 c239cb473a3f
equal deleted inserted replaced
5329:abfc41832374 5331:3f93e036ca94
     1 package org.libsdl.app;
       
     2 
       
     3 import java.io.IOException;
       
     4 import java.util.Iterator;
       
     5 import java.util.Map.Entry;
       
     6 
       
     7 import javax.microedition.khronos.egl.EGL10;
       
     8 import javax.microedition.khronos.egl.EGLConfig;
       
     9 import javax.microedition.khronos.egl.EGLContext;
       
    10 import javax.microedition.khronos.egl.EGLDisplay;
       
    11 import javax.microedition.khronos.egl.EGLSurface;
       
    12 
       
    13 import android.app.Activity;
       
    14 import android.content.Context;
       
    15 import android.graphics.Canvas;
       
    16 import android.graphics.PixelFormat;
       
    17 import android.hardware.Sensor;
       
    18 import android.hardware.SensorEvent;
       
    19 import android.hardware.SensorEventListener;
       
    20 import android.hardware.SensorManager;
       
    21 import android.media.AudioFormat;
       
    22 import android.media.AudioManager;
       
    23 import android.media.AudioTrack;
       
    24 import android.os.Bundle;
       
    25 import android.os.Handler;
       
    26 import android.os.Message;
       
    27 import android.util.Log;
       
    28 import android.view.KeyEvent;
       
    29 import android.view.MotionEvent;
       
    30 import android.view.SurfaceHolder;
       
    31 import android.view.SurfaceView;
       
    32 import android.view.View;
       
    33 
       
    34 
       
    35 /**
       
    36     SDL Activity
       
    37  */
       
    38 public class SDLActivity extends Activity {
       
    39 
       
    40 	// Main components
       
    41 	private static SDLActivity mSingleton;
       
    42 	private static SDLSurface mSurface;
       
    43 
       
    44 	// Audio
       
    45 	private static Thread mAudioThread;
       
    46 	private static AudioTrack mAudioTrack;
       
    47 
       
    48 	// Load the .so
       
    49 	static {
       
    50 
       
    51 		System.loadLibrary("SDL");
       
    52 		System.loadLibrary("SDL_image");
       
    53 		System.loadLibrary("mikmod");
       
    54 		System.loadLibrary("SDL_net");
       
    55 		System.loadLibrary("SDL_mixer");
       
    56 		System.loadLibrary("SDL_ttf");
       
    57 
       
    58 		System.loadLibrary("main");
       
    59 	}
       
    60 
       
    61 	// Setup
       
    62 	protected void onCreate(Bundle savedInstanceState) {
       
    63 		//Log.v("SDL", "onCreate()");
       
    64 		super.onCreate(savedInstanceState);
       
    65 
       
    66 		// So we can call stuff from static callbacks
       
    67 		mSingleton = this;
       
    68 
       
    69 		// Set up the surface
       
    70 		mSurface = new SDLSurface(getApplication());
       
    71 		setContentView(mSurface);
       
    72 		SurfaceHolder holder = mSurface.getHolder();
       
    73 		holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
       
    74 	}
       
    75 
       
    76 	// Events
       
    77 	protected void onPause() {
       
    78 		//Log.v("SDL", "onPause()");
       
    79 		super.onPause();
       
    80 	}
       
    81 
       
    82 	protected void onResume() {
       
    83 		//Log.v("SDL", "onResume()");
       
    84 		super.onResume();
       
    85 	}
       
    86 
       
    87 	// Messages from the SDLMain thread
       
    88 	static int COMMAND_CHANGE_TITLE = 1;
       
    89 
       
    90 	// Handler for the messages
       
    91 	Handler commandHandler = new Handler() {
       
    92 		public void handleMessage(Message msg) {
       
    93 			if (msg.arg1 == COMMAND_CHANGE_TITLE) {
       
    94 				setTitle((String)msg.obj);
       
    95 			}
       
    96 		}
       
    97 	};
       
    98 
       
    99 	// Send a message from the SDLMain thread
       
   100 	void sendCommand(int command, Object data) {
       
   101 		Message msg = commandHandler.obtainMessage();
       
   102 		msg.arg1 = command;
       
   103 		msg.obj = data;
       
   104 		commandHandler.sendMessage(msg);
       
   105 	}
       
   106 
       
   107 	// C functions we call
       
   108 	public static native void nativeInit();
       
   109 	public static native void nativeQuit();
       
   110 	public static native void onNativeResize(int x, int y, int format);
       
   111 	public static native void onNativeKeyDown(int keycode);
       
   112 	public static native void onNativeKeyUp(int keycode);
       
   113 	public static native void onNativeTouch(int action, float x, 
       
   114 			float y, float p);
       
   115 	public static native void onNativeAccel(float x, float y, float z);
       
   116 	public static native void nativeRunAudioThread();
       
   117 
       
   118 
       
   119 	// Java functions called from C
       
   120 
       
   121 	public static boolean createGLContext(int majorVersion, int minorVersion) {
       
   122 		return mSurface.initEGL(majorVersion, minorVersion);
       
   123 	}
       
   124 
       
   125 	public static void flipBuffers() {
       
   126 		mSurface.flipEGL();
       
   127 	}
       
   128 
       
   129 	public static void setActivityTitle(String title) {
       
   130 		// Called from SDLMain() thread and can't directly affect the view
       
   131 		mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title);
       
   132 	}
       
   133 
       
   134 	// Audio
       
   135 	private static Object buf;
       
   136 
       
   137 	public static Object audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
       
   138 		int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
       
   139 		int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
       
   140 		int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
       
   141 
       
   142 		Log.v("SDL", "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + ((float)sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
       
   143 
       
   144 		// Let the user pick a larger buffer if they really want -- but ye
       
   145 		// gods they probably shouldn't, the minimums are horrifyingly high
       
   146 		// latency already
       
   147 		desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
       
   148 
       
   149 		mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
       
   150 				channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
       
   151 
       
   152 		audioStartThread();
       
   153 
       
   154 		Log.v("SDL", "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + ((float)mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
       
   155 
       
   156 		if (is16Bit) {
       
   157 			buf = new short[desiredFrames * (isStereo ? 2 : 1)];
       
   158 		} else {
       
   159 			buf = new byte[desiredFrames * (isStereo ? 2 : 1)]; 
       
   160 		}
       
   161 		return buf;
       
   162 	}
       
   163 
       
   164 	public static void audioStartThread() {
       
   165 		mAudioThread = new Thread(new Runnable() {
       
   166 			public void run() {
       
   167 				mAudioTrack.play();
       
   168 				nativeRunAudioThread();
       
   169 			}
       
   170 		});
       
   171 
       
   172 		// I'd take REALTIME if I could get it!
       
   173 		mAudioThread.setPriority(Thread.MAX_PRIORITY);
       
   174 		mAudioThread.start();
       
   175 	}
       
   176 
       
   177 	public static void audioWriteShortBuffer(short[] buffer) {
       
   178 		for (int i = 0; i < buffer.length; ) {
       
   179 			int result = mAudioTrack.write(buffer, i, buffer.length - i);
       
   180 			if (result > 0) {
       
   181 				i += result;
       
   182 			} else if (result == 0) {
       
   183 				try {
       
   184 					Thread.sleep(1);
       
   185 				} catch(InterruptedException e) {
       
   186 					// Nom nom
       
   187 				}
       
   188 			} else {
       
   189 				Log.w("SDL", "SDL audio: error return from write(short)");
       
   190 				return;
       
   191 			}
       
   192 		}
       
   193 	}
       
   194 
       
   195 	public static void audioWriteByteBuffer(byte[] buffer) {
       
   196 		for (int i = 0; i < buffer.length; ) {
       
   197 			int result = mAudioTrack.write(buffer, i, buffer.length - i);
       
   198 			if (result > 0) {
       
   199 				i += result;
       
   200 			} else if (result == 0) {
       
   201 				try {
       
   202 					Thread.sleep(1);
       
   203 				} catch(InterruptedException e) {
       
   204 					// Nom nom
       
   205 				}
       
   206 			} else {
       
   207 				Log.w("SDL", "SDL audio: error return from write(short)");
       
   208 				return;
       
   209 			}
       
   210 		}
       
   211 	}
       
   212 
       
   213 	public static void audioQuit() {
       
   214 		if (mAudioThread != null) {
       
   215 			try {
       
   216 				mAudioThread.join();
       
   217 			} catch(Exception e) {
       
   218 				Log.v("SDL", "Problem stopping audio thread: " + e);
       
   219 			}
       
   220 			mAudioThread = null;
       
   221 
       
   222 			//Log.v("SDL", "Finished waiting for audio thread");
       
   223 		}
       
   224 
       
   225 		if (mAudioTrack != null) {
       
   226 			mAudioTrack.stop();
       
   227 			mAudioTrack = null;
       
   228 		}
       
   229 	}
       
   230 }
       
   231 
       
   232 /**
       
   233     Simple nativeInit() runnable
       
   234  */
       
   235 class SDLMain implements Runnable {
       
   236 	public void run() {
       
   237 		// Runs SDL_main()
       
   238 		SDLActivity.nativeInit();
       
   239 
       
   240 		//Log.v("SDL", "SDL thread terminated");
       
   241 	}
       
   242 }
       
   243 
       
   244 
       
   245 /**
       
   246     SDLSurface. This is what we draw on, so we need to know when it's created
       
   247     in order to do anything useful. 
       
   248 
       
   249     Because of this, that's where we set up the SDL thread
       
   250  */
       
   251 class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, 
       
   252 View.OnKeyListener, View.OnTouchListener, SensorEventListener  {
       
   253 
       
   254 	// This is what SDL runs in. It invokes SDL_main(), eventually
       
   255 	private Thread mSDLThread;    
       
   256 
       
   257 	// EGL private objects
       
   258 	private EGLContext  mEGLContext;
       
   259 	private EGLSurface  mEGLSurface;
       
   260 	private EGLDisplay  mEGLDisplay;
       
   261 
       
   262 	// Sensors
       
   263 	private static SensorManager mSensorManager;
       
   264 
       
   265 	// Startup    
       
   266 	public SDLSurface(Context context) {
       
   267 		super(context);
       
   268 		getHolder().addCallback(this); 
       
   269 
       
   270 		setFocusable(true);
       
   271 		setFocusableInTouchMode(true);
       
   272 		requestFocus();
       
   273 		setOnKeyListener(this); 
       
   274 		setOnTouchListener(this);   
       
   275 
       
   276 		mSensorManager = (SensorManager)context.getSystemService("sensor");  
       
   277 	}
       
   278 
       
   279 	// Called when we have a valid drawing surface
       
   280 	public void surfaceCreated(SurfaceHolder holder) {
       
   281 		//Log.v("SDL", "surfaceCreated()");
       
   282 
       
   283 		enableSensor(Sensor.TYPE_ACCELEROMETER, true);
       
   284 	}
       
   285 
       
   286 	// Called when we lose the surface
       
   287 	public void surfaceDestroyed(SurfaceHolder holder) {
       
   288 		//Log.v("SDL", "surfaceDestroyed()");
       
   289 
       
   290 		// Send a quit message to the application
       
   291 		SDLActivity.nativeQuit();
       
   292 
       
   293 		// Now wait for the SDL thread to quit
       
   294 		if (mSDLThread != null) {
       
   295 			try {
       
   296 				mSDLThread.join();
       
   297 			} catch(Exception e) {
       
   298 				Log.v("SDL", "Problem stopping thread: " + e);
       
   299 			}
       
   300 			mSDLThread = null;
       
   301 
       
   302 			//Log.v("SDL", "Finished waiting for SDL thread");
       
   303 		}
       
   304 
       
   305 		enableSensor(Sensor.TYPE_ACCELEROMETER, false);
       
   306 	}
       
   307 
       
   308 	// Called when the surface is resized
       
   309 	public void surfaceChanged(SurfaceHolder holder,
       
   310 			int format, int width, int height) {
       
   311 		Log.d("SDL", "surfaceChanged()"+ width + " + " + height);
       
   312 
       
   313 		int sdlFormat = 0x85151002; // SDL_PIXELFORMAT_RGB565 by default
       
   314 		switch (format) {
       
   315 		case PixelFormat.A_8:
       
   316 			Log.v("SDL", "pixel format A_8");
       
   317 			break;
       
   318 		case PixelFormat.LA_88:
       
   319 			Log.v("SDL", "pixel format LA_88");
       
   320 			break;
       
   321 		case PixelFormat.L_8:
       
   322 			Log.v("SDL", "pixel format L_8");
       
   323 			break;
       
   324 		case PixelFormat.RGBA_4444:
       
   325 			Log.v("SDL", "pixel format RGBA_4444");
       
   326 			sdlFormat = 0x85421002; // SDL_PIXELFORMAT_RGBA4444
       
   327 			break;
       
   328 		case PixelFormat.RGBA_5551:
       
   329 			Log.v("SDL", "pixel format RGBA_5551");
       
   330 			sdlFormat = 0x85441002; // SDL_PIXELFORMAT_RGBA5551
       
   331 			break;
       
   332 		case PixelFormat.RGBA_8888:
       
   333 			Log.v("SDL", "pixel format RGBA_8888");
       
   334 			sdlFormat = 0x86462004; // SDL_PIXELFORMAT_RGBA8888
       
   335 			break;
       
   336 		case PixelFormat.RGBX_8888:
       
   337 			Log.v("SDL", "pixel format RGBX_8888");
       
   338 			sdlFormat = 0x86262004; // SDL_PIXELFORMAT_RGBX8888
       
   339 			break;
       
   340 		case PixelFormat.RGB_332:
       
   341 			Log.v("SDL", "pixel format RGB_332");
       
   342 			sdlFormat = 0x84110801; // SDL_PIXELFORMAT_RGB332
       
   343 			break;
       
   344 		case PixelFormat.RGB_565:
       
   345 			Log.v("SDL", "pixel format RGB_565");
       
   346 			sdlFormat = 0x85151002; // SDL_PIXELFORMAT_RGB565
       
   347 			break;
       
   348 		case PixelFormat.RGB_888:
       
   349 			Log.v("SDL", "pixel format RGB_888");
       
   350 			// Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead?
       
   351 			sdlFormat = 0x86161804; // SDL_PIXELFORMAT_RGB888
       
   352 			break;
       
   353 		default:
       
   354 			Log.v("SDL", "pixel format unknown " + format);
       
   355 			break;
       
   356 		}
       
   357 		SDLActivity.onNativeResize(width, height, sdlFormat);
       
   358 
       
   359 		// Now start up the C app thread
       
   360 		if (mSDLThread == null) {
       
   361 			mSDLThread = new Thread(new SDLMain(), "SDLThread"); 
       
   362 			mSDLThread.start();       
       
   363 		}
       
   364 	}
       
   365 
       
   366 	// unused
       
   367 	public void onDraw(Canvas canvas) {}
       
   368 
       
   369 
       
   370 	// EGL functions
       
   371 	public boolean initEGL(int majorVersion, int minorVersion) {
       
   372 		Log.v("SDL", "Starting up OpenGL ES " + majorVersion + "." + minorVersion);
       
   373 
       
   374 		try {
       
   375 			EGL10 egl = (EGL10)EGLContext.getEGL();
       
   376 
       
   377 			EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
       
   378 
       
   379 			int[] version = new int[2];
       
   380 			egl.eglInitialize(dpy, version);
       
   381 
       
   382 			int EGL_OPENGL_ES_BIT = 1;
       
   383 			int EGL_OPENGL_ES2_BIT = 4;
       
   384 			int renderableType = 0;
       
   385 			if (majorVersion == 2) {
       
   386 				renderableType = EGL_OPENGL_ES2_BIT;
       
   387 			} else if (majorVersion == 1) {
       
   388 				renderableType = EGL_OPENGL_ES_BIT;
       
   389 			}
       
   390 			int[] configSpec = {
       
   391 					//EGL10.EGL_DEPTH_SIZE,   16,
       
   392 					EGL10.EGL_RENDERABLE_TYPE, renderableType,
       
   393 					EGL10.EGL_NONE
       
   394 			};
       
   395 			EGLConfig[] configs = new EGLConfig[1];
       
   396 			int[] num_config = new int[1];
       
   397 			if (!egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config) || num_config[0] == 0) {
       
   398 				Log.e("SDL", "No EGL config available");
       
   399 				return false;
       
   400 			}
       
   401 			EGLConfig config = configs[0];
       
   402 
       
   403 			EGLContext ctx = egl.eglCreateContext(dpy, config, EGL10.EGL_NO_CONTEXT, null);
       
   404 			if (ctx == EGL10.EGL_NO_CONTEXT) {
       
   405 				Log.e("SDL", "Couldn't create context");
       
   406 				return false;
       
   407 			}
       
   408 
       
   409 			EGLSurface surface = egl.eglCreateWindowSurface(dpy, config, this, null);
       
   410 			if (surface == EGL10.EGL_NO_SURFACE) {
       
   411 				Log.e("SDL", "Couldn't create surface");
       
   412 				return false;
       
   413 			}
       
   414 
       
   415 			if (!egl.eglMakeCurrent(dpy, surface, surface, ctx)) {
       
   416 				Log.e("SDL", "Couldn't make context current");
       
   417 				return false;
       
   418 			}
       
   419 
       
   420 			mEGLContext = ctx;
       
   421 			mEGLDisplay = dpy;
       
   422 			mEGLSurface = surface;
       
   423 
       
   424 		} catch(Exception e) {
       
   425 			Log.v("SDL", e + "");
       
   426 			for (StackTraceElement s : e.getStackTrace()) {
       
   427 				Log.v("SDL", s.toString());
       
   428 			}
       
   429 		}
       
   430 
       
   431 		return true;
       
   432 	}
       
   433 
       
   434 	// EGL buffer flip
       
   435 	public void flipEGL() {
       
   436 		try {
       
   437 			EGL10 egl = (EGL10)EGLContext.getEGL();
       
   438 
       
   439 			egl.eglWaitNative(EGL10.EGL_NATIVE_RENDERABLE, null);
       
   440 
       
   441 			// drawing here
       
   442 
       
   443 			egl.eglWaitGL();
       
   444 
       
   445 			egl.eglSwapBuffers(mEGLDisplay, mEGLSurface);
       
   446 
       
   447 
       
   448 		} catch(Exception e) {
       
   449 			Log.v("SDL", "flipEGL(): " + e);
       
   450 			for (StackTraceElement s : e.getStackTrace()) {
       
   451 				Log.v("SDL", s.toString());
       
   452 			}
       
   453 		}
       
   454 	}
       
   455 
       
   456 	// Key events
       
   457 	public boolean onKey(View  v, int keyCode, KeyEvent event) {
       
   458 
       
   459 		if (event.getAction() == KeyEvent.ACTION_DOWN) {
       
   460 			//Log.v("SDL", "key down: " + keyCode);
       
   461 			SDLActivity.onNativeKeyDown(keyCode);
       
   462 			return true;
       
   463 		}
       
   464 		else if (event.getAction() == KeyEvent.ACTION_UP) {
       
   465 			//Log.v("SDL", "key up: " + keyCode);
       
   466 			SDLActivity.onNativeKeyUp(keyCode);
       
   467 			return true;
       
   468 		}
       
   469 
       
   470 		return false;
       
   471 	}
       
   472 
       
   473 	// Touch events
       
   474 	public boolean onTouch(View v, MotionEvent event) {
       
   475 
       
   476 		int action = event.getAction();
       
   477 		float x = event.getX();
       
   478 		float y = event.getY();
       
   479 		float p = event.getPressure();
       
   480 		// TODO: Anything else we need to pass?        
       
   481 		SDLActivity.onNativeTouch(action, x, y, p);
       
   482 		return true;
       
   483 	}
       
   484 
       
   485 	// Sensor events
       
   486 	public void enableSensor(int sensortype, boolean enabled) {
       
   487 		// TODO: This uses getDefaultSensor - what if we have >1 accels?
       
   488 		if (enabled) {
       
   489 			mSensorManager.registerListener(this, 
       
   490 					mSensorManager.getDefaultSensor(sensortype), 
       
   491 					SensorManager.SENSOR_DELAY_GAME, null);
       
   492 		} else {
       
   493 			mSensorManager.unregisterListener(this, 
       
   494 					mSensorManager.getDefaultSensor(sensortype));
       
   495 		}
       
   496 	}
       
   497 
       
   498 	public void onAccuracyChanged(Sensor sensor, int accuracy) {
       
   499 		// TODO
       
   500 	}
       
   501 
       
   502 	public void onSensorChanged(SensorEvent event) {
       
   503 		if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
       
   504 			SDLActivity.onNativeAccel(event.values[0],
       
   505 					event.values[1],
       
   506 					event.values[2]);
       
   507 		}
       
   508 	}
       
   509 
       
   510 }
       
   511