# HG changeset patch # User Xeli # Date 1308779941 -7200 # Node ID e29aa9e29f00fcd1f764255ce31aa8967f735588 # Parent 690e88ef6ee7b2e93eda40c183167311b1d14a6a Added SDL-android-project, this is the Android/Java part of the application diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/AndroidManifest.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/AndroidManifest.xml Wed Jun 22 23:59:01 2011 +0200 @@ -0,0 +1,15 @@ + + + + + + + + + + + diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/build.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/build.properties Wed Jun 22 23:59:01 2011 +0200 @@ -0,0 +1,17 @@ +# This file is used to override default values used by the Ant build system. +# +# This file must be checked in Version Control Systems, as it is +# integral to the build system of your project. + +# This file is only used by the Ant script. + +# You can use this to override default values such as +# 'source.dir' for the location of your java source folder and +# 'out.dir' for the location of your output folder. + +# You can also use it define how the release builds are signed by declaring +# the following properties: +# 'key.store' for the location of your keystore and +# 'key.alias' for the name of the key to use. +# The password will be asked during the build when you use the 'release' target. + diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/build.xml Wed Jun 22 23:59:01 2011 +0200 @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/default.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/default.properties Wed Jun 22 23:59:01 2011 +0200 @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-7 diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/jni/Android.mk --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/jni/Android.mk Wed Jun 22 23:59:01 2011 +0200 @@ -0,0 +1,2 @@ +include $(call all-subdir-makefiles) + diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/jni/src/Android.mk --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/jni/src/Android.mk Wed Jun 22 23:59:01 2011 +0200 @@ -0,0 +1,21 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := main + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../SDL/include + +# Add your application source files here... +LOCAL_SRC_FILES := ../SDL/src/main/android/SDL_android_main.cpp hedgewars_main.c + +LOCAL_SHARED_LIBRARIES := SDL + +LOCAL_LDLIBS := -llog -lGLESv1_CM + +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := hwengine +LOCAL_SRC_FILES := $(LOCAL_DIR)../../../out/libhwengine.so +include $(PREBUILT_SHARED_LIBRARY) diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/jni/src/hedgewars_main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/jni/src/hedgewars_main.c Wed Jun 22 23:59:01 2011 +0200 @@ -0,0 +1,40 @@ + +#include "android/log.h" +#include "SDL.h" +#include "dlfcn.h" +#include "GLES/gl.h" + +#define TAG "HWEngine Loader" + +typedef (*HWEngine_Game)(int); + +main(int argc, char *argv[]){ + void *handle; + char *error; + HWEngine_Game Game; + + + __android_log_print(ANDROID_LOG_INFO, TAG, "HWEngine being loaded"); + handle = dlopen("/data/data/org.hedgewars/lib/libhwengine.so", RTLD_NOW|RTLD_GLOBAL); + if(!handle){ + __android_log_print(ANDROID_LOG_INFO, "foo", dlerror()); + __android_log_print(ANDROID_LOG_INFO, "foo", "error dlopen"); + exit(EXIT_FAILURE); + } + dlerror(); + + __android_log_print(ANDROID_LOG_INFO, TAG, "HWEngine successfully loaded.."); + + + Game = (HWEngine_Game) dlsym(handle,"Game"); + if((error = dlerror()) != NULL){ + __android_log_print(ANDROID_LOG_INFO, "foo", error); + __android_log_print(ANDROID_LOG_INFO, "foo", "error dlsym"); + exit(EXIT_FAILURE); + } + __android_log_print(ANDROID_LOG_INFO, "foo", "dlsym succeeded"); + Game(1); + __android_log_print(ANDROID_LOG_INFO, "foo", "Game() succeeded"); + + dlclose(handle); +} diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/local.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/local.properties Wed Jun 22 23:59:01 2011 +0200 @@ -0,0 +1,10 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must *NOT* be checked in Version Control Systems, +# as it contains information specific to your local configuration. + +# location of the SDK. This is only used by Ant +# For customization when using a Version Control System, please read the +# header note. +sdk.dir=/home/richard/SoftDev/android/android-sdk-linux_86 diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/res/drawable-hdpi/icon.png Binary file project_files/Android-build/SDL-android-project/res/drawable-hdpi/icon.png has changed diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/res/drawable-ldpi/icon.png Binary file project_files/Android-build/SDL-android-project/res/drawable-ldpi/icon.png has changed diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/res/drawable-mdpi/hwengine.ico Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hwengine.ico has changed diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/res/drawable-mdpi/icon.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/icon.png has changed diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/res/layout/main.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/res/layout/main.xml Wed Jun 22 23:59:01 2011 +0200 @@ -0,0 +1,13 @@ + + + + + diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/res/values/strings.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/res/values/strings.xml Wed Jun 22 23:59:01 2011 +0200 @@ -0,0 +1,4 @@ + + + SDL App + diff -r 690e88ef6ee7 -r e29aa9e29f00 project_files/Android-build/SDL-android-project/src/org/libsdl/app/SDLActivity.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/libsdl/app/SDLActivity.java Wed Jun 22 23:59:01 2011 +0200 @@ -0,0 +1,511 @@ +package org.libsdl.app; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map.Entry; + +import javax.microedition.khronos.egl.EGL10; +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.graphics.Canvas; +import android.graphics.PixelFormat; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioTrack; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; + + +/** + SDL Activity + */ +public class SDLActivity extends Activity { + + // Main components + private static SDLActivity mSingleton; + private static SDLSurface mSurface; + + // Audio + private static Thread mAudioThread; + private static AudioTrack mAudioTrack; + + // Load the .so + static { + + System.loadLibrary("SDL"); + System.loadLibrary("SDL_image"); + System.loadLibrary("mikmod"); + System.loadLibrary("SDL_net"); + System.loadLibrary("SDL_mixer"); + System.loadLibrary("SDL_ttf"); + + System.loadLibrary("main"); + } + + // Setup + protected void onCreate(Bundle savedInstanceState) { + //Log.v("SDL", "onCreate()"); + super.onCreate(savedInstanceState); + + // So we can call stuff from static callbacks + mSingleton = this; + + // Set up the surface + mSurface = new SDLSurface(getApplication()); + setContentView(mSurface); + SurfaceHolder holder = mSurface.getHolder(); + holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); + } + + // Events + protected void onPause() { + //Log.v("SDL", "onPause()"); + super.onPause(); + } + + protected void onResume() { + //Log.v("SDL", "onResume()"); + super.onResume(); + } + + // Messages from the SDLMain thread + static int COMMAND_CHANGE_TITLE = 1; + + // Handler for the messages + Handler commandHandler = new Handler() { + public void handleMessage(Message msg) { + if (msg.arg1 == COMMAND_CHANGE_TITLE) { + setTitle((String)msg.obj); + } + } + }; + + // Send a message from the SDLMain thread + void sendCommand(int command, Object data) { + Message msg = commandHandler.obtainMessage(); + msg.arg1 = command; + msg.obj = data; + commandHandler.sendMessage(msg); + } + + // C functions we call + public static native void nativeInit(); + public static native void nativeQuit(); + public static native void onNativeResize(int x, int y, int format); + public static native void onNativeKeyDown(int keycode); + public static native void onNativeKeyUp(int keycode); + public static native void onNativeTouch(int action, float x, + float y, float p); + public static native void onNativeAccel(float x, float y, float z); + public static native void nativeRunAudioThread(); + + + // Java functions called from C + + public static boolean createGLContext(int majorVersion, int minorVersion) { + return mSurface.initEGL(majorVersion, minorVersion); + } + + public static void flipBuffers() { + mSurface.flipEGL(); + } + + public static void setActivityTitle(String title) { + // Called from SDLMain() thread and can't directly affect the view + mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); + } + + // Audio + private static Object buf; + + public static Object audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) { + int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO; + int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT; + int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1); + + Log.v("SDL", "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + ((float)sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer"); + + // Let the user pick a larger buffer if they really want -- but ye + // gods they probably shouldn't, the minimums are horrifyingly high + // latency already + desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize); + + mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, + channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); + + audioStartThread(); + + 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"); + + if (is16Bit) { + buf = new short[desiredFrames * (isStereo ? 2 : 1)]; + } else { + buf = new byte[desiredFrames * (isStereo ? 2 : 1)]; + } + return buf; + } + + public static void audioStartThread() { + mAudioThread = new Thread(new Runnable() { + public void run() { + mAudioTrack.play(); + nativeRunAudioThread(); + } + }); + + // I'd take REALTIME if I could get it! + mAudioThread.setPriority(Thread.MAX_PRIORITY); + mAudioThread.start(); + } + + public static void audioWriteShortBuffer(short[] buffer) { + for (int i = 0; i < buffer.length; ) { + int result = mAudioTrack.write(buffer, i, buffer.length - i); + if (result > 0) { + i += result; + } else if (result == 0) { + try { + Thread.sleep(1); + } catch(InterruptedException e) { + // Nom nom + } + } else { + Log.w("SDL", "SDL audio: error return from write(short)"); + return; + } + } + } + + public static void audioWriteByteBuffer(byte[] buffer) { + for (int i = 0; i < buffer.length; ) { + int result = mAudioTrack.write(buffer, i, buffer.length - i); + if (result > 0) { + i += result; + } else if (result == 0) { + try { + Thread.sleep(1); + } catch(InterruptedException e) { + // Nom nom + } + } else { + Log.w("SDL", "SDL audio: error return from write(short)"); + return; + } + } + } + + public static void audioQuit() { + if (mAudioThread != null) { + try { + mAudioThread.join(); + } catch(Exception e) { + Log.v("SDL", "Problem stopping audio thread: " + e); + } + mAudioThread = null; + + //Log.v("SDL", "Finished waiting for audio thread"); + } + + if (mAudioTrack != null) { + mAudioTrack.stop(); + mAudioTrack = null; + } + } +} + +/** + Simple nativeInit() runnable + */ +class SDLMain implements Runnable { + public void run() { + // Runs SDL_main() + SDLActivity.nativeInit(); + + //Log.v("SDL", "SDL thread terminated"); + } +} + + +/** + SDLSurface. This is what we draw on, so we need to know when it's created + in order to do anything useful. + + Because of this, that's where we set up the SDL thread + */ +class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, +View.OnKeyListener, View.OnTouchListener, SensorEventListener { + + // This is what SDL runs in. It invokes SDL_main(), eventually + private Thread mSDLThread; + + // EGL private objects + private EGLContext mEGLContext; + private EGLSurface mEGLSurface; + private EGLDisplay mEGLDisplay; + + // Sensors + private static SensorManager mSensorManager; + + // Startup + public SDLSurface(Context context) { + super(context); + getHolder().addCallback(this); + + setFocusable(true); + setFocusableInTouchMode(true); + requestFocus(); + setOnKeyListener(this); + setOnTouchListener(this); + + mSensorManager = (SensorManager)context.getSystemService("sensor"); + } + + // Called when we have a valid drawing surface + public void surfaceCreated(SurfaceHolder holder) { + //Log.v("SDL", "surfaceCreated()"); + + enableSensor(Sensor.TYPE_ACCELEROMETER, true); + } + + // Called when we lose the surface + public void surfaceDestroyed(SurfaceHolder holder) { + //Log.v("SDL", "surfaceDestroyed()"); + + // Send a quit message to the application + SDLActivity.nativeQuit(); + + // Now wait for the SDL thread to quit + if (mSDLThread != null) { + try { + mSDLThread.join(); + } catch(Exception e) { + Log.v("SDL", "Problem stopping thread: " + e); + } + mSDLThread = null; + + //Log.v("SDL", "Finished waiting for SDL thread"); + } + + enableSensor(Sensor.TYPE_ACCELEROMETER, false); + } + + // Called when the surface is resized + public void surfaceChanged(SurfaceHolder holder, + int format, int width, int height) { + //Log.v("SDL", "surfaceChanged()"); + + int sdlFormat = 0x85151002; // SDL_PIXELFORMAT_RGB565 by default + switch (format) { + case PixelFormat.A_8: + Log.v("SDL", "pixel format A_8"); + break; + case PixelFormat.LA_88: + Log.v("SDL", "pixel format LA_88"); + break; + case PixelFormat.L_8: + Log.v("SDL", "pixel format L_8"); + break; + case PixelFormat.RGBA_4444: + Log.v("SDL", "pixel format RGBA_4444"); + sdlFormat = 0x85421002; // SDL_PIXELFORMAT_RGBA4444 + break; + case PixelFormat.RGBA_5551: + Log.v("SDL", "pixel format RGBA_5551"); + sdlFormat = 0x85441002; // SDL_PIXELFORMAT_RGBA5551 + break; + case PixelFormat.RGBA_8888: + Log.v("SDL", "pixel format RGBA_8888"); + sdlFormat = 0x86462004; // SDL_PIXELFORMAT_RGBA8888 + break; + case PixelFormat.RGBX_8888: + Log.v("SDL", "pixel format RGBX_8888"); + sdlFormat = 0x86262004; // SDL_PIXELFORMAT_RGBX8888 + break; + case PixelFormat.RGB_332: + Log.v("SDL", "pixel format RGB_332"); + sdlFormat = 0x84110801; // SDL_PIXELFORMAT_RGB332 + break; + case PixelFormat.RGB_565: + Log.v("SDL", "pixel format RGB_565"); + sdlFormat = 0x85151002; // SDL_PIXELFORMAT_RGB565 + break; + case PixelFormat.RGB_888: + Log.v("SDL", "pixel format RGB_888"); + // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead? + sdlFormat = 0x86161804; // SDL_PIXELFORMAT_RGB888 + break; + default: + Log.v("SDL", "pixel format unknown " + format); + break; + } + SDLActivity.onNativeResize(width, height, sdlFormat); + + // Now start up the C app thread + if (mSDLThread == null) { + mSDLThread = new Thread(new SDLMain(), "SDLThread"); + mSDLThread.start(); + } + } + + // unused + public void onDraw(Canvas canvas) {} + + + // EGL functions + public boolean initEGL(int majorVersion, int minorVersion) { + Log.v("SDL", "Starting up OpenGL ES " + majorVersion + "." + minorVersion); + + try { + EGL10 egl = (EGL10)EGLContext.getEGL(); + + EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + + int[] version = new int[2]; + egl.eglInitialize(dpy, version); + + int EGL_OPENGL_ES_BIT = 1; + int EGL_OPENGL_ES2_BIT = 4; + int renderableType = 0; + if (majorVersion == 2) { + renderableType = EGL_OPENGL_ES2_BIT; + } else if (majorVersion == 1) { + renderableType = EGL_OPENGL_ES_BIT; + } + int[] configSpec = { + //EGL10.EGL_DEPTH_SIZE, 16, + EGL10.EGL_RENDERABLE_TYPE, renderableType, + EGL10.EGL_NONE + }; + EGLConfig[] configs = new EGLConfig[1]; + int[] num_config = new int[1]; + if (!egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config) || num_config[0] == 0) { + Log.e("SDL", "No EGL config available"); + return false; + } + EGLConfig config = configs[0]; + + EGLContext ctx = egl.eglCreateContext(dpy, config, EGL10.EGL_NO_CONTEXT, null); + if (ctx == EGL10.EGL_NO_CONTEXT) { + Log.e("SDL", "Couldn't create context"); + return false; + } + + EGLSurface surface = egl.eglCreateWindowSurface(dpy, config, this, null); + if (surface == EGL10.EGL_NO_SURFACE) { + Log.e("SDL", "Couldn't create surface"); + return false; + } + + if (!egl.eglMakeCurrent(dpy, surface, surface, ctx)) { + Log.e("SDL", "Couldn't make context current"); + return false; + } + + mEGLContext = ctx; + mEGLDisplay = dpy; + mEGLSurface = surface; + + } catch(Exception e) { + Log.v("SDL", e + ""); + for (StackTraceElement s : e.getStackTrace()) { + Log.v("SDL", s.toString()); + } + } + + return true; + } + + // EGL buffer flip + public void flipEGL() { + try { + EGL10 egl = (EGL10)EGLContext.getEGL(); + + egl.eglWaitNative(EGL10.EGL_NATIVE_RENDERABLE, null); + + // drawing here + + egl.eglWaitGL(); + + egl.eglSwapBuffers(mEGLDisplay, mEGLSurface); + + + } catch(Exception e) { + Log.v("SDL", "flipEGL(): " + e); + for (StackTraceElement s : e.getStackTrace()) { + Log.v("SDL", s.toString()); + } + } + } + + // Key events + public boolean onKey(View v, int keyCode, KeyEvent event) { + + if (event.getAction() == KeyEvent.ACTION_DOWN) { + //Log.v("SDL", "key down: " + keyCode); + SDLActivity.onNativeKeyDown(keyCode); + return true; + } + else if (event.getAction() == KeyEvent.ACTION_UP) { + //Log.v("SDL", "key up: " + keyCode); + SDLActivity.onNativeKeyUp(keyCode); + return true; + } + + return false; + } + + // Touch events + public boolean onTouch(View v, MotionEvent event) { + + int action = event.getAction(); + float x = event.getX(); + float y = event.getY(); + float p = event.getPressure(); + // TODO: Anything else we need to pass? + SDLActivity.onNativeTouch(action, x, y, p); + return true; + } + + // Sensor events + public void enableSensor(int sensortype, boolean enabled) { + // TODO: This uses getDefaultSensor - what if we have >1 accels? + if (enabled) { + mSensorManager.registerListener(this, + mSensorManager.getDefaultSensor(sensortype), + SensorManager.SENSOR_DELAY_GAME, null); + } else { + mSensorManager.unregisterListener(this, + mSensorManager.getDefaultSensor(sensortype)); + } + } + + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // TODO + } + + public void onSensorChanged(SensorEvent event) { + if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { + SDLActivity.onNativeAccel(event.values[0], + event.values[1], + event.values[2]); + } + } + +} +