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