# 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 */