# HG changeset patch # User nkeynes # Date 1330696150 -36000 # Node ID 01e0020adf88ce58d18ebaf401b61192831dddc0 # Parent 6b54ef5ed41388c47b60f2407958a5464b157e1e Android WIP: * Rename gui_jni.c to gui_android.c - now quite android specific. * Implement generic EGL driver with very minimal Java wrapper * Run emulation in separate thread, and implement simple queue for inter-thread communication. * Add menu/action-bar items for start + reset --- a/Makefile.am Tue Feb 28 18:22:52 2012 +1000 +++ b/Makefile.am Fri Mar 02 23:49:10 2012 +1000 @@ -100,7 +100,7 @@ bundle: all if GUI_ANDROID -Lxdream-debug.apk: src/liblxdream.so +apk: src/liblxdream.so $(mkdir_p) android/libs/armeabi $(INSTALL) src/liblxdream.so android/libs/armeabi/liblxdream.so $(INSTALL) $(ANDROID_GDBSERVER) android/libs/armeabi/gdbserver @@ -109,8 +109,9 @@ $(ANT) -buildfile build.xml -Dsdk.dir=$(ANDROID_SDK_HOME) \ -Dout.dir="$$TARGETDIR" \ -Dnative.libs.dir="$$TARGETDIR/libs" \ + -Dnative.libs.absolute.dir="$$TARGETDIR/libs" \ -Dtarget=$(ANDROID_SDK_VERSION) debug ) cp android/Lxdream-debug.apk Lxdream-debug.apk -all-local: Lxdream-debug.apk +all-local: apk endif --- a/Makefile.in Tue Feb 28 18:22:52 2012 +1000 +++ b/Makefile.in Fri Mar 02 23:49:10 2012 +1000 @@ -96,6 +96,7 @@ AMTAR = @AMTAR@ ANDROID_GDBSERVER = @ANDROID_GDBSERVER@ ANDROID_NDK_HOME = @ANDROID_NDK_HOME@ +ANDROID_NDK_VERSION = @ANDROID_NDK_VERSION@ ANDROID_SDK_HOME = @ANDROID_SDK_HOME@ ANDROID_SDK_VERSION = @ANDROID_SDK_VERSION@ ANT = @ANT@ @@ -889,7 +890,7 @@ bundle: all -@GUI_ANDROID_TRUE@Lxdream-debug.apk: src/liblxdream.so +@GUI_ANDROID_TRUE@apk: src/liblxdream.so @GUI_ANDROID_TRUE@ $(mkdir_p) android/libs/armeabi @GUI_ANDROID_TRUE@ $(INSTALL) src/liblxdream.so android/libs/armeabi/liblxdream.so @GUI_ANDROID_TRUE@ $(INSTALL) $(ANDROID_GDBSERVER) android/libs/armeabi/gdbserver @@ -898,10 +899,11 @@ @GUI_ANDROID_TRUE@ $(ANT) -buildfile build.xml -Dsdk.dir=$(ANDROID_SDK_HOME) \ @GUI_ANDROID_TRUE@ -Dout.dir="$$TARGETDIR" \ @GUI_ANDROID_TRUE@ -Dnative.libs.dir="$$TARGETDIR/libs" \ +@GUI_ANDROID_TRUE@ -Dnative.libs.absolute.dir="$$TARGETDIR/libs" \ @GUI_ANDROID_TRUE@ -Dtarget=$(ANDROID_SDK_VERSION) debug ) @GUI_ANDROID_TRUE@ cp android/Lxdream-debug.apk Lxdream-debug.apk -@GUI_ANDROID_TRUE@all-local: Lxdream-debug.apk +@GUI_ANDROID_TRUE@all-local: apk # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: --- a/android/AndroidManifest.xml Tue Feb 28 18:22:52 2012 +1000 +++ b/android/AndroidManifest.xml Fri Mar 02 23:49:10 2012 +1000 @@ -2,8 +2,8 @@ @@ -12,5 +12,5 @@ - + --- a/android/build.xml Tue Feb 28 18:22:52 2012 +1000 +++ b/android/build.xml Fri Mar 02 23:49:10 2012 +1000 @@ -1,15 +1,14 @@ - + - - + - - + - - - + + - - + + --- a/android/gen/org/lxdream/R.java Tue Feb 28 18:22:52 2012 +1000 +++ b/android/gen/org/lxdream/R.java Fri Mar 02 23:49:10 2012 +1000 @@ -10,7 +10,24 @@ public final class R { public static final class attr { } + public static final class drawable { + public static final int tb_cdrom=0x7f020000; + public static final int tb_preferences=0x7f020001; + public static final int tb_reset=0x7f020002; + public static final int tb_run=0x7f020003; + } + public static final class id { + public static final int menu_reset=0x7f050001; + public static final int menu_run=0x7f050000; + public static final int menu_settings=0x7f050002; + } + public static final class menu { + public static final int main=0x7f040000; + } public static final class string { - public static final int lxdream_activity=0x7f020000; + public static final int lxdream_activity=0x7f030000; + public static final int menu_preferences=0x7f030003; + public static final int menu_reset=0x7f030002; + public static final int menu_run=0x7f030001; } } Binary file android/res/drawable/tb_cdrom.png has changed Binary file android/res/drawable/tb_preferences.png has changed Binary file android/res/drawable/tb_reset.png has changed Binary file android/res/drawable/tb_run.png has changed --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/android/res/menu/main.xml Fri Mar 02 23:49:10 2012 +1000 @@ -0,0 +1,17 @@ + + + + + --- a/android/res/values/strings.xml Tue Feb 28 18:22:52 2012 +1000 +++ b/android/res/values/strings.xml Fri Mar 02 23:49:10 2012 +1000 @@ -1,4 +1,7 @@ Lxdream + Run + Reset + Preferences --- a/android/src/org/lxdream/Dreamcast.java Tue Feb 28 18:22:52 2012 +1000 +++ b/android/src/org/lxdream/Dreamcast.java Fri Mar 02 23:49:10 2012 +1000 @@ -26,14 +26,18 @@ /* Core emulation */ public static native void init( String appHome ); - public static native void setViewSize(int width, int height); public static native void run(); public static native void stop(); - /* - public static native void start(); - public static native void run_slice(); - public static boolean canRun(); -*/ + public static native void reset(); + public static native void toggleRun(); + public static native boolean isRunnable(); + public static native boolean isRunning(); + + /* GD-Rom */ + public static native boolean mount( String filename ); + public static native void unmount(); + + /* Save state management */ /* public static native boolean saveState( String filename ); public static native boolean loadState( String filename ); @@ -41,9 +45,4 @@ public static native boolean quickLoad(); public static native void setQuickState(int state); */ - /* GD-Rom */ -/* public static native boolean mount_disc( String filename ); - public static native void unmount_disc(); - */ - /* ... */ } --- a/android/src/org/lxdream/LxdreamActivity.java Tue Feb 28 18:22:52 2012 +1000 +++ b/android/src/org/lxdream/LxdreamActivity.java Fri Mar 02 23:49:10 2012 +1000 @@ -22,6 +22,9 @@ import android.content.Context; import android.os.Bundle; import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.WindowManager; import java.io.File; @@ -34,20 +37,42 @@ protected void onCreate(Bundle bundle) { super.onCreate(bundle); Context ctx = getApplication(); + + Log.i("LxdreamActivity", "Calling Dreamcast.init"); Dreamcast.init( ctx.getFilesDir().toString() ); + Log.i("LxdreamActivity", "Finished Dreamcast.init"); view = new LxdreamView(ctx); setContentView(view); } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.main, menu); + return true; + } + @Override protected void onPause() { super.onPause(); - view.onPause(); + Dreamcast.stop(); } @Override protected void onResume() { super.onResume(); - view.onResume(); + } + + public void onRunClicked( MenuItem item ) { + Dreamcast.toggleRun(); + } + + public void onResetClicked( MenuItem item ) { + Dreamcast.reset(); + } + + public void onPreferencesClicked( MenuItem item ) { + /* TODO */ } } --- a/android/src/org/lxdream/LxdreamView.java Tue Feb 28 18:22:52 2012 +1000 +++ b/android/src/org/lxdream/LxdreamView.java Fri Mar 02 23:49:10 2012 +1000 @@ -36,114 +36,39 @@ import android.content.Context; import android.graphics.PixelFormat; -import android.opengl.GLSurfaceView; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; -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.opengles.GL10; - -/** - * A simple GLSurfaceView sub-class that demonstrate how to perform - * OpenGL ES 2.0 rendering into a GL Surface. Note the following important - * details: - * - * - The class must use a custom context factory to enable 2.0 rendering. - * See ContextFactory class definition below. - * - * - The class must use a custom EGLConfigChooser to be able to select - * an EGLConfig that supports 2.0. This is done by providing a config - * specification to eglChooseConfig() that has the attribute - * EGL10.ELG_RENDERABLE_TYPE containing the EGL_OPENGL_ES2_BIT flag - * set. See ConfigChooser class definition below. - * - * - The class must select the surface's format, then choose an EGLConfig - * that matches it exactly (with regards to red/green/blue/alpha channels - * bit depths). Failure to do so would result in an EGL_BAD_MATCH error. - */ -class LxdreamView extends GLSurfaceView { +class LxdreamView extends SurfaceView implements SurfaceHolder.Callback { private static String TAG = "LxdreamView"; private static final boolean DEBUG = false; public LxdreamView(Context context) { super(context); - init(false, 0, 0); + getHolder().addCallback(this); } - public LxdreamView(Context context, boolean translucent, int depth, int stencil) { - super(context); - init(translucent, depth, stencil); + @Override + public void surfaceCreated( SurfaceHolder holder ) { + /* Ignore */ } - - private void init(boolean translucent, int depth, int stencil) { - - /* By default, GLSurfaceView() creates a RGB_565 opaque surface. - * If we want a translucent one, we should change the surface's - * format here, using PixelFormat.TRANSLUCENT for GL Surfaces - * is interpreted as any 32-bit surface with alpha by SurfaceFlinger. - */ - if (translucent) { - this.getHolder().setFormat(PixelFormat.TRANSLUCENT); - } - - /* Setup the context factory for 2.0 rendering. - * See ContextFactory class definition below - */ - setEGLContextFactory(new ContextFactory()); - - /* We need to choose an EGLConfig that matches the format of - * our surface exactly. This is going to be done in our - * custom config chooser. See ConfigChooser class definition - * below. - */ - setEGLConfigChooser( translucent ? - new ConfigChooser(8, 8, 8, 8, depth, stencil) : - new ConfigChooser(5, 6, 5, 0, depth, stencil) ); - - /* Set the renderer responsible for frame rendering */ - setRenderer(new Renderer()); + + @Override + public void surfaceChanged( SurfaceHolder holder, int format, int width, int height ) { + setSurface( holder.getSurface(), width, height ); } - - private static class ContextFactory implements GLSurfaceView.EGLContextFactory { - private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { - Log.w(TAG, "creating OpenGL ES 2.0 context"); - checkEglError("Before eglCreateContext", egl); - int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; - EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); - checkEglError("After eglCreateContext", egl); - return context; - } - - public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { - egl.eglDestroyContext(display, context); - } + + @Override + public void surfaceDestroyed( SurfaceHolder holder ) { + clearSurface( holder.getSurface() ); } - - private static void checkEglError(String prompt, EGL10 egl) { - int error; - while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { - Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); - } - } - - - private static class Renderer implements GLSurfaceView.Renderer { - public void onDrawFrame(GL10 gl) { - Dreamcast.run(); - } - - public void onSurfaceChanged(GL10 gl, int width, int height) { - Dreamcast.setViewSize(width,height); - } - - public void onSurfaceCreated(GL10 gl, EGLConfig config) { - // Do nothing. - } - } + + private native void setSurface( Surface surface, int width, int height ); + private native void clearSurface( Surface surface ); + } --- a/configure Tue Feb 28 18:22:52 2012 +1000 +++ b/configure Fri Mar 02 23:49:10 2012 +1000 @@ -686,6 +686,7 @@ ANDROID_SDK_HOME ANDROID_NDK_HOME ANDROID_SDK_VERSION +ANDROID_NDK_VERSION ANDROID_GDBSERVER GUI_ANDROID_TRUE GUI_ANDROID_FALSE @@ -1465,6 +1466,7 @@ --with-android=SDK Specify the location of the Android SDK --with-android-ndk=NDK Specify the location of the Android NDK --with-android-version Specify target Android SDK version + --with-android-version Specify target Android NDK version --with-osmesa Build with the osmesa GL library (software rendering) --with-gtk Build with the GTK UI. Default on X11 systems @@ -2543,7 +2545,15 @@ if test "${with_android_version+set}" = set; then withval=$with_android_version; else - with_android_version="android-8" + with_android_version="android-11" +fi + + +# Check whether --with-android-ndk-version was given. +if test "${with_android_ndk_version+set}" = set; then + withval=$with_android_ndk_version; +else + with_ndk_version="android-9" fi @@ -2562,6 +2572,7 @@ ANDROID_SDK_HOME="$with_android" ANDROID_NDK_HOME="$with_android_ndk" ANDROID_SDK_VERSION="$with_android_version" + ANDROID_NDK_VERSION="$with_ndk_version" as_ac_File=`echo "ac_cv_file_$ANDROID_SDK_HOME/tools/ant/pre_setup.xml" | $as_tr_sh` { echo "$as_me:$LINENO: checking for $ANDROID_SDK_HOME/tools/ant/pre_setup.xml" >&5 @@ -2653,7 +2664,7 @@ host_os="linux-androideabi" ANDROID_NDK_BIN=`echo $ANDROID_NDK_HOME/toolchains/arm-*/prebuilt/*/bin` ANDROID_GDBSERVER=`echo $ANDROID_NDK_HOME/toolchains/arm-*/prebuilt/gdbserver` - ANDROID_SYSROOT="$ANDROID_NDK_HOME/platforms/$ANDROID_SDK_VERSION/arch-arm" + ANDROID_SYSROOT="$ANDROID_NDK_HOME/platforms/$ANDROID_NDK_VERSION/arch-arm" TARGETFLAGS="-ffunction-sections -funwind-tables -fstack-protector -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -DANDROID -Wno-psabi -Wa,--noexecstack" TARGETFLAGS="$TARGETFLAGS -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -march=armv5te -mtune=xscale -msoft-float -mthumb -Os" ;; @@ -2664,7 +2675,7 @@ host_os="linux" ANDROID_NDK_BIN=`echo $ANDROID_NDK_HOME/toolchains/x86-*/prebuilt/*/bin` ANDROID_GDBSERVER=`echo $ANDROID_NDK_HOME/toolchains/x86-*/prebuilt/gdbserver` - ANDROID_SYSROOT="$ANDROID_NDK_HOME/platforms/$ANDROID_SDK_VERSION/arch-x86" + ANDROID_SYSROOT="$ANDROID_NDK_HOME/platforms/$ANDROID_NDK_VERSION/arch-x86" TARGETFLAGS="" ;; *) @@ -2725,7 +2736,8 @@ OBJDUMP="$ANDROID_NDK_BIN/${host_alias}-objdump" CPPFLAGS="-fPIC --sysroot=$ANDROID_SYSROOT -I$ANDROID_SYSROOT/usr/include $TARGETFLAGS $CPPFLAGS" LDFLAGS="-nostdlib -Wl,--no-undefined -L${ANDROID_SYSROOT}/usr/lib -Wl,-rpath-link,${ANDROID_SYSROOT}/usr/lib -Wl,-allow-shlib-undefined -Wl,-z,noexecstack $LDFLAGS" - LIBS="$LIBS -liconv -llog -lgcc -lc" + LIBS="$LIBS -liconv -landroid -llog -lgcc -lc -lm" + @@ -8966,16 +8978,16 @@ - if test "$ANDROID_BUILD" = "yes"; then with_gtk=no; EXTRA_OUTPUT_FILES="src/android/build.properties" - LIBS="-lGLESv2 $LIBS" + LIBS="-lEGL -lGLESv2 $LIBS" cat >>confdefs.h <<\_ACEOF #define HAVE_GLES2 1 _ACEOF + UI_DRIVER="Android" with_sdl=no else @@ -9442,6 +9454,7 @@ #define APPLE_BUILD 1 _ACEOF + UI_DRIVER="Cocoa" if test "x$with_gtk" = "xx11"; then with_gtk=no fi @@ -11020,6 +11033,7 @@ #define HAVE_GTK 1 _ACEOF + UI_DRIVER="GTK" fi @@ -16749,6 +16763,7 @@ ANDROID_SDK_HOME!$ANDROID_SDK_HOME$ac_delim ANDROID_NDK_HOME!$ANDROID_NDK_HOME$ac_delim ANDROID_SDK_VERSION!$ANDROID_SDK_VERSION$ac_delim +ANDROID_NDK_VERSION!$ANDROID_NDK_VERSION$ac_delim ANDROID_GDBSERVER!$ANDROID_GDBSERVER$ac_delim GUI_ANDROID_TRUE!$GUI_ANDROID_TRUE$ac_delim GUI_ANDROID_FALSE!$GUI_ANDROID_FALSE$ac_delim @@ -16772,7 +16787,6 @@ CC!$CC$ac_delim CFLAGS!$CFLAGS$ac_delim CPPFLAGS!$CPPFLAGS$ac_delim -CC_FOR_BUILD!$CC_FOR_BUILD$ac_delim _ACEOF if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then @@ -16814,6 +16828,7 @@ ac_delim='%!_!# ' for ac_last_try in false false false false false :; do cat >conf$$subs.sed <<_ACEOF +CC_FOR_BUILD!$CC_FOR_BUILD$ac_delim ac_ct_CC_FOR_BUILD!$ac_ct_CC_FOR_BUILD$ac_delim CC_FOR_BUILDDEPMODE!$CC_FOR_BUILDDEPMODE$ac_delim am__fastdepCC_FOR_BUILD_TRUE!$am__fastdepCC_FOR_BUILD_TRUE$ac_delim @@ -16910,7 +16925,6 @@ BUILD_ARMTEST_TRUE!$BUILD_ARMTEST_TRUE$ac_delim BUILD_ARMTEST_FALSE!$BUILD_ARMTEST_FALSE$ac_delim LXDREAM_LIBS!$LXDREAM_LIBS$ac_delim -GETTEXT_PACKAGE!$GETTEXT_PACKAGE$ac_delim _ACEOF if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then @@ -16952,6 +16966,7 @@ ac_delim='%!_!# ' for ac_last_try in false false false false false :; do cat >conf$$subs.sed <<_ACEOF +GETTEXT_PACKAGE!$GETTEXT_PACKAGE$ac_delim USE_NLS!$USE_NLS$ac_delim MSGFMT!$MSGFMT$ac_delim MSGFMT_OPTS!$MSGFMT_OPTS$ac_delim @@ -16972,7 +16987,7 @@ LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF - if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 18; then + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 19; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 @@ -17613,18 +17628,14 @@ echo "Configuration complete" echo -if test "x$HAVE_GTK" = x; then - if test "x$HAVE_COCOA" = x; then - echo " User interface: none" - else - echo " User interface: Cocoa" - fi -else - echo " User interface: GTK" +if test "x$UI_DRIVER" = x; then + echo " User interface: none" +else + echo " User interface: $UI_DRIVER" fi if test "x$SH4_TRANSLATOR" = "x"; then - echo " SH4 translator: None (emulation core only)" + echo " SH4 translator: none (emulation core only)" else echo " SH4 translator: $SH4_TRANSLATOR" fi --- a/configure.in Tue Feb 28 18:22:52 2012 +1000 +++ b/configure.in Fri Mar 02 23:49:10 2012 +1000 @@ -79,15 +79,15 @@ AC_ARG_WITH( sdl, AS_HELP_STRING( [--with-sdl], [Build with support for SDL audio]) ) - -dnl ------------ Check if we're building on Darwin -------------- +dnl ------------ Check if we're building on Darwin or Android -------------- dnl For starters, do we have a working objective-c compiler? if test "$ANDROID_BUILD" = "yes"; then with_gtk=no; EXTRA_OUTPUT_FILES="src/android/build.properties" - LIBS="-lGLESv2 $LIBS" + LIBS="-lEGL -lGLESv2 $LIBS" AC_DEFINE(HAVE_GLES2, 1, [Using GLESv2]) + UI_DRIVER="Android" with_sdl=no else AC_HAVE_OBJC([ @@ -100,6 +100,7 @@ LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names" AC_DEFINE(HAVE_COCOA,[1],[Have Cocoa framework]) AC_DEFINE(APPLE_BUILD,[1],[Building on an apple platform. Things are different...]) + UI_DRIVER="Cocoa" if test "x$with_gtk" = "xx11"; then with_gtk=no fi @@ -230,6 +231,7 @@ PKG_CHECK_MODULES(GTK, gtk+-2.0, [ HAVE_GTK='yes' AC_DEFINE([HAVE_GTK],1,[Have GTK libraries]) + UI_DRIVER="GTK" ]) dnl Which GTK port do we have? @@ -485,18 +487,14 @@ echo "Configuration complete" echo -if test "x$HAVE_GTK" = x; then - if test "x$HAVE_COCOA" = x; then - echo " User interface: none" - else - echo " User interface: Cocoa" - fi +if test "x$UI_DRIVER" = x; then + echo " User interface: none" else - echo " User interface: GTK" + echo " User interface: $UI_DRIVER" fi if test "x$SH4_TRANSLATOR" = "x"; then - echo " SH4 translator: None (emulation core only)" + echo " SH4 translator: none (emulation core only)" else echo " SH4 translator: $SH4_TRANSLATOR" fi --- a/m4/android.m4 Tue Feb 28 18:22:52 2012 +1000 +++ b/m4/android.m4 Fri Mar 02 23:49:10 2012 +1000 @@ -6,7 +6,8 @@ AC_REQUIRE([AC_CANONICAL_HOST]) AC_ARG_WITH( android, AS_HELP_STRING( [--with-android=SDK], [Specify the location of the Android SDK] ) ) AC_ARG_WITH( android-ndk, AS_HELP_STRING( [--with-android-ndk=NDK], [Specify the location of the Android NDK] ) ) - AC_ARG_WITH( android-version, AS_HELP_STRING( [--with-android-version], [Specify target Android SDK version]), [], [with_android_version="android-8"] ) + AC_ARG_WITH( android-version, AS_HELP_STRING( [--with-android-version], [Specify target Android SDK version]), [], [with_android_version="android-11"] ) + AC_ARG_WITH( android-ndk-version, AS_HELP_STRING( [--with-android-version], [Specify target Android NDK version]), [], [with_ndk_version="android-9"] ) if test "x$with_android" != "x"; then if test "$with_android" = "yes"; then @@ -19,6 +20,7 @@ ANDROID_SDK_HOME="$with_android" ANDROID_NDK_HOME="$with_android_ndk" ANDROID_SDK_VERSION="$with_android_version" + ANDROID_NDK_VERSION="$with_ndk_version" AC_CHECK_FILE( [$ANDROID_SDK_HOME/tools/ant/pre_setup.xml], [], [ AC_MSG_ERROR([Android SDK not found in $ANDROID_SDK_HOME]) ]) AC_CHECK_FILE( [$ANDROID_SDK_HOME/platforms/$ANDROID_SDK_VERSION/sdk.properties], [], [ AC_MSG_ERROR([Android platform version $ANDROID_SDK_VERSION not found in $ANDROID_SDK_HOME]) ]) @@ -32,7 +34,7 @@ host_os="linux-androideabi" ANDROID_NDK_BIN=`echo $ANDROID_NDK_HOME/toolchains/arm-*/prebuilt/*/bin` ANDROID_GDBSERVER=`echo $ANDROID_NDK_HOME/toolchains/arm-*/prebuilt/gdbserver` - ANDROID_SYSROOT="$ANDROID_NDK_HOME/platforms/$ANDROID_SDK_VERSION/arch-arm" + ANDROID_SYSROOT="$ANDROID_NDK_HOME/platforms/$ANDROID_NDK_VERSION/arch-arm" TARGETFLAGS="-ffunction-sections -funwind-tables -fstack-protector -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -DANDROID -Wno-psabi -Wa,--noexecstack" TARGETFLAGS="$TARGETFLAGS -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -march=armv5te -mtune=xscale -msoft-float -mthumb -Os" ;; @@ -43,7 +45,7 @@ host_os="linux" ANDROID_NDK_BIN=`echo $ANDROID_NDK_HOME/toolchains/x86-*/prebuilt/*/bin` ANDROID_GDBSERVER=`echo $ANDROID_NDK_HOME/toolchains/x86-*/prebuilt/gdbserver` - ANDROID_SYSROOT="$ANDROID_NDK_HOME/platforms/$ANDROID_SDK_VERSION/arch-x86" + ANDROID_SYSROOT="$ANDROID_NDK_HOME/platforms/$ANDROID_NDK_VERSION/arch-x86" TARGETFLAGS="" ;; *) @@ -63,11 +65,12 @@ OBJDUMP="$ANDROID_NDK_BIN/${host_alias}-objdump" CPPFLAGS="-fPIC --sysroot=$ANDROID_SYSROOT -I$ANDROID_SYSROOT/usr/include $TARGETFLAGS $CPPFLAGS" LDFLAGS="-nostdlib -Wl,--no-undefined -L${ANDROID_SYSROOT}/usr/lib -Wl,-rpath-link,${ANDROID_SYSROOT}/usr/lib -Wl,-allow-shlib-undefined -Wl,-z,noexecstack $LDFLAGS" - LIBS="$LIBS -liconv -llog -lgcc -lc" + LIBS="$LIBS -liconv -landroid -llog -lgcc -lc -lm" AC_SUBST(ANDROID_SDK_HOME) AC_SUBST(ANDROID_NDK_HOME) AC_SUBST(ANDROID_SDK_VERSION) + AC_SUBST(ANDROID_NDK_VERSION) AC_SUBST(ANDROID_GDBSERVER) ANDROID_BUILD=yes --- a/src/Makefile.am Tue Feb 28 18:22:52 2012 +1000 +++ b/src/Makefile.am Fri Mar 02 23:49:10 2012 +1000 @@ -111,11 +111,11 @@ endif if GUI_ANDROID -lxdream_SOURCES += gui_none.c +lxdream_SOURCES += gui_none.c drivers/video_egl.c drivers/video_egl.h noinst_PROGRAMS=liblxdream.so liblxdream_so_LINK = $(LINK) -Wl,-soname,liblxdream.so -shared liblxdream_so_LDADD = liblxdream-core.a @GLIB_LIBS@ @GTK_LIBS@ @LIBPNG_LIBS@ @LIBISOFS_LIBS@ $(INTLLIBS) @LXDREAM_LIBS@ -lm -liblxdream_so_SOURCES = gui_jni.c drivers/cdrom/cd_none.c +liblxdream_so_SOURCES = gui_android.c drivers/cdrom/cd_none.c drivers/video_egl.c drivers/video_egl.h tqueue.c tqueue.h liblxdream_so_LIBS = liblxdream-core.a endif --- a/src/Makefile.in Tue Feb 28 18:22:52 2012 +1000 +++ b/src/Makefile.in Fri Mar 02 23:49:10 2012 +1000 @@ -58,7 +58,7 @@ @GUI_GTK_TRUE@ gtkui/gtk_ctrl.c gtkui/gtk_gd.c \ @GUI_GTK_TRUE@ drivers/video_gtk.c -@GUI_ANDROID_TRUE@am__append_5 = gui_none.c +@GUI_ANDROID_TRUE@am__append_5 = gui_none.c drivers/video_egl.c drivers/video_egl.h @GUI_ANDROID_TRUE@noinst_PROGRAMS = liblxdream.so$(EXEEXT) @GUI_COCOA_TRUE@am__append_6 = cocoaui/paths_osx.m drivers/io_osx.m drivers/mac_keymap.h drivers/mac_keymap.txt @GUI_COCOA_TRUE@am__append_7 = cocoaui/cocoaui.m cocoaui/cocoaui.h cocoaui/cocoa_cfg.m \ @@ -216,9 +216,11 @@ input_lirc_@SOEXT@_OBJECTS = $(am_input_lirc_@SOEXT@_OBJECTS) @BUILD_PLUGINS_TRUE@@INPUT_LIRC_TRUE@input_lirc_@SOEXT@_DEPENDENCIES = \ @BUILD_PLUGINS_TRUE@@INPUT_LIRC_TRUE@ input_lirc.lo -am__liblxdream_so_SOURCES_DIST = gui_jni.c drivers/cdrom/cd_none.c -@GUI_ANDROID_TRUE@am_liblxdream_so_OBJECTS = gui_jni.$(OBJEXT) \ -@GUI_ANDROID_TRUE@ cd_none.$(OBJEXT) +am__liblxdream_so_SOURCES_DIST = gui_android.c drivers/cdrom/cd_none.c \ + drivers/video_egl.c drivers/video_egl.h tqueue.c tqueue.h +@GUI_ANDROID_TRUE@am_liblxdream_so_OBJECTS = gui_android.$(OBJEXT) \ +@GUI_ANDROID_TRUE@ cd_none.$(OBJEXT) video_egl.$(OBJEXT) \ +@GUI_ANDROID_TRUE@ tqueue.$(OBJEXT) liblxdream_so_OBJECTS = $(am_liblxdream_so_OBJECTS) am__DEPENDENCIES_1 = @GUI_ANDROID_TRUE@liblxdream_so_DEPENDENCIES = liblxdream-core.a \ @@ -227,14 +229,15 @@ gtkui/gtkui.h gtkui/gtk_win.c gtkui/gtkcb.c gtkui/gtk_cfg.c \ gtkui/gtk_mmio.c gtkui/gtk_debug.c gtkui/gtk_dump.c \ gtkui/gtk_ctrl.c gtkui/gtk_gd.c drivers/video_gtk.c gui_none.c \ - cocoaui/cocoaui.m cocoaui/cocoaui.h cocoaui/cocoa_cfg.m \ - cocoaui/cocoa_win.m cocoaui/cocoa_gd.m cocoaui/cocoa_prefs.m \ - cocoaui/cocoa_ctrl.m drivers/video_osx.m drivers/video_gdk.c \ - drivers/video_glx.c drivers/video_glx.h drivers/video_nsgl.m \ - drivers/video_nsgl.h drivers/audio_osx.m drivers/audio_sdl.c \ - drivers/audio_pulse.c drivers/audio_esd.c drivers/audio_alsa.c \ - drivers/input_lirc.c drivers/cdrom/cd_linux.c \ - drivers/cdrom/cd_osx.c drivers/osx_iokit.m drivers/osx_iokit.h \ + drivers/video_egl.c drivers/video_egl.h cocoaui/cocoaui.m \ + cocoaui/cocoaui.h cocoaui/cocoa_cfg.m cocoaui/cocoa_win.m \ + cocoaui/cocoa_gd.m cocoaui/cocoa_prefs.m cocoaui/cocoa_ctrl.m \ + drivers/video_osx.m drivers/video_gdk.c drivers/video_glx.c \ + drivers/video_glx.h drivers/video_nsgl.m drivers/video_nsgl.h \ + drivers/audio_osx.m drivers/audio_sdl.c drivers/audio_pulse.c \ + drivers/audio_esd.c drivers/audio_alsa.c drivers/input_lirc.c \ + drivers/cdrom/cd_linux.c drivers/cdrom/cd_osx.c \ + drivers/osx_iokit.m drivers/osx_iokit.h \ drivers/cdrom/cd_none.c drivers/joy_linux.c \ drivers/joy_linux.h @BUILD_PLUGINS_TRUE@am__objects_4 = plugin.$(OBJEXT) @@ -243,7 +246,8 @@ @GUI_GTK_TRUE@ gtk_mmio.$(OBJEXT) gtk_debug.$(OBJEXT) \ @GUI_GTK_TRUE@ gtk_dump.$(OBJEXT) gtk_ctrl.$(OBJEXT) \ @GUI_GTK_TRUE@ gtk_gd.$(OBJEXT) video_gtk.$(OBJEXT) -@GUI_ANDROID_TRUE@am__objects_6 = gui_none.$(OBJEXT) +@GUI_ANDROID_TRUE@am__objects_6 = gui_none.$(OBJEXT) \ +@GUI_ANDROID_TRUE@ video_egl.$(OBJEXT) @GUI_COCOA_TRUE@am__objects_7 = cocoaui.$(OBJEXT) cocoa_cfg.$(OBJEXT) \ @GUI_COCOA_TRUE@ cocoa_win.$(OBJEXT) cocoa_gd.$(OBJEXT) \ @GUI_COCOA_TRUE@ cocoa_prefs.$(OBJEXT) cocoa_ctrl.$(OBJEXT) \ @@ -343,6 +347,7 @@ AMTAR = @AMTAR@ ANDROID_GDBSERVER = @ANDROID_GDBSERVER@ ANDROID_NDK_HOME = @ANDROID_NDK_HOME@ +ANDROID_NDK_VERSION = @ANDROID_NDK_VERSION@ ANDROID_SDK_HOME = @ANDROID_SDK_HOME@ ANDROID_SDK_VERSION = @ANDROID_SDK_VERSION@ ANT = @ANT@ @@ -624,7 +629,7 @@ @GUI_ANDROID_TRUE@liblxdream_so_LINK = $(LINK) -Wl,-soname,liblxdream.so -shared @GUI_ANDROID_TRUE@liblxdream_so_LDADD = liblxdream-core.a @GLIB_LIBS@ @GTK_LIBS@ @LIBPNG_LIBS@ @LIBISOFS_LIBS@ $(INTLLIBS) @LXDREAM_LIBS@ -lm -@GUI_ANDROID_TRUE@liblxdream_so_SOURCES = gui_jni.c drivers/cdrom/cd_none.c +@GUI_ANDROID_TRUE@liblxdream_so_SOURCES = gui_android.c drivers/cdrom/cd_none.c drivers/video_egl.c drivers/video_egl.h tqueue.c tqueue.h @GUI_ANDROID_TRUE@liblxdream_so_LIBS = liblxdream-core.a @BUILD_PLUGINS_TRUE@lxdream_dummy_@SOEXT@_SOURCES = @BUILD_PLUGINS_TRUE@lxdream_dummy_@SOEXT@_LDADD = lxdream_dummy.lo @SDL_LIBS@ @@ -844,7 +849,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk_win.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtkcb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtkui.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gui_jni.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gui_android.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gui_none.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hotkeys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i386-dis.Po@am__quote@ @@ -897,8 +902,10 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testxlt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/texcache.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tqueue.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/video_egl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/video_gdk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/video_gl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/video_glx.Po@am__quote@ @@ -1852,6 +1859,20 @@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cd_none.obj `if test -f 'drivers/cdrom/cd_none.c'; then $(CYGPATH_W) 'drivers/cdrom/cd_none.c'; else $(CYGPATH_W) '$(srcdir)/drivers/cdrom/cd_none.c'; fi` +video_egl.o: drivers/video_egl.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT video_egl.o -MD -MP -MF "$(DEPDIR)/video_egl.Tpo" -c -o video_egl.o `test -f 'drivers/video_egl.c' || echo '$(srcdir)/'`drivers/video_egl.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/video_egl.Tpo" "$(DEPDIR)/video_egl.Po"; else rm -f "$(DEPDIR)/video_egl.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='drivers/video_egl.c' object='video_egl.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o video_egl.o `test -f 'drivers/video_egl.c' || echo '$(srcdir)/'`drivers/video_egl.c + +video_egl.obj: drivers/video_egl.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT video_egl.obj -MD -MP -MF "$(DEPDIR)/video_egl.Tpo" -c -o video_egl.obj `if test -f 'drivers/video_egl.c'; then $(CYGPATH_W) 'drivers/video_egl.c'; else $(CYGPATH_W) '$(srcdir)/drivers/video_egl.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/video_egl.Tpo" "$(DEPDIR)/video_egl.Po"; else rm -f "$(DEPDIR)/video_egl.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='drivers/video_egl.c' object='video_egl.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o video_egl.obj `if test -f 'drivers/video_egl.c'; then $(CYGPATH_W) 'drivers/video_egl.c'; else $(CYGPATH_W) '$(srcdir)/drivers/video_egl.c'; fi` + gtkui.o: gtkui/gtkui.c @am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gtkui.o -MD -MP -MF "$(DEPDIR)/gtkui.Tpo" -c -o gtkui.o `test -f 'gtkui/gtkui.c' || echo '$(srcdir)/'`gtkui/gtkui.c; \ @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/gtkui.Tpo" "$(DEPDIR)/gtkui.Po"; else rm -f "$(DEPDIR)/gtkui.Tpo"; exit 1; fi --- a/src/display.c Tue Feb 28 18:22:52 2012 +1000 +++ b/src/display.c Fri Mar 02 23:49:10 2012 +1000 @@ -23,6 +23,7 @@ #include "dream.h" #include "display.h" #include "pvr2/pvr2.h" +#include "pvr2/glutil.h" display_driver_t display_driver_list[] = { #ifdef HAVE_GTK @@ -33,7 +34,7 @@ #endif #endif #ifdef __ANDROID__ - &display_gl_driver, + &display_egl_driver, #endif &display_null_driver, NULL }; --- a/src/display.h Tue Feb 28 18:22:52 2012 +1000 +++ b/src/display.h Fri Mar 02 23:49:10 2012 +1000 @@ -282,6 +282,7 @@ extern struct display_driver display_gtk_driver; extern struct display_driver display_osx_driver; extern struct display_driver display_gl_driver; +extern struct display_driver display_egl_driver; extern struct display_driver display_null_driver; /****************** Input methods **********************/ --- a/src/drivers/gl_fbo.c Tue Feb 28 18:22:52 2012 +1000 +++ b/src/drivers/gl_fbo.c Fri Mar 02 23:49:10 2012 +1000 @@ -67,7 +67,7 @@ gboolean gl_fbo_is_supported() { - return isGLExtensionSupported("GL_EXT_framebuffer_object"); + return isGLExtensionSupported("GL_EXT_framebuffer_object") || isOpenGLES2(); } /** @@ -353,7 +353,8 @@ glDrawBuffer( GL_FRONT ); glReadBuffer( GL_FRONT ); #endif - display_driver->swap_buffers(); + if( display_driver->swap_buffers ) + display_driver->swap_buffers(); } static gboolean gl_fbo_read_render_buffer( unsigned char *target, render_buffer_t buffer, --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/drivers/video_egl.c Fri Mar 02 23:49:10 2012 +1000 @@ -0,0 +1,168 @@ +/** + * $Id$ + * + * Window management using EGL. + * + * Copyright (c) 2012 Nathan Keynes. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "lxdream.h" +#include "display.h" +#include "video_egl.h" +#include "video_gl.h" +#include "pvr2/pvr2.h" +#include "pvr2/glutil.h" + +static const char *getEGLErrorString( EGLint code ) +{ + switch( code ) { + case EGL_SUCCESS: return "OK"; + case EGL_NOT_INITIALIZED: return "EGL not initialized"; + case EGL_BAD_ACCESS: return "Bad access"; + case EGL_BAD_ALLOC: return "Allocation failed"; + case EGL_BAD_ATTRIBUTE: return "Bad attribute"; + case EGL_BAD_CONTEXT: return "Bad context"; + case EGL_BAD_CONFIG: return "Bad config"; + case EGL_BAD_CURRENT_SURFACE: return "Bad current surface"; + case EGL_BAD_DISPLAY: return "Bad display"; + case EGL_BAD_MATCH: return "Bad match"; + case EGL_BAD_PARAMETER: return "Bad parameter"; + case EGL_BAD_NATIVE_PIXMAP: return "Bad native pixmap"; + case EGL_BAD_NATIVE_WINDOW: return "Bad native window"; + default: return "Unknown error"; + } +} + + +static void logEGLError(const char *msg) +{ + EGLint error = eglGetError(); + const char *errorStr = getEGLErrorString(error); + + ERROR( "%s: %s (%x)", msg, errorStr, error ); +} + +static const EGLint RGB888_attributes[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT, + EGL_NONE, EGL_NONE }; + +static const EGLint RGB565_attributes[] = { + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT, + EGL_NONE, EGL_NONE }; + +static const EGLint context_attributes[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE, EGL_NONE }; + +static EGLDisplay display; +static EGLContext context = EGL_NO_CONTEXT; +static EGLSurface surface = EGL_NO_SURFACE; +static gboolean fbo_created = FALSE; + +gboolean video_egl_set_window(EGLNativeWindowType window, int width, int height, int format) +{ + EGLConfig config; + EGLint num_config, major = 0, minor = 0; + const EGLint *attribute_list; + + display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if( eglInitialize(display, &major, &minor) != EGL_TRUE ) { + logEGLError( "Unable to initialise EGL display" ); + return FALSE; + } + + if( format == COLFMT_RGB565 || format == COLFMT_BGRA1555 ) { + attribute_list = RGB565_attributes; + } else { + attribute_list = RGB888_attributes; + } + + + eglChooseConfig(display, attribute_list, &config, 1, &num_config); + + context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attributes); + if( context == EGL_NO_CONTEXT ) { + logEGLError( "Unable to create EGL context" ); + video_egl_clear_window(); + return FALSE; + } + + surface = eglCreateWindowSurface(display, config, window, NULL); + if( surface == EGL_NO_SURFACE ) { + logEGLError( "Unable to create EGL surface" ); + video_egl_clear_window(); + return FALSE; + } + + if( eglMakeCurrent( display, surface, surface, context ) == EGL_FALSE ) { + video_egl_clear_window(); + return FALSE; + } + + if( gl_fbo_is_supported() ) { + display_gl_driver.capabilities.has_gl = TRUE; + gl_fbo_init(&display_egl_driver); + gl_vbo_init(&display_egl_driver); + fbo_created = TRUE; + } else { + ERROR( "Display does not support FBO" ); + video_egl_clear_window(); + return FALSE; + } + pvr2_setup_gl_context(); + INFO( "Initialised EGL %d.%d\n", major, minor ); + return TRUE; +} + +void video_egl_clear_window() +{ + if( fbo_created ) { + gl_fbo_shutdown(); + fbo_created = FALSE; + } + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if( surface != EGL_NO_SURFACE ) { + eglDestroySurface(display, surface); + surface = EGL_NO_SURFACE; + } + if( context != EGL_NO_CONTEXT ) { + eglDestroyContext(display, context); + context = EGL_NO_CONTEXT; + } + eglTerminate(display); +} + + + +/** + * Minimal init and shutdown. The real work is done from set_window + */ +struct display_driver display_egl_driver = { + "egl", N_("OpenGLES driver"), NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + gl_load_frame_buffer, gl_display_render_buffer, gl_display_blank, + NULL, gl_read_render_buffer, NULL, NULL +}; --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/drivers/video_egl.h Fri Mar 02 23:49:10 2012 +1000 @@ -0,0 +1,38 @@ +/** + * $Id$ + * + * Window management using EGL. + * + * Copyright (c) 2012 Nathan Keynes. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#ifndef lxdream_video_egl_H +#define lxdream_video_egl_H 1 + +#include "glib/gtypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +gboolean video_egl_set_window(EGLNativeWindowType window, int width, int height, int format); +void video_egl_clear_window(); + +#ifdef __cplusplus +} +#endif + +#endif /* !lxdream_video_egl_H */ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gui_android.c Fri Mar 02 23:49:10 2012 +1000 @@ -0,0 +1,331 @@ +/** + * $Id$ + * + * Native shims for the Android front-end (implemented in Java). + * + * This is complicated by the fact that all the emulation runs in a + * separate thread. + * + * Copyright (c) 2012 Nathan Keynes. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include "dream.h" +#include "dreamcast.h" +#include "gui.h" +#include "config.h" +#include "lxpaths.h" +#include "tqueue.h" +#include "display.h" +#include "gdlist.h" +#include "gdrom/gdrom.h" +#include "hotkeys.h" +#include "serial.h" +#include "aica/audio.h" +#include "drivers/video_egl.h" +#include "maple/maple.h" +#include "vmu/vmulist.h" + +struct surface_info { + ANativeWindow *win; + int width, height, format; +}; + +static struct surface_info current_surface; +static const char *appHome = NULL; + +/** + * Count of running nanoseconds - used to cut back on the GUI runtime + */ +static uint32_t android_gui_nanos = 0; +static uint32_t android_gui_ticks = 0; +static struct timeval android_gui_lasttv; + + +void android_gui_start( void ) +{ + /* Dreamcast starting up hook */ +} + +void android_gui_stop( void ) +{ + /* Dreamcast stopping hook */ +} + +void android_gui_reset( void ) +{ + /* Dreamcast reset hook */ +} + +/** + * The main emulation thread. (as opposed to the UI thread). + */ +void *android_thread_main(void *data) +{ + while(1) { + tqueue_process_wait(); + } + return NULL; +} + +int android_set_surface(void *data) +{ + struct surface_info *surface = (struct surface_info *)data; + video_egl_set_window(surface->win, surface->width, surface->height, surface->format); + return 0; +} + +int android_clear_surface(void *data) +{ + struct surface_info *surface = (struct surface_info *)data; + + if( dreamcast_is_running() ) { + dreamcast_stop(); + } + video_egl_clear_window(); + ANativeWindow_release(surface->win); + surface->win = NULL; + return 0; +} + +static pthread_t dreamcast_thread; + +void android_start_thread() +{ + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + int status = pthread_create(&dreamcast_thread, &attr, android_thread_main, NULL); + if( status != 0 ) { + /* Handle errors */ + } +} + +/** tqueue callback wrapper to get the right call type for simple events */ +int android_callback_wrapper( void *fn ) +{ + void (*cast_fn)(void) = fn; + cast_fn(); +} + +int android_mount_disc( void *data ) +{ + char *s = (char *)data; + ERROR err; + gboolean result = gdrom_mount_image( s, &err ); /* TODO: Report error */ + return result; +} + +int android_toggle_run( void *data ) +{ + if( dreamcast_is_running() ) { + dreamcast_stop(); + } else { + dreamcast_run(); + } +} + +uint32_t android_gui_run_slice( uint32_t nanosecs ) +{ + android_gui_nanos += nanosecs; + if( android_gui_nanos > GUI_TICK_PERIOD ) { /* 10 ms */ + android_gui_nanos -= GUI_TICK_PERIOD; + android_gui_ticks ++; + uint32_t current_period = android_gui_ticks * GUI_TICK_PERIOD; + + // Run the event loop + tqueue_process_all(); + + struct timeval tv; + gettimeofday(&tv,NULL); + uint32_t ns = ((tv.tv_sec - android_gui_lasttv.tv_sec) * 1000000000) + + (tv.tv_usec - android_gui_lasttv.tv_usec)*1000; + if( (ns * 1.05) < current_period ) { + // We've gotten ahead - sleep for a little bit + struct timespec tv; + tv.tv_sec = 0; + tv.tv_nsec = current_period - ns; + nanosleep(&tv, &tv); + } + +#if 0 + /* Update the display every 10 ticks (ie 10 times a second) and + * save the current tv value */ + if( android_gui_ticks > 10 ) { + android_gui_ticks -= 10; + + double speed = (float)( (double)current_period * 100.0 / ns ); + android_gui_lasttv.tv_sec = tv.tv_sec; + android_gui_lasttv.tv_usec = tv.tv_usec; + main_window_set_speed( main_win, speed ); + } +#endif + } + return nanosecs; + + +} + +struct dreamcast_module android_gui_module = { "gui", NULL, + android_gui_reset, + android_gui_start, + android_gui_run_slice, + android_gui_stop, + NULL, NULL }; + +gboolean gui_error_dialog( const char *fmt, ... ) +{ + return FALSE; /* TODO */ +} + +void gui_update_state() +{ + /* TODO */ +} + +void gui_set_use_grab( gboolean grab ) +{ + /* No implementation - mouse grab doesn't exist */ +} + +void gui_update_io_activity( io_activity_type activity, gboolean active ) +{ + /* No implementation */ +} + +void gui_do_later( do_later_callback_t func ) +{ + func(); /* TODO */ +} + +static void android_init( const char *appHomeDir ) +{ + set_global_log_level("info"); + appHome = appHomeDir; + const char *confFile = g_strdup_printf("%s/lxdreamrc", appHome); + set_user_data_path(appHome); + lxdream_set_config_filename( confFile ); + lxdream_make_config_dir( ); + lxdream_load_config( ); + iso_init(); + gdrom_list_init(); + vmulist_init(); + dreamcast_init(1); + + dreamcast_register_module( &android_gui_module ); + audio_init_driver(NULL); + display_driver_t display_driver = get_display_driver_by_name(NULL); + display_set_driver(display_driver); + + hotkeys_init(); + serial_init(); + maple_reattach_all(); + INFO( "%s! ready...", APP_NAME ); + android_start_thread(); +} + + +/************************* Dreamcast native entry points **************************/ + +static char *getStringChars( JNIEnv *env, jstring str ); + +/** + * Main initialization entry point. We need to do all the setup that main() + * would normally do, as well as any UI specific setup. + */ +JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_init(JNIEnv * env, jclass obj, jstring homeDir ) +{ + android_init( getStringChars(env, homeDir) ); +} + +JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_run(JNIEnv * env, jclass obj) +{ + tqueue_post_message( android_callback_wrapper, dreamcast_run ); +} + +JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_toggleRun(JNIEnv * env, jclass obj) +{ + tqueue_post_message( android_toggle_run, NULL ); +} + +JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_reset(JNIEnv * env, jclass obj) +{ + tqueue_post_message( android_callback_wrapper, dreamcast_reset ); +} + +JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_stop(JNIEnv * env, jclass obj) +{ + /* Need to make sure this completely shuts down before we return */ + tqueue_send_message( android_callback_wrapper, dreamcast_stop ); +} + +JNIEXPORT jboolean JNICALL Java_org_lxdream_Dreamcast_isRunning(JNIEnv *env, jclass obj) +{ + return dreamcast_is_running(); +} + +JNIEXPORT jboolean JNICALL Java_org_lxdream_Dreamcast_isRunnable(JNIEnv *env, jclass obj) +{ + return dreamcast_can_run(); +} + +JNIEXPORT jboolean JNICALL Java_org_lxdream_Dreamcast_mount(JNIEnv *env, jclass obj, jstring str) +{ + char *s = getStringChars(env, str); + return tqueue_send_message( android_mount_disc, s ); +} + +JNIEXPORT jboolean JNICALL Java_org_lxdream_Dreamcast_unmount(JNIEnv *env, jclass obj) +{ + tqueue_post_message( android_callback_wrapper, gdrom_unmount_disc ); +} + + +/************************* LxdreamView native entry points **************************/ + +JNIEXPORT void JNICALL Java_org_lxdream_LxdreamView_setSurface(JNIEnv * env, jobject view, jobject surface, jint width, jint height) +{ + current_surface.win = ANativeWindow_fromSurface(env, surface); + current_surface.width = width; + current_surface.height = height; + int fmt = ANativeWindow_getFormat(current_surface.win); + if( fmt == WINDOW_FORMAT_RGB_565 ) { + current_surface.format = COLFMT_RGB565; + } else { + current_surface.format = COLFMT_RGB888; + } + tqueue_post_message( android_set_surface, ¤t_surface ); +} + +JNIEXPORT void JNICALL Java_org_lxdream_LxdreamView_clearSurface(JNIEnv * env, jobject view, jobject surface) +{ + /* Need to make sure this completely shuts down before we return */ + tqueue_send_message( android_clear_surface, ¤t_surface ); +} + + +/************************* JNI Support functions **************************/ + +static char *getStringChars( JNIEnv *env, jstring str ) +{ + jboolean iscopy; + const char *p = (*env)->GetStringUTFChars(env, str, &iscopy); + char *result = strdup(p); + (*env)->ReleaseStringUTFChars(env,str,p); + return result; +} --- a/src/gui_jni.c Tue Feb 28 18:22:52 2012 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,120 +0,0 @@ -/** - * $Id$ - * - * JNI wrappers for operating the emulator from Java. - * - * Copyright (c) 2012 Nathan Keynes. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include "dreamcast.h" -#include "gui.h" -#include "config.h" -#include "lxpaths.h" -#include "display.h" -#include "gdlist.h" -#include "hotkeys.h" -#include "serial.h" -#include "aica/audio.h" -#include "drivers/video_gl.h" -#include "maple/maple.h" -#include "vmu/vmulist.h" - -static char *getStringChars( JNIEnv *env, jstring str ) -{ - jboolean iscopy; - const char *p = (*env)->GetStringUTFChars(env, str, &iscopy); - char *result = strdup(p); - (*env)->ReleaseStringUTFChars(env,str,p); - return result; -} - -static const char *appHome = NULL; - -JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_init(JNIEnv * env, jclass obj, jstring homeDir ) -{ - appHome = getStringChars(env, homeDir); - const char *confFile = g_strdup_printf("%s/lxdreamrc", appHome); - set_user_data_path(appHome); - lxdream_set_config_filename( confFile ); - lxdream_make_config_dir( ); - lxdream_load_config( ); - iso_init(); - gdrom_list_init(); - vmulist_init(); - dreamcast_init(1); - - audio_init_driver(NULL); - display_driver_t display_driver = get_display_driver_by_name(NULL); - display_set_driver(display_driver); - - hotkeys_init(); - serial_init(); - maple_reattach_all(); - INFO( "%s! ready...", APP_NAME ); -} - -JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_setViewSize(JNIEnv * env, jclass obj, jint width, jint height) -{ - gl_set_video_size(width, height); -} - -JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_run(JNIEnv * env, jclass obj) -{ - dreamcast_run(); -} - -JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_stop(JNIEnv * env, jclass obj) -{ - dreamcast_stop(); -} - -gboolean gui_parse_cmdline( int *argc, char **argv[] ) -{ - return TRUE; -} - -gboolean gui_init( gboolean debug, gboolean fullscreen ) -{ - return TRUE; -} - -void gui_main_loop( gboolean run ) { - if( run ) { - dreamcast_run(); - } -} - -gboolean gui_error_dialog( const char *fmt, ... ) -{ - return TRUE; -} - -void gui_update_state() -{ -} - -void gui_set_use_grab( gboolean grab ) -{ -} - -void gui_update_io_activity( io_activity_type activity, gboolean active ) -{ -} - -void gui_do_later( do_later_callback_t func ) -{ - func(); -} --- a/src/pvr2/glutil.c Tue Feb 28 18:22:52 2012 +1000 +++ b/src/pvr2/glutil.c Fri Mar 02 23:49:10 2012 +1000 @@ -41,6 +41,14 @@ return isGLExtensionSupported("GL_ARB_texture_mirrored_repeat"); } +gboolean isOpenGLES2() +{ + const char *str = glGetString(GL_VERSION); + if( strncmp(str, "OpenGL ES 2.", 12) == 0 ) { + return TRUE; + } +} + /** * Check if there's at least 2 texture units */ --- a/src/pvr2/glutil.h Tue Feb 28 18:22:52 2012 +1000 +++ b/src/pvr2/glutil.h Fri Mar 02 23:49:10 2012 +1000 @@ -33,6 +33,8 @@ */ gboolean isGLExtensionSupported( const char *extension ); +gboolean isOpenGLES2(); + /** * Dump GL information to the output stream, usually for debugging purposes */ @@ -106,6 +108,18 @@ #define GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_EXT #endif +#if defined(GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT) && !defined(GL_UNSIGNED_SHORT_1_5_5_5_REV) +#define GL_UNSIGNED_SHORT_1_5_5_5_REV GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT +#endif + +#if defined(GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT) && !defined(GL_UNSIGNED_SHORT_4_4_4_4_REV) +#define GL_UNSIGNED_SHORT_4_4_4_4_REV GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT +#endif + +#if defined(GL_BGRA_EXT) && !defined(GL_BGRA) +#define GL_BGRA GL_BGRA_EXT +#endif + #if defined(HAVE_OPENGL_FBO_EXT) && !defined(HAVE_OPENGL_FBO) #define glGenFramebuffers glGenFramebuffersEXT #define glGenRenderbuffers glGenRenderbuffersEXT --- a/src/tools/Makefile.in Tue Feb 28 18:22:52 2012 +1000 +++ b/src/tools/Makefile.in Fri Mar 02 23:49:10 2012 +1000 @@ -80,6 +80,7 @@ AMTAR = @AMTAR@ ANDROID_GDBSERVER = @ANDROID_GDBSERVER@ ANDROID_NDK_HOME = @ANDROID_NDK_HOME@ +ANDROID_NDK_VERSION = @ANDROID_NDK_VERSION@ ANDROID_SDK_HOME = @ANDROID_SDK_HOME@ ANDROID_SDK_VERSION = @ANDROID_SDK_VERSION@ ANT = @ANT@ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tqueue.c Fri Mar 02 23:49:10 2012 +1000 @@ -0,0 +1,137 @@ +/** + * $Id$ + * + * Bounded, blocking queue for inter-thread communication. + * + * Copyright (c) 2012 Nathan Keynes. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "tqueue.h" + +#define TQUEUE_LENGTH 64 + +typedef struct { + tqueue_callback callback; + void *data; + gboolean synchronous; +} tqueue_entry; + +struct { + pthread_mutex_t mutex; + pthread_cond_t consumer_wait; + pthread_cond_t producer_sync_wait; + pthread_cond_t producer_full_wait; + int head; /* next item returned by dequeue */ + int tail; /* next item filled in by enqueue */ + int last_result; /* Result value of last dequeued callback */ + tqueue_entry tqueue[TQUEUE_LENGTH]; +} tqueue = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, PTHREAD_COND_INITIALIZER, PTHREAD_COND_INITIALIZER, 0, 0, -1}; + +/************** Producer thread **************/ +#define TQUEUE_EMPTY() (tqueue.head == tqueue.tail) +#define TQUEUE_FULL() ((tqueue.head == tqueue.tail+1) || (tqueue.head == 0 && tqueue.tail == TQUEUE_LENGTH)) + +static void tqueue_enqueue( tqueue_callback callback, void *data, gboolean sync ) +{ + assert( !TQUEUE_FULL() ); + tqueue.tqueue[tqueue.tail].callback = callback; + tqueue.tqueue[tqueue.tail].data = data; + tqueue.tqueue[tqueue.tail].synchronous = sync; + tqueue.tail++; +} + +/** + * Add a message to the UI queue and return immediately. + */ +void tqueue_post_message( tqueue_callback callback, void *data ) +{ + pthread_mutex_lock(&tqueue.mutex); + if( TQUEUE_FULL() ) { + /* Wait for the queue to clear */ + pthread_cond_wait(&tqueue.producer_full_wait, &tqueue.mutex); + } + tqueue_enqueue( callback, data, FALSE ); + pthread_cond_signal(&tqueue.consumer_wait); + pthread_mutex_unlock(&tqueue.mutex); +} + +/** + * Add a message to the UI queue and wait for it to be handled. + * @return the result from the handler function. + */ +int tqueue_send_message( tqueue_callback callback, void *data ) +{ + pthread_mutex_lock(&tqueue.mutex); + if( TQUEUE_FULL() ) { + /* Wait for the queue to clear */ + pthread_cond_wait(&tqueue.producer_full_wait, &tqueue.mutex); + } + tqueue_enqueue( callback, data, TRUE ); + pthread_cond_signal(&tqueue.consumer_wait); + pthread_cond_wait(&tqueue.producer_sync_wait, &tqueue.mutex); + return tqueue.last_result; + pthread_mutex_unlock(&tqueue.mutex); +} + +/************** Consumer thread **************/ + +/* Note: must be called with mutex locked */ +static void tqueue_process_loop() { + while( !TQUEUE_EMPTY() ) { + gboolean wasFull = TQUEUE_FULL(); + tqueue_callback callback = tqueue.tqueue[tqueue.head].callback; + void *data = tqueue.tqueue[tqueue.head].data; + gboolean sync = tqueue.tqueue[tqueue.head].synchronous; + tqueue.head++; + + if( wasFull ) { + pthread_cond_signal( &tqueue.producer_full_wait ); + } + + pthread_mutex_unlock(&tqueue.mutex); + int result = callback(data); + pthread_mutex_lock(&tqueue.mutex); + if( sync ) { + tqueue.last_result = result; + pthread_cond_signal( &tqueue.producer_sync_wait ); + } + } +} + +/** + * Process all messages in the queue, if any. + */ +void tqueue_process_all() +{ + pthread_mutex_lock(&tqueue.mutex); + if( !TQUEUE_EMPTY() ) { + tqueue_process_loop(); + } + pthread_mutex_unlock(&tqueue.mutex); +} + +/** + * Process the first message in the queue. If no messages are on the + * queue, waits for the next one to be queued and then processes it. + */ +void tqueue_process_wait() +{ + pthread_mutex_lock(&tqueue.mutex); + if( TQUEUE_EMPTY() ) { + pthread_cond_wait( &tqueue.consumer_wait, &tqueue.mutex ); + } + tqueue_process_loop(); + pthread_mutex_unlock(&tqueue.mutex); +} --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tqueue.h Fri Mar 02 23:49:10 2012 +1000 @@ -0,0 +1,62 @@ +/** + * $Id$ + * + * Bounded, blocking queue for inter-thread communication. Note: consumer side is + * re-entrant. + * + * Copyright (c) 2012 Nathan Keynes. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef lxdream_tqueue_H +#define lxdream_tqueue_H 1 + +#include "glib/gtypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Callback function to be invoked on the consumer side. + */ +typedef int (*tqueue_callback)(void *); + +/** + * Add a message to the UI queue and return immediately. + */ +void tqueue_post_message( tqueue_callback callback, void *data ); + +/** + * Add a message to the UI queue and wait for it to be handled. + * @return the result from the handler function. + */ +int tqueue_send_message( tqueue_callback callback, void *data ); + +/************** Consumer thread **************/ + +/** + * Process all messages in the queue, if any. + */ +void tqueue_process_all(); + +/** + * Process the first message in the queue. If no messages are on the + * queue, waits for the next one to be queued and then processes it. + */ +void tqueue_process_wait(); + +#ifdef __cplusplus +} +#endif + +#endif /* !lxdream_tqueue_H */