Add support for Android 2.3+ 1408/head
authorsapier <Sapier at GMX dot net>
Mon, 21 Apr 2014 12:10:59 +0000 (14:10 +0200)
committersapier <Sapier at GMX dot net>
Sun, 29 Jun 2014 16:17:56 +0000 (18:17 +0200)
There have been plenty of ppl involved in creating this version.
I don't wanna mention names as I'm sure I'd forget someone so I
just tell where help has been done:
- The partial android versions done by various ppl
- Testing on different android devices
- reviewing code (especially the in core changes)
- testing controls
- reviewing texts

A big thank you to everyone helping this to be completed!

66 files changed:
.gitignore
build/android/AndroidManifest.xml.template [new file with mode: 0644]
build/android/Makefile [new file with mode: 0644]
build/android/build.xml [new file with mode: 0644]
build/android/irrlicht-back_button.patch [new file with mode: 0644]
build/android/irrlicht-texturehack.patch [new file with mode: 0644]
build/android/irrlicht-touchcount.patch [new file with mode: 0644]
build/android/jni/Android.mk [new file with mode: 0644]
build/android/jni/Application.mk [new file with mode: 0644]
build/android/libvorbis-libogg-fpu.patch [new file with mode: 0644]
build/android/project.properties [new file with mode: 0644]
build/android/res/drawable-hdpi/irr_icon.png [new file with mode: 0644]
build/android/res/drawable-ldpi/irr_icon.png [new file with mode: 0644]
build/android/res/drawable-mdpi/irr_icon.png [new file with mode: 0644]
build/android/res/drawable-xhdpi/irr_icon.png [new file with mode: 0644]
build/android/res/layout/assetcopy.xml [new file with mode: 0644]
build/android/res/values/styles.xml [new file with mode: 0644]
build/android/src/org/minetest/minetest/MinetestAssetCopy.java [new file with mode: 0644]
build/android/src/org/minetest/minetest/MinetestTextEntry.java [new file with mode: 0644]
build/android/src/org/minetest/minetest/MtNativeActivity.java [new file with mode: 0644]
builtin/mainmenu/dlg_delete_world.lua
builtin/mainmenu/init_android.lua [new file with mode: 0644]
builtin/mainmenu/tab_settings.lua
builtin/mainmenu/tab_simple_main.lua
doc/README.android [new file with mode: 0644]
src/client.cpp
src/config.h
src/debug.cpp
src/defaultsettings.cpp
src/drawscene.cpp
src/filesys.cpp
src/game.cpp
src/guiEngine.cpp
src/guiFormSpecMenu.cpp
src/guiFormSpecMenu.h
src/httpfetch.cpp
src/hud.cpp
src/itemdef.cpp
src/jthread/pthread/jsemaphore.cpp
src/jthread/pthread/jthread.cpp
src/keycode.cpp
src/keycode.h
src/log.cpp
src/lua/src/llex.c
src/main.cpp
src/mainmenumanager.h
src/modalMenu.h
src/porting.cpp
src/porting.h
src/porting_android.cpp [new file with mode: 0644]
src/porting_android.h [new file with mode: 0644]
src/tile.cpp
src/tile.h
src/touchscreengui.cpp [new file with mode: 0644]
src/touchscreengui.h [new file with mode: 0644]
src/util/string.cpp
src/version.cpp
textures/base/pack/chat_btn.png [new file with mode: 0644]
textures/base/pack/down.png [new file with mode: 0644]
textures/base/pack/down_arrow.png [new file with mode: 0644]
textures/base/pack/inventory_btn.png [new file with mode: 0644]
textures/base/pack/jump_btn.png [new file with mode: 0644]
textures/base/pack/ladder_down.png [new file with mode: 0644]
textures/base/pack/left_arrow.png [new file with mode: 0644]
textures/base/pack/right_arrow.png [new file with mode: 0644]
textures/base/pack/up_arrow.png [new file with mode: 0644]

index 2e1584b1df22346dc917c1a9c79b5423947e9d85..98a7f35b9cde502a73412834189256591e3b37df 100644 (file)
@@ -64,3 +64,16 @@ locale/
 *.layout
 *.o
 
+#build variants
+build/android/assets
+build/android/bin
+build/android/Debug
+build/android/deps
+build/android/gen
+build/android/jni/src/*
+build/android/libs
+build/android/obj
+timestamp
+
+
+
diff --git a/build/android/AndroidManifest.xml.template b/build/android/AndroidManifest.xml.template
new file mode 100644 (file)
index 0000000..dcffa35
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+       package="org.minetest.minetest"
+       android:versionCode="###ANDROID_VERSION###"
+       android:versionName="###BASE_VERSION###.###ANDROID_VERSION###"
+       android:installLocation="auto">
+       <uses-sdk android:minSdkVersion="9"/>
+       <uses-feature android:glEsVersion="0x00010000" android:required="true"/>
+       <uses-permission android:name="android.permission.INTERNET" />
+       <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+       <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+       ###DEBUG_BUILD###
+       <application android:icon="@drawable/irr_icon" android:label="Minetest" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" ###DEBUG_FLAG###>
+       <activity android:name=".MtNativeActivity"
+               android:label="Minetest"
+               android:launchMode="singleTask"
+               android:configChanges="orientation|keyboardHidden"
+               android:screenOrientation="landscape"
+               android:clearTaskOnLaunch="true">
+               <intent-filter>
+                       <action android:name="android.intent.action.MAIN" />
+                       <category android:name="android.intent.category.LAUNCHER" />
+               </intent-filter>
+               <meta-data android:name="android.app.lib_name" android:value="minetest" />
+       </activity>
+       <activity android:name=".MinetestTextEntry"
+               android:theme="@style/Theme.Transparent"
+               android:excludeFromRecents="true">
+       </activity>
+       <activity android:name=".MinetestAssetCopy"
+               android:theme="@style/Theme.Transparent"
+               android:excludeFromRecents="true">
+       </activity>
+       </application>
+</manifest>
diff --git a/build/android/Makefile b/build/android/Makefile
new file mode 100644 (file)
index 0000000..9e69343
--- /dev/null
@@ -0,0 +1,724 @@
+# build options
+
+OS := $(shell uname)
+
+#automaticaly set number of jobs
+ifeq ($(OS),Linux)
+  PARALLEL := $(shell grep -c ^processor /proc/cpuinfo)
+else
+  PARALLEL := 1
+endif
+
+# compile with GPROF
+# GPROF = 1
+
+# build for build platform
+APP_PLATFORM = android-9
+
+# paths used for timestaps, dependencys, tree config and libs
+PATHCFGFILE = path.cfg
+
+ROOT = $(shell pwd)
+
+################################################################################
+# Android Version code
+# Increase for each build!
+################################################################################
+ANDROID_VERSION_CODE = 3
+
+################################################################################
+# toolchain config for arm old processors
+################################################################################
+TARGET_HOST = arm-linux
+TARGET_ABI = armeabi
+TARGET_LIBDIR = armeabi
+TARGET_TOOLCHAIN = arm-linux-androideabi-
+TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfp
+CROSS_PREFIX = arm-linux-androideabi-
+COMPILER_VERSION = 4.8
+
+################################################################################
+# toolchain config for arm new processors
+################################################################################
+#TARGET_HOST = arm-linux
+#TARGET_ABI = armeabi-v7a-hard
+#TARGET_LIBDIR = armeabi-v7a
+#TARGET_TOOLCHAIN = arm-linux-androideabi-
+#TARGET_CFLAGS_ADDON =  -mfpu=vfpv3-d16 -D_NDK_MATH_NO_SOFTFP=1 \
+#                                              -mfloat-abi=hard -march=armv7-a
+#TARGET_CXXFLAGS_ADDON = $(TARGET_CFLAGS_ADDON)
+#TARGET_LDFLAGS_ADDON = -Wl,--no-warn-mismatch -lm_hard
+#CROSS_PREFIX = arm-linux-androideabi-
+#COMPILER_VERSION = 4.8
+
+################################################################################
+# toolchain config for little endian mips
+################################################################################
+#TARGET_HOST = mipsel-linux
+#TARGET_ABI = mips
+#TARGET_LIBDIR = mips
+#TARGET_TOOLCHAIN = mipsel-linux-android-
+#CROSS_PREFIX = mipsel-linux-android-
+#COMPILER_VERSION = 4.8
+
+################################################################################
+# toolchain config for x86
+################################################################################
+#TARGET_HOST = x86-linux
+#TARGET_ABI = x86
+#TARGET_LIBDIR = x86
+#TARGET_TOOLCHAIN = x86-
+#CROSS_PREFIX = i686-linux-android-
+#COMPILER_VERSION = 4.8
+
+################################################################################
+ASSETS_TIMESTAMP = deps/assets_timestamp
+
+LEVELDB_DIR = $(ROOT)/deps/leveldb/
+LEVELDB_LIB = $(LEVELDB_DIR)libleveldb.a
+LEVELDB_TIMESTAMP = $(LEVELDB_DIR)/timestamp
+LEVELDB_TIMESTAMP_INT = $(ROOT)/deps/leveldb_timestamp
+LEVELDB_URL_GIT = https://code.google.com/p/leveldb/
+
+OPENAL_DIR = $(ROOT)/deps/openal-soft/
+OPENAL_LIB = $(OPENAL_DIR)libs/$(TARGET_ABI)/libopenal.so
+OPENAL_TIMESTAMP = $(OPENAL_DIR)/timestamp
+OPENAL_TIMESTAMP_INT = $(ROOT)/deps/openal_timestamp
+OPENAL_URL_GIT = https://github.com/apportable/openal-soft
+
+OGG_DIR = $(ROOT)/deps/libvorbis-libogg-android/
+OGG_LIB = $(OGG_DIR)libs/$(TARGET_ABI)/libogg.so
+VORBIS_LIB = $(OGG_DIR)libs/$(TARGET_ABI)/libogg.so
+OGG_TIMESTAMP = $(OGG_DIR)timestamp
+OGG_TIMESTAMP_INT = $(ROOT)/deps/ogg_timestamp
+OGG_URL_GIT = https://github.com/vincentjames501/libvorbis-libogg-android
+
+IRRLICHT_DIR = $(ROOT)/deps/irrlicht/
+IRRLICHT_LIB = $(IRRLICHT_DIR)lib/Android/libIrrlicht.a
+IRRLICHT_TIMESTAMP = $(IRRLICHT_DIR)timestamp
+IRRLICHT_TIMESTAMP_INT = $(ROOT)/deps/irrlicht_timestamp
+IRRLICHT_URL_SVN = http://svn.code.sf.net/p/irrlicht/code/branches/ogl-es/
+
+OPENSSL_BASEDIR = openssl-android
+OPENSSL_DIR = $(ROOT)/deps/$(OPENSSL_BASEDIR)/
+OPENSSL_LIB = $(OPENSSL_DIR)libs/$(TARGET_ABI)/libopenssl.so
+OPENSSL_TIMESTAMP = $(OPENSSL_DIR)timestamp
+OPENSSL_TIMESTAMP_INT = $(ROOT)/deps/openssl_timestamp
+OPENSSL_URL_GIT = https://github.com/wobbals/openssl-android
+
+CURL_VERSION = 7.35.0
+CURL_DIR = $(ROOT)/deps/curl-$(CURL_VERSION)
+CURL_LIB = $(CURL_DIR)/lib/.libs/libcurl.a
+CURL_TIMESTAMP = $(CURL_DIR)/timestamp
+CURL_TIMESTAMP_INT = $(ROOT)/deps/curl_timestamp
+CURL_URL_HTTP = http://curl.haxx.se/download/curl-${CURL_VERSION}.tar.bz2
+
+FREETYPE_DIR = $(ROOT)/deps/freetype2-android/
+FREETYPE_LIB = $(FREETYPE_DIR)/Android/obj/local/$(TARGER_ABI)/libfreetype2-static.a
+FREETYPE_TIMESTAMP = $(FREETYPE_DIR)timestamp
+FREETYPE_TIMESTAMP_INT = $(ROOT)/deps/freetype_timestamp
+FREETYPE_URL_GIT = https://github.com/cdave1/freetype2-android
+
+-include $(PATHCFGFILE)
+
+.PHONY : debug release reconfig delconfig \
+       leveldb_download clean_leveldb leveldb\
+       irrlicht_download clean_irrlicht irrlicht \
+       clean_assets assets \
+       freetype_download clean_freetype freetype \
+       apk clean_apk \
+       clean_all clean prep_srcdir \
+       install_debug install envpaths all \
+       manifest clean_manifest\
+       $(ASSETS_TIMESTAMP) $(LEVELDB_TIMESTAMP) \
+       $(OPENAL_TIMESTAMP) $(OGG_TIMESTAMP) \
+       $(IRRLICHT_TIMESTAMP) $(CURL_TIMESTAMP) \
+       $(OPENSSL_TIMESTAMP) curl_binary \
+       $(ROOT)/jni/src/android_version.h
+
+debug : $(PATHCFGFILE)
+       export NDEBUG=;                                                            \
+       export BUILD_TYPE=debug;                                                   \
+       $(MAKE) -j${PARALLEL} apk
+       
+all : debug release
+       
+release : $(PATHCFGFILE)
+       @export NDEBUG=1;                                                          \
+       export BUILD_TYPE=release;                                                 \
+       $(MAKE) -j${PARALLEL} apk
+
+reconfig: delconfig
+       @$(MAKE) -j${PARALLEL} $(PATHCFGFILE)
+
+delconfig :
+       $(RM) ${PATHCFGFILE}
+
+$(PATHCFGFILE) :
+       @echo "Please specify path of ANDROID NDK";                                \
+       echo "e.g. /home/user/android-ndk-r9c/";                                   \
+       read ANDROID_NDK ;                                                         \
+       if [ ! -d $$ANDROID_NDK ] ; then                                           \
+               echo "$$ANDROID_NDK is not a valid folder";                            \
+               exit 1;                                                                \
+       fi;                                                                        \
+       echo "ANDROID_NDK = $$ANDROID_NDK" > ${PATHCFGFILE};                       \
+       echo "NDK_MODULE_PATH = $$ANDROID_NDK/tools" >> ${PATHCFGFILE};            \
+       echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";\
+       echo "+ Note: NDK_MODULE_PATH is set to $$ANDROID_NDK/tools";              \
+       echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";\
+       echo "Please specify path of ANDROID SDK";                                 \
+       echo "e.g. /home/user/adt-bundle-linux-x86_64-20131030/sdk/";              \
+       read SDKFLDR ;                                                             \
+       if [ ! -d $$SDKFLDR ] ; then                                               \
+               echo "$$SDKFLDR is not a valid folder";                                \
+               exit 1;                                                                \
+       fi;                                                                        \
+       echo "SDKFOLDER = $$SDKFLDR" >> ${PATHCFGFILE};
+
+$(OPENAL_TIMESTAMP) : openal_download
+       @LAST_MODIF=$$(find ${OPENAL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${OPENAL_TIMESTAMP};                                            \
+       fi
+       
+openal_download :
+       @if [ ! -d ${OPENAL_DIR} ] ; then                                          \
+               echo "openal sources missing, downloading...";                         \
+               mkdir -p ${ROOT}/deps;                                                 \
+               cd ${ROOT}/deps ;                                                      \
+               git clone ${OPENAL_URL_GIT} || exit 1;                                 \
+       fi
+       
+openal : $(OPENAL_LIB)
+
+$(OPENAL_LIB): $(OPENAL_TIMESTAMP)
+       @REFRESH=0;                                                                \
+       if [ ! -e ${OPENAL_TIMESTAMP_INT} ] ; then                                 \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ${OPENAL_TIMESTAMP} -nt ${OPENAL_TIMESTAMP_INT} ] ; then              \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ $$REFRESH -ne 0 ] ; then                                              \
+       export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK};             \
+       echo "changed timestamp for openal detected building...";                  \
+       cd ${OPENAL_DIR};                                                          \
+       ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH}              \
+       APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} -j${PARALLEL}           \
+       TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}"                                    \
+       TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}"                                  \
+       TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1;                     \
+       touch ${OPENAL_TIMESTAMP};                                                 \
+       touch ${OPENAL_TIMESTAMP_INT};                                             \
+       else                                                                       \
+               echo "nothing to be done for openal";                                  \
+       fi
+       
+clean_openal :
+       $(RM) -rf ${OPENAL_DIR}
+       
+$(OGG_TIMESTAMP) : ogg_download
+       @LAST_MODIF=$$(find ${OGG_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${OGG_TIMESTAMP};                                                \
+       fi
+       
+ogg_download :
+       @if [ ! -d ${OGG_DIR} ] ; then                                             \
+               echo "ogg sources missing, downloading...";                            \
+               mkdir -p ${ROOT}/deps;                                                 \
+               cd ${ROOT}/deps ;                                                      \
+               git clone ${OGG_URL_GIT}|| exit 1;                                     \
+               cd libvorbis-libogg-android ;                                          \
+               patch -p1 < ../../libvorbis-libogg-fpu.patch || exit 1;                \
+       fi
+       
+ogg : $(OGG_LIB)
+
+$(OGG_LIB): $(OGG_TIMESTAMP)
+       @REFRESH=0;                                                                \
+       if [ ! -e ${OGG_TIMESTAMP_INT} ] ; then                                    \
+               echo "${OGG_TIMESTAMP_INT} doesn't exist";                             \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ${OGG_TIMESTAMP} -nt ${OGG_TIMESTAMP_INT} ] ; then                    \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ $$REFRESH -ne 0 ] ; then                                              \
+       export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK};             \
+       echo "changed timestamp for ogg detected building...";                     \
+       cd ${OGG_DIR};                                                             \
+       ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH}              \
+       APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} -j${PARALLEL}           \
+       TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}"                                    \
+       TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}"                                  \
+       TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1;                     \
+       touch ${OGG_TIMESTAMP};                                                    \
+       touch ${OGG_TIMESTAMP_INT};                                                \
+       else                                                                       \
+               echo "nothing to be done for libogg/libvorbis";                        \
+       fi
+
+clean_ogg :
+       $(RM) -rf ${OGG_DIR}
+       
+$(OPENSSL_TIMESTAMP) : openssl_download
+       @LAST_MODIF=$$(find ${OPENSSL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${OPENSSL_TIMESTAMP};                                            \
+       fi
+       
+openssl_download :
+       @if [ ! -d ${OPENSSL_DIR} ] ; then                                         \
+               echo "openssl sources missing, downloading...";                        \
+               mkdir -p ${ROOT}/deps;                                                 \
+               cd ${ROOT}/deps ;                                                      \
+               git clone ${OPENSSL_URL_GIT} || exit 1;                                \
+       fi
+       
+openssl : $(OPENSSL_LIB)
+
+$(OPENSSL_LIB): $(OPENSSL_TIMESTAMP)
+       @REFRESH=0;                                                                \
+       if [ ! -e ${OPENSSL_TIMESTAMP_INT} ] ; then                                \
+               echo "${OPENSSL_TIMESTAMP_INT} doesn't exist";                         \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ${OPENSSL_TIMESTAMP} -nt ${OPENSSL_TIMESTAMP_INT} ] ; then            \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ $$REFRESH -ne 0 ] ; then                                              \
+       export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK};             \
+       echo "changed timestamp for openssl detected building...";                 \
+       cd ${OPENSSL_DIR};                                                         \
+       cat jni/Application.mk | grep -v NDK_TOOLCHAIN_VERSION >jni/Application.mk.new;\
+       mv jni/Application.mk.new jni/Application.mk;                              \
+       ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH}              \
+       APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} -j${PARALLEL}           \
+       TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}"                                    \
+       TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}"                                  \
+       TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1;                     \
+       touch ${OPENSSL_TIMESTAMP};                                                \
+       touch ${OPENSSL_TIMESTAMP_INT};                                            \
+       else                                                                       \
+               echo "nothing to be done for openssl";                        \
+       fi
+
+clean_openssl :
+       $(RM) -rf ${OPENSSL_DIR}
+
+$(LEVELDB_TIMESTAMP) : leveldb_download
+       @LAST_MODIF=$$(find ${LEVELDB_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${LEVELDB_TIMESTAMP};                                            \
+       fi
+
+leveldb_download :
+       @if [ ! -d ${LEVELDB_DIR} ] ; then                                         \
+               echo "leveldb sources missing, downloading...";                        \
+               mkdir -p ${ROOT}/deps;                                                 \
+               cd ${ROOT}/deps ;                                                      \
+               git clone ${LEVELDB_URL_GIT} || exit 1;                                \
+       fi
+       
+leveldb : $(LEVELDB_LIB)
+
+$(LEVELDB_LIB): $(LEVELDB_TIMESTAMP)
+       @REFRESH=0;                                                                \
+       if [ ! -e ${LEVELDB_TIMESTAMP_INT} ] ; then                                \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ${LEVELDB_TIMESTAMP} -nt ${LEVELDB_TIMESTAMP_INT} ] ; then            \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ $$REFRESH -ne 0 ] ; then                                              \
+       export PATH=$${PATH}:${SDKFOLDER}/platform-tools:${ANDROID_NDK};           \
+       echo "changed timestamp for leveldb detected building...";                 \
+       cd deps/leveldb;                                                           \
+       export CROSS_PREFIX=${CROSS_PREFIX};                                       \
+       export TOOLCHAIN=/tmp/ndk-arm;                                             \
+       ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh                    \
+       --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION}                         \
+       --install-dir=$${TOOLCHAIN} --system=linux-x86_64;                         \
+       export PATH="$${TOOLCHAIN}/bin:$${PATH}";                                  \
+       export CC=${CROSS_PREFIX}gcc;                                              \
+       export CXX=${CROSS_PREFIX}g++;                                             \
+       export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}";                         \
+       export CPPFLAGS="$${CPPFLAGS} ${TARGET_CFLAGS_ADDON}";                     \
+       export LDFLAGS="$${LDFLAGS} ${TARGET_LDFLAGS_ADDON}";                      \
+       export TARGET_OS=OS_ANDROID_CROSSCOMPILE;                                  \
+       $(MAKE) -j${PARALLEL} -s || exit 1;                                        \
+       touch ${LEVELDB_TIMESTAMP};                                                \
+       touch ${LEVELDB_TIMESTAMP_INT};                                            \
+       else                                                                       \
+               echo "nothing to be done for leveldb";                                 \
+       fi
+       
+clean_leveldb :
+       $(RM) -rf deps/leveldb
+       
+$(FREETYPE_TIMESTAMP) : freetype_download
+       @LAST_MODIF=$$(find ${FREETYPE_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${FREETYPE_TIMESTAMP};                                           \
+       fi
+
+freetype_download :
+       @if [ ! -d ${FREETYPE_DIR} ] ; then                                        \
+               echo "freetype sources missing, downloading...";                       \
+               mkdir -p ${ROOT}/deps;                                                 \
+               cd deps;                                                               \
+               git clone ${FREETYPE_URL_GIT} || exit 1;                               \
+       fi
+       
+$(IRRLICHT_TIMESTAMP) : irrlicht_download
+       @LAST_MODIF=$$(find ${IRRLICHT_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${IRRLICHT_TIMESTAMP};                                           \
+       fi
+       
+freetype : $(FREETYPE_LIB)
+
+$(FREETYPE_LIB) : $(FREETYPE_TIMESTAMP)
+       @REFRESH=0;                                                                \
+       if [ ! -e ${FREETYPE_TIMESTAMP_INT} ] ; then                               \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ! -e ${FREETYPE_LIB} ] ; then                                         \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ${FREETYPE_TIMESTAMP} -nt ${FREETYPE_TIMESTAMP_INT} ] ; then          \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ $$REFRESH -ne 0 ] ; then                                              \
+       mkdir -p ${FREETYPE_DIR};                                                  \
+       export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK};             \
+       echo "changed timestamp for freetype detected building...";                \
+       cd ${FREETYPE_DIR}/Android/jni;                                            \
+       ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH}              \
+       APP_PLATFORM=${APP_PLATFORM} APP_ABI=${TARGET_ABI} -j${PARALLEL}           \
+       TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}"                                    \
+       TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}"                                  \
+       TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1;                     \
+       touch ${FREETYPE_TIMESTAMP};                                               \
+       touch ${FREETYPE_TIMESTAMP_INT};                                           \
+       else                                                                       \
+               echo "nothing to be done for freetype";                                \
+       fi
+       
+clean_freetype :
+       $(RM) -rf ${FREETYPE_DIR}
+
+#Note: Texturehack patch is required for gpu's not supporting color format
+#      correctly. Known bad GPU:
+#      -geforce on emulator
+#      -Vivante Corporation GC1000 core (e.g. Galaxy Tab 3)
+
+irrlicht_download :
+       @if [ ! -d "deps/irrlicht" ] ; then                                        \
+               echo "irrlicht sources missing, downloading...";                       \
+               mkdir -p ${ROOT}/deps;                                                 \
+               cd deps;                                                               \
+               svn co ${IRRLICHT_URL_SVN} irrlicht || exit 1;                         \
+               cd irrlicht;                                                           \
+               patch -p1 < ../../irrlicht-touchcount.patch || exit 1;                 \
+               patch -p1 < ../../irrlicht-back_button.patch || exit 1;                \
+               patch -p1 < ../../irrlicht-texturehack.patch || exit 1;                \
+       fi
+
+irrlicht : $(IRRLICHT_LIB)
+
+$(IRRLICHT_LIB): $(IRRLICHT_TIMESTAMP) $(FREETYPE_LIB)
+       @REFRESH=0;                                                                \
+       if [ ! -e ${IRRLICHT_TIMESTAMP_INT} ] ; then                               \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ! -e ${IRRLICHT_LIB} ] ; then                                         \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ${IRRLICHT_TIMESTAMP} -nt ${IRRLICHT_TIMESTAMP_INT} ] ; then          \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ $$REFRESH -ne 0 ] ; then                                              \
+       mkdir -p ${IRRLICHT_DIR};                                                  \
+       export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK};             \
+       echo "changed timestamp for irrlicht detected building...";                \
+       cd deps/irrlicht/source/Irrlicht/Android;                                  \
+       ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH}              \
+       APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} -j${PARALLEL}           \
+       TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}"                                    \
+       TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}"                                  \
+       TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1;                     \
+       touch ${IRRLICHT_TIMESTAMP};                                               \
+       touch ${IRRLICHT_TIMESTAMP_INT};                                           \
+       else                                                                       \
+               echo "nothing to be done for irrlicht";                                \
+       fi
+       
+clean_irrlicht :
+       $(RM) -rf deps/irrlicht
+       
+$(CURL_TIMESTAMP) : curl_download
+       @LAST_MODIF=$$(find ${CURL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${CURL_TIMESTAMP};                                               \
+       fi
+
+curl_download :
+       @if [ ! -d "deps/curl-${CURL_VERSION}" ] ; then                            \
+               echo "curl sources missing, downloading...";                           \
+               mkdir -p ${ROOT}/deps;                                                 \
+               cd deps;                                                               \
+               wget ${CURL_URL_HTTP} || exit 1;                                       \
+               tar -xjf curl-${CURL_VERSION}.tar.bz2 || exit 1;                       \
+               rm curl-${CURL_VERSION}.tar.bz2;                                       \
+       fi
+
+curl : $(CURL_LIB)
+
+$(CURL_LIB): $(CURL_TIMESTAMP) $(OPENSSL_LIB)
+       @REFRESH=0;                                                                \
+       if [ ! -e ${CURL_TIMESTAMP_INT} ] ; then                                   \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ! -e ${CURL_LIB} ] ; then                                             \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ${CURL_TIMESTAMP} -nt ${CURL_TIMESTAMP_INT} ] ; then                  \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ $$REFRESH -ne 0 ] ; then                                              \
+       mkdir -p ${CURL_DIR};                                                      \
+       export PATH="$${PATH}:${SDKFOLDER}/platform-tools:${ANDROID_NDK}";         \
+       echo "changed timestamp for curl detected building...";                    \
+       cd deps/curl-${CURL_VERSION};                                              \
+       export CROSS_PREFIX=${CROSS_PREFIX};                                       \
+       export TOOLCHAIN=/tmp/ndk-arm;                                             \
+       ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh                    \
+       --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION}                         \
+       --install-dir=$${TOOLCHAIN} --system=linux-x86_64;                         \
+       export PATH="$${TOOLCHAIN}/bin:$${PATH}";                                  \
+       export CC=${CROSS_PREFIX}gcc;                                              \
+       export CXX=${CROSS_PREFIX}g++;                                             \
+       export TARGET_OS=OS_ANDROID_CROSSCOMPILE;                                  \
+       export CPPFLAGS="$${CPPFLAGS} -I${OPENSSL_DIR}/include                     \
+       -L${OPENSSL_DIR}/libs/${TARGET_ABI}/ ${TARGET_CFLAGS_ADDON}";              \
+       export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}";                         \
+       export LDFLAGS="$${LDFLAGS} -L${OPENSSL_DIR}/libs/${TARGET_ABI}/           \
+       ${TARGET_LDFLAGS_ADDON}";                                                  \
+       ./configure --host=${TARGET_HOST} --disable-shared --enable-static --with-ssl;  \
+       $(MAKE) -j${PARALLEL} -s || exit 1;                                        \
+       touch ${CURL_TIMESTAMP};                                                   \
+       touch ${CURL_TIMESTAMP_INT};                                               \
+       else                                                                       \
+               echo "nothing to be done for curl";                                    \
+       fi
+       
+clean_curl :
+       $(RM) -rf deps/curl-${CURL_VERSION}
+       
+       
+curl_binary: 
+       @if [ ! -d "deps/curl-${CURL_VERSION_BINARY}" ] ; then                     \
+               echo "curl sources missing, downloading...";                           \
+               mkdir -p ${ROOT}/deps;                                                 \
+               cd deps;                                                               \
+               wget http://curl.haxx.se/gknw.net/7.34.0/dist-android/curl-7.34.0-rtmp-ssh2-ssl-zlib-static-bin-android.tar.gz || exit 1;\
+               tar -xzf curl-7.34.0-rtmp-ssh2-ssl-zlib-static-bin-android.tar.gz || exit 1;\
+               mv curl-7.34.0-rtmp-ssh2-ssl-zlib-static-bin-android curl-${CURL_VERSION_BINARY};\
+               rm curl-7.34.0-rtmp-ssh2-ssl-zlib-static-bin-android.tar.gz;           \
+       fi
+
+$(ASSETS_TIMESTAMP) : $(IRRLICHT_LIB)
+       @mkdir -p ${ROOT}/deps;                                                     \
+       LAST_MODIF=$$(find ${ROOT}/../../builtin -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${ROOT}/../../builtin/timestamp;                                 \
+               touch ${ASSETS_TIMESTAMP};                                             \
+               echo builtin changed $$LAST_MODIF;                                     \
+       fi;                                                                        \
+       LAST_MODIF=$$(find ${ROOT}/../../client -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${ROOT}/../../client/timestamp;                                  \
+               touch ${ASSETS_TIMESTAMP};                                             \
+       fi;                                                                        \
+       LAST_MODIF=$$(find ${ROOT}/../../doc -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${ROOT}/../../doc/timestamp;                                     \
+               touch ${ASSETS_TIMESTAMP};                                             \
+       fi;                                                                        \
+       LAST_MODIF=$$(find ${ROOT}/../../fonts -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${ROOT}/../../fonts/timestamp;                                   \
+               touch ${ASSETS_TIMESTAMP};                                             \
+       fi;                                                                        \
+       LAST_MODIF=$$(find ${ROOT}/../../games -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${ROOT}/../../games/timestamp;                                   \
+               touch ${ASSETS_TIMESTAMP};                                             \
+       fi;                                                                        \
+       LAST_MODIF=$$(find ${ROOT}/../../mods -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${ROOT}/../../mods/timestamp;                                    \
+               touch ${ASSETS_TIMESTAMP};                                             \
+       fi;                                                                        \
+       LAST_MODIF=$$(find ${ROOT}/../../po -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${ROOT}/../../po/timestamp;                                      \
+               touch ${ASSETS_TIMESTAMP};                                             \
+       fi;                                                                        \
+       LAST_MODIF=$$(find ${ROOT}/../../textures -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${ROOT}/../../textures/timestamp;                                \
+               touch ${ASSETS_TIMESTAMP};                                             \
+       fi;                                                                        \
+       LAST_MODIF=$$(find ${IRRLICHT_DIR}/media -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${IRRLICHT_DIR}/media/timestamp;                                 \
+               touch ${ASSETS_TIMESTAMP};                                             \
+       fi;                                                                        \
+       if [ ${ROOT}/../../minetest.conf.example -nt ${ASSETS_TIMESTAMP} ] ; then  \
+               echo "conf changed";                                                   \
+               touch ${ASSETS_TIMESTAMP};                                             \
+       fi;                                                                        \
+       if [ ${ROOT}/../../README.txt -nt ${ASSETS_TIMESTAMP} ] ; then             \
+               touch ${ASSETS_TIMESTAMP};                                             \
+       fi;                                                                        \
+       if [ ! -e $(ASSETS_TIMESTAMP) ] ; then                                     \
+               touch $(ASSETS_TIMESTAMP);                                             \
+       fi
+       
+assets : $(ASSETS_TIMESTAMP)
+       @REFRESH=0;                                                                \
+       if [ ! -e ${ASSETS_TIMESTAMP}.old ] ; then                                 \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ${ASSETS_TIMESTAMP} -nt ${ASSETS_TIMESTAMP}.old ] ; then              \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ! -d ${ROOT}/assets ] ; then                                          \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ $$REFRESH -ne 0 ] ; then                                              \
+       echo "assets changed, refreshing...";                                      \
+       $(MAKE) -j${PARALLEL} clean_assets;                                        \
+       mkdir -p ${ROOT}/assets/Minetest;                                          \
+       cp ${ROOT}/../../minetest.conf.example ${ROOT}/assets/Minetest;            \
+       cp ${ROOT}/../../README.txt ${ROOT}/assets/Minetest;                       \
+       cp -r ${ROOT}/../../builtin ${ROOT}/assets/Minetest;                       \
+       cp -r ${ROOT}/../../client ${ROOT}/assets/Minetest;                        \
+       cp -r ${ROOT}/../../doc ${ROOT}/assets/Minetest;                           \
+       cp -r ${ROOT}/../../fonts ${ROOT}/assets/Minetest;                         \
+       cp -r ${ROOT}/../../games ${ROOT}/assets/Minetest;                         \
+       cp -r ${ROOT}/../../mods ${ROOT}/assets/Minetest;                          \
+       cp -r ${ROOT}/../../po ${ROOT}/assets/Minetest;                            \
+       cp -r ${ROOT}/../../textures ${ROOT}/assets/Minetest;                      \
+       mkdir -p ${ROOT}/assets/Minetest/media;                                    \
+       cp -r ${IRRLICHT_DIR}/media/Shaders ${ROOT}/assets/Minetest/media;         \
+       cd ${ROOT}/assets;                                                         \
+       find . -name "timestamp" -exec rm {} \; ;                                  \
+       find . -name "*.blend" -exec rm {} \; ;                                    \
+       ls -R | grep ":$$" | sed -e 's/:$$//' -e 's/\.//' -e 's/^\///' > "index.txt"; \
+       cp ${ROOT}/${ASSETS_TIMESTAMP} ${ROOT}/${ASSETS_TIMESTAMP}.old;            \
+       else                                                                       \
+               echo "nothing to be done for assets";                                  \
+       fi
+
+clean_assets :
+       @$(RM) -r assets
+       
+apk: $(PATHCFGFILE) assets $(IRRLICHT_LIB) $(CURL_LIB)                   \
+       $(OPENAL_LIB) $(OGG_LIB) prep_srcdir $(ROOT)/jni/src/android_version.h
+       @export NDEBUG=$$NDEBUG; $(MAKE) -j${PARALLEL} manifest;                   \
+       export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK};             \
+       export ANDROID_HOME=${SDKFOLDER};                                          \
+       mkdir -p ${ROOT}/src;                                                      \
+       ndk-build NDK_MODULE_PATH=${NDK_MODULE_PATH} -j${PARALLEL}                 \
+       GPROF=${GPROF} APP_ABI=${TARGET_ABI}                                       \
+       APP_PLATFORM=${APP_PLATFORM}                                               \
+       TARGET_LIBDIR=${TARGET_LIBDIR}                                             \
+       TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}"                                    \
+       TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}"                                  \
+       TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" &&                             \
+       ant $$BUILD_TYPE &&                                                        \
+       echo "++ Success!" &&                                                      \
+       echo "APK: bin/Minetest-$$BUILD_TYPE.apk" &&                               \
+       echo "You can install it with \`adb install -r bin/Minetest-$$BUILD_TYPE.apk\`"
+       
+prep_srcdir :
+       @rm ${ROOT}/jni/src;                                                       \
+       ln -s ${ROOT}/../../src ${ROOT}/jni/src
+       
+clean_apk : manifest
+       @export PATH=$$PATH:${SDKFOLDER}platform-tools:${ANDROID_NDK};             \
+       export ANDROID_HOME=${SDKFOLDER};                                          \
+       ant clean
+       
+install_debug : 
+       @export PATH=$$PATH:${SDKFOLDER}platform-tools:${ANDROID_NDK};             \
+       adb install -r ${ROOT}/bin/Minetest-debug.apk
+       
+install :
+       @export PATH=$$PATH:${SDKFOLDER}platform-tools:${ANDROID_NDK};             \
+       adb install -r ${ROOT}/bin/Minetest-release.apk
+       
+envpaths :
+       @echo "export PATH=$$PATH:${SDKFOLDER}platform-tools:${ANDROID_NDK}" > and_env;\
+       echo "export ANDROID_HOME=${SDKFOLDER}" >> and_env;
+       
+clean_all : 
+       @$(MAKE) -j${PARALLEL} clean_apk;                                          \
+       $(MAKE) clean_assets clean_irrlicht clean_leveldb clean_curl clean_openssl \
+       clean_openal clean_ogg clean_manifest;                                     \
+       sleep 1;                                                                   \
+       $(RM) -r gen libs obj deps bin Debug and_env
+       
+$(ROOT)/jni/src/android_version.h :
+       @echo "#define STR_HELPER(x) #x"                                           \
+       >${ROOT}/jni/src/android_version.h;                                        \
+       echo "#define STR(x) STR_HELPER(x)"                                        \
+       >> ${ROOT}/jni/src/android_version.h;                                      \
+       echo "#define VERSION_MAJOR $$(cat ${ROOT}/../../CMakeLists.txt |          \
+       grep ^set\(VERSION_MAJOR\ | sed 's/)/ /' | awk '{print $$2;}')"            \
+       >> ${ROOT}/jni/src/android_version.h;                                      \
+       echo "#define VERSION_MINOR $$(cat ${ROOT}/../../CMakeLists.txt |          \
+       grep ^set\(VERSION_MINOR\ | sed 's/)/ /' | awk '{print $$2;}')"            \
+       >> ${ROOT}/jni/src/android_version.h;                                      \
+       echo "#define VERSION_PATCH $$(cat ${ROOT}/../../CMakeLists.txt |          \
+       grep ^set\(VERSION_PATCH\ | sed 's/)/ /' | awk '{print $$2;}')"            \
+       >> ${ROOT}/jni/src/android_version.h;                                      \
+       echo "#define VERSION_PATCH_ORIG $$(cat ${ROOT}/../../CMakeLists.txt |     \
+       grep ^set\(VERSION_PATCH\ | sed 's/)/ /' | awk '{print $$2;}')"            \
+       >> ${ROOT}/jni/src/android_version.h;                                      \
+       echo "#define CMAKE_VERSION_GITHASH \"$$(git rev-parse --short=8 HEAD)\""  \
+       >> ${ROOT}/jni/src/android_version.h;                                      \
+       echo "#define CMAKE_VERSION_STRING STR(VERSION_MAJOR)\".\"STR(VERSION_MINOR)\
+       \".\"STR(VERSION_PATCH)"                                                   \
+       >> ${ROOT}/jni/src/android_version.h;
+       
+manifest :
+       @VERS_MAJOR=$$(cat ${ROOT}/../../CMakeLists.txt |                          \
+       grep ^set\(VERSION_MAJOR\ | sed 's/)/ /' | awk '{print $$2;}');            \
+       VERS_MINOR=$$(cat ${ROOT}/../../CMakeLists.txt |                           \
+       grep ^set\(VERSION_MINOR\ | sed 's/)/ /' | awk '{print $$2;}');            \
+       VERS_PATCH=$$(cat ${ROOT}/../../CMakeLists.txt |                           \
+       grep ^set\(VERSION_PATCH\ | sed 's/)/ /' | awk '{print $$2;}');            \
+       BASE_VERSION="$$VERS_MAJOR.$$VERS_MINOR.$$VERS_PATCH";                     \
+       if [ "${NDEBUG}x" != "x" ] ; then                                          \
+               DBG='';                                                                \
+               DBG_FLAG="android:debuggable=\"false\"";                               \
+       else                                                                       \
+               DBG="<uses-permission android:name=\"android.permission.SET_DEBUG_APP\" />"; \
+               DBG_FLAG="android:debuggable=\"true\"";                                \
+       fi;                                                                        \
+       cat ${ROOT}/AndroidManifest.xml.template |                                 \
+       sed s/###ANDROID_VERSION###/${ANDROID_VERSION_CODE}/g |                    \
+       sed s/###BASE_VERSION###/$$BASE_VERSION/g |                                \
+       sed -e "s@###DEBUG_BUILD###@$$DBG@g" |                                     \
+       sed -e "s@###DEBUG_FLAG###@$$DBG_FLAG@g" >${ROOT}/AndroidManifest.xml
+       
+clean_manifest :
+       rm -rf ${ROOT}/AndroidManifest.xml
+
+clean : clean_apk clean_assets
diff --git a/build/android/build.xml b/build/android/build.xml
new file mode 100644 (file)
index 0000000..50a3e95
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="Minetest" default="help">
+       <property file="local.properties" />
+       <property file="ant.properties" />
+       <property environment="env" />
+       <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+               <isset property="env.ANDROID_HOME" />
+       </condition>
+       <loadproperties srcFile="project.properties" />
+       <fail
+               message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+               unless="sdk.dir"
+       />
+       <import file="custom_rules.xml" optional="true" />
+       <import file="${sdk.dir}/tools/ant/build.xml" />
+</project>
diff --git a/build/android/irrlicht-back_button.patch b/build/android/irrlicht-back_button.patch
new file mode 100644 (file)
index 0000000..227749b
--- /dev/null
@@ -0,0 +1,19 @@
+--- irrlicht/source/Irrlicht/Android/CIrrDeviceAndroid.cpp     2014-06-03 20:56:21.289559503 +0200
++++ irrlicht/source/Irrlicht/Android/CIrrDeviceAndroid.cpp.orig        2014-06-03 20:57:39.281556749 +0200
+@@ -423,6 +423,7 @@
+                       }\r
+                       \r
+                       device->postEventFromUser(event);\r
++                      status = 1;\r
+               }\r
+               break;\r
+               default:\r
+@@ -479,7 +480,7 @@
+     KeyMap[1] = KEY_LBUTTON; // AKEYCODE_SOFT_LEFT\r
+     KeyMap[2] = KEY_RBUTTON; // AKEYCODE_SOFT_RIGHT\r
+     KeyMap[3] = KEY_HOME; // AKEYCODE_HOME\r
+-    KeyMap[4] = KEY_BACK; // AKEYCODE_BACK\r
++    KeyMap[4] = KEY_CANCEL; // AKEYCODE_BACK\r
+     KeyMap[5] = KEY_UNKNOWN; // AKEYCODE_CALL\r
+     KeyMap[6] = KEY_UNKNOWN; // AKEYCODE_ENDCALL\r
+     KeyMap[7] = KEY_KEY_0; // AKEYCODE_0\r
diff --git a/build/android/irrlicht-texturehack.patch b/build/android/irrlicht-texturehack.patch
new file mode 100644 (file)
index 0000000..a458ede
--- /dev/null
@@ -0,0 +1,240 @@
+--- irrlicht/source/Irrlicht/COGLESTexture.cpp.orig    2014-06-22 17:01:13.266568869 +0200
++++ irrlicht/source/Irrlicht/COGLESTexture.cpp 2014-06-22 17:03:59.298572810 +0200
+@@ -366,112 +366,140 @@
+       void(*convert)(const void*, s32, void*) = 0;\r
+       getFormatParameters(ColorFormat, InternalFormat, filtering, PixelFormat, PixelType, convert);\r
\r
+-      // make sure we don't change the internal format of existing images\r
+-      if (!newTexture)\r
+-              InternalFormat = oldInternalFormat;\r
+-\r
+-    Driver->setActiveTexture(0, this);\r
+-\r
+-      if (Driver->testGLError())\r
+-              os::Printer::log("Could not bind Texture", ELL_ERROR);\r
+-\r
+-      // mipmap handling for main texture\r
+-      if (!level && newTexture)\r
+-      {\r
+-              // auto generate if possible and no mipmap data is given\r
+-              if (!IsCompressed && HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE))\r
+-              {\r
+-                      if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))\r
+-                              glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);\r
+-                      else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY))\r
+-                              glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);\r
+-                      else\r
+-                              glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE);\r
++      bool retry = false;\r
++      \r
++      do { \r
++              if (retry) {\r
++                      InternalFormat = GL_RGBA;\r
++                      PixelFormat = GL_RGBA;\r
++                      convert = CColorConverter::convert_A8R8G8B8toA8B8G8R8;\r
++              }\r
++              // make sure we don't change the internal format of existing images\r
++              if (!newTexture)\r
++                      InternalFormat = oldInternalFormat;\r
\r
+-            glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);\r
+-                      AutomaticMipmapUpdate=true;\r
+-              }\r
++              Driver->setActiveTexture(0, this);\r
\r
+-              // enable bilinear filter without mipmaps\r
+-              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);\r
+-              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);\r
+-      }\r
++              if (Driver->testGLError())\r
++                      os::Printer::log("Could not bind Texture", ELL_ERROR);\r
\r
+-      // now get image data and upload to GPU\r
++              // mipmap handling for main texture\r
++              if (!level && newTexture)\r
++              {\r
++                      // auto generate if possible and no mipmap data is given\r
++                      if (!IsCompressed && HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE))\r
++                      {\r
++                              if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))\r
++                                      glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);\r
++                              else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY))\r
++                                      glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);\r
++                              else\r
++                                      glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE);\r
++\r
++                              glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);\r
++                              AutomaticMipmapUpdate=true;\r
++                      }\r
++\r
++                      // enable bilinear filter without mipmaps\r
++                      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);\r
++                      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);\r
++              }\r
\r
+-      u32 compressedImageSize = IImage::getCompressedImageSize(ColorFormat, image->getDimension().Width, image->getDimension().Height);\r
++              // now get image data and upload to GPU\r
\r
+-      void* source = image->lock();\r
++              u32 compressedImageSize = IImage::getCompressedImageSize(ColorFormat, image->getDimension().Width, image->getDimension().Height);\r
\r
+-      IImage* tmpImage = 0;\r
++              void* source = image->lock();\r
\r
+-      if (convert)\r
+-      {\r
+-              tmpImage = new CImage(image->getColorFormat(), image->getDimension());\r
+-              void* dest = tmpImage->lock();\r
+-              convert(source, image->getDimension().getArea(), dest);\r
+-              image->unlock();\r
+-              source = dest;\r
+-      }\r
++              IImage* tmpImage = 0;\r
\r
+-      if (newTexture)\r
+-      {\r
+-              if (IsCompressed)\r
++              if (convert)\r
+               {\r
+-                      glCompressedTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, image->getDimension().Width,\r
+-                              image->getDimension().Height, 0, compressedImageSize, source);\r
++                      tmpImage = new CImage(image->getColorFormat(), image->getDimension());\r
++                      void* dest = tmpImage->lock();\r
++                      convert(source, image->getDimension().getArea(), dest);\r
++                      image->unlock();\r
++                      source = dest;\r
+               }\r
+-              else\r
+-                      glTexImage2D(GL_TEXTURE_2D, level, InternalFormat, image->getDimension().Width,\r
+-                              image->getDimension().Height, 0, PixelFormat, PixelType, source);\r
+-      }\r
+-      else\r
+-      {\r
+-              if (IsCompressed)\r
++\r
++              if (newTexture)\r
+               {\r
+-                      glCompressedTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width,\r
+-                              image->getDimension().Height, PixelFormat, compressedImageSize, source);\r
++                      if (IsCompressed)\r
++                      {\r
++                              glCompressedTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, image->getDimension().Width,\r
++                                      image->getDimension().Height, 0, compressedImageSize, source);\r
++                      }\r
++                      else\r
++                              glTexImage2D(GL_TEXTURE_2D, level, InternalFormat, image->getDimension().Width,\r
++                                      image->getDimension().Height, 0, PixelFormat, PixelType, source);\r
+               }\r
+               else\r
+-                      glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width,\r
+-                              image->getDimension().Height, PixelFormat, PixelType, source);\r
+-      }\r
+-\r
+-      if (convert)\r
+-      {\r
+-              tmpImage->unlock();\r
+-              tmpImage->drop();\r
+-      }\r
+-      else\r
+-              image->unlock();\r
+-\r
+-      if (!level && newTexture)\r
+-      {\r
+-              if (IsCompressed && !mipmapData)\r
+               {\r
+-                      if (image->hasMipMaps())\r
+-                              mipmapData = static_cast<u8*>(image->lock())+compressedImageSize;\r
++                      if (IsCompressed)\r
++                      {\r
++                              glCompressedTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width,\r
++                                      image->getDimension().Height, PixelFormat, compressedImageSize, source);\r
++                      }\r
+                       else\r
+-                              HasMipMaps = false;\r
++                              glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width,\r
++                                      image->getDimension().Height, PixelFormat, PixelType, source);\r
+               }\r
\r
+-              regenerateMipMapLevels(mipmapData);\r
+-\r
+-              if (HasMipMaps) // might have changed in regenerateMipMapLevels\r
++              if (convert)\r
+               {\r
+-                      // enable bilinear mipmap filter\r
+-                      GLint filteringMipMaps = GL_LINEAR_MIPMAP_NEAREST;\r
+-\r
+-                      if (filtering != GL_LINEAR)\r
+-                              filteringMipMaps = GL_NEAREST_MIPMAP_NEAREST;\r
++                      tmpImage->unlock();\r
++                      tmpImage->drop();\r
++              }\r
++              else\r
++                      image->unlock();\r
++              \r
++              if (glGetError() != GL_NO_ERROR) {\r
++                      static bool warned = false;\r
++                      if ((!retry) && (ColorFormat == ECF_A8R8G8B8)) {\r
++\r
++                              if (!warned) {\r
++                                      os::Printer::log("Your driver claims to support GL_BGRA but fails on trying to upload a texture, converting to GL_RGBA and trying again", ELL_ERROR);\r
++                                      warned = true;\r
++                              }\r
++                      }\r
++                      else if (retry) {\r
++                              os::Printer::log("Neither uploading texture as GL_BGRA nor, converted one using GL_RGBA succeeded", ELL_ERROR);\r
++                      }\r
++                      retry = !retry;\r
++                      continue;\r
++              } else {\r
++                      retry = false;\r
++              }\r
\r
+-                      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filteringMipMaps);\r
+-                      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);\r
++              if (!level && newTexture)\r
++              {\r
++                      if (IsCompressed && !mipmapData)\r
++                      {\r
++                              if (image->hasMipMaps())\r
++                                      mipmapData = static_cast<u8*>(image->lock())+compressedImageSize;\r
++                              else\r
++                                      HasMipMaps = false;\r
++                      }\r
++\r
++                      regenerateMipMapLevels(mipmapData);\r
++\r
++                      if (HasMipMaps) // might have changed in regenerateMipMapLevels\r
++                      {\r
++                              // enable bilinear mipmap filter\r
++                              GLint filteringMipMaps = GL_LINEAR_MIPMAP_NEAREST;\r
++\r
++                              if (filtering != GL_LINEAR)\r
++                                      filteringMipMaps = GL_NEAREST_MIPMAP_NEAREST;\r
++\r
++                              glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filteringMipMaps);\r
++                              glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);\r
++                      }\r
+               }\r
+-      }\r
\r
+-      if (Driver->testGLError())\r
+-              os::Printer::log("Could not glTexImage2D", ELL_ERROR);\r
++              if (Driver->testGLError())\r
++                      os::Printer::log("Could not glTexImage2D", ELL_ERROR);\r
++      }\r
++      while(retry);\r
+ }\r
\r
\r
+--- irrlicht/source/Irrlicht/COGLESTexture.cpp.orig    2014-06-25 00:28:50.820501856 +0200
++++ irrlicht/source/Irrlicht/COGLESTexture.cpp 2014-06-25 00:08:37.712544692 +0200
+@@ -422,6 +422,9 @@
+                       source = dest;\r
+               }\r
\r
++              //clear old error\r
++              glGetError();\r
++\r
+               if (newTexture)\r
+               {\r
+                       if (IsCompressed)\r
diff --git a/build/android/irrlicht-touchcount.patch b/build/android/irrlicht-touchcount.patch
new file mode 100644 (file)
index 0000000..d4e4b9c
--- /dev/null
@@ -0,0 +1,30 @@
+--- irrlicht.orig/include/IEventReceiver.h     2014-06-03 19:43:50.433713133 +0200
++++ irrlicht/include/IEventReceiver.h  2014-06-03 19:44:36.993711489 +0200
+@@ -375,6 +375,9 @@
+         // Y position of simple touch.\r
+               s32 Y;\r
\r
++              // number of current touches\r
++              s32 touchedCount;\r
++\r
+               //! Type of touch event.\r
+               ETOUCH_INPUT_EVENT Event;\r
+       };\r
+--- irrlicht.orig/source/Irrlicht/Android/CIrrDeviceAndroid.cpp        2014-06-03 19:43:50.505713130 +0200
++++ irrlicht/source/Irrlicht/Android/CIrrDeviceAndroid.cpp     2014-06-03 19:45:37.265709359 +0200
+@@ -315,6 +315,7 @@
+                                               event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, i);\r
+                                               event.TouchInput.X = AMotionEvent_getX(androidEvent, i);\r
+                                               event.TouchInput.Y = AMotionEvent_getY(androidEvent, i);\r
++                                              event.TouchInput.touchedCount = AMotionEvent_getPointerCount(androidEvent);\r
\r
+                                               device->postEventFromUser(event);\r
+                                       }\r
+@@ -326,6 +327,7 @@
+                                       event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, pointerIndex);\r
+                                       event.TouchInput.X = AMotionEvent_getX(androidEvent, pointerIndex);\r
+                                       event.TouchInput.Y = AMotionEvent_getY(androidEvent, pointerIndex);\r
++                                      event.TouchInput.touchedCount = AMotionEvent_getPointerCount(androidEvent);\r
\r
+                                       device->postEventFromUser(event);\r
+                               }\r
diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk
new file mode 100644 (file)
index 0000000..3559c2b
--- /dev/null
@@ -0,0 +1,310 @@
+LOCAL_PATH := $(call my-dir)/..
+
+#LOCAL_ADDRESS_SANITIZER:=true
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := Irrlicht
+LOCAL_SRC_FILES := deps/irrlicht/lib/Android/libIrrlicht.a
+include $(PREBUILT_STATIC_LIBRARY)
+
+#include $(CLEAR_VARS)
+#LOCAL_MODULE := LevelDB
+#LOCAL_SRC_FILES := deps/leveldb/libleveldb.a
+#include $(PREBUILT_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := curl
+LOCAL_SRC_FILES := deps/curl-7.35.0/lib/.libs/libcurl.a
+include $(PREBUILT_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := freetype
+LOCAL_SRC_FILES := deps/freetype2-android/Android/obj/local/$(TARGET_ARCH_ABI)/libfreetype2-static.a
+include $(PREBUILT_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := openal
+LOCAL_SRC_FILES := deps/openal-soft/libs/$(TARGET_LIBDIR)/libopenal.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ogg
+LOCAL_SRC_FILES := deps/libvorbis-libogg-android/libs/$(TARGET_LIBDIR)/libogg.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := vorbis
+LOCAL_SRC_FILES := deps/libvorbis-libogg-android/libs/$(TARGET_LIBDIR)/libvorbis.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ssl
+LOCAL_SRC_FILES := deps/openssl-android/libs/$(TARGET_LIBDIR)/libssl.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := crypto
+LOCAL_SRC_FILES := deps/openssl-android/libs/$(TARGET_LIBDIR)/libcrypto.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := minetest
+
+LOCAL_CPP_FEATURES += exceptions
+
+ifdef GPROF
+GPROF_DEF=-DGPROF
+endif
+
+LOCAL_CFLAGS := -D_IRR_ANDROID_PLATFORM_ \
+                               -DHAVE_TOUCHSCREENGUI    \
+                               -DUSE_CURL=1             \
+                               -DUSE_SOUND=1            \
+                               -DUSE_FREETYPE=1         \
+                               $(GPROF_DEF)             \
+                               -pipe -fstrict-aliasing
+
+ifndef NDEBUG
+LOCAL_CFLAGS += -g -D_DEBUG -O0 -fno-omit-frame-pointer
+else
+LOCAL_CFLAGS += -fexpensive-optimizations -O3
+endif
+
+ifdef GPROF
+PROFILER_LIBS := android-ndk-profiler
+LOCAL_CFLAGS += -pg
+endif
+
+# LOCAL_CFLAGS += -fsanitize=address
+# LOCAL_LDFLAGS += -fsanitize=address
+
+ifeq ($(TARGET_ARCH_ABI),x86)
+LOCAL_CFLAGS += -fno-stack-protector
+endif
+
+LOCAL_C_INCLUDES :=                               \
+               jni/src jni/src/sqlite                    \
+               jni/src/script                            \
+               jni/src/lua/src                           \
+               jni/src/json                              \
+               jni/src/cguittfont                        \
+               deps/irrlicht/include                     \
+               deps/freetype2-android/include            \
+               deps/curl-7.35.0/include                  \
+               deps/openal-soft/jni/OpenAL/include       \
+               deps/libvorbis-libogg-android/jni/include
+               
+#              deps/leveldb/include                      \
+
+LOCAL_SRC_FILES :=                                \
+               jni/src/ban.cpp                           \
+               jni/src/base64.cpp                        \
+               jni/src/biome.cpp                         \
+               jni/src/camera.cpp                        \
+               jni/src/cavegen.cpp                       \
+               jni/src/chat.cpp                          \
+               jni/src/client.cpp                        \
+               jni/src/clientiface.cpp                   \
+               jni/src/clientmap.cpp                     \
+               jni/src/clientmedia.cpp                   \
+               jni/src/clientobject.cpp                  \
+               jni/src/clouds.cpp                        \
+               jni/src/collision.cpp                     \
+               jni/src/connection.cpp                    \
+               jni/src/content_abm.cpp                   \
+               jni/src/content_cao.cpp                   \
+               jni/src/content_cso.cpp                   \
+               jni/src/content_mapblock.cpp              \
+               jni/src/content_mapnode.cpp               \
+               jni/src/content_nodemeta.cpp              \
+               jni/src/content_sao.cpp                   \
+               jni/src/convert_json.cpp                  \
+               jni/src/craftdef.cpp                      \
+               jni/src/database-dummy.cpp                \
+               jni/src/database-sqlite3.cpp              \
+               jni/src/database.cpp                      \
+               jni/src/debug.cpp                         \
+               jni/src/defaultsettings.cpp               \
+               jni/src/drawscene.cpp                     \
+               jni/src/dungeongen.cpp                    \
+               jni/src/emerge.cpp                        \
+               jni/src/environment.cpp                   \
+               jni/src/filecache.cpp                     \
+               jni/src/filesys.cpp                       \
+               jni/src/game.cpp                          \
+               jni/src/genericobject.cpp                 \
+               jni/src/gettext.cpp                       \
+               jni/src/guiChatConsole.cpp                \
+               jni/src/guiEngine.cpp                     \
+               jni/src/guiFileSelectMenu.cpp             \
+               jni/src/guiFormSpecMenu.cpp               \
+               jni/src/guiKeyChangeMenu.cpp              \
+               jni/src/guiPasswordChange.cpp             \
+               jni/src/guiTable.cpp                      \
+               jni/src/guiVolumeChange.cpp               \
+               jni/src/httpfetch.cpp                     \
+               jni/src/hud.cpp                           \
+               jni/src/inventory.cpp                     \
+               jni/src/inventorymanager.cpp              \
+               jni/src/itemdef.cpp                       \
+               jni/src/keycode.cpp                       \
+               jni/src/light.cpp                         \
+               jni/src/localplayer.cpp                   \
+               jni/src/log.cpp                           \
+               jni/src/main.cpp                          \
+               jni/src/map.cpp                           \
+               jni/src/mapblock.cpp                      \
+               jni/src/mapblock_mesh.cpp                 \
+               jni/src/mapgen.cpp                        \
+               jni/src/mapgen_indev.cpp                  \
+               jni/src/mapgen_math.cpp                   \
+               jni/src/mapgen_singlenode.cpp             \
+               jni/src/mapgen_v6.cpp                     \
+               jni/src/mapgen_v7.cpp                     \
+               jni/src/mapnode.cpp                       \
+               jni/src/mapsector.cpp                     \
+               jni/src/mesh.cpp                          \
+               jni/src/mods.cpp                          \
+               jni/src/nameidmapping.cpp                 \
+               jni/src/nodedef.cpp                       \
+               jni/src/nodemetadata.cpp                  \
+               jni/src/nodetimer.cpp                     \
+               jni/src/noise.cpp                         \
+               jni/src/object_properties.cpp             \
+               jni/src/particles.cpp                     \
+               jni/src/pathfinder.cpp                    \
+               jni/src/player.cpp                        \
+               jni/src/porting_android.cpp               \
+               jni/src/porting.cpp                       \
+               jni/src/quicktune.cpp                     \
+               jni/src/rollback.cpp                      \
+               jni/src/rollback_interface.cpp            \
+               jni/src/serialization.cpp                 \
+               jni/src/server.cpp                        \
+               jni/src/serverlist.cpp                    \
+               jni/src/serverobject.cpp                  \
+               jni/src/sha1.cpp                          \
+               jni/src/shader.cpp                        \
+               jni/src/sky.cpp                           \
+               jni/src/socket.cpp                        \
+               jni/src/sound.cpp                         \
+               jni/src/sound_openal.cpp                  \
+               jni/src/staticobject.cpp                  \
+               jni/src/subgame.cpp                       \
+               jni/src/test.cpp                          \
+               jni/src/tile.cpp                          \
+               jni/src/tool.cpp                          \
+               jni/src/treegen.cpp                       \
+               jni/src/version.cpp                       \
+               jni/src/voxel.cpp                         \
+               jni/src/voxelalgorithms.cpp               \
+               jni/src/util/directiontables.cpp          \
+               jni/src/util/numeric.cpp                  \
+               jni/src/util/pointedthing.cpp             \
+               jni/src/util/serialize.cpp                \
+               jni/src/util/string.cpp                   \
+               jni/src/util/timetaker.cpp                \
+               jni/src/touchscreengui.cpp
+               
+#              jni/src/database-leveldb.cpp              \
+
+# lua api
+LOCAL_SRC_FILES +=                                \
+               jni/src/script/common/c_content.cpp       \
+               jni/src/script/common/c_converter.cpp     \
+               jni/src/script/common/c_internal.cpp      \
+               jni/src/script/common/c_types.cpp         \
+               jni/src/script/cpp_api/s_base.cpp         \
+               jni/src/script/cpp_api/s_entity.cpp       \
+               jni/src/script/cpp_api/s_env.cpp          \
+               jni/src/script/cpp_api/s_inventory.cpp    \
+               jni/src/script/cpp_api/s_item.cpp         \
+               jni/src/script/cpp_api/s_mainmenu.cpp     \
+               jni/src/script/cpp_api/s_node.cpp         \
+               jni/src/script/cpp_api/s_nodemeta.cpp     \
+               jni/src/script/cpp_api/s_player.cpp       \
+               jni/src/script/cpp_api/s_server.cpp       \
+               jni/src/script/cpp_api/s_async.cpp        \
+               jni/src/script/lua_api/l_base.cpp         \
+               jni/src/script/lua_api/l_craft.cpp        \
+               jni/src/script/lua_api/l_env.cpp          \
+               jni/src/script/lua_api/l_inventory.cpp    \
+               jni/src/script/lua_api/l_item.cpp         \
+               jni/src/script/lua_api/l_mainmenu.cpp     \
+               jni/src/script/lua_api/l_mapgen.cpp       \
+               jni/src/script/lua_api/l_nodemeta.cpp     \
+               jni/src/script/lua_api/l_nodetimer.cpp    \
+               jni/src/script/lua_api/l_noise.cpp        \
+               jni/src/script/lua_api/l_object.cpp       \
+               jni/src/script/lua_api/l_particles.cpp    \
+               jni/src/script/lua_api/l_rollback.cpp     \
+               jni/src/script/lua_api/l_server.cpp       \
+               jni/src/script/lua_api/l_settings.cpp     \
+               jni/src/script/lua_api/l_util.cpp         \
+               jni/src/script/lua_api/l_vmanip.cpp       \
+               jni/src/script/scripting_game.cpp         \
+               jni/src/script/scripting_mainmenu.cpp
+               
+#freetype2 support
+LOCAL_SRC_FILES +=                                \
+               jni/src/cguittfont/xCGUITTFont.cpp
+
+# lua
+LOCAL_SRC_FILES +=                                \
+               jni/src/lua/src/lapi.c                    \
+               jni/src/lua/src/lauxlib.c                 \
+               jni/src/lua/src/lbaselib.c                \
+               jni/src/lua/src/lcode.c                   \
+               jni/src/lua/src/ldblib.c                  \
+               jni/src/lua/src/ldebug.c                  \
+               jni/src/lua/src/ldo.c                     \
+               jni/src/lua/src/ldump.c                   \
+               jni/src/lua/src/lfunc.c                   \
+               jni/src/lua/src/lgc.c                     \
+               jni/src/lua/src/linit.c                   \
+               jni/src/lua/src/liolib.c                  \
+               jni/src/lua/src/llex.c                    \
+               jni/src/lua/src/lmathlib.c                \
+               jni/src/lua/src/lmem.c                    \
+               jni/src/lua/src/loadlib.c                 \
+               jni/src/lua/src/lobject.c                 \
+               jni/src/lua/src/lopcodes.c                \
+               jni/src/lua/src/loslib.c                  \
+               jni/src/lua/src/lparser.c                 \
+               jni/src/lua/src/lstate.c                  \
+               jni/src/lua/src/lstring.c                 \
+               jni/src/lua/src/lstrlib.c                 \
+               jni/src/lua/src/ltable.c                  \
+               jni/src/lua/src/ltablib.c                 \
+               jni/src/lua/src/ltm.c                     \
+               jni/src/lua/src/lundump.c                 \
+               jni/src/lua/src/lvm.c                     \
+               jni/src/lua/src/lzio.c                    \
+               jni/src/lua/src/print.c
+
+# sqlite
+LOCAL_SRC_FILES += jni/src/sqlite/sqlite3.c
+
+# jthread
+LOCAL_SRC_FILES +=                                \
+               jni/src/jthread/pthread/jevent.cpp        \
+               jni/src/jthread/pthread/jmutex.cpp        \
+               jni/src/jthread/pthread/jsemaphore.cpp    \
+               jni/src/jthread/pthread/jthread.cpp
+
+# json
+LOCAL_SRC_FILES += jni/src/json/jsoncpp.cpp
+
+LOCAL_SHARED_LIBRARIES := openal ogg vorbis ssl crypto
+LOCAL_STATIC_LIBRARIES := Irrlicht freetype curl android_native_app_glue $(PROFILER_LIBS)
+# LevelDB
+LOCAL_LDLIBS := -lEGL -llog -lGLESv1_CM -lGLESv2 -lz -landroid
+
+include $(BUILD_SHARED_LIBRARY)
+
+# at the end of Android.mk
+ifdef GPROF
+$(call import-module,android-ndk-profiler)
+endif
+$(call import-module,android/native_app_glue)
diff --git a/build/android/jni/Application.mk b/build/android/jni/Application.mk
new file mode 100644 (file)
index 0000000..b7ffc56
--- /dev/null
@@ -0,0 +1,8 @@
+# NDK_TOOLCHAIN_VERSION := clang3.3
+
+APP_PLATFORM := android-9
+APP_MODULES := minetest
+APP_STL := gnustl_static
+
+APP_CPPFLAGS += -fexceptions
+APP_GNUSTL_FORCE_CPP_FEATURES := rtti
diff --git a/build/android/libvorbis-libogg-fpu.patch b/build/android/libvorbis-libogg-fpu.patch
new file mode 100644 (file)
index 0000000..52ab397
--- /dev/null
@@ -0,0 +1,37 @@
+--- libvorbis-libogg-android/jni/libvorbis-jni/Android.mk.orig 2014-06-17 19:22:50.621559073 +0200
++++ libvorbis-libogg-android/jni/libvorbis-jni/Android.mk      2014-06-17 19:38:20.641581140 +0200
+@@ -4,9 +4,6 @@
+ LOCAL_MODULE := vorbis-jni
+ LOCAL_CFLAGS += -I$(LOCAL_PATH)/../include -fsigned-char
+-ifeq ($(TARGET_ARCH),arm)
+-      LOCAL_CFLAGS += -march=armv6 -marm -mfloat-abi=softfp -mfpu=vfp
+-endif
+ LOCAL_SHARED_LIBRARIES := libogg libvorbis
+--- libvorbis-libogg-android/jni/libvorbis/Android.mk.orig     2014-06-17 19:22:39.077558797 +0200
++++ libvorbis-libogg-android/jni/libvorbis/Android.mk  2014-06-17 19:38:52.121581887 +0200
+@@ -4,9 +4,6 @@
+ LOCAL_MODULE := libvorbis
+ LOCAL_CFLAGS += -I$(LOCAL_PATH)/../include -ffast-math -fsigned-char
+-ifeq ($(TARGET_ARCH),arm)
+-      LOCAL_CFLAGS += -march=armv6 -marm -mfloat-abi=softfp -mfpu=vfp
+-endif
+ LOCAL_SHARED_LIBRARIES := libogg
+ LOCAL_SRC_FILES := \
+--- libvorbis-libogg-android/jni/libogg/Android.mk.orig        2014-06-17 19:22:33.965558675 +0200
++++ libvorbis-libogg-android/jni/libogg/Android.mk     2014-06-17 19:38:25.337581252 +0200
+@@ -4,10 +4,6 @@
+ LOCAL_MODULE := libogg
+ LOCAL_CFLAGS += -I$(LOCAL_PATH)/../include -ffast-math -fsigned-char
+-ifeq ($(TARGET_ARCH),arm)
+-      LOCAL_CFLAGS += -march=armv6 -marm -mfloat-abi=softfp -mfpu=vfp
+-endif
+-
+ LOCAL_SRC_FILES := \
+       bitwise.c \
diff --git a/build/android/project.properties b/build/android/project.properties
new file mode 100644 (file)
index 0000000..cc2a7c5
--- /dev/null
@@ -0,0 +1 @@
+target=android-10
diff --git a/build/android/res/drawable-hdpi/irr_icon.png b/build/android/res/drawable-hdpi/irr_icon.png
new file mode 100644 (file)
index 0000000..0b6861a
Binary files /dev/null and b/build/android/res/drawable-hdpi/irr_icon.png differ
diff --git a/build/android/res/drawable-ldpi/irr_icon.png b/build/android/res/drawable-ldpi/irr_icon.png
new file mode 100644 (file)
index 0000000..b8c5d01
Binary files /dev/null and b/build/android/res/drawable-ldpi/irr_icon.png differ
diff --git a/build/android/res/drawable-mdpi/irr_icon.png b/build/android/res/drawable-mdpi/irr_icon.png
new file mode 100644 (file)
index 0000000..951a7f8
Binary files /dev/null and b/build/android/res/drawable-mdpi/irr_icon.png differ
diff --git a/build/android/res/drawable-xhdpi/irr_icon.png b/build/android/res/drawable-xhdpi/irr_icon.png
new file mode 100644 (file)
index 0000000..2ec528e
Binary files /dev/null and b/build/android/res/drawable-xhdpi/irr_icon.png differ
diff --git a/build/android/res/layout/assetcopy.xml b/build/android/res/layout/assetcopy.xml
new file mode 100644 (file)
index 0000000..ade4b0c
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <ProgressBar
+        android:id="@+id/progressBar1"
+        style="?android:attr/progressBarStyleHorizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical" />
+
+    <TextView
+        android:id="@+id/textView1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:text="preparing media ..."
+        android:textAppearance="?android:attr/textAppearanceSmall" />
+
+</LinearLayout>
diff --git a/build/android/res/values/styles.xml b/build/android/res/values/styles.xml
new file mode 100644 (file)
index 0000000..25b8df5
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+       <style name="Theme.Transparent" parent="android:Theme">
+               <item name="android:windowIsTranslucent">true</item>
+               <item name="android:windowBackground">@android:color/transparent</item>
+               <item name="android:windowContentOverlay">@null</item>
+               <item name="android:windowNoTitle">true</item>
+               <item name="android:windowIsFloating">true</item>
+               <item name="android:backgroundDimEnabled">false</item>
+       </style>
+</resources>
\ No newline at end of file
diff --git a/build/android/src/org/minetest/minetest/MinetestAssetCopy.java b/build/android/src/org/minetest/minetest/MinetestAssetCopy.java
new file mode 100644 (file)
index 0000000..652a008
--- /dev/null
@@ -0,0 +1,288 @@
+package org.minetest.minetest;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Vector;
+
+import android.app.Activity;
+import android.content.res.AssetFileDescriptor;
+
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Log;
+import android.view.Display;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+public class MinetestAssetCopy extends Activity {
+       
+       @Override
+       public void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+               
+               setContentView(R.layout.assetcopy);
+               
+               m_ProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
+               m_Filename = (TextView) findViewById(R.id.textView1);
+               
+               Display display = getWindowManager().getDefaultDisplay();
+               m_ProgressBar.getLayoutParams().width = (int) (display.getWidth() * 0.8);
+               m_ProgressBar.invalidate();
+               
+               m_AssetCopy = new copyAssetTask();
+               m_AssetCopy.execute();
+       }
+       
+       ProgressBar m_ProgressBar;
+       TextView m_Filename;
+       
+       copyAssetTask m_AssetCopy;
+       
+       private class copyAssetTask extends AsyncTask<String, Integer, String>{
+               
+               private void copyElement(String name, String path) {
+                       String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath();
+                       String full_path;
+                       if (path != "") {
+                               full_path = path + "/" + name;
+                       }
+                       else {
+                               full_path = name;
+                       }
+                       //is a folder read asset list
+                       if (m_foldernames.contains(full_path)) {
+                               m_Foldername = full_path;
+                               publishProgress(0);
+                               File current_folder = new File(baseDir + "/" + full_path);
+                               if (!current_folder.exists()) {
+                                       if (!current_folder.mkdirs()) {
+                                               Log.w("MinetestAssetCopy","\t failed create folder: " + baseDir + "/" + full_path);
+                                       }
+                                       else {
+                                               Log.w("MinetestAssetCopy","\t created folder: " + baseDir + "/" + full_path);
+                                       }
+                               }
+                               try {
+                                       String[] current_assets = getAssets().list(full_path);
+                                       for(int i=0; i < current_assets.length; i++) {
+                                               copyElement(current_assets[i],full_path);
+                                       }
+                               } catch (IOException e) {
+                                       Log.w("MinetestAssetCopy","\t failed to read contents of folder");
+                                       // TODO Auto-generated catch block
+                                       e.printStackTrace();
+                               }
+                       }
+                       //is a file just copy
+                       else {
+                               boolean refresh = true;
+                               
+                               File testme = new File(baseDir + "/" + full_path);
+                               
+                               long asset_filesize = -1;
+                               long stored_filesize = -1;
+                               
+                               if (testme.exists()) {
+                                       try {
+                                               AssetFileDescriptor fd = getAssets().openFd(full_path);
+                                               asset_filesize = fd.getLength();
+                                               fd.close();
+                                       } catch (IOException e) {
+                                               refresh = true;
+                                               m_asset_size_unknown.add(full_path);
+                                       }
+                                       
+                                       stored_filesize = testme.length();
+                                       
+                                       if (asset_filesize == stored_filesize) {
+                                               refresh = false;
+                                       }
+                                       
+                               }
+                               
+                               if (refresh) {
+                                       m_tocopy.add(full_path);
+                               }
+                       }
+               }
+               
+               private long getFullSize(String filename) {
+                       long size = 0;
+                       try {
+                       InputStream src = getAssets().open(filename);
+                       byte[] buf = new byte[1024];
+                       
+                       int len = 0;
+                       while ((len = src.read(buf)) > 0) {
+                               size += len;
+                       }
+                       }
+                       catch (IOException e) {
+                               e.printStackTrace();
+                       }
+                       return size;
+               }
+
+               @Override
+               protected String doInBackground(String... files) {
+                       
+                       m_foldernames  = new Vector<String>();
+                       m_tocopy       = new Vector<String>();
+                       m_asset_size_unknown = new Vector<String>();
+                       String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/";
+                       
+                       File TempFolder = new File(baseDir + "Minetest/tmp/");
+                       
+                       if (!TempFolder.exists()) {
+                               TempFolder.mkdir();
+                       }
+                       else {
+                               File[] todel = TempFolder.listFiles();
+                               
+                               for(int i=0; i < todel.length; i++) {
+                                       Log.w("MinetestAssetCopy","deleting: " + todel[i].getAbsolutePath());
+                                       todel[i].delete();
+                               }
+                       }
+                       
+                       // add a .nomedia file
+                       try {
+                               OutputStream dst = new FileOutputStream(baseDir + "Minetest/.nomedia");
+                               dst.close();
+                       } catch (IOException e) {
+                               Log.w("MinetestAssetCopy","Failed to create .nomedia file");
+                               e.printStackTrace();
+                       }
+                       
+                       try {
+                               InputStream is = getAssets().open("index.txt");
+                               BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+               
+                               String line = reader.readLine();
+                               while(line != null){
+                                       m_foldernames.add(line);
+                                       line = reader.readLine();
+                               }
+                       } catch (IOException e1) {
+                               // TODO Auto-generated catch block
+                               e1.printStackTrace();
+                       }
+                       
+                       copyElement("Minetest","");
+                       
+                       m_copy_started = true;
+                       m_ProgressBar.setMax(m_tocopy.size());
+                       
+                       for (int i = 0; i < m_tocopy.size(); i++) {
+                               try {
+                                       String filename = m_tocopy.get(i);
+                                       publishProgress(i);
+                                       
+                                       boolean asset_size_unknown = false;
+                                       long filesize = -1;
+                                       
+                                       if (m_asset_size_unknown.contains(filename)) {
+                                               File testme = new File(baseDir + "/" + filename);
+                                               
+                                               if(testme.exists()) {
+                                                       filesize = testme.length();
+                                               }
+                                               asset_size_unknown = true;
+                                       }
+                                       
+                                       InputStream src;
+                                       try {
+                                               src = getAssets().open(filename);
+                                       } catch (IOException e) {
+                                               Log.w("MinetestAssetCopy","Copying file: " + filename + " FAILED (not in assets)");
+                                               // TODO Auto-generated catch block
+                                               e.printStackTrace();
+                                               continue;
+                                       }
+                                       
+                                       // Transfer bytes from in to out
+                                       byte[] buf = new byte[1*1024];
+                                       int len = src.read(buf, 0, 1024);
+                                       
+                                       /* following handling is crazy but we need to deal with    */
+                                       /* compressed assets.Flash chips limited livetime sue to   */
+                                       /* write operations, we can't allow large files to destroy */
+                                       /* users flash.                                            */
+                                       if (asset_size_unknown) {
+                                               if ( (len > 0) && (len < buf.length) && (len == filesize)) {
+                                                       src.close();
+                                                       continue;
+                                               }
+                                               
+                                               if (len == buf.length) {
+                                                       src.close();
+                                                       long size = getFullSize(filename);
+                                                       if ( size == filesize) {
+                                                               continue;
+                                                       }
+                                                       src = getAssets().open(filename);
+                                                       len = src.read(buf, 0, 1024);
+                                               }
+                                       }
+                                       if (len > 0) {
+                                               int total_filesize = 0;
+                                               OutputStream dst;
+                                               try {
+                                                       dst = new FileOutputStream(baseDir + "/" + filename);
+                                               } catch (IOException e) {
+                                                       Log.w("MinetestAssetCopy","Copying file: " + baseDir + 
+                                                       "/" + filename + " FAILED (couldn't open output file)");
+                                                       e.printStackTrace();
+                                                       src.close();
+                                                       continue;
+                                               }
+                                               dst.write(buf, 0, len);
+                                               total_filesize += len;
+                                               
+                                               while ((len = src.read(buf)) > 0) {
+                                                       dst.write(buf, 0, len);
+                                                       total_filesize += len;
+                                               }
+                                               
+                                               dst.close();
+                                               Log.w("MinetestAssetCopy","Copied file: " + m_tocopy.get(i) + " (" + total_filesize + " bytes)");
+                                       }
+                                       else if (len < 0) {
+                                               Log.w("MinetestAssetCopy","Copying file: " + m_tocopy.get(i) + " failed, size < 0");
+                                       }
+                                       src.close();
+                               } catch (IOException e) {
+                                       Log.w("MinetestAssetCopy","Copying file: " + m_tocopy.get(i) + " failed");
+                                       e.printStackTrace();
+                               }
+                       }
+                       
+                       return "";
+               }
+               
+               protected void onProgressUpdate(Integer... progress) {
+                       if (m_copy_started) {
+                               m_ProgressBar.setProgress(progress[0]);
+                               m_Filename.setText(m_tocopy.get(progress[0]));
+                       }
+                       else {
+                               m_Filename.setText("scanning " + m_Foldername + " ...");
+                       }
+               }
+               
+               protected void onPostExecute (String result) {
+                       finish();
+               }
+               boolean m_copy_started = false;
+               String m_Foldername = "media";
+               Vector<String> m_foldernames;
+               Vector<String> m_tocopy;
+               Vector<String> m_asset_size_unknown;
+       }
+}
diff --git a/build/android/src/org/minetest/minetest/MinetestTextEntry.java b/build/android/src/org/minetest/minetest/MinetestTextEntry.java
new file mode 100644 (file)
index 0000000..db175a4
--- /dev/null
@@ -0,0 +1,91 @@
+package org.minetest.minetest;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.InputType;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnKeyListener;
+import android.widget.EditText;
+
+public class MinetestTextEntry extends Activity {
+       public AlertDialog mTextInputDialog;
+       public EditText mTextInputWidget;
+       
+       private final int MultiLineTextInput              = 1;
+       private final int SingleLineTextInput             = 2;
+       private final int SingleLinePasswordInput         = 3;
+       
+       @Override
+       public void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+               
+               Bundle b = getIntent().getExtras();
+               String acceptButton = b.getString("EnterButton");
+               String hint         = b.getString("hint");
+               String current      = b.getString("current");
+               int    editType     = b.getInt("editType");
+               
+               AlertDialog.Builder builder = new AlertDialog.Builder(this);
+               mTextInputWidget = new EditText(this);
+               mTextInputWidget.setHint(hint);
+               mTextInputWidget.setText(current);
+               mTextInputWidget.setMinWidth(300);
+               if (editType == SingleLinePasswordInput) {
+                       mTextInputWidget.setInputType(InputType.TYPE_CLASS_TEXT | 
+                                       InputType.TYPE_TEXT_VARIATION_PASSWORD);
+               }
+               else {
+                       mTextInputWidget.setInputType(InputType.TYPE_CLASS_TEXT);
+               }
+               
+               
+               builder.setView(mTextInputWidget);
+               
+               if (editType == MultiLineTextInput) {
+                       builder.setPositiveButton(acceptButton, new DialogInterface.OnClickListener() {
+                               public void onClick(DialogInterface dialog, int whichButton) 
+                               { pushResult(mTextInputWidget.getText().toString()); }
+                               });
+               }
+               
+               builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
+                       public void onCancel(DialogInterface dialog) {
+                               cancelDialog();
+                       }
+               });
+               
+               mTextInputWidget.setOnKeyListener(new OnKeyListener() {
+                       @Override
+                       public boolean onKey(View view, int KeyCode, KeyEvent event) {
+                               if ( KeyCode == KeyEvent.KEYCODE_ENTER){
+
+                                       pushResult(mTextInputWidget.getText().toString());
+                                       return true;
+                               }
+                               return false;
+                       }
+               });
+               
+               mTextInputDialog = builder.create();
+               mTextInputDialog.show();
+       }
+       
+       public void pushResult(String text) {
+               Intent resultData = new Intent();
+               resultData.putExtra("text", text);
+               setResult(Activity.RESULT_OK,resultData);
+               mTextInputDialog.dismiss();
+               finish();
+       }
+       
+       public void cancelDialog() {
+               setResult(Activity.RESULT_CANCELED);
+               mTextInputDialog.dismiss();
+               finish();
+       }
+}
diff --git a/build/android/src/org/minetest/minetest/MtNativeActivity.java b/build/android/src/org/minetest/minetest/MtNativeActivity.java
new file mode 100644 (file)
index 0000000..ba7d621
--- /dev/null
@@ -0,0 +1,93 @@
+package org.minetest.minetest;
+
+import android.app.NativeActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+public class MtNativeActivity extends NativeActivity {
+       @Override
+       public void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+               m_MessagReturnCode = -1;
+               m_MessageReturnValue = "";
+               
+       }
+
+       @Override
+       public void onDestroy() {
+               super.onDestroy();
+       }
+       
+       
+       public void copyAssets() {
+               Intent intent = new Intent(this, MinetestAssetCopy.class);
+               startActivity(intent);
+       }
+       
+       public void showDialog(String acceptButton, String hint, String current,
+                       int editType) {
+               
+               Intent intent = new Intent(this, MinetestTextEntry.class);
+               Bundle params = new Bundle();
+               params.putString("acceptButton", acceptButton);
+               params.putString("hint", hint);
+               params.putString("current", current);
+               params.putInt("editType", editType);
+               intent.putExtras(params);
+               startActivityForResult(intent, 101);
+               m_MessageReturnValue = "";
+               m_MessagReturnCode   = -1;
+       }
+       
+       public static native void putMessageBoxResult(String text);
+       
+       /* ugly code to workaround putMessageBoxResult not beeing found */
+       public int getDialogState() {
+               return m_MessagReturnCode;
+       }
+       
+       public String getDialogValue() {
+               m_MessagReturnCode = -1;
+               return m_MessageReturnValue;
+       }
+       
+       public float getDensity() {
+               return getResources().getDisplayMetrics().density;
+       }
+       
+       public int getDisplayWidth() {
+               return getResources().getDisplayMetrics().widthPixels;
+       }
+       
+       public int getDisplayHeight() {
+               return getResources().getDisplayMetrics().heightPixels;
+       }
+       
+       @Override
+       protected void onActivityResult(int requestCode, int resultCode,
+                       Intent data) {
+               if (requestCode == 101) {
+                       if (resultCode == RESULT_OK) {
+                               String text = data.getStringExtra("text"); 
+                               m_MessagReturnCode = 0;
+                               m_MessageReturnValue = text;
+                       }
+                       else {
+                               m_MessagReturnCode = 1;
+                       }
+               }
+       }
+       
+       static {
+               System.loadLibrary("openal");
+               System.loadLibrary("ogg");
+               System.loadLibrary("vorbis");
+               System.loadLibrary("ssl");
+               System.loadLibrary("crypto");
+       }
+       
+       private int m_MessagReturnCode;
+       private String m_MessageReturnValue;
+}
index e979bd5554b5bf0f84be684c42357ded1e7ee4fb..aa710ef3b2e34824cc05d92c378e7c5379dd28cf 100644 (file)
@@ -16,7 +16,7 @@
 --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 
-local function create_world_formspec(dialogdata)
+local function delete_world_formspec(dialogdata)
 
        local retval =
                "size[12,6,true]" ..
@@ -27,7 +27,7 @@ local function create_world_formspec(dialogdata)
        return retval
 end
 
-local function create_world_buttonhandler(this, fields)
+local function delete_world_buttonhandler(this, fields)
        if fields["world_delete_confirm"] then
 
                if this.data.delete_index > 0 and
@@ -53,9 +53,9 @@ function create_delete_world_dlg(name_to_del,index_to_del)
        assert(name_to_del ~= nil and type(name_to_del) == "string" and name_to_del ~= "")
        assert(index_to_del ~= nil and type(index_to_del) == "number")
 
-       local retval = dialog_create("sp_create_world",
-                                       create_world_formspec,
-                                       create_world_buttonhandler,
+       local retval = dialog_create("delete_world",
+                                       delete_world_formspec,
+                                       delete_world_buttonhandler,
                                        nil)
        retval.data.delete_name  = name_to_del
        retval.data.delete_index = index_to_del
diff --git a/builtin/mainmenu/init_android.lua b/builtin/mainmenu/init_android.lua
new file mode 100644 (file)
index 0000000..348923f
--- /dev/null
@@ -0,0 +1,102 @@
+--Minetest
+--Copyright (C) 2014 sapier
+--
+--This program is free software; you can redistribute it and/or modify
+--it under the terms of the GNU Lesser General Public License as published by
+--the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+--
+--You should have received a copy of the GNU Lesser General Public License along
+--with this program; if not, write to the Free Software Foundation, Inc.,
+--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+mt_color_grey  = "#AAAAAA"
+mt_color_blue  = "#0000DD"
+mt_color_green = "#00DD00"
+mt_color_dark_green = "#003300"
+
+--marker for android specific code
+ANDROID = true
+
+local menupath = core.get_mainmenu_path()
+local basepath = core.get_builtin_path()
+defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
+                                       DIR_DELIM .. "pack" .. DIR_DELIM
+
+dofile(basepath .. DIR_DELIM .. "common" .. DIR_DELIM .. "async_event.lua")
+dofile(basepath .. DIR_DELIM .. "common" .. DIR_DELIM .. "filterlist.lua")
+dofile(basepath .. DIR_DELIM .. "fstk" .. DIR_DELIM .. "buttonbar.lua")
+dofile(basepath .. DIR_DELIM .. "fstk" .. DIR_DELIM .. "dialog.lua")
+dofile(basepath .. DIR_DELIM .. "fstk" .. DIR_DELIM .. "tabview.lua")
+dofile(basepath .. DIR_DELIM .. "fstk" .. DIR_DELIM .. "ui.lua")
+dofile(menupath .. DIR_DELIM .. "common.lua")
+dofile(menupath .. DIR_DELIM .. "gamemgr.lua")
+dofile(menupath .. DIR_DELIM .. "modmgr.lua")
+dofile(menupath .. DIR_DELIM .. "store.lua")
+dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
+dofile(menupath .. DIR_DELIM .. "tab_simple_main.lua")
+dofile(menupath .. DIR_DELIM .. "tab_credits.lua")
+dofile(menupath .. DIR_DELIM .. "tab_mods.lua")
+dofile(menupath .. DIR_DELIM .. "tab_settings.lua")
+
+--------------------------------------------------------------------------------
+local function main_event_handler(tabview,event)
+       if event == "MenuQuit" then
+               core.close()
+       end
+       return true
+end
+
+local function init_globals()
+       --init gamedata
+       gamedata.worldindex = 0
+
+       local worldlist = core.get_worlds()
+
+       local found_singleplayerworld = false
+
+       for i=1,#worldlist,1 do
+               if worldlist[i].name == "singleplayerworld" then
+                       found_singleplayerworld = true
+                       gamedata.worldindex = i
+               end
+       end
+
+       if not found_singleplayerworld then
+               core.create_world("singleplayerworld", 1)
+
+               local worldlist = core.get_worlds()
+
+               for i=1,#worldlist,1 do
+                       if worldlist[i].name == "singleplayerworld" then
+                               gamedata.worldindex = i
+                       end
+               end
+       end
+
+       --create main tabview
+       local tv_main = tabview_create("maintab",{x=12,y=5.2},{x=-0,y=-0})
+       tv_main:add(tab_simple_main)
+       tv_main:add(tab_mods)
+       tv_main:add(tab_settings)
+       tv_main:add(tab_credits)
+       tv_main:set_global_event_handler(main_event_handler)
+       tv_main:set_fixed_size(false)
+       ui.set_default("maintab")
+       tv_main:show()
+
+       --create modstore ui
+       modstore.init({x=12,y=6},3,2)
+
+       ui.update()
+
+       core.sound_play("main_menu", true)
+end
+
+init_globals()
+
index b6ffa86ed66bc2d6b1a7db49fa6ed2962afc520a..dec28a96121f58890cabc68e2ba2daa22dadab4b 100644 (file)
@@ -31,7 +31,6 @@ end
 local function dlg_confirm_reset_btnhandler(this, fields, dialogdata)
 
        if fields["dlg_reset_singleplayer_confirm"] ~= nil then
-
                local worldlist = core.get_worlds()
                local found_singleplayerworld = false
 
@@ -63,19 +62,43 @@ local function dlg_confirm_reset_btnhandler(this, fields, dialogdata)
        this.parent:show()
        this:hide()
        this:delete()
+       return true
 end
 
 local function showconfirm_reset(tabview)
        local new_dlg = dialog_create("reset_spworld",
                dlg_confirm_reset_formspec,
                dlg_confirm_reset_btnhandler,
-               nil,
-               tabview)
+               nil)
+       new_dlg:set_parent(tabview)
        tabview:hide()
        new_dlg:show()
 end
 
+local function gui_scale_index()
+
+       local current_value = tonumber(core.setting_get("gui_scaling"))
 
+       if (current_value == nil) then
+               return 0
+       elseif current_value <= 0.5 then
+               return 1
+       elseif current_value <= 0.625 then
+               return 2
+       elseif current_value <= 0.75 then
+               return 3
+       elseif current_value <= 0.875 then
+               return 4
+       elseif current_value <= 1.0 then
+               return 5
+       elseif current_value <= 1.25 then
+               return 6
+       elseif current_value <= 1.5 then
+               return 7
+       else
+               return 8
+       end
+end
 
 local function formspec(tabview, name, tabdata)
        local tab_string =
@@ -93,8 +116,6 @@ local function formspec(tabview, name, tabdata)
                                .. dump(core.setting_getbool("preload_item_visuals"))   .. "]"..
                "checkbox[1,2.5;cb_particles;".. fgettext("Enable Particles") .. ";"
                                .. dump(core.setting_getbool("enable_particles"))       .. "]"..
-               "checkbox[1,3.0;cb_finite_liquid;".. fgettext("Finite Liquid") .. ";"
-                               .. dump(core.setting_getbool("liquid_finite")) .. "]"..
                "box[4.25,0;3.25,2.5;#999999]" ..
                "checkbox[4.5,0;cb_mipmapping;".. fgettext("Mip-Mapping") .. ";"
                                .. dump(core.setting_getbool("mip_map")) .. "]"..
@@ -106,16 +127,25 @@ local function formspec(tabview, name, tabdata)
                                .. dump(core.setting_getbool("trilinear_filter")) .. "]"..
                "box[7.75,0;4,4;#999999]" ..
                "checkbox[8,0;cb_shaders;".. fgettext("Shaders") .. ";"
-                               .. dump(core.setting_getbool("enable_shaders")) .. "]"..
-               "button[1,4.5;2.25,0.5;btn_change_keys;".. fgettext("Change keys") .. "]"
+                               .. dump(core.setting_getbool("enable_shaders")) .. "]"
+       if not ANDROID then
+               tab_string = tab_string ..
+               "button[8,4.75;3.75,0.5;btn_change_keys;".. fgettext("Change keys") .. "]"
+       else
+               tab_string = tab_string ..
+               "button[8,4.75;3.75,0.5;btn_reset_singleplayer;".. fgettext("Reset singleplayer world") .. "]"
+       end
+       tab_string = tab_string ..
+               "box[0.75,4.25;3.25,1.25;#999999]" ..
+               "label[1,4.25;" .. fgettext("GUI scale factor") .. "]" ..
+               "dropdown[1,4.75;3.0;dd_gui_scaling;0.5,0.625,0.75,0.875,1.0,1.25,1.5,2.0;"
+                       .. gui_scale_index() .. "]"
 
-       local android = false
-       if android then
+       if ANDROID then
                tab_string = tab_string ..
-               "box[4.25,2.75;3.25,2.5;#999999]" ..
+               "box[4.25,2.75;3.25,2.15;#999999]" ..
                "checkbox[4.5,2.75;cb_touchscreen_target;".. fgettext("Touch free target") .. ";"
-                               .. dump(core.setting_getbool("touchtarget")) .. "]" ..
-               "button[8,4.5;3.75,0.5;btn_reset_singleplayer;".. fgettext("Reset singleplayer world") .. "]"
+                               .. dump(core.setting_getbool("touchtarget")) .. "]"
        end
 
        if core.setting_get("touchscreen_threshold") ~= nil then
@@ -202,10 +232,6 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
                core.setting_set("enable_particles", fields["cb_particles"])
                return true
        end
-       if fields["cb_finite_liquid"] then
-               core.setting_set("liquid_finite", fields["cb_finite_liquid"])
-               return true
-       end
        if fields["cb_bumpmapping"] then
                core.setting_set("enable_bumpmapping", fields["cb_bumpmapping"])
        end
@@ -235,14 +261,23 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
                core.setting_set("touchtarget", fields["cb_touchscreen_target"])
                return true
        end
-       if fields["dd_touchthreshold"] then
-               core.setting_set("touchscreen_threshold",fields["dd_touchthreshold"])
-               return true
-       end
        if fields["btn_reset_singleplayer"] then
+               print("sp reset")
                showconfirm_reset(this)
                return true
        end
+       --Note dropdowns have to be handled LAST!
+       local ddhandled = false
+       if fields["dd_touchthreshold"] then
+               core.setting_set("touchscreen_threshold",fields["dd_touchthreshold"])
+               ddhandled = true
+       end
+       if fields["dd_gui_scaling"] then
+               core.setting_set("gui_scaling",fields["dd_gui_scaling"])
+               ddhandled = true
+       end
+       
+       return ddhandled
 end
 
 tab_settings = {
index 46f4b6190f19c26afb30d49318659674ba246e0b..09cd685ce94a474d60b79753c80b3549b9a3a557 100644 (file)
@@ -22,7 +22,6 @@ local function get_formspec(tabview, name, tabdata)
        local render_details = dump(core.setting_getbool("public_serverlist"))
 
        retval = retval ..
-               "label[0,3.0;".. fgettext("Address/Port") .. "]"..
                "label[8,0.5;".. fgettext("Name/Password") .. "]" ..
                "field[0.25,3.25;5.5,0.5;te_address;;" ..core.setting_get("address") .."]" ..
                "field[5.75,3.25;2.25,0.5;te_port;;" ..core.setting_get("remote_port") .."]" ..
@@ -66,7 +65,8 @@ local function get_formspec(tabview, name, tabdata)
                dump(core.setting_getbool("free_move")) .. "]"
        -- buttons
        retval = retval ..
-               "button[3.0,4.5;6,1.5;btn_start_singleplayer;" .. fgettext("Start Singleplayer") .. "]"
+               "button[2.0,4.5;6,1.5;btn_start_singleplayer;" .. fgettext("Start Singleplayer") .. "]" ..
+               "button[8.25,4.5;2.5,1.5;btn_config_sp_world;" .. fgettext("Config MODs") .. "]"
 
        return retval
 end
@@ -74,19 +74,21 @@ end
 --------------------------------------------------------------------------------
 
 local function main_button_handler(tabview, fields, name, tabdata)
+
        if fields["btn_start_singleplayer"] then
                gamedata.selected_world = gamedata.worldindex
                gamedata.singleplayer   = true
                core.start()
+               return true
        end
 
        if fields["favourites"] ~= nil then
                local event = core.explode_textlist_event(fields["favourites"])
 
                if event.type == "CHG" then
-                       if event.index <= #maintab_favorites then
-                               local address = maintab_favorites[event.index].address
-                               local port = maintab_favorites[event.index].port
+                       if event.index <= #menudata.favorites then
+                               local address = menudata.favorites[event.index].address
+                               local port = menudata.favorites[event.index].port
 
                                if address ~= nil and
                                        port ~= nil then
@@ -97,7 +99,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
                                tabdata.fav_selected = event.index
                        end
                end
-               return
+               return true
        end
 
        if fields["cb_public_serverlist"] ~= nil then
@@ -106,21 +108,24 @@ local function main_button_handler(tabview, fields, name, tabdata)
                if core.setting_getbool("public_serverlist") then
                        asyncOnlineFavourites()
                else
-                       maintab_favorites = core.get_favorites("local")
+                       menudata.favorites = core.get_favorites("local")
                end
-               return
+               return true
        end
 
        if fields["cb_creative"] then
                core.setting_set("creative_mode", fields["cb_creative"])
+               return true
        end
 
        if fields["cb_damage"] then
                core.setting_set("enable_damage", fields["cb_damage"])
+               return true
        end
 
        if fields["cb_fly_mode"] then
                core.setting_set("free_move", fields["cb_fly_mode"])
+               return true
        end
 
        if fields["btn_mp_connect"] ~= nil or
@@ -150,7 +155,18 @@ local function main_button_handler(tabview, fields, name, tabdata)
                core.setting_set("remote_port",fields["te_port"])
 
                core.start()
-               return
+               return true
+       end
+
+       if fields["btn_config_sp_world"] ~= nil then
+               local configdialog = create_configure_world_dlg(1)
+
+               if (configdialog ~= nil) then
+                       configdialog:set_parent(tabview)
+                       tabview:hide()
+                       configdialog:show()
+               end
+               return true
        end
 end
 
diff --git a/doc/README.android b/doc/README.android
new file mode 100644 (file)
index 0000000..d515b48
--- /dev/null
@@ -0,0 +1,130 @@
+Minetest Android port
+=====================
+Date: 2014 06 28
+
+Controls
+--------
+The Android port doesn't support everything you can do on PC due to the
+limited capabilities of common devices. What can be done is described
+below:
+
+While you're playing the game normally (that is, no menu or inventory is
+shown), the following controls are available:
+* Look around: touch screen and slide finger
+* double tap: place a node or use selected item
+* long tap: dig node
+* touch shown buttons: press button
+* Buttons:
+** left upper corner: chat
+** right lower corner: jump
+** right lower corner: crouch
+** left lower corner: walk/step...
+   left up right
+       down
+** left lower corner: display inventory
+
+When a menu or inventory is displayed:
+* double tap outside menu area: close menu
+* tap on an item stack: select that stack
+* tap on an empty slot: if you selected a stack already, that stack is placed here
+* drag and drop: touch stack and hold finger down, move the stack to another
+  slot, tap another finger while keeping first finger on screen
+  --> places a single item from dragged stack into current (first touched) slot
+
+Special settings
+----------------
+There are some settings esspecially usefull for Android users. Minetest's config
+file can usually be found at /mnt/sdcard/Minetest.
+
+* gui_scaling: this is a user-specified scaling factor for the GUI- In case
+               main menu is to big or small on your device, try changing this
+               value.
+* inventory_image_hack: if your inventory items are messed up, try setting
+               this to true
+
+Known issues
+------------
+Not all issues are fixed by now:
+
+* Unable to exit from volume menu -- don't use the volume menu, use Android's
+  volume controls instead.
+* 512 MB RAM seems to be inadequate -- this depends on the server you join.
+  Try to play on more lightweight servers.
+
+Versioning
+----------
+Android version numbers are 4 digits instead of Minetest's 3 digits.  The last
+number of Android's version represents the Android internal version code. This
+version code is strictly incremental. It's incremented for each official
+Minetest Android build.
+
+E.g. pre-release Minetest Android builds have been 0.4.9.3, while the first
+official version most likely will be 0.4.10.4
+
+Requirements
+------------
+
+In order to build, your PC has to be set up to build Minetest in the usual
+manner (see the regular Minetest documentation for how to get this done).
+In addition to what is required for Minetest in general, you will need the
+following software packages. The version number in parenthesis denotes the
+version that was tested at the time this README was drafted; newer/older
+versions may or may not work.
+
+* android SDK (x86_64 20131030)
+* android NDK (r9d)
+* wget (1.13.4)
+
+Additionally, you'll need to have an Internet connection available on the
+build system, as the Android build will download some source packages.
+
+Build
+-----
+
+Debug build:
+* Enter "build/android" subdirectory
+* Execute "make"
+* Answer the questions about where SDK and NDK are located on your filesystem
+* Wait for build to finish
+
+After the build is finished, the resulting apk can be fond in
+build/android/bin/. It will be called Minetest-debug.apk
+
+Release build:
+
+* In order to make a release build you'll have to have a keystore setup to sign
+  the resulting apk package. How this is done is not part of this README. There
+  are different tutorials on the web explaining how to do it
+  - choose one yourself.
+
+* Once your keystore is setup, enter build/android subdirectory and create a new
+  file "ant.properties" there. Add following lines to that file:
+  
+  > key.store=<path to your keystore>
+  > key.alias=Minetest
+
+* Execute "make release"
+* Enter your keystore as well as your Mintest key password once asked. Be
+  carefull it's shown on console in clear text!
+* The result can be found at "bin/Minetest-release.apk"
+
+Other things that may be nice to know
+------------
+* The environment for Android development tools is saved within Android build
+  build folder. If you want direct access to it do:
+  
+  > make envpaths
+  > . and_env
+  
+  After you've done this you'll have your path and path variables set correct
+  to use adb and all other Android development tools
+
+* You can build a single dependency by calling make and the dependency's name,
+  e.g.:
+
+  > make irrlicht
+
+* You can completely cleanup a dependency by calling make and the "clean" target,
+  e.g.:
+
+  > make clean_irrlicht
index 8b89dd63cae8b52943445e7a5b04be076a0d4ed4..601561f7d4b57a04ce18058ee3bc84b3012ef4dc 100644 (file)
@@ -47,7 +47,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "serialization.h"
 #include "util/serialize.h"
 #include "config.h"
-#include "cmake_config_githash.h"
 #include "util/directiontables.h"
 #include "util/pointedthing.h"
 #include "version.h"
index 8a9d7d63df1b6d8586d0e3fa27bb822ccae4e7df..54c13d4405b9eb988ee05eb15c56c24b0ca7461d 100644 (file)
@@ -8,7 +8,10 @@
 
 #define PROJECT_NAME "Minetest"
 #define RUN_IN_PLACE 0
+#define STATIC_SHAREDIR ""
+
 #define USE_GETTEXT 0
+
 #ifndef USE_SOUND
        #define USE_SOUND 0
 #endif
@@ -17,8 +20,9 @@
        #define USE_CURL 0
 #endif
 
-#define USE_FREETYPE 0
-#define STATIC_SHAREDIR ""
+#ifndef USE_FREETYPE
+       #define USE_FREETYPE 0
+#endif
 
 #ifndef USE_LEVELDB
        #define USE_LEVELDB 0
        #define VERSION_EXTRA_STRING CMAKE_VERSION_EXTRA_STRING
 #endif
 
+#ifdef __ANDROID__
+       #include "android_version.h"
+#else
+       #include "cmake_config_githash.h"
+#endif
+
 #endif
 
index 278902a081b7fa2836063ace205771cbbcd4c871..8c02f1d6b00ad54a403b13b72afd2c1c575e8c7f 100644 (file)
@@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 
+#include "porting.h"
 #include "debug.h"
 #include "exceptions.h"
 #include "threads.h"
@@ -27,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <map>
 #include "jthread/jmutex.h"
 #include "jthread/jmutexautolock.h"
-
+#include "config.h"
 /*
        Debug output
 */
@@ -95,6 +96,9 @@ public:
        }
        std::streamsize xsputn(const char *s, std::streamsize n)
        {
+#ifdef __ANDROID__
+               __android_log_print(ANDROID_LOG_VERBOSE, PROJECT_NAME, "%s", s);
+#endif
                for(int i=0; i<DEBUGSTREAM_COUNT; i++)
                {
                        if(g_debugstreams[i] == stderr && m_disable_stderr)
index cf9101e34c8ecfa02d14d2e859be078e9482e557..f356aaf960aa62405fc649d87b2e7f1996ff97c9 100644 (file)
@@ -64,7 +64,7 @@ void set_default_settings(Settings *settings)
        settings->setDefault("doubletap_jump", "false");
        settings->setDefault("always_fly_fast", "true");
        settings->setDefault("directional_colored_fog", "true");
-       settings->setDefault("tooltip_show_delay", "400");      
+       settings->setDefault("tooltip_show_delay", "400");
 
        // Some (temporary) keys for debugging
        settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P");
@@ -154,6 +154,7 @@ void set_default_settings(Settings *settings)
        settings->setDefault("curl_timeout", "5000");
        settings->setDefault("curl_parallel_limit", "8");
        settings->setDefault("curl_file_download_timeout", "300000");
+       settings->setDefault("curl_verify_cert", "true");
 
        settings->setDefault("enable_remote_media_server", "true");
 
@@ -278,6 +279,39 @@ void set_default_settings(Settings *settings)
        settings->setDefault("high_precision_fpu", "true");
 
        settings->setDefault("language", "");
+
+#ifdef __ANDROID__
+       settings->setDefault("screenW", "0");
+       settings->setDefault("screenH", "0");
+       settings->setDefault("enable_shaders", "false");
+       settings->setDefault("fullscreen", "true");
+       settings->setDefault("enable_particles", "false");
+       settings->setDefault("video_driver", "ogles1");
+       settings->setDefault("touchtarget", "true");
+       settings->setDefault("main_menu_script","/sdcard/Minetest/builtin/mainmenu/init_android.lua");
+       settings->setDefault("TMPFolder","/sdcard/Minetest/tmp/");
+       settings->setDefault("touchscreen_threshold","20");
+       settings->setDefault("smooth_lighting", "false");
+       settings->setDefault("max_simultaneous_block_sends_per_client", "3");
+       settings->setDefault("emergequeue_limit_diskonly", "8");
+       settings->setDefault("emergequeue_limit_generate", "8");
+       settings->setDefault("preload_item_visuals", "false");
+
+       settings->setDefault("viewing_range_nodes_max", "50");
+       settings->setDefault("viewing_range_nodes_min", "20");
+       settings->setDefault("inventory_image_hack", "false");
+
+       //check for device with small screen
+       float x_inches = ((double) porting::getDisplaySize().X /
+                       (160 * porting::getDisplayDensity()));
+       if (x_inches  < 3.5) {
+               settings->setDefault("gui_scaling", "0.6");
+       }
+       else if (x_inches < 4.5) {
+               settings->setDefault("gui_scaling", "0.7");
+       }
+       settings->setDefault("curl_verify_cert","false");
+#endif
 }
 
 void late_init_default_settings(Settings* settings)
index a69cf4403fe3240ada05b203284001137d13887b..9672affeae65841ae3b6e20b3a9c50d653a03316 100644 (file)
@@ -427,6 +427,13 @@ void draw_scene(video::IVideoDriver* driver, scene::ISceneManager* smgr,
                bool draw_crosshair = ((player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) &&
                                (camera.getCameraMode() != CAMERA_MODE_THIRD_FRONT));
 
+#ifdef HAVE_TOUCHSCREENGUI
+               try {
+                       draw_crosshair = !g_settings->getBool("touchtarget");
+               }
+               catch(SettingNotFoundException) {}
+#endif
+
                std::string draw_mode = g_settings->get("3d_mode");
 
                smgr->drawAll();
index eda36c83307a39e93392e52048933b5185091f13..7c72a4b2757f9f6d7601af372ecc82a77fff6b99 100644 (file)
@@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <errno.h>
 #include <fstream>
 #include "log.h"
+#include "config.h"
 
 namespace fs
 {
@@ -34,8 +35,8 @@ namespace fs
 #define _WIN32_WINNT 0x0501
 #include <windows.h>
 #include <malloc.h>
-#include <tchar.h> 
-#include <wchar.h> 
+#include <tchar.h>
+#include <wchar.h>
 
 #define BUFSIZE MAX_PATH
 
@@ -73,12 +74,12 @@ std::vector<DirListNode> GetDirListing(std::string pathstring)
        // Find the first file in the directory.
        hFind = FindFirstFile(DirSpec, &FindFileData);
 
-       if (hFind == INVALID_HANDLE_VALUE) 
+       if (hFind == INVALID_HANDLE_VALUE)
        {
                retval = (-1);
                goto Cleanup;
-       } 
-       else 
+       }
+       else
        {
                // NOTE:
                // Be very sure to not include '..' in the results, it will
@@ -91,7 +92,7 @@ std::vector<DirListNode> GetDirListing(std::string pathstring)
                        listing.push_back(node);
 
                // List all the other files in the directory.
-               while (FindNextFile(hFind, &FindFileData) != 0) 
+               while (FindNextFile(hFind, &FindFileData) != 0)
                {
                        DirListNode node;
                        node.name = FindFileData.cFileName;
@@ -102,7 +103,7 @@ std::vector<DirListNode> GetDirListing(std::string pathstring)
 
                dwError = GetLastError();
                FindClose(hFind);
-               if (dwError != ERROR_NO_MORE_FILES) 
+               if (dwError != ERROR_NO_MORE_FILES)
                {
                        errorstream<<"GetDirListing: FindNextFile error. Error is "
                                        <<dwError<<std::endl;
@@ -401,7 +402,11 @@ std::string TempPath()
                compatible with lua's os.tmpname which under the default
                configuration hardcodes mkstemp("/tmp/lua_XXXXXX").
        */
-       return std::string(DIR_DELIM) + "tmp";
+#ifdef __ANDROID__
+       return DIR_DELIM "sdcard" DIR_DELIM PROJECT_NAME DIR_DELIM "tmp";
+#else
+       return DIR_DELIM "tmp";
+#endif
 }
 
 #endif
index e74e4697b88c8d2d5f52d37038c220704058f054..4f034676cd510273e67bc47b564fa3f470449233 100644 (file)
@@ -70,6 +70,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "drawscene.h"
 #include "content_cao.h"
 
+#ifdef HAVE_TOUCHSCREENGUI
+#include "touchscreengui.h"
+#endif
+
 /*
        Text input system
 */
@@ -942,14 +946,20 @@ static inline void create_formspec_menu(GUIFormSpecMenu** cur_formspec,
        }
 }
 
+#ifdef __ANDROID__
+#define SIZE_TAG "size[11,5.5]"
+#else
+#define SIZE_TAG "size[11,5.5,true]"
+#endif
+
 static void show_chat_menu(GUIFormSpecMenu** cur_formspec,
                InventoryManager *invmgr, IGameDef *gamedef,
                IWritableTextureSource* tsrc, IrrlichtDevice * device,
                Client* client, std::string text)
 {
        std::string formspec =
-                       FORMSPEC_VERSION_STRING
-               "size[11,5.5,true]"
+               FORMSPEC_VERSION_STRING
+               SIZE_TAG
                "field[3,2.35;6,0.5;f_text;;" + text + "]"
                "button_exit[4,3;3,0.5;btn_send;" + wide_to_narrow(wstrgettext("Proceed")) + "]"
                ;
@@ -969,7 +979,7 @@ static void show_deathscreen(GUIFormSpecMenu** cur_formspec,
 {
        std::string formspec =
                std::string(FORMSPEC_VERSION_STRING) +
-               "size[11,5.5,true]"
+               SIZE_TAG
                "bgcolor[#320000b4;true]"
                "label[4.85,1.35;You died.]"
                "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]"
@@ -990,6 +1000,21 @@ static void show_pause_menu(GUIFormSpecMenu** cur_formspec,
                IWritableTextureSource* tsrc, IrrlichtDevice * device,
                bool singleplayermode)
 {
+#ifdef __ANDROID__
+       std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
+                       "No menu visible:\n"
+                       "- single tap: button activate\n"
+                       "- double tap: place/use\n"
+                       "- slide finger: look around\n"
+                       "Menu/Inventory visible:\n"
+                       "- double tap (outside):\n"
+                       " -->close\n"
+                       "- touch stack, touch slot:\n"
+                       " --> move stack\n"
+                       "- touch&drag, tap 2nd finger\n"
+                       " --> place single item to slot\n"
+                       ));
+#else
        std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
                        "- WASD: move\n"
                        "- Space: jump/climb\n"
@@ -1002,11 +1027,11 @@ static void show_pause_menu(GUIFormSpecMenu** cur_formspec,
                        "- Mouse wheel: select item\n"
                        "- T: chat\n"
                        ));
-
+#endif
        float ypos = singleplayermode ? 1.0 : 0.5;
        std::ostringstream os;
 
-       os << FORMSPEC_VERSION_STRING << "size[11,5.5,true]"
+       os << FORMSPEC_VERSION_STRING  << SIZE_TAG
                        << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
                                        << wide_to_narrow(wstrgettext("Continue"))     << "]";
 
@@ -1021,7 +1046,7 @@ static void show_pause_menu(GUIFormSpecMenu** cur_formspec,
                                        << wide_to_narrow(wstrgettext("Exit to Menu")) << "]";
        os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
                                        << wide_to_narrow(wstrgettext("Exit to OS"))   << "]"
-                       << "textarea[7.5,0.25;3.75,6;;" << control_text << ";]"
+                       << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
                        << "textarea[0.4,0.25;3.5,6;;" << "Minetest\n"
                        << minetest_build_info << "\n"
                        << "path_user = " << wrap_rows(porting::path_user, 20)
@@ -1253,18 +1278,18 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
                                server->step(dtime);
 
                        // End condition
-                       if(client.getState() == LC_Init){
+                       if(client.getState() == LC_Init) {
                                could_connect = true;
                                break;
                        }
                        // Break conditions
-                       if(client.accessDenied()){
+                       if(client.accessDenied()) {
                                error_message = L"Access denied. Reason: "
                                                +client.accessDeniedReason();
                                errorstream<<wide_to_narrow(error_message)<<std::endl;
                                break;
                        }
-                       if(input->wasKeyDown(EscapeKey)){
+                       if(input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
                                connect_aborted = true;
                                infostream<<"Connect aborted [Escape]"<<std::endl;
                                break;
@@ -1310,8 +1335,8 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
        /*
                Handle failure to connect
        */
-       if(!could_connect){
-               if(error_message == L"" && !connect_aborted){
+       if(!could_connect) {
+               if(error_message == L"" && !connect_aborted) {
                        error_message = L"Connection failed";
                        errorstream<<wide_to_narrow(error_message)<<std::endl;
                }
@@ -1330,8 +1355,7 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
                float fps_max = g_settings->getFloat("fps_max");
                bool cloud_menu_background = g_settings->getBool("menu_clouds");
                u32 lasttime = device->getTimer()->getTime();
-               while(device->run())
-               {
+               while (device->run()) {
                        f32 dtime = 0.033; // in seconds
                        if (cloud_menu_background) {
                                u32 time = device->getTimer()->getTime();
@@ -1343,29 +1367,29 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
                        }
                        // Update client and server
                        client.step(dtime);
-                       if(server != NULL)
+                       if (server != NULL)
                                server->step(dtime);
 
                        // End condition
-                       if(client.mediaReceived() &&
+                       if (client.mediaReceived() &&
                                        client.itemdefReceived() &&
-                                       client.nodedefReceived()){
+                                       client.nodedefReceived()) {
                                got_content = true;
                                break;
                        }
                        // Break conditions
-                       if(client.accessDenied()){
+                       if (client.accessDenied()) {
                                error_message = L"Access denied. Reason: "
                                                +client.accessDeniedReason();
                                errorstream<<wide_to_narrow(error_message)<<std::endl;
                                break;
                        }
-                       if(client.getState() < LC_Init){
+                       if (client.getState() < LC_Init) {
                                error_message = L"Client disconnected";
                                errorstream<<wide_to_narrow(error_message)<<std::endl;
                                break;
                        }
-                       if(input->wasKeyDown(EscapeKey)){
+                       if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
                                content_aborted = true;
                                infostream<<"Connect aborted [Escape]"<<std::endl;
                                break;
@@ -1548,6 +1572,11 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
        guitext_profiler->setVisible(false);
        guitext_profiler->setWordWrap(true);
 
+#ifdef HAVE_TOUCHSCREENGUI
+       if (g_touchscreengui)
+               g_touchscreengui->init(tsrc,porting::getDisplayDensity());
+#endif
+
        /*
                Some statistics are collected in these
        */
@@ -1641,7 +1670,8 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
 
        for(;;)
        {
-               if(device->run() == false || kill == true)
+               if(device->run() == false || kill == true ||
+                               g_gamecallback->shutdown_requested)
                        break;
 
                v2u32 screensize = driver->getScreenSize();
@@ -1858,6 +1888,15 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
 
                // Input handler step() (used by the random input generator)
                input->step(dtime);
+#ifdef HAVE_TOUCHSCREENGUI
+               if (g_touchscreengui) {
+                       g_touchscreengui->step(dtime);
+               }
+#endif
+#ifdef __ANDROID__
+               if (current_formspec != 0)
+                       current_formspec->getAndroidUIInput();
+#endif
 
                // Increase timer for doubleclick of "jump"
                if(g_settings->getBool("doubletap_jump") && jump_timer <= 0.2)
@@ -1890,7 +1929,7 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
                        inventoryloc.setCurrentPlayer();
                        current_formspec->setFormSpec(fs_src->getForm(), inventoryloc);
                }
-               else if(input->wasKeyDown(EscapeKey))
+               else if(input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey))
                {
                        show_pause_menu(&current_formspec, &client, gamedef, tsrc, device,
                                        simple_singleplayer_mode);
@@ -2214,21 +2253,29 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
                float turn_amount = 0;
                if((device->isWindowActive() && noMenuActive()) || random_input)
                {
+#ifndef __ANDROID__
                        if(!random_input)
                        {
                                // Mac OSX gets upset if this is set every frame
                                if(device->getCursorControl()->isVisible())
                                        device->getCursorControl()->setVisible(false);
                        }
+#endif
 
                        if(first_loop_after_window_activation){
                                //infostream<<"window active, first loop"<<std::endl;
                                first_loop_after_window_activation = false;
-                       }
-                       else{
-                               s32 dx = input->getMousePos().X - (driver->getScreenSize().Width/2);
-                               s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height/2);
-                               if(invert_mouse || camera.getCameraMode() == CAMERA_MODE_THIRD_FRONT) {
+                       } else {
+#ifdef HAVE_TOUCHSCREENGUI
+                               if (g_touchscreengui) {
+                                       camera_yaw   = g_touchscreengui->getYaw();
+                                       camera_pitch = g_touchscreengui->getPitch();
+                               } else {
+#endif
+                                       s32 dx = input->getMousePos().X - (driver->getScreenSize().Width/2);
+                                       s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height/2);
+                               if ((invert_mouse)
+                                               || (camera.getCameraMode() == CAMERA_MODE_THIRD_FRONT)) {
                                        dy = -dy;
                                }
                                //infostream<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
@@ -2247,18 +2294,23 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
                                d = rangelim(d, 0.01, 100.0);
                                camera_yaw -= dx*d;
                                camera_pitch += dy*d;
+                               turn_amount = v2f(dx, dy).getLength() * d;
+
+#ifdef HAVE_TOUCHSCREENGUI
+                               }
+#endif
                                if(camera_pitch < -89.5) camera_pitch = -89.5;
                                if(camera_pitch > 89.5) camera_pitch = 89.5;
-
-                               turn_amount = v2f(dx, dy).getLength() * d;
                        }
                        input->setMousePos((driver->getScreenSize().Width/2),
                                        (driver->getScreenSize().Height/2));
                }
                else{
+#ifndef ANDROID
                        // Mac OSX gets upset if this is set every frame
                        if(device->getCursorControl()->isVisible() == false)
                                device->getCursorControl()->setVisible(true);
+#endif
 
                        //infostream<<"window inactive"<<std::endl;
                        first_loop_after_window_activation = true;
@@ -2668,10 +2720,19 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
                core::line3d<f32> shootline(camera_position,
                                camera_position + camera_direction * BS * (d+1));
 
+
                // prevent player pointing anything in front-view
                if (camera.getCameraMode() == CAMERA_MODE_THIRD_FRONT)
                        shootline = core::line3d<f32>(0,0,0,0,0,0);
 
+#ifdef HAVE_TOUCHSCREENGUI
+               if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) {
+                       shootline = g_touchscreengui->getShootline();
+                       shootline.start += intToFloat(camera_offset,BS);
+                       shootline.end += intToFloat(camera_offset,BS);
+               }
+#endif
+
                ClientActiveObject *selected_object = NULL;
 
                PointedThing pointed = getPointedThing(
@@ -3156,8 +3217,9 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
                }
                else if(show_hud || show_chat)
                {
+                       u16 fps = (1.0/dtime_avg1);
                        std::ostringstream os(std::ios_base::binary);
-                       os<<"Minetest "<<minetest_version_hash;
+                       os<<"Minetest "<<minetest_version_hash <<" FPS = "<<fps;
                        guitext->setText(narrow_to_wide(os.str()).c_str());
                        guitext->setVisible(true);
                }
index 5307332164bef4a4e62c9781d22b6c121abc7482..0a1d722069ebd6c573bf5381b9839e14a0693057 100644 (file)
@@ -32,6 +32,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "clouds.h"
 #include "httpfetch.h"
 #include "util/numeric.h"
+#ifdef __ANDROID__
+#include "tile.h"
+#include <GLES/gl.h>
+#endif
 
 #include <IGUIStaticText.h>
 #include <ICameraSceneNode.h>
@@ -83,6 +87,16 @@ video::ITexture* MenuTextureSource::getTexture(const std::string &name, u32 *id)
        if(name.empty())
                return NULL;
        m_to_delete.insert(name);
+
+#ifdef __ANDROID__
+       video::IImage *image = m_driver->createImageFromFile(name.c_str());
+       if (image) {
+               image = Align2Npot2(image, m_driver);
+               video::ITexture* retval = m_driver->addTexture(name.c_str(), image);
+               image->drop();
+               return retval;
+       }
+#endif
        return m_driver->getTexture(name.c_str());
 }
 
@@ -266,6 +280,10 @@ void GUIEngine::run()
                        sleep_ms(25);
 
                m_script->step();
+
+#ifdef __ANDROID__
+               m_menu->getAndroidUIInput();
+#endif
        }
 }
 
index fd12c4d4de382a895cfd8f0f2ab53f089e1bcafc..54414d7e77533da1aa1532ba20d068aedc2a4a72 100644 (file)
@@ -88,6 +88,9 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
        m_ext_ptr(ext_ptr),
        m_font(dev->getGUIEnvironment()->getSkin()->getFont()),
        m_formspec_version(0)
+#ifdef __ANDROID__
+       ,m_JavaDialogFieldName(L"")
+#endif
 {
        current_keys_pending.key_down = false;
        current_keys_pending.key_up = false;
@@ -1878,6 +1881,52 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                setInitialFocus();
 }
 
+#ifdef __ANDROID__
+bool GUIFormSpecMenu::getAndroidUIInput()
+{
+       /* no dialog shown */
+       if (m_JavaDialogFieldName == L"") {
+               return false;
+       }
+
+       /* still waiting */
+       if (porting::getInputDialogState() == -1) {
+               return true;
+       }
+
+       std::wstring fieldname = m_JavaDialogFieldName;
+       m_JavaDialogFieldName = L"";
+
+       /* no value abort dialog processing */
+       if (porting::getInputDialogState() != 0) {
+               return false;
+       }
+
+       for(std::vector<FieldSpec>::iterator iter =  m_fields.begin();
+                       iter != m_fields.end(); iter++) {
+
+               if (iter->fname != fieldname) {
+                       continue;
+               }
+               IGUIElement* tochange = getElementFromId(iter->fid);
+
+               if (tochange == 0) {
+                       return false;
+               }
+
+               if (tochange->getType() != irr::gui::EGUIET_EDIT_BOX) {
+                       return false;
+               }
+
+               std::string text = porting::getInputDialogValue();
+
+               ((gui::IGUIEditBox*) tochange)->
+                       setText(narrow_to_wide(text).c_str());
+       }
+       return false;
+}
+#endif
+
 GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
 {
        core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
@@ -1886,8 +1935,7 @@ GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
        {
                const ListDrawSpec &s = m_inventorylists[i];
 
-               for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
-               {
+               for(s32 i=0; i<s.geom.X*s.geom.Y; i++) {
                        s32 item_i = i + s.start_item_i;
                        s32 x = (i%s.geom.X) * spacing.X;
                        s32 y = (i/s.geom.X) * spacing.Y;
@@ -2051,8 +2099,6 @@ void GUIFormSpecMenu::drawMenu()
                }
        }
 
-       m_pointer = m_device->getCursorControl()->getPosition();
-
        updateSelectedItem();
 
        gui::IGUISkin* skin = Environment->getSkin();
@@ -2195,6 +2241,11 @@ void GUIFormSpecMenu::drawMenu()
        */
        gui::IGUIElement::draw();
 
+/* TODO find way to show tooltips on touchscreen */
+#ifndef HAVE_TOUCHSCREENGUI
+       m_pointer = m_device->getCursorControl()->getPosition();
+#endif
+
        /*
                Draw fields/buttons tooltips
        */
@@ -2491,7 +2542,8 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
        // Fix Esc/Return key being eaten by checkboxen and tables
        if(event.EventType==EET_KEY_INPUT_EVENT) {
                KeyPress kp(event.KeyInput);
-               if (kp == EscapeKey || kp == getKeySetting("keymap_inventory")
+               if (kp == EscapeKey || kp == CancelKey
+                               || kp == getKeySetting("keymap_inventory")
                                || event.KeyInput.Key==KEY_RETURN) {
                        gui::IGUIElement *focused = Environment->getFocus();
                        if (focused && isMyChild(focused) &&
@@ -2533,6 +2585,156 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
                }
        }
 
+       #ifdef __ANDROID__
+       // display software keyboard when clicking edit boxes
+       if (event.EventType == EET_MOUSE_INPUT_EVENT
+                       && event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
+               gui::IGUIElement *hovered =
+                       Environment->getRootGUIElement()->getElementFromPoint(
+                               core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
+               if ((hovered) && (hovered->getType() == irr::gui::EGUIET_EDIT_BOX)) {
+                       bool retval = hovered->OnEvent(event);
+                       if (retval) {
+                               Environment->setFocus(hovered);
+                       }
+                       m_JavaDialogFieldName = getNameByID(hovered->getID());
+                       std::string message   = gettext("Enter ");
+                       std::string label     = wide_to_narrow(getLabelByID(hovered->getID()));
+                       if (label == "") {
+                               label = "text";
+                       }
+                       message += gettext(label) + ":";
+
+                       /* single line text input */
+                       int type = 2;
+
+                       /* multi line text input */
+                       if (((gui::IGUIEditBox*) hovered)->isMultiLineEnabled()) {
+                               type = 1;
+                       }
+
+                       /* passwords are always single line */
+                       if (((gui::IGUIEditBox*) hovered)->isPasswordBox()) {
+                               type = 3;
+                       }
+
+                       porting::showInputDialog(gettext("ok"), "",
+                                       wide_to_narrow(((gui::IGUIEditBox*) hovered)->getText()),
+                                       type);
+                       return retval;
+               }
+       }
+
+       if (event.EventType == EET_TOUCH_INPUT_EVENT)
+       {
+               SEvent translated;
+               memset(&translated, 0, sizeof(SEvent));
+               translated.EventType   = EET_MOUSE_INPUT_EVENT;
+               gui::IGUIElement* root = Environment->getRootGUIElement();
+
+               if (!root) {
+                       errorstream
+                       << "GUIFormSpecMenu::preprocessEvent unable to get root element"
+                       << std::endl;
+                       return false;
+               }
+               gui::IGUIElement* hovered = root->getElementFromPoint(
+                       core::position2d<s32>(
+                                       event.TouchInput.X,
+                                       event.TouchInput.Y));
+
+               translated.MouseInput.X = event.TouchInput.X;
+               translated.MouseInput.Y = event.TouchInput.Y;
+               translated.MouseInput.Control = false;
+
+               bool dont_send_event = false;
+
+               if (event.TouchInput.touchedCount == 1) {
+                       switch (event.TouchInput.Event) {
+                               case ETIE_PRESSED_DOWN:
+                                       m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
+                                       translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
+                                       translated.MouseInput.ButtonStates = EMBSM_LEFT;
+                                       m_down_pos = m_pointer;
+                                       break;
+                               case ETIE_MOVED:
+                                       m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
+                                       translated.MouseInput.Event = EMIE_MOUSE_MOVED;
+                                       translated.MouseInput.ButtonStates = EMBSM_LEFT;
+                                       break;
+                               case ETIE_LEFT_UP:
+                                       translated.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
+                                       translated.MouseInput.ButtonStates = 0;
+                                       hovered = root->getElementFromPoint(m_down_pos);
+                                       /* we don't have a valid pointer element use last
+                                        * known pointer pos */
+                                       translated.MouseInput.X = m_pointer.X;
+                                       translated.MouseInput.Y = m_pointer.Y;
+
+                                       /* reset down pos */
+                                       m_down_pos = v2s32(0,0);
+                                       break;
+                               default:
+                                       dont_send_event = true;
+                                       //this is not supposed to happen
+                                       errorstream
+                                       << "GUIFormSpecMenu::preprocessEvent unexpected usecase Event="
+                                       << event.TouchInput.Event << std::endl;
+                       }
+               } else if ( (event.TouchInput.touchedCount == 2) &&
+                               (event.TouchInput.Event == ETIE_PRESSED_DOWN) ) {
+                       hovered = root->getElementFromPoint(m_down_pos);
+
+                       translated.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
+                       translated.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT;
+                       translated.MouseInput.X = m_pointer.X;
+                       translated.MouseInput.Y = m_pointer.Y;
+
+                       if (hovered) {
+                               hovered->OnEvent(translated);
+                       }
+
+                       translated.MouseInput.Event = EMIE_RMOUSE_LEFT_UP;
+                       translated.MouseInput.ButtonStates = EMBSM_LEFT;
+
+
+                       if (hovered) {
+                               hovered->OnEvent(translated);
+                       }
+                       dont_send_event = true;
+               }
+               /* ignore unhandled 2 touch events ... accidental moving for example */
+               else if (event.TouchInput.touchedCount == 2) {
+                       dont_send_event = true;
+               }
+               else if (event.TouchInput.touchedCount > 2) {
+                       errorstream
+                       << "GUIFormSpecMenu::preprocessEvent to many multitouch events "
+                       << event.TouchInput.touchedCount << " ignoring them" << std::endl;
+               }
+
+               if (dont_send_event) {
+                       return true;
+               }
+
+               /* check if translated event needs to be preprocessed again */
+               if (preprocessEvent(translated)) {
+                       return true;
+               }
+               if (hovered) {
+                       grab();
+                       bool retval = hovered->OnEvent(translated);
+
+                       if (event.TouchInput.Event == ETIE_LEFT_UP) {
+                               /* reset pointer */
+                               m_pointer = v2s32(0,0);
+                       }
+                       drop();
+                       return retval;
+               }
+       }
+       #endif
+
        return false;
 }
 
@@ -2584,8 +2786,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
 {
        if(event.EventType==EET_KEY_INPUT_EVENT) {
                KeyPress kp(event.KeyInput);
-               if (event.KeyInput.PressedDown && (kp == EscapeKey ||
-                       kp == getKeySetting("keymap_inventory"))) {
+               if (event.KeyInput.PressedDown && ( (kp == EscapeKey) ||
+                       (kp == getKeySetting("keymap_inventory")) || (kp == CancelKey))) {
                        if (m_allowclose) {
                                doPause = false;
                                acceptInput(quit_mode_cancel);
@@ -3015,6 +3217,38 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
        return Parent ? Parent->OnEvent(event) : false;
 }
 
+/**
+ * get name of element by element id
+ * @param id of element
+ * @return name string or empty string
+ */
+std::wstring GUIFormSpecMenu::getNameByID(s32 id)
+{
+       for(std::vector<FieldSpec>::iterator iter =  m_fields.begin();
+                               iter != m_fields.end(); iter++) {
+               if (iter->fid == id) {
+                       return iter->fname;
+               }
+       }
+       return L"";
+}
+
+/**
+ * get label of element by id
+ * @param id of element
+ * @return label string or empty string
+ */
+std::wstring GUIFormSpecMenu::getLabelByID(s32 id)
+{
+       for(std::vector<FieldSpec>::iterator iter =  m_fields.begin();
+                               iter != m_fields.end(); iter++) {
+               if (iter->fid == id) {
+                       return iter->flabel;
+               }
+       }
+       return L"";
+}
+
 bool GUIFormSpecMenu::parseColor(const std::string &value, video::SColor &color,
                bool quiet)
 {
index 5d74978a941fffdefb141fc54a904cf1e20fc612..72a188bc528d02d8b1675a7a57ae67fd6adbb83e 100644 (file)
@@ -151,7 +151,7 @@ class GUIFormSpecMenu : public GUIModalMenu
                {
                }
                FieldSpec(const std::wstring &name, const std::wstring &label,
-                         const std::wstring &fdeflt, int id) :
+                               const std::wstring &fdeflt, int id) :
                        fname(name),
                        flabel(label),
                        fdefault(fdeflt),
@@ -274,6 +274,10 @@ public:
        static bool parseColor(const std::string &value,
                        video::SColor &color, bool quiet);
 
+#ifdef __ANDROID__
+       bool getAndroidUIInput();
+#endif
+
 protected:
        v2s32 getBasePos() const
        {
@@ -409,6 +413,14 @@ private:
        clickpos m_doubleclickdetect[2];
 
        int m_btn_height;
+
+       std::wstring getLabelByID(s32 id);
+       std::wstring getNameByID(s32 id);
+#ifdef __ANDROID__
+       v2s32 m_down_pos;
+       std::wstring m_JavaDialogFieldName;
+#endif
+
 };
 
 class FormspecFormSource: public IFormSource
index 313988fd8800033e14aa612e9324510e2177f539..69c366ee00734f0a529ab7bfb32983f28075259c 100644 (file)
@@ -46,7 +46,7 @@ HTTPFetchRequest::HTTPFetchRequest()
        request_id = 0;
        timeout = g_settings->getS32("curl_timeout");
        connect_timeout = timeout;
-       
+
        useragent = std::string("Minetest/") + minetest_version_hash + " (" + porting::get_sysinfo() + ")";
 }
 
@@ -259,6 +259,10 @@ struct HTTPFetchOngoing
                                        request.extra_headers[i].c_str());
                        }
                        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, httpheader);
+
+                       if (!g_settings->getBool("curl_verify_cert")) {
+                               curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
+                       }
                }
        }
 
@@ -302,7 +306,7 @@ struct HTTPFetchOngoing
                }
 
                if (res != CURLE_OK) {
-                       infostream<<request.url<<" not found ("
+                       errorstream<<request.url<<" not found ("
                                <<curl_easy_strerror(res)<<")"
                                <<" (response code "<<result.response_code<<")"
                                <<std::endl;
index f29e5249dd1eae5588f6e591e01ba2d408fbda99..02071835f2b0b9c6a1aca92838ce0e8d2d3a65e6 100644 (file)
@@ -33,6 +33,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "porting.h"
 #include <IGUIStaticText.h>
 
+#ifdef HAVE_TOUCHSCREENGUI
+#include "touchscreengui.h"
+#endif
 
 Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr,
                gui::IGUIEnvironment* guienv, gui::IGUIFont *font,
@@ -160,6 +163,11 @@ void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect, bool sele
 void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
                InventoryList *mainlist, u16 selectitem, u16 direction)
 {
+#ifdef HAVE_TOUCHSCREENGUI
+       if ( (g_touchscreengui) && (offset == 0))
+               g_touchscreengui->resetHud();
+#endif
+
        s32 height  = m_hotbar_imagesize + m_padding * 2;
        s32 width   = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
 
@@ -222,6 +230,11 @@ void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
                }
 
                drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i +1) == selectitem );
+
+#ifdef HAVE_TOUCHSCREENGUI
+               if (g_touchscreengui)
+                       g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
+#endif
        }
 }
 
index 0187c73877cdf02398140e87cd709e020250b127..10e1afe2dbaedcf160e1ad692a5030d0ffda3b8e 100644 (file)
@@ -38,6 +38,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <map>
 #include <set>
 
+#ifdef __ANDROID__
+#include <GLES/gl.h>
+#endif
+
 /*
        ItemDefinition
 */
@@ -433,6 +437,11 @@ public:
                                params.light_color.set(1.0, 0.5, 0.5, 0.5);
                                params.light_radius = 1000;
 
+#ifdef __ANDROID__
+                               params.camera_position.set(0, -1.0, -1.5);
+                               params.camera_position.rotateXZBy(45);
+                               params.light_position.set(10, -100, -50);
+#endif
                                cc->inventory_texture =
                                        tsrc->generateTextureFromMesh(params);
 
index f6d7f022fecf1a51f0778f68a93fb6f4ce935a87..609e2f518f47609614c50491db3f64379ed6731c 100644 (file)
@@ -51,7 +51,15 @@ JSemaphore::JSemaphore() {
 
 JSemaphore::~JSemaphore() {
        int sem_destroy_retval = sem_destroy(&m_semaphore);
+#ifdef __ANDROID__
+// WORKAROUND for broken bionic semaphore implementation!
+       assert(
+                       (sem_destroy_retval == 0) ||
+                       (errno == EBUSY)
+               );
+#else
        assert(sem_destroy_retval == 0);
+#endif
        UNUSED(sem_destroy_retval);
 }
 
index a8e54e315467651fb08516b9a4dafbd8168a9368..e90c03456db59bf767bf39de4bb7922faa5687b6 100644 (file)
@@ -111,7 +111,11 @@ int JThread::Kill()
                }
                return ERR_JTHREAD_NOTRUNNING;
        }
+#ifdef __ANDROID__
+       pthread_kill(threadid, SIGKILL);
+#else
        pthread_cancel(threadid);
+#endif
        if (started) {
                int pthread_join_retval = pthread_join(threadid,&status);
                assert(pthread_join_retval == 0);
index 96631b4eaf1e158d408769350fff538505b34a31..890c97cc27a875e123a60b6c6bb9b9e1c0722406 100644 (file)
@@ -334,6 +334,7 @@ const char *KeyPress::name() const
 }
 
 const KeyPress EscapeKey("KEY_ESCAPE");
+const KeyPress CancelKey("KEY_CANCEL");
 const KeyPress NumberKey[] = {
        KeyPress("KEY_KEY_0"), KeyPress("KEY_KEY_1"), KeyPress("KEY_KEY_2"),
        KeyPress("KEY_KEY_3"), KeyPress("KEY_KEY_4"), KeyPress("KEY_KEY_5"),
index 65f04d8d738aade49277d0748c2114d18dc23ad6..459a85a467dfbf230b3594c2ba610b1ca33c70eb 100644 (file)
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define KEYCODE_HEADER
 
 #include "irrlichttypes.h"
+#include "Keycodes.h"
 #include <IEventReceiver.h>
 #include <string>
 
@@ -57,6 +58,7 @@ protected:
 };
 
 extern const KeyPress EscapeKey;
+extern const KeyPress CancelKey;
 extern const KeyPress NumberKey[10];
 
 // Key configuration getter
@@ -65,5 +67,7 @@ KeyPress getKeySetting(const char *settingname);
 // Clear fast lookup cache
 void clearKeyCache();
 
+irr::EKEY_CODE keyname_to_keycode(const char *name);
+
 #endif
 
index 97f25cc77e95a1a0cf7962b99e0af3b67557ad2a..ff2e163333d6c85ac50c229d33ea5307e367d0b1 100644 (file)
@@ -26,6 +26,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "threads.h"
 #include "debug.h"
 #include "gettime.h"
+#include "porting.h"
+#include "config.h"
 
 std::list<ILogOutput*> log_outputs[LMT_NUM_VALUES];
 std::map<threadid_t, std::string> log_threadnames;
@@ -139,6 +141,9 @@ public:
        void printbuf()
        {
                log_printline(m_lev, m_buf);
+#ifdef __ANDROID__
+               __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME, "%s", m_buf.c_str());
+#endif
        }
 
        void bufchar(char c)
index 6dc319358c0f76a88dcc545e8c5375f85f7a569c..98068c1aa06a7aeb9e8508dfee619d79af130004 100644 (file)
@@ -176,9 +176,15 @@ static void buffreplace (LexState *ls, char from, char to) {
 
 static void trydecpoint (LexState *ls, SemInfo *seminfo) {
   /* format error: try to update decimal point separator */
+#ifndef __ANDROID__
   struct lconv *cv = localeconv();
+#endif
   char old = ls->decpoint;
+#ifndef __ANDROID__
   ls->decpoint = (cv ? cv->decimal_point[0] : '.');
+#else
+  ls->decpoint = '.';
+#endif
   buffreplace(ls, old, ls->decpoint);  /* try updated decimal separator */
   if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) {
     /* format error with correct decimal point: no more options */
index 5a0be2f75df5ad691f37a5d62cafa0fec8fc6328..1caa918b85448d948f9116c133cc6348465ad7df 100644 (file)
@@ -84,10 +84,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #ifdef USE_LEVELDB
 #include "database-leveldb.h"
 #endif
+
 #if USE_REDIS
 #include "database-redis.h"
 #endif
 
+#ifdef HAVE_TOUCHSCREENGUI
+#include "touchscreengui.h"
+#endif
 /*
        Settings.
        These are loaded from the config file.
@@ -253,6 +257,11 @@ public:
                        React to nothing here if a menu is active
                */
                if (noMenuActive() == false) {
+#ifdef HAVE_TOUCHSCREENGUI
+                       if (m_touchscreengui != 0) {
+                               m_touchscreengui->Toggle(false);
+                       }
+#endif
                        return g_menumgr.preprocessEvent(event);
                }
 
@@ -266,7 +275,16 @@ public:
                        }
                }
 
-               if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) {
+#ifdef HAVE_TOUCHSCREENGUI
+               // case of touchscreengui we have to handle different events
+               if ((m_touchscreengui != 0) &&
+                               (event.EventType == irr::EET_TOUCH_INPUT_EVENT)) {
+                       m_touchscreengui->translateEvent(event);
+                       return true;
+               }
+#endif
+               // handle mouse events
+               if(event.EventType == irr::EET_MOUSE_INPUT_EVENT) {
                        if (noMenuActive() == false) {
                                left_active = false;
                                middle_active = false;
@@ -293,8 +311,8 @@ public:
                                }
                        }
                }
-               if (event.EventType == irr::EET_LOG_TEXT_EVENT) {
-                       dstream << "Irrlicht log: " << event.LogEvent.Text << std::endl;
+               if(event.EventType == irr::EET_LOG_TEXT_EVENT) {
+                       dstream<< std::string("Irrlicht log: ") + std::string(event.LogEvent.Text)<<std::endl;
                        return true;
                }
                /* always return false in order to continue processing events */
@@ -342,6 +360,9 @@ public:
        MyEventReceiver()
        {
                clearInput();
+#ifdef HAVE_TOUCHSCREENGUI
+               m_touchscreengui = NULL;
+#endif
        }
 
        bool leftclicked;
@@ -355,7 +376,12 @@ public:
 
        s32 mouse_wheel;
 
+#ifdef HAVE_TOUCHSCREENGUI
+       TouchScreenGUI* m_touchscreengui;
+#endif
+
 private:
+       IrrlichtDevice *m_device;
 
        // The current state of keys
        KeyList keyIsDown;
@@ -372,7 +398,8 @@ class RealInputHandler : public InputHandler
 public:
        RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
                m_device(device),
-               m_receiver(receiver)
+               m_receiver(receiver),
+               m_mousepos(0,0)
        {
        }
        virtual bool isKeyDown(const KeyPress &keyCode)
@@ -385,11 +412,21 @@ public:
        }
        virtual v2s32 getMousePos()
        {
-               return m_device->getCursorControl()->getPosition();
+               if (m_device->getCursorControl()) {
+                       return m_device->getCursorControl()->getPosition();
+               }
+               else {
+                       return m_mousepos;
+               }
        }
        virtual void setMousePos(s32 x, s32 y)
        {
-               m_device->getCursorControl()->setPosition(x, y);
+               if (m_device->getCursorControl()) {
+                       m_device->getCursorControl()->setPosition(x, y);
+               }
+               else {
+                       m_mousepos = v2s32(x,y);
+               }
        }
 
        virtual bool getLeftState()
@@ -445,8 +482,9 @@ public:
                m_receiver->clearInput();
        }
 private:
-       IrrlichtDevice *m_device;
+       IrrlichtDevice  *m_device;
        MyEventReceiver *m_receiver;
+       v2s32           m_mousepos;
 };
 
 class RandomInputHandler : public InputHandler
@@ -855,8 +893,18 @@ int main(int argc, char *argv[])
 
        porting::initializePaths();
 
+#ifdef __ANDROID__
+       porting::initAndroid();
+
+       porting::setExternalStorageDir(porting::jnienv);
+       if (!fs::PathExists(porting::path_user)) {
+               fs::CreateDir(porting::path_user);
+       }
+       porting::copyAssets();
+#else
        // Create user data directory
        fs::CreateDir(porting::path_user);
+#endif
 
        infostream << "path_share = " << porting::path_share << std::endl;
        infostream << "path_user  = " << porting::path_user << std::endl;
@@ -975,14 +1023,15 @@ int main(int argc, char *argv[])
        // Initialize HTTP fetcher
        httpfetch_init(g_settings->getS32("curl_parallel_limit"));
 
+#ifndef __ANDROID__
        /*
                Run unit tests
        */
-
        if ((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
                        || cmd_args.getFlag("enable-unittests") == true) {
                                run_tests();
        }
+#endif
 #ifdef _MSC_VER
        init_gettext((porting::path_share + DIR_DELIM + "locale").c_str(),
                g_settings->get("language"), argc, argv);
@@ -1348,7 +1397,7 @@ int main(int argc, char *argv[])
                List video modes if requested
        */
 
-       MyEventReceiver receiver;
+       MyEventReceiver* receiver = new MyEventReceiver();
 
        if (cmd_args.getFlag("videomodes")) {
                IrrlichtDevice *nulldevice;
@@ -1361,7 +1410,7 @@ int main(int argc, char *argv[])
                params.Fullscreen    = false;
                params.Stencilbuffer = false;
                params.Vsync         = vsync;
-               params.EventReceiver = &receiver;
+               params.EventReceiver = receiver;
                params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
 
                nulldevice = createDeviceEx(params);
@@ -1397,15 +1446,13 @@ int main(int argc, char *argv[])
 
                nulldevice->drop();
 
+               delete receiver;
                return 0;
        }
 
        /*
                Create device and exit if creation failed
        */
-
-       IrrlichtDevice *device;
-
        SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
        params.DriverType    = driverType;
        params.WindowSize    = core::dimension2d<u32>(screenW, screenH);
@@ -1414,12 +1461,18 @@ int main(int argc, char *argv[])
        params.Fullscreen    = fullscreen;
        params.Stencilbuffer = false;
        params.Vsync         = vsync;
-       params.EventReceiver = &receiver;
+       params.EventReceiver = receiver;
        params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
+#ifdef __ANDROID__
+       params.PrivateData = porting::app_global;
+       params.OGLES2ShaderPath = std::string(porting::path_user + DIR_DELIM +
+                       "media" + DIR_DELIM + "Shaders" + DIR_DELIM).c_str();
+#endif
 
-       device = createDeviceEx(params);
+       IrrlichtDevice * device = createDeviceEx(params);
 
        if (device == 0) {
+               delete receiver;
                return 1; // could not create selected driver.
        }
 
@@ -1476,10 +1529,11 @@ int main(int argc, char *argv[])
        bool random_input = g_settings->getBool("random_input")
                        || cmd_args.getFlag("random-input");
        InputHandler *input = NULL;
+
        if (random_input) {
                input = new RandomInputHandler();
        } else {
-               input = new RealInputHandler(device, &receiver);
+               input = new RealInputHandler(device,receiver);
        }
 
        scene::ISceneManager* smgr = device->getSceneManager();
@@ -1564,7 +1618,8 @@ int main(int argc, char *argv[])
        /*
                Menu-game loop
        */
-       while (device->run() && kill == false)
+       while (device->run() && (kill == false) &&
+                       (g_gamecallback->shutdown_requested == false))
        {
                // Set the window caption
                wchar_t* text = wgettext("Main Menu");
@@ -1612,7 +1667,9 @@ int main(int argc, char *argv[])
                                first_loop = false;
 
                                // Cursor can be non-visible when coming from the game
+                               #ifndef ANDROID
                                device->getCursorControl()->setVisible(true);
+                               #endif
                                // Some stuff are left to scene manager when coming from the game
                                // (map at least?)
                                smgr->clear();
@@ -1661,10 +1718,9 @@ int main(int argc, char *argv[])
                                        }
                                        infostream << "Waited for other menus" << std::endl;
 
-                                       GUIEngine* temp = new GUIEngine(device, guiroot,
-                                               &g_menumgr, smgr, &menudata, kill);
+                                       /* show main menu */
+                                       GUIEngine mymenu(device, guiroot, &g_menumgr,smgr,&menudata,kill);
 
-                                       delete temp;
                                        //once finished you'll never end up here
                                        smgr->clear();
                                }
@@ -1788,6 +1844,10 @@ int main(int argc, char *argv[])
                        /*
                                Run game
                        */
+#ifdef HAVE_TOUCHSCREENGUI
+       receiver->m_touchscreengui = new TouchScreenGUI(device, receiver);
+       g_touchscreengui = receiver->m_touchscreengui;
+#endif
                        the_game(
                                kill,
                                random_input,
@@ -1805,6 +1865,11 @@ int main(int argc, char *argv[])
                                simple_singleplayer_mode
                        );
                        smgr->clear();
+#ifdef HAVE_TOUCHSCREENGUI
+       delete g_touchscreengui;
+       g_touchscreengui = NULL;
+       receiver->m_touchscreengui = NULL;
+#endif
 
                } //try
                catch(con::PeerNotFoundException &e)
@@ -1849,7 +1914,7 @@ int main(int argc, char *argv[])
        if (use_freetype)
                font->drop();
 #endif
-
+       delete receiver;
 #endif // !SERVER
 
        // Update configuration file
index 78ae1fcfa514623368bf63cb12204c63651ab662..28fe1ac116a6707906aebe7fec5ffa72ad3e5ddc 100644 (file)
@@ -124,13 +124,17 @@ public:
                disconnect_requested(false),
                changepassword_requested(false),
                changevolume_requested(false),
+               shutdown_requested(false),
                device(a_device)
        {
        }
 
        virtual void exitToOS()
        {
+               shutdown_requested = true;
+#ifndef __ANDROID__
                device->closeDevice();
+#endif
        }
 
        virtual void disconnect()
@@ -151,6 +155,7 @@ public:
        bool disconnect_requested;
        bool changepassword_requested;
        bool changevolume_requested;
+       bool shutdown_requested;
        IrrlichtDevice *device;
 };
 
index 251b7aa3b60a86af18524631f8377621974320dd..2c512d3ca08eb4fc921272a46f65ca5e00f97800 100644 (file)
@@ -21,6 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define MODALMENU_HEADER
 
 #include "irrlichttypes_extrabloated.h"
+#ifdef HAVE_TOUCHSCREENGUI
+#include "touchscreengui.h"
+#endif
 
 class GUIModalMenu;
 
@@ -101,6 +104,10 @@ public:
                Environment->removeFocus(this);
                m_menumgr->deletingMenu(this);
                this->remove();
+#ifdef HAVE_TOUCHSCREENGUI
+               if (g_touchscreengui)
+                       g_touchscreengui->Show();
+#endif
        }
 
        void removeChildren()
index 3c2a5c82488f88dfea5b5b1d9c79c7d0e1b9f417..ad942b0bc32c5ba75da9d797fb8b2ad92e3a9cde 100644 (file)
@@ -167,6 +167,7 @@ int getNumberOfProcessors() {
 }
 
 
+#ifndef __ANDROID__
 bool threadBindToProcessor(threadid_t tid, int pnumber) {
 #if defined(_WIN32)
 
@@ -194,7 +195,7 @@ bool threadBindToProcessor(threadid_t tid, int pnumber) {
                                                pnumber, NULL) == 0;
 
 #elif defined(_AIX)
-       
+
        return bindprocessor(BINDTHREAD, (tid_t)tid, pnumber) == 0;
 
 #elif defined(__hpux) || defined(hpux)
@@ -203,11 +204,11 @@ bool threadBindToProcessor(threadid_t tid, int pnumber) {
 
        return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
                                                                        &answer, pnumber, tid) == 0;
-       
+
 #elif defined(__APPLE__)
 
        struct thread_affinity_policy tapol;
-       
+
        thread_port_t threadport = pthread_mach_thread_np(tid);
        tapol.affinity_tag = pnumber + 1;
        return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
@@ -219,7 +220,7 @@ bool threadBindToProcessor(threadid_t tid, int pnumber) {
 
 #endif
 }
-
+#endif
 
 bool threadSetPriority(threadid_t tid, int prio) {
 #if defined(_WIN32)
@@ -232,21 +233,21 @@ bool threadSetPriority(threadid_t tid, int prio) {
 
        CloseHandle(hThread);
        return success;
-       
+
 #else
 
        struct sched_param sparam;
        int policy;
-       
+
        if (pthread_getschedparam(tid, &policy, &sparam) != 0)
                return false;
-               
+
        int min = sched_get_priority_min(policy);
        int max = sched_get_priority_max(policy);
 
        sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
        return pthread_setschedparam(tid, policy, &sparam) == 0;
-       
+
 #endif
 }
 
@@ -458,9 +459,15 @@ void initializePaths()
        {
                char buf[BUFSIZ];
                memset(buf, 0, BUFSIZ);
-               assert(readlink("/proc/self/exe", buf, BUFSIZ-1) != -1);
-               pathRemoveFile(buf, '/');
-               bindir = buf;
+               if (readlink("/proc/self/exe", buf, BUFSIZ-1) == -1) {
+                       errorstream << "Unable to read bindir "<< std::endl;
+#ifndef __ANDROID__
+                       assert("Unable to read bindir" == 0);
+#endif
+               } else {
+                       pathRemoveFile(buf, '/');
+                       bindir = buf;
+               }
        }
 
        // Find share directory from these.
@@ -472,6 +479,9 @@ void initializePaths()
        trylist.push_back(
                        bindir + DIR_DELIM + ".." + DIR_DELIM + "share" + DIR_DELIM + PROJECT_NAME);
        trylist.push_back(bindir + DIR_DELIM + "..");
+#ifdef __ANDROID__
+       trylist.push_back(DIR_DELIM "sdcard" DIR_DELIM PROJECT_NAME);
+#endif
 
        for(std::list<std::string>::const_iterator i = trylist.begin();
                        i != trylist.end(); i++)
@@ -490,8 +500,11 @@ void initializePaths()
                path_share = trypath;
                break;
        }
-
+#ifndef __ANDROID__
        path_user = std::string(getenv("HOME")) + DIR_DELIM + "." + PROJECT_NAME;
+#else
+       path_user = std::string(DIR_DELIM "sdcard" DIR_DELIM PROJECT_NAME DIR_DELIM);
+#endif
 
        /*
                OS X
@@ -539,6 +552,7 @@ v2u32 getWindowSize() {
        return device->getVideoDriver()->getScreenSize();
 }
 
+#ifndef __ANDROID__
 
 float getDisplayDensity() {
        float gui_scaling = g_settings->getFloat("gui_scaling");
@@ -562,6 +576,7 @@ v2u32 getDisplaySize() {
        return deskres;
 }
 #endif
+#endif
 
 } //namespace porting
 
index b5a5d00f284120184e4cb1ffba0b53fb94181111..9be09da75d65bf396a00e1d933c038f1c7e5cffe 100644 (file)
@@ -53,12 +53,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #ifdef _WIN32
        #include <windows.h>
-       
+
        #define sleep_ms(x) Sleep(x)
 #else
        #include <unistd.h>
        #include <stdint.h> //for uintptr_t
-       
+
        #if (defined(linux) || defined(__linux)) && !defined(_GNU_SOURCE)
                #define _GNU_SOURCE
        #endif
@@ -79,7 +79,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        #endif
 
        #define sleep_ms(x) usleep(x*1000)
-       
+
        #define THREAD_PRIORITY_LOWEST       0
        #define THREAD_PRIORITY_BELOW_NORMAL 1
        #define THREAD_PRIORITY_NORMAL       2
@@ -197,17 +197,17 @@ void initIrrlicht(irr::IrrlichtDevice * );
        #define _WIN32_WINNT 0x0501
 #endif
        #include <windows.h>
-       
+
        inline u32 getTimeS()
        {
                return GetTickCount() / 1000;
        }
-       
+
        inline u32 getTimeMs()
        {
                return GetTickCount();
        }
-       
+
        inline u32 getTimeUs()
        {
                LARGE_INTEGER freq, t;
@@ -215,7 +215,7 @@ void initIrrlicht(irr::IrrlichtDevice * );
                QueryPerformanceCounter(&t);
                return (double)(t.QuadPart) / ((double)(freq.QuadPart) / 1000000.0);
        }
-       
+
        inline u32 getTimeNs()
        {
                LARGE_INTEGER freq, t;
@@ -223,7 +223,7 @@ void initIrrlicht(irr::IrrlichtDevice * );
                QueryPerformanceCounter(&t);
                return (double)(t.QuadPart) / ((double)(freq.QuadPart) / 1000000000.0);
        }
-       
+
 #else // Posix
 #include <sys/time.h>
 #include <time.h>
@@ -238,21 +238,21 @@ void initIrrlicht(irr::IrrlichtDevice * );
                gettimeofday(&tv, NULL);
                return tv.tv_sec;
        }
-       
+
        inline u32 getTimeMs()
        {
                struct timeval tv;
                gettimeofday(&tv, NULL);
                return tv.tv_sec * 1000 + tv.tv_usec / 1000;
        }
-       
+
        inline u32 getTimeUs()
        {
                struct timeval tv;
                gettimeofday(&tv, NULL);
                return tv.tv_sec * 1000000 + tv.tv_usec;
        }
-       
+
        inline u32 getTimeNs()
        {
                struct timespec ts;
@@ -270,7 +270,7 @@ void initIrrlicht(irr::IrrlichtDevice * );
 #endif
                return ts.tv_sec * 1000000000 + ts.tv_nsec;
        }
-       
+
        /*#include <sys/timeb.h>
        inline u32 getTimeMs()
        {
@@ -373,5 +373,9 @@ v2u32 getWindowSize();
 
 } // namespace porting
 
+#ifdef __ANDROID__
+#include "porting_android.h"
+#endif
+
 #endif // PORTING_HEADER
 
diff --git a/src/porting_android.cpp b/src/porting_android.cpp
new file mode 100644 (file)
index 0000000..96c9385
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+Minetest
+Copyright (C) 2014 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __ANDROID__
+#error This file may only be compiled for android!
+#endif
+
+#include "porting.h"
+#include "porting_android.h"
+#include "config.h"
+#include "filesys.h"
+#include "log.h"
+#include <sstream>
+
+#ifdef GPROF
+#include "prof.h"
+#endif
+
+extern int main(int argc, char *argv[]);
+
+void android_main(android_app *app)
+{
+       int retval = 0;
+       porting::app_global = app;
+
+       porting::setThreadName("MainThread");
+
+       try {
+               app_dummy();
+               char *argv[] = { (char*) "minetest" };
+               main(sizeof(argv) / sizeof(argv[0]), argv);
+               }
+       catch(BaseException e) {
+               std::stringstream msg;
+               msg << "Exception handled by main: " << e.what();
+               const char* message = msg.str().c_str();
+               __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME, "%s", message);
+               errorstream << msg << std::endl;
+               retval = -1;
+       }
+       catch(...) {
+               __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME,
+                               "Some exception occured");
+               errorstream << "Uncaught exception in main thread!" << std::endl;
+               retval = -1;
+       }
+
+       porting::cleanupAndroid();
+       errorstream << "Shutting down minetest." << std::endl;
+       exit(retval);
+}
+
+/* handler for finished message box input */
+/* Intentionally NOT in namespace porting */
+/* TODO this doesn't work as expected, no idea why but there's a workaround   */
+/* for it right now */
+extern "C" {
+       JNIEXPORT void JNICALL Java_org_minetest_MtNativeActivity_putMessageBoxResult(
+                       JNIEnv * env, jclass thiz, jstring text)
+       {
+               errorstream << "Java_org_minetest_MtNativeActivity_putMessageBoxResult got: "
+                               << std::string((const char*)env->GetStringChars(text,0))
+                               << std::endl;
+       }
+}
+
+namespace porting {
+
+std::string path_storage = DIR_DELIM "sdcard" DIR_DELIM;
+
+android_app* app_global;
+JNIEnv*      jnienv;
+jclass       nativeActivity;
+
+jclass findClass(std::string classname)
+{
+       if (jnienv == 0) {
+               return 0;
+       }
+
+       jclass nativeactivity = jnienv->FindClass("android/app/NativeActivity");
+       jmethodID getClassLoader =
+                       jnienv->GetMethodID(nativeactivity,"getClassLoader",
+                                       "()Ljava/lang/ClassLoader;");
+       jobject cls =
+                       jnienv->CallObjectMethod(app_global->activity->clazz, getClassLoader);
+       jclass classLoader = jnienv->FindClass("java/lang/ClassLoader");
+       jmethodID findClass =
+                       jnienv->GetMethodID(classLoader, "loadClass",
+                                       "(Ljava/lang/String;)Ljava/lang/Class;");
+       jstring strClassName =
+                       jnienv->NewStringUTF(classname.c_str());
+       return (jclass) jnienv->CallObjectMethod(cls, findClass, strClassName);
+}
+
+void copyAssets()
+{
+       jmethodID assetcopy = jnienv->GetMethodID(nativeActivity,"copyAssets","()V");
+
+       if (assetcopy == 0) {
+               assert("porting::copyAssets unable to find copy assets method" == 0);
+       }
+
+       jnienv->CallVoidMethod(app_global->activity->clazz, assetcopy);
+}
+
+void initAndroid()
+{
+       porting::jnienv = NULL;
+       JavaVM *jvm = app_global->activity->vm;
+       JavaVMAttachArgs lJavaVMAttachArgs;
+       lJavaVMAttachArgs.version = JNI_VERSION_1_6;
+       lJavaVMAttachArgs.name = "MinetestNativeThread";
+       lJavaVMAttachArgs.group = NULL;
+#ifdef NDEBUG
+       // This is a ugly hack as arm v7a non debuggable builds crash without this
+       // printf ... if someone finds out why please fix it!
+       infostream << "Attaching native thread. " << std::endl;
+#endif
+       if ( jvm->AttachCurrentThread(&porting::jnienv, &lJavaVMAttachArgs) == JNI_ERR) {
+               errorstream << "Failed to attach native thread to jvm" << std::endl;
+               exit(-1);
+       }
+
+       nativeActivity = findClass("org/minetest/minetest/MtNativeActivity");
+       if (nativeActivity == 0) {
+               errorstream <<
+                       "porting::initAndroid unable to find java native activity class" <<
+                       std::endl;
+       }
+
+#ifdef GPROF
+       /* in the start-up code */
+       __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME,
+                       "Initializing GPROF profiler");
+       monstartup("libminetest.so");
+#endif
+}
+
+void cleanupAndroid()
+{
+
+#ifdef GPROF
+       errorstream << "Shutting down GPROF profiler" << std::endl;
+       setenv("CPUPROFILE", (path_user + DIR_DELIM + "gmon.out").c_str(), 1);
+       moncleanup();
+#endif
+
+       JavaVM *jvm = app_global->activity->vm;
+       jvm->DetachCurrentThread();
+}
+
+void setExternalStorageDir(JNIEnv* lJNIEnv)
+{
+       // Android: Retrieve ablsolute path to external storage device (sdcard)
+       jclass ClassEnv      = lJNIEnv->FindClass("android/os/Environment");
+       jmethodID MethodDir  =
+                       lJNIEnv->GetStaticMethodID(ClassEnv,
+                                       "getExternalStorageDirectory","()Ljava/io/File;");
+       jobject ObjectFile   = lJNIEnv->CallStaticObjectMethod(ClassEnv, MethodDir);
+       jclass ClassFile     = lJNIEnv->FindClass("java/io/File");
+
+       jmethodID MethodPath =
+                       lJNIEnv->GetMethodID(ClassFile, "getAbsolutePath",
+                                       "()Ljava/lang/String;");
+       jstring StringPath   =
+                       (jstring) lJNIEnv->CallObjectMethod(ObjectFile, MethodPath);
+
+       const char *externalPath = lJNIEnv->GetStringUTFChars(StringPath, NULL);
+       std::string userPath(externalPath);
+       lJNIEnv->ReleaseStringUTFChars(StringPath, externalPath);
+
+       path_storage             = userPath;
+       path_user                = userPath + DIR_DELIM + PROJECT_NAME;
+       path_share               = userPath + DIR_DELIM + PROJECT_NAME;
+}
+
+void showInputDialog(const std::string& acceptButton, const  std::string& hint,
+               const std::string& current, int editType)
+{
+       jmethodID showdialog = jnienv->GetMethodID(nativeActivity,"showDialog",
+               "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
+
+       if (showdialog == 0) {
+               assert("porting::showInputDialog unable to find java show dialog method" == 0);
+       }
+
+       jstring jacceptButton = jnienv->NewStringUTF(acceptButton.c_str());
+       jstring jhint         = jnienv->NewStringUTF(hint.c_str());
+       jstring jcurrent      = jnienv->NewStringUTF(current.c_str());
+       jint    jeditType     = editType;
+
+       jnienv->CallVoidMethod(app_global->activity->clazz, showdialog,
+                       jacceptButton, jhint, jcurrent, jeditType);
+}
+
+int getInputDialogState()
+{
+       jmethodID dialogstate = jnienv->GetMethodID(nativeActivity,
+                       "getDialogState", "()I");
+
+       if (dialogstate == 0) {
+               assert("porting::getInputDialogState unable to find java dialog state method" == 0);
+       }
+
+       return jnienv->CallIntMethod(app_global->activity->clazz, dialogstate);
+}
+
+std::string getInputDialogValue()
+{
+       jmethodID dialogvalue = jnienv->GetMethodID(nativeActivity,
+                       "getDialogValue", "()Ljava/lang/String;");
+
+       if (dialogvalue == 0) {
+               assert("porting::getInputDialogValue unable to find java dialog value method" == 0);
+       }
+
+       jobject result = jnienv->CallObjectMethod(app_global->activity->clazz,
+                       dialogvalue);
+
+       const char* javachars = jnienv->GetStringUTFChars((jstring) result,0);
+       std::string text(javachars);
+       jnienv->ReleaseStringUTFChars((jstring) result, javachars);
+
+       return text;
+}
+
+#if not defined(SERVER)
+float getDisplayDensity()
+{
+       static bool firstrun = true;
+       static float value = 0;
+
+       if (firstrun) {
+               jmethodID getDensity = jnienv->GetMethodID(nativeActivity, "getDensity",
+                                       "()F");
+
+               if (getDensity == 0) {
+                       assert("porting::getDisplayDensity unable to find java getDensity method" == 0);
+               }
+
+               value = jnienv->CallFloatMethod(app_global->activity->clazz, getDensity);
+               firstrun = false;
+       }
+       return value;
+}
+
+v2u32 getDisplaySize()
+{
+       static bool firstrun = true;
+       static v2u32 retval;
+
+       if (firstrun) {
+               jmethodID getDisplayWidth = jnienv->GetMethodID(nativeActivity,
+                               "getDisplayWidth", "()I");
+
+               if (getDisplayWidth == 0) {
+                       assert("porting::getDisplayWidth unable to find java getDisplayWidth method" == 0);
+               }
+
+               retval.X = jnienv->CallIntMethod(app_global->activity->clazz,
+                               getDisplayWidth);
+
+               jmethodID getDisplayHeight = jnienv->GetMethodID(nativeActivity,
+                               "getDisplayHeight", "()I");
+
+               if (getDisplayHeight == 0) {
+                       assert("porting::getDisplayHeight unable to find java getDisplayHeight method" == 0);
+               }
+
+               retval.Y = jnienv->CallIntMethod(app_global->activity->clazz,
+                               getDisplayHeight);
+
+               firstrun = false;
+       }
+       return retval;
+}
+#endif //SERVER
+}
diff --git a/src/porting_android.h b/src/porting_android.h
new file mode 100644 (file)
index 0000000..bfdadfb
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+Minetest
+Copyright (C) 2014 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef __PORTING_ANDROID_H__
+#define __PORTING_ANDROID_H__
+
+#ifndef __ANDROID__
+#error this include has to be included on android port only!
+#endif
+
+#include <jni.h>
+#include <android_native_app_glue.h>
+#include <android/log.h>
+
+#include <string>
+
+namespace porting {
+/** java app **/
+extern android_app *app_global;
+
+/** java <-> c++ interaction interface **/
+extern JNIEnv *jnienv;
+
+/**
+ * do initialization required on android only
+ */
+void initAndroid();
+void cleanupAndroid();
+
+/**
+ * set storage dir on external sdcard#
+ * @param lJNIEnv environment from android
+ */
+void setExternalStorageDir(JNIEnv* lJNIEnv);
+
+/**
+ * use java function to copy media from assets to external storage
+ */
+void copyAssets();
+
+/**
+ * show text input dialog in java
+ * @param acceptButton text to display on accept button
+ * @param hint hint to show
+ * @param current initial value to display
+ * @param editType type of texfield
+ * (1==multiline text input; 2==single line text input; 3=password field)
+ */
+void showInputDialog(const std::string& acceptButton,
+               const  std::string& hint, const std::string& current, int editType);
+
+/**
+ * WORKAROUND for not working callbacks from java -> c++
+ * get current state of input dialog
+ */
+int getInputDialogState();
+
+/**
+ * WORKAROUND for not working callbacks from java -> c++
+ * get text in current input dialog
+ */
+std::string getInputDialogValue();
+
+}
+
+#endif
index c9a36918a3d46d941e1086f37f5fa8fcb3fd1899..17ec51614fe1265eaf8f2cf72762c875d6060004 100644 (file)
@@ -32,6 +32,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/thread.h"
 #include "util/numeric.h"
 
+#ifdef __ANDROID__
+#include <GLES/gl.h>
+#endif
+
 /*
        A cache from texture name to texture path
 */
@@ -702,6 +706,9 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
 
        if(baseimg != NULL)
        {
+#ifdef __ANDROID__
+               baseimg = Align2Npot2(baseimg, driver);
+#endif
                // Create texture from resulting image
                t = driver->addTexture(name.c_str(), baseimg);
                baseimg->drop();
@@ -790,11 +797,17 @@ void TextureSource::rebuildImagesAndTextures()
        JMutexAutoLock lock(m_textureinfo_cache_mutex);
 
        video::IVideoDriver* driver = m_device->getVideoDriver();
+       assert(driver != 0);
 
        // Recreate textures
        for(u32 i=0; i<m_textureinfo_cache.size(); i++){
                TextureInfo *ti = &m_textureinfo_cache[i];
                video::IImage *img = generateImageFromScratch(ti->name);
+#ifdef __ANDROID__
+               img = Align2Npot2(img,driver);
+               assert(img->getDimension().Height == npot2(img->getDimension().Height));
+               assert(img->getDimension().Width == npot2(img->getDimension().Width));
+#endif
                // Create texture from resulting image
                video::ITexture *t = NULL;
                if(img) {
@@ -816,6 +829,126 @@ video::ITexture* TextureSource::generateTextureFromMesh(
        video::IVideoDriver *driver = m_device->getVideoDriver();
        assert(driver);
 
+#ifdef __ANDROID__
+       const GLubyte* renderstr = glGetString(GL_RENDERER);
+       std::string renderer((char*) renderstr);
+
+       // use no render to texture hack
+       if (
+               (renderer.find("Adreno") != std::string::npos) ||
+               (renderer.find("Mali") != std::string::npos) ||
+               (renderer.find("Immersion") != std::string::npos) ||
+               (renderer.find("Tegra") != std::string::npos) ||
+               g_settings->getBool("inventory_image_hack")
+               ) {
+               // Get a scene manager
+               scene::ISceneManager *smgr_main = m_device->getSceneManager();
+               assert(smgr_main);
+               scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
+               assert(smgr);
+
+               const float scaling = 0.2;
+
+               scene::IMeshSceneNode* meshnode =
+                               smgr->addMeshSceneNode(params.mesh, NULL,
+                                               -1, v3f(0,0,0), v3f(0,0,0),
+                                               v3f(1.0 * scaling,1.0 * scaling,1.0 * scaling), true);
+               meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
+               meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
+               meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter);
+               meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter);
+               meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter);
+
+               scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
+                               params.camera_position, params.camera_lookat);
+               // second parameter of setProjectionMatrix (isOrthogonal) is ignored
+               camera->setProjectionMatrix(params.camera_projection_matrix, false);
+
+               smgr->setAmbientLight(params.ambient_light);
+               smgr->addLightSceneNode(0,
+                               params.light_position,
+                               params.light_color,
+                               params.light_radius*scaling);
+
+               core::dimension2d<u32> screen = driver->getScreenSize();
+
+               // Render scene
+               driver->beginScene(true, true, video::SColor(0,0,0,0));
+               driver->clearZBuffer();
+               smgr->drawAll();
+
+               core::dimension2d<u32> partsize(screen.Width * scaling,screen.Height * scaling);
+
+               irr::video::IImage* rawImage =
+                               driver->createImage(irr::video::ECF_A8R8G8B8, partsize);
+
+               u8* pixels = static_cast<u8*>(rawImage->lock());
+               if (!pixels)
+               {
+                       rawImage->drop();
+                       return NULL;
+               }
+
+               core::rect<s32> source(
+                               screen.Width /2 - (screen.Width  * (scaling / 2)),
+                               screen.Height/2 - (screen.Height * (scaling / 2)),
+                               screen.Width /2 + (screen.Width  * (scaling / 2)),
+                               screen.Height/2 + (screen.Height * (scaling / 2))
+                       );
+
+               glReadPixels(source.UpperLeftCorner.X, source.UpperLeftCorner.Y,
+                               partsize.Width, partsize.Height, GL_RGBA,
+                               GL_UNSIGNED_BYTE, pixels);
+
+               driver->endScene();
+
+               // Drop scene manager
+               smgr->drop();
+
+               unsigned int pixelcount = partsize.Width*partsize.Height;
+
+               u8* runptr = pixels;
+               for (unsigned int i=0; i < pixelcount; i++) {
+
+                       u8 B = *runptr;
+                       u8 G = *(runptr+1);
+                       u8 R = *(runptr+2);
+                       u8 A = *(runptr+3);
+
+                       //BGRA -> RGBA
+                       *runptr = R;
+                       runptr ++;
+                       *runptr = G;
+                       runptr ++;
+                       *runptr = B;
+                       runptr ++;
+                       *runptr = A;
+                       runptr ++;
+               }
+
+               video::IImage* inventory_image =
+                               driver->createImage(irr::video::ECF_A8R8G8B8, params.dim);
+
+               rawImage->copyToScaling(inventory_image);
+               rawImage->drop();
+
+               video::ITexture *rtt = driver->addTexture(params.rtt_texture_name.c_str(), inventory_image);
+               inventory_image->drop();
+
+               if (rtt == NULL) {
+                       errorstream << "TextureSource::generateTextureFromMesh(): failed to recreate texture from image: " << params.rtt_texture_name << std::endl;
+                       return NULL;
+               }
+
+               driver->makeColorKeyTexture(rtt, v2s32(0,0));
+
+               if(params.delete_texture_on_shutdown)
+                       m_texture_trash.push_back(rtt);
+
+               return rtt;
+       }
+#endif
+
        if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
        {
                static bool warned = false;
@@ -840,7 +973,12 @@ video::ITexture* TextureSource::generateTextureFromMesh(
        }
 
        // Set render target
-       driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0));
+       if (!driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0))) {
+               driver->removeTexture(rtt);
+               errorstream<<"TextureSource::generateTextureFromMesh(): "
+                       <<"failed to set render target"<<std::endl;
+               return NULL;
+       }
 
        // Get a scene manager
        scene::ISceneManager *smgr_main = m_device->getSceneManager();
@@ -848,7 +986,9 @@ video::ITexture* TextureSource::generateTextureFromMesh(
        scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
        assert(smgr);
 
-       scene::IMeshSceneNode* meshnode = smgr->addMeshSceneNode(params.mesh, NULL, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true);
+       scene::IMeshSceneNode* meshnode =
+                       smgr->addMeshSceneNode(params.mesh, NULL,
+                                       -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true);
        meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
        meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
        meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter);
@@ -871,11 +1011,6 @@ video::ITexture* TextureSource::generateTextureFromMesh(
        smgr->drawAll();
        driver->endScene();
 
-       // NOTE: The scene nodes should not be dropped, otherwise
-       //       smgr->drop() segfaults
-       /*cube->drop();
-       camera->drop();
-       light->drop();*/
        // Drop scene manager
        smgr->drop();
 
@@ -938,6 +1073,57 @@ video::IImage* TextureSource::generateImageFromScratch(std::string name)
        return baseimg;
 }
 
+#ifdef __ANDROID__
+#include <GLES/gl.h>
+/**
+ * Check and align image to npot2 if required by hardware
+ * @param image image to check for npot2 alignment
+ * @param driver driver to use for image operations
+ * @return image or copy of image aligned to npot2
+ */
+video::IImage * Align2Npot2(video::IImage * image,
+               video::IVideoDriver* driver)
+{
+       if(image == NULL) {
+               return image;
+       }
+
+       core::dimension2d<u32> dim = image->getDimension();
+
+       std::string extensions = (char*) glGetString(GL_EXTENSIONS);
+       if (extensions.find("GL_OES_texture_npot") != std::string::npos) {
+               return image;
+       }
+
+       unsigned int height = npot2(dim.Height);
+       unsigned int width  = npot2(dim.Width);
+
+       if ((dim.Height == height) &&
+                       (dim.Width == width)) {
+               return image;
+       }
+
+       if (dim.Height > height) {
+               height *= 2;
+       }
+
+       if (dim.Width > width) {
+               width *= 2;
+       }
+
+       video::IImage *targetimage =
+                       driver->createImage(video::ECF_A8R8G8B8,
+                                       core::dimension2d<u32>(width, height));
+
+       if (targetimage != NULL) {
+               image->copyToScaling(targetimage);
+       }
+       image->drop();
+       return targetimage;
+}
+
+#endif
+
 bool TextureSource::generateImage(std::string part_of_name, video::IImage *& baseimg)
 {
        video::IVideoDriver* driver = m_device->getVideoDriver();
@@ -947,21 +1133,9 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas
        if(part_of_name.size() == 0 || part_of_name[0] != '[')
        {
                video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device);
-
-               if (image != NULL) {
-                       if (!driver->queryFeature(irr::video::EVDF_TEXTURE_NPOT)) {
-                               core::dimension2d<u32> dim = image->getDimension();
-
-
-                               if ((dim.Height %2 != 0) ||
-                                               (dim.Width %2 != 0)) {
-                                       infostream << "TextureSource::generateImage "
-                                                       << part_of_name << " size npot2 x=" << dim.Width
-                                                       << " y=" << dim.Height << std::endl;
-                               }
-                       }
-               }
-
+#ifdef __ANDROID__
+               image = Align2Npot2(image,driver);
+#endif
                if (image == NULL) {
                        if (part_of_name != "") {
                                if (part_of_name.find("_normal.png") == std::string::npos){
@@ -1284,7 +1458,16 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas
                        video::IImage *img_right =
                                generateImageFromScratch(imagename_right);
                        assert(img_top && img_left && img_right);
+#ifdef __ANDROID__
+                       assert(img_top->getDimension().Height == npot2(img_top->getDimension().Height));
+                       assert(img_top->getDimension().Width == npot2(img_top->getDimension().Width));
+
+                       assert(img_left->getDimension().Height == npot2(img_left->getDimension().Height));
+                       assert(img_left->getDimension().Width == npot2(img_left->getDimension().Width));
 
+                       assert(img_right->getDimension().Height == npot2(img_right->getDimension().Height));
+                       assert(img_right->getDimension().Width == npot2(img_right->getDimension().Width));
+#endif
                        // Create textures from images
                        video::ITexture *texture_top = driver->addTexture(
                                        (imagename_top + "__temp__").c_str(), img_top);
index 486d193fdcfa6af4801a5ade58de7878dd096800..29c6b69f2e293bb20f7c1f160f772d2d4c498886 100644 (file)
@@ -131,6 +131,25 @@ public:
 
 IWritableTextureSource* createTextureSource(IrrlichtDevice *device);
 
+#ifdef __ANDROID__
+/**
+ * @param size get next npot2 value
+ * @return npot2 value
+ */
+inline unsigned int npot2(unsigned int size)
+{
+       if (size == 0) return 0;
+       unsigned int npot = 1;
+
+       while ((size >>= 1) > 0) {
+               npot <<= 1;
+       }
+       return npot;
+}
+
+video::IImage * Align2Npot2(video::IImage * image, video::IVideoDriver* driver);
+#endif
+
 enum MaterialType{
        TILE_MATERIAL_BASIC,
        TILE_MATERIAL_ALPHA,
diff --git a/src/touchscreengui.cpp b/src/touchscreengui.cpp
new file mode 100644 (file)
index 0000000..a04b7fe
--- /dev/null
@@ -0,0 +1,690 @@
+/*
+Copyright (C) 2014 sapier
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "touchscreengui.h"
+#include "irrlichttypes.h"
+#include "irr_v2d.h"
+#include "log.h"
+#include "keycode.h"
+#include "settings.h"
+#include "gettime.h"
+#include "util/numeric.h"
+#include "porting.h"
+
+#include <iostream>
+#include <algorithm>
+
+#include <ISceneCollisionManager.h>
+
+using namespace irr::core;
+
+extern Settings *g_settings;
+
+const char** touchgui_button_imagenames = (const char*[]) {
+       "up_arrow.png",
+       "down_arrow.png",
+       "left_arrow.png",
+       "right_arrow.png",
+       "jump_btn.png",
+       "down.png",
+       "inventory_btn.png",
+       "chat_btn.png"
+};
+
+static irr::EKEY_CODE id2keycode(touch_gui_button_id id)
+{
+       std::string key = "";
+       switch (id) {
+               case forward_id:
+                       key = "forward";
+                       break;
+               case left_id:
+                       key = "left";
+                       break;
+               case right_id:
+                       key = "right";
+                       break;
+               case backward_id:
+                       key = "backward";
+                       break;
+               case jump_id:
+                       key = "jump";
+                       break;
+               case inventory_id:
+                       key = "inventory";
+                       break;
+               case chat_id:
+                       key = "chat";
+                       break;
+               case crunch_id:
+                       key = "sneak";
+                       break;
+       }
+       assert(key != "");
+       return keyname_to_keycode(g_settings->get("keymap_" + key).c_str());
+}
+
+TouchScreenGUI *g_touchscreengui;
+
+TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver):
+       m_device(device),
+       m_guienv(device->getGUIEnvironment()),
+       m_camera_yaw(0.0),
+       m_camera_pitch(0.0),
+       m_visible(false),
+       m_move_id(-1),
+       m_receiver(receiver)
+{
+       for (unsigned int i=0; i < after_last_element_id; i++) {
+               m_buttons[i].guibutton     =  0;
+               m_buttons[i].repeatcounter = -1;
+       }
+
+       m_screensize = m_device->getVideoDriver()->getScreenSize();
+}
+
+void TouchScreenGUI::loadButtonTexture(button_info* btn, const char* path)
+{
+       unsigned int tid;
+       video::ITexture *texture = m_texturesource->getTexture(path,&tid);
+       if (texture) {
+               btn->guibutton->setUseAlphaChannel(true);
+               btn->guibutton->setImage(texture);
+               btn->guibutton->setPressedImage(texture);
+               btn->guibutton->setScaleImage(true);
+               btn->guibutton->setDrawBorder(false);
+               btn->guibutton->setText(L"");
+               }
+}
+
+void TouchScreenGUI::initButton(touch_gui_button_id id, rect<s32> button_rect,
+               std::wstring caption, bool immediate_release )
+{
+
+       button_info* btn       = &m_buttons[id];
+       btn->guibutton         = m_guienv->addButton(button_rect, 0, id, caption.c_str());
+       btn->guibutton->grab();
+       btn->repeatcounter     = -1;
+       btn->keycode           = id2keycode(id);
+       btn->immediate_release = immediate_release;
+       btn->ids.clear();
+
+       loadButtonTexture(btn,touchgui_button_imagenames[id]);
+}
+
+static int getMaxControlPadSize(float density) {
+       return 200 * density * g_settings->getFloat("gui_scaling");
+}
+
+void TouchScreenGUI::init(ISimpleTextureSource* tsrc, float density)
+{
+       assert(tsrc != 0);
+
+       u32 control_pad_size =
+                       MYMIN((2 * m_screensize.Y) / 3,getMaxControlPadSize(density));
+
+       u32 button_size      = control_pad_size / 3;
+       m_visible            = true;
+       m_texturesource      = tsrc;
+       m_control_pad_rect   = rect<s32>(0, m_screensize.Y - 3 * button_size,
+                       3 * button_size, m_screensize.Y);
+       /*
+       draw control pad
+       0 1 2
+       3 4 5
+       for now only 0, 1, 2, and 4 are used
+       */
+       int number = 0;
+       for (int y = 0; y < 2; ++y)
+               for (int x = 0; x < 3; ++x, ++number) {
+                       rect<s32> button_rect(
+                                       x * button_size, m_screensize.Y - button_size * (2 - y),
+                                       (x + 1) * button_size, m_screensize.Y - button_size * (1 - y)
+                       );
+                       touch_gui_button_id id = after_last_element_id;
+                       std::wstring caption;
+                       switch (number) {
+                       case 0:
+                               id = left_id;
+                               caption = L"<";
+                               break;
+                       case 1:
+                               id = forward_id;
+                               caption = L"^";
+                               break;
+                       case 2:
+                               id = right_id;
+                               caption = L">";
+                               break;
+                       case 4:
+                               id = backward_id;
+                               caption = L"v";
+                               break;
+                       }
+                       if (id != after_last_element_id) {
+                               initButton(id, button_rect, caption, false);
+                               }
+               }
+
+       /* init inventory button */
+       initButton(inventory_id,
+                       rect<s32>(0, m_screensize.Y - (button_size/2),
+                                       (button_size/2), m_screensize.Y), L"inv", true);
+
+       /* init jump button */
+       initButton(jump_id,
+                       rect<s32>(m_screensize.X-(1.75*button_size),
+                                       m_screensize.Y - (0.5*button_size),
+                                       m_screensize.X-(0.25*button_size),
+                                       m_screensize.Y),
+                       L"x",false);
+
+       /* init crunch button */
+       initButton(crunch_id,
+                       rect<s32>(m_screensize.X-(3.25*button_size),
+                                       m_screensize.Y - (0.5*button_size),
+                                       m_screensize.X-(1.75*button_size),
+                                       m_screensize.Y),
+                       L"H",false);
+
+       /* init chat button */
+       initButton(chat_id,
+                       rect<s32>(m_screensize.X-(1.5*button_size), 0,
+                                       m_screensize.X, button_size),
+                       L"Chat", true);
+}
+
+touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y)
+{
+       IGUIElement* rootguielement = m_guienv->getRootGUIElement();
+
+       if (rootguielement != NULL) {
+               gui::IGUIElement *element =
+                               rootguielement->getElementFromPoint(core::position2d<s32>(x,y));
+
+               if (element) {
+                       for (unsigned int i=0; i < after_last_element_id; i++) {
+                               if (element == m_buttons[i].guibutton) {
+                                       return (touch_gui_button_id) i;
+                               }
+                       }
+               }
+       }
+       return after_last_element_id;
+}
+
+touch_gui_button_id TouchScreenGUI::getButtonID(int eventID)
+{
+       for (unsigned int i=0; i < after_last_element_id; i++) {
+               button_info* btn = &m_buttons[i];
+
+               std::vector<int>::iterator id =
+                               std::find(btn->ids.begin(),btn->ids.end(), eventID);
+
+               if (id != btn->ids.end())
+                       return (touch_gui_button_id) i;
+       }
+
+       return after_last_element_id;
+}
+
+bool TouchScreenGUI::isHUDButton(const SEvent &event)
+{
+       // check if hud item is pressed
+       for (std::map<int,rect<s32> >::iterator iter = m_hud_rects.begin();
+                       iter != m_hud_rects.end(); iter++) {
+               if (iter->second.isPointInside(
+                               v2s32(event.TouchInput.X,
+                                               event.TouchInput.Y)
+                       )) {
+                       if ( iter->first < 8) {
+                               SEvent* translated = new SEvent();
+                               memset(translated,0,sizeof(SEvent));
+                               translated->EventType = irr::EET_KEY_INPUT_EVENT;
+                               translated->KeyInput.Key         = (irr::EKEY_CODE) (KEY_KEY_1 + iter->first);
+                               translated->KeyInput.Control     = false;
+                               translated->KeyInput.Shift       = false;
+                               translated->KeyInput.PressedDown = true;
+                               m_receiver->OnEvent(*translated);
+                               m_hud_ids[event.TouchInput.ID]   = translated->KeyInput.Key;
+                               delete translated;
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+bool TouchScreenGUI::isReleaseHUDButton(int eventID)
+{
+       std::map<int,irr::EKEY_CODE>::iterator iter = m_hud_ids.find(eventID);
+
+       if (iter != m_hud_ids.end()) {
+               SEvent* translated = new SEvent();
+               memset(translated,0,sizeof(SEvent));
+               translated->EventType            = irr::EET_KEY_INPUT_EVENT;
+               translated->KeyInput.Key         = iter->second;
+               translated->KeyInput.PressedDown = false;
+               translated->KeyInput.Control     = false;
+               translated->KeyInput.Shift       = false;
+               m_receiver->OnEvent(*translated);
+               m_hud_ids.erase(iter);
+               delete translated;
+               return true;
+       }
+       return false;
+}
+
+void TouchScreenGUI::ButtonEvent(touch_gui_button_id button,
+               int eventID, bool action)
+{
+       button_info* btn = &m_buttons[button];
+       SEvent* translated = new SEvent();
+       memset(translated,0,sizeof(SEvent));
+       translated->EventType            = irr::EET_KEY_INPUT_EVENT;
+       translated->KeyInput.Key         = btn->keycode;
+       translated->KeyInput.Control     = false;
+       translated->KeyInput.Shift       = false;
+       translated->KeyInput.Char        = 0;
+
+       /* add this event */
+       if (action) {
+               assert(std::find(btn->ids.begin(),btn->ids.end(), eventID) == btn->ids.end());
+
+               btn->ids.push_back(eventID);
+
+               if (btn->ids.size() > 1) return;
+
+               btn->repeatcounter = 0;
+               translated->KeyInput.PressedDown = true;
+               translated->KeyInput.Key = btn->keycode;
+               m_receiver->OnEvent(*translated);
+       }
+       /* remove event */
+       if ((!action) || (btn->immediate_release)) {
+
+               std::vector<int>::iterator pos =
+                               std::find(btn->ids.begin(),btn->ids.end(), eventID);
+               /* has to be in touch list */
+               assert(pos != btn->ids.end());
+               btn->ids.erase(pos);
+
+               if (btn->ids.size() > 0)  { return; }
+
+               translated->KeyInput.PressedDown = false;
+               btn->repeatcounter               = -1;
+               m_receiver->OnEvent(*translated);
+       }
+       delete translated;
+}
+
+void TouchScreenGUI::translateEvent(const SEvent &event)
+{
+       if (!m_visible) {
+               infostream << "TouchScreenGUI::translateEvent got event but not visible?!" << std::endl;
+               return;
+       }
+
+       if (event.EventType != EET_TOUCH_INPUT_EVENT) {
+               return;
+       }
+
+       if (event.TouchInput.Event == ETIE_PRESSED_DOWN) {
+
+               /* add to own copy of eventlist ...
+                * android would provide this information but irrlicht guys don't
+                * wanna design a efficient interface
+                */
+               id_status toadd;
+               toadd.id = event.TouchInput.ID;
+               toadd.X  = event.TouchInput.X;
+               toadd.Y  = event.TouchInput.Y;
+               m_known_ids.push_back(toadd);
+
+               int eventID = event.TouchInput.ID;
+
+               touch_gui_button_id button =
+                               getButtonID(event.TouchInput.X, event.TouchInput.Y);
+
+               /* handle button events */
+               if (button != after_last_element_id) {
+                       ButtonEvent(button,eventID,true);
+               }
+               else if (isHUDButton(event))
+               {
+                       /* already handled in isHUDButton() */
+               }
+               /* handle non button events */
+               else {
+                       /* if we don't already have a moving point make this the moving one */
+                       if (m_move_id == -1) {
+                               m_move_id                  = event.TouchInput.ID;
+                               m_move_has_really_moved    = false;
+                               m_move_downtime            = getTimeMs();
+                               m_move_downlocation        = v2s32(event.TouchInput.X, event.TouchInput.Y);
+                               m_move_sent_as_mouse_event = false;
+                       }
+               }
+
+               m_pointerpos[event.TouchInput.ID] = v2s32(event.TouchInput.X, event.TouchInput.Y);
+       }
+       else if (event.TouchInput.Event == ETIE_LEFT_UP) {
+               verbosestream << "Up event for pointerid: " << event.TouchInput.ID << std::endl;
+
+               touch_gui_button_id button = getButtonID(event.TouchInput.ID);
+
+               /* handle button events */
+               if (button != after_last_element_id) {
+                       ButtonEvent(button,event.TouchInput.ID,false);
+               }
+               /* handle hud button events */
+               else if (isReleaseHUDButton(event.TouchInput.ID)) {
+                       /* nothing to do here */
+               }
+               /* handle the point used for moving view */
+               else if (event.TouchInput.ID == m_move_id) {
+                       m_move_id = -1;
+
+                       /* if this pointer issued a mouse event issue symmetric release here */
+                       if (m_move_sent_as_mouse_event) {
+                               SEvent* translated = new SEvent;
+                               memset(translated,0,sizeof(SEvent));
+                               translated->EventType               = EET_MOUSE_INPUT_EVENT;
+                               translated->MouseInput.X            = m_move_downlocation.X;
+                               translated->MouseInput.Y            = m_move_downlocation.Y;
+                               translated->MouseInput.Shift        = false;
+                               translated->MouseInput.Control      = false;
+                               translated->MouseInput.ButtonStates = 0;
+                               translated->MouseInput.Event        = EMIE_LMOUSE_LEFT_UP;
+                               m_receiver->OnEvent(*translated);
+                               delete translated;
+                       }
+                       else {
+                               /* do double tap detection */
+                               doubleTapDetection();
+                       }
+               }
+               else {
+                       infostream
+                               << "TouchScreenGUI::translateEvent released unknown button: "
+                               << event.TouchInput.ID << std::endl;
+               }
+
+               for (std::vector<id_status>::iterator iter = m_known_ids.begin();
+                               iter != m_known_ids.end(); iter++) {
+                       if (iter->id == event.TouchInput.ID) {
+                               m_known_ids.erase(iter);
+                               break;
+                       }
+               }
+       }
+       else {
+               assert(event.TouchInput.Event == ETIE_MOVED);
+               int move_idx = event.TouchInput.ID;
+
+               if (m_pointerpos[event.TouchInput.ID] ==
+                               v2s32(event.TouchInput.X, event.TouchInput.Y)) {
+                       return;
+               }
+
+               if (m_move_id != -1) {
+                       if ((event.TouchInput.ID == m_move_id) &&
+                               (!m_move_sent_as_mouse_event)) {
+
+                               double distance = sqrt(
+                                               (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) *
+                                               (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) +
+                                               (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y) *
+                                               (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y));
+
+                               if ((distance > g_settings->getU16("touchscreen_threshold")) ||
+                                               (m_move_has_really_moved)) {
+                                       m_move_has_really_moved = true;
+                                       s32 X = event.TouchInput.X;
+                                       s32 Y = event.TouchInput.Y;
+
+                                       // update camera_yaw and camera_pitch
+                                       s32 dx = X - m_pointerpos[event.TouchInput.ID].X;
+                                       s32 dy = Y - m_pointerpos[event.TouchInput.ID].Y;
+
+                                       /* adapt to similar behaviour as pc screen */
+                                       double d         = g_settings->getFloat("mouse_sensitivity") *4;
+                                       double old_yaw   = m_camera_yaw;
+                                       double old_pitch = m_camera_pitch;
+
+                                       m_camera_yaw   -= dx * d;
+                                       m_camera_pitch  = MYMIN(MYMAX( m_camera_pitch + (dy * d),-180),180);
+
+                                       while (m_camera_yaw < 0)
+                                               m_camera_yaw += 360;
+
+                                       while (m_camera_yaw > 360)
+                                               m_camera_yaw -= 360;
+
+                                       // update shootline
+                                       m_shootline = m_device
+                                                       ->getSceneManager()
+                                                       ->getSceneCollisionManager()
+                                                       ->getRayFromScreenCoordinates(v2s32(X, Y));
+                                       m_pointerpos[event.TouchInput.ID] = v2s32(X, Y);
+                               }
+                       }
+                       else if ((event.TouchInput.ID == m_move_id) &&
+                                       (m_move_sent_as_mouse_event)) {
+                               m_shootline = m_device
+                                               ->getSceneManager()
+                                               ->getSceneCollisionManager()
+                                               ->getRayFromScreenCoordinates(
+                                                               v2s32(event.TouchInput.X,event.TouchInput.Y));
+                       }
+               }
+               else {
+                       handleChangedButton(event);
+               }
+       }
+}
+
+void TouchScreenGUI::handleChangedButton(const SEvent &event)
+{
+       for (unsigned int i = 0; i < after_last_element_id; i++) {
+
+               if (m_buttons[i].ids.empty()) {
+                       continue;
+               }
+               for(std::vector<int>::iterator iter = m_buttons[i].ids.begin();
+                               iter != m_buttons[i].ids.end(); iter++) {
+
+                       if (event.TouchInput.ID == *iter) {
+
+                               int current_button_id =
+                                               getButtonID(event.TouchInput.X, event.TouchInput.Y);
+
+                               if (current_button_id == i) {
+                                       continue;
+                               }
+
+                               /* remove old button */
+                               ButtonEvent((touch_gui_button_id) i,*iter,false);
+
+                               if (current_button_id == after_last_element_id) {
+                                       return;
+                               }
+                               ButtonEvent((touch_gui_button_id) current_button_id,*iter,true);
+                               return;
+
+                       }
+               }
+       }
+
+       int current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y);
+
+       if (current_button_id == after_last_element_id) {
+               return;
+       }
+
+       button_info* btn = &m_buttons[current_button_id];
+       if (std::find(btn->ids.begin(),btn->ids.end(), event.TouchInput.ID) == btn->ids.end()) {
+               ButtonEvent((touch_gui_button_id) current_button_id,event.TouchInput.ID,true);
+       }
+
+}
+
+bool TouchScreenGUI::doubleTapDetection()
+{
+       m_key_events[0].down_time = m_key_events[1].down_time;
+       m_key_events[0].x         = m_key_events[1].x;
+       m_key_events[0].y         = m_key_events[1].y;
+       m_key_events[1].down_time = m_move_downtime;
+       m_key_events[1].x         = m_move_downlocation.X;
+       m_key_events[1].y         = m_move_downlocation.Y;
+
+       u32 delta = porting::getDeltaMs(m_key_events[0].down_time,getTimeMs());
+       if (delta > 400)
+               return false;
+
+       double distance = sqrt(
+                       (m_key_events[0].x - m_key_events[1].x) * (m_key_events[0].x - m_key_events[1].x) +
+                       (m_key_events[0].y - m_key_events[1].y) * (m_key_events[0].y - m_key_events[1].y));
+
+
+       if (distance >(20 + g_settings->getU16("touchscreen_threshold")))
+               return false;
+
+       SEvent* translated = new SEvent();
+       memset(translated,0,sizeof(SEvent));
+       translated->EventType               = EET_MOUSE_INPUT_EVENT;
+       translated->MouseInput.X            = m_key_events[0].x;
+       translated->MouseInput.Y            = m_key_events[0].y;
+       translated->MouseInput.Shift        = false;
+       translated->MouseInput.Control      = false;
+       translated->MouseInput.ButtonStates = EMBSM_RIGHT;
+
+       // update shootline
+       m_shootline = m_device
+                       ->getSceneManager()
+                       ->getSceneCollisionManager()
+                       ->getRayFromScreenCoordinates(v2s32(m_key_events[0].x, m_key_events[0].y));
+
+       translated->MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
+       verbosestream << "TouchScreenGUI::translateEvent right click press" << std::endl;
+       m_receiver->OnEvent(*translated);
+
+       translated->MouseInput.ButtonStates = 0;
+       translated->MouseInput.Event        = EMIE_RMOUSE_LEFT_UP;
+       verbosestream << "TouchScreenGUI::translateEvent right click release" << std::endl;
+       m_receiver->OnEvent(*translated);
+       delete translated;
+       return true;
+
+}
+
+TouchScreenGUI::~TouchScreenGUI()
+{
+       for (unsigned int i=0; i < after_last_element_id; i++) {
+               button_info* btn = &m_buttons[i];
+               if (btn->guibutton != 0) {
+                       btn->guibutton->drop();
+                       btn->guibutton = NULL;
+               }
+       }
+}
+
+void TouchScreenGUI::step(float dtime)
+{
+       /* simulate keyboard repeats */
+       for (unsigned int i=0; i < after_last_element_id; i++) {
+               button_info* btn = &m_buttons[i];
+
+               if (btn->ids.size() > 0) {
+                       btn->repeatcounter += dtime;
+
+                       if (btn->repeatcounter < 0.2) continue;
+
+                       btn->repeatcounter              = 0;
+                       SEvent translated;
+                       memset(&translated,0,sizeof(SEvent));
+                       translated.EventType            = irr::EET_KEY_INPUT_EVENT;
+                       translated.KeyInput.Key         = btn->keycode;
+                       translated.KeyInput.PressedDown = false;
+                       m_receiver->OnEvent(translated);
+
+                       translated.KeyInput.PressedDown = true;
+                       m_receiver->OnEvent(translated);
+               }
+       }
+
+       /* if a new placed pointer isn't moved for some time start digging */
+       if ((m_move_id != -1) &&
+                       (!m_move_has_really_moved) &&
+                       (!m_move_sent_as_mouse_event)) {
+
+               u32 delta = porting::getDeltaMs(m_move_downtime,getTimeMs());
+
+               if (delta > MIN_DIG_TIME_MS) {
+                       m_shootline = m_device
+                                       ->getSceneManager()
+                                       ->getSceneCollisionManager()
+                                       ->getRayFromScreenCoordinates(
+                                                       v2s32(m_move_downlocation.X,m_move_downlocation.Y));
+
+                       SEvent translated;
+                       memset(&translated,0,sizeof(SEvent));
+                       translated.EventType               = EET_MOUSE_INPUT_EVENT;
+                       translated.MouseInput.X            = m_move_downlocation.X;
+                       translated.MouseInput.Y            = m_move_downlocation.Y;
+                       translated.MouseInput.Shift        = false;
+                       translated.MouseInput.Control      = false;
+                       translated.MouseInput.ButtonStates = EMBSM_LEFT;
+                       translated.MouseInput.Event        = EMIE_LMOUSE_PRESSED_DOWN;
+                       verbosestream << "TouchScreenGUI::step left click press" << std::endl;
+                       m_receiver->OnEvent(translated);
+                       m_move_sent_as_mouse_event         = true;
+               }
+       }
+}
+
+void TouchScreenGUI::resetHud()
+{
+       m_hud_rects.clear();
+}
+
+void TouchScreenGUI::registerHudItem(int index, const rect<s32> &rect)
+{
+       m_hud_rects[index] = rect;
+}
+
+void TouchScreenGUI::Toggle(bool visible)
+{
+       m_visible = visible;
+       for (unsigned int i=0; i < after_last_element_id; i++) {
+               button_info* btn = &m_buttons[i];
+               if (btn->guibutton != 0) {
+                       btn->guibutton->setVisible(visible);
+               }
+       }
+}
+
+void TouchScreenGUI::Hide()
+{
+       Toggle(false);
+}
+
+void TouchScreenGUI::Show()
+{
+       Toggle(true);
+}
diff --git a/src/touchscreengui.h b/src/touchscreengui.h
new file mode 100644 (file)
index 0000000..8dc4820
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+Copyright (C) 2014 sapier
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef TOUCHSCREENGUI_HEADER
+#define TOUCHSCREENGUI_HEADER
+
+#include <IGUIEnvironment.h>
+#include <IGUIButton.h>
+#include <IEventReceiver.h>
+
+#include <vector>
+#include <map>
+
+#include "game.h"
+#include "tile.h"
+
+using namespace irr;
+using namespace irr::core;
+using namespace irr::gui;
+
+typedef enum {
+       forward_id = 0,
+       backward_id,
+       left_id,
+       right_id,
+       jump_id,
+       crunch_id,
+       inventory_id,
+       chat_id,
+       after_last_element_id
+} touch_gui_button_id;
+
+#define MIN_DIG_TIME_MS 500
+#define MAX_TOUCH_COUNT 64
+
+extern const char** touchgui_button_imagenames;
+
+class TouchScreenGUI
+{
+public:
+       TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver);
+       ~TouchScreenGUI();
+
+       void translateEvent(const SEvent &event);
+
+       void init(ISimpleTextureSource* tsrc,float density);
+
+       double getYaw() { return m_camera_yaw; }
+       double getPitch() { return m_camera_pitch; }
+       line3d<f32> getShootline() { return m_shootline; }
+
+       void step(float dtime);
+       void resetHud();
+       void registerHudItem(int index, const rect<s32> &rect);
+       void Toggle(bool visible);
+
+       void Hide();
+       void Show();
+
+private:
+       IrrlichtDevice*         m_device;
+       IGUIEnvironment*        m_guienv;
+       IEventReceiver*         m_receiver;
+       ISimpleTextureSource*   m_texturesource;
+       v2u32                   m_screensize;
+       std::map<int,rect<s32> > m_hud_rects;
+       std::map<int,irr::EKEY_CODE> m_hud_ids;
+       bool                    m_visible; // is the gui visible
+
+       /* value in degree */
+       double                  m_camera_yaw;
+       double                  m_camera_pitch;
+
+       line3d<f32>             m_shootline;
+
+       rect<s32>               m_control_pad_rect;
+
+       int                     m_move_id;
+       bool                    m_move_has_really_moved;
+       s32                     m_move_downtime;
+       bool                    m_move_sent_as_mouse_event;
+       v2s32                   m_move_downlocation;
+
+       struct button_info {
+               float            repeatcounter;
+               irr::EKEY_CODE   keycode;
+               std::vector<int> ids;
+               IGUIButton*      guibutton;
+               bool             immediate_release;
+       };
+
+       button_info m_buttons[after_last_element_id];
+
+       /* gui button detection */
+       touch_gui_button_id getButtonID(s32 x, s32 y);
+
+       /* gui button by eventID */
+       touch_gui_button_id getButtonID(int eventID);
+
+       /* check if a button has changed */
+       void handleChangedButton(const SEvent &event);
+
+       /* initialize a button */
+       void initButton(touch_gui_button_id id, rect<s32> button_rect,
+                       std::wstring caption, bool immediate_release );
+
+       /* load texture */
+       void loadButtonTexture(button_info* btn, const char* path);
+
+       struct id_status{
+               int id;
+               int X;
+               int Y;
+       };
+
+       /* vector to store known ids and their initial touch positions*/
+       std::vector<id_status> m_known_ids;
+
+       /* handle a button event */
+       void ButtonEvent(touch_gui_button_id bID, int eventID, bool action);
+
+       /* handle pressed hud buttons */
+       bool isHUDButton(const SEvent &event);
+
+       /* handle released hud buttons */
+       bool isReleaseHUDButton(int eventID);
+
+       /* handle double taps */
+       bool doubleTapDetection();
+
+       /* doubleclick detection variables */
+       struct key_event {
+               unsigned int down_time;
+               s32 x;
+               s32 y;
+       };
+
+       /* array for saving last known position of a pointer */
+       v2s32 m_pointerpos[MAX_TOUCH_COUNT];
+
+       /* array for doubletap detection */
+       key_event m_key_events[2];
+};
+extern TouchScreenGUI *g_touchscreengui;
+#endif
index a3888c9ce9d16767059437e27721ee12b759e02a..363a15e650414675035c4497d03ede54e95aa1b2 100644 (file)
@@ -29,6 +29,58 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "../hex.h"
 #include "../porting.h"
 
+#ifdef __ANDROID__
+const wchar_t* wide_chars = L" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
+
+int wctomb(char *s, wchar_t wc)
+{
+       for (unsigned int j = 0; j < (sizeof(wide_chars)/sizeof(wchar_t));j++) {
+               if (wc == wide_chars[j]) {
+                       *s = (char) (j+32);
+                       return 1;
+               }
+               else if (wc == L'\n') {
+                       *s = '\n';
+                       return 1;
+               }
+       }
+       return -1;
+}
+
+int mbtowc(wchar_t *pwc, const char *s, size_t n)
+{
+       std::wstring intermediate = narrow_to_wide(s);
+
+       if (intermediate.length() > 0) {
+               *pwc = intermediate[0];
+               return 1;
+       }
+       else {
+               return -1;
+       }
+}
+
+std::wstring narrow_to_wide(const std::string& mbs) {
+       size_t wcl = mbs.size();
+
+       std::wstring retval = L"";
+
+       for (unsigned int i = 0; i < wcl; i++) {
+               if (((unsigned char) mbs[i] >31) &&
+                ((unsigned char) mbs[i] < 127)) {
+
+                       retval += wide_chars[(unsigned char) mbs[i] -32];
+               }
+               //handle newline
+               else if (mbs[i] == '\n') {
+                       retval += L'\n';
+               }
+       }
+
+       return retval;
+}
+#else
+
 std::wstring narrow_to_wide(const std::string& mbs)
 {
        size_t wcl = mbs.size();
@@ -40,6 +92,35 @@ std::wstring narrow_to_wide(const std::string& mbs)
        return *wcs;
 }
 
+#endif
+
+#ifdef __ANDROID__
+std::string wide_to_narrow(const std::wstring& wcs) {
+       size_t mbl = wcs.size()*4;
+
+       std::string retval = "";
+       for (unsigned int i = 0; i < wcs.size(); i++) {
+               wchar_t char1 = (wchar_t) wcs[i];
+
+               if (char1 == L'\n') {
+                       retval += '\n';
+                       continue;
+               }
+
+               for (unsigned int j = 0; j < wcslen(wide_chars);j++) {
+                       wchar_t char2 = (wchar_t) wide_chars[j];
+
+                       if (char1 == char2) {
+                               char toadd = (j+32);
+                               retval += toadd;
+                               break;
+                       }
+               }
+       }
+
+       return retval;
+}
+#else
 std::string wide_to_narrow(const std::wstring& wcs)
 {
        size_t mbl = wcs.size()*4;
@@ -53,6 +134,8 @@ std::string wide_to_narrow(const std::wstring& wcs)
        return *mbs;
 }
 
+#endif
+
 // Get an sha-1 hash of the player's name combined with
 // the password entered. That's what the server uses as
 // their password. (Exception : if the password field is
index e5de8a61edc29aa56b1e6910034d05a7dcf49887..83b0a4c40b349f899aba2e7c97c68c0688dc9677 100644 (file)
@@ -20,19 +20,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "version.h"
 #include "config.h"
 
-#ifdef USE_CMAKE_CONFIG_H
-
-#include "cmake_config_githash.h"
-
 const char *minetest_version_simple = CMAKE_VERSION_STRING;
 const char *minetest_version_hash = CMAKE_VERSION_GITHASH;
+
+#ifdef USE_CMAKE_CONFIG_H
 const char *minetest_build_info =
                "VER=" CMAKE_VERSION_GITHASH " " CMAKE_BUILD_INFO;
-
+#elif defined(ANDROID)
+const char *minetest_build_info = "android jni";
 #else
-
-const char *minetest_version_simple = "unknown";
-const char *minetest_version_hash = "unknown";
 const char *minetest_build_info = "non-cmake";
 
 #endif
diff --git a/textures/base/pack/chat_btn.png b/textures/base/pack/chat_btn.png
new file mode 100644 (file)
index 0000000..a452bd0
Binary files /dev/null and b/textures/base/pack/chat_btn.png differ
diff --git a/textures/base/pack/down.png b/textures/base/pack/down.png
new file mode 100644 (file)
index 0000000..7d13857
Binary files /dev/null and b/textures/base/pack/down.png differ
diff --git a/textures/base/pack/down_arrow.png b/textures/base/pack/down_arrow.png
new file mode 100644 (file)
index 0000000..7b34b1b
Binary files /dev/null and b/textures/base/pack/down_arrow.png differ
diff --git a/textures/base/pack/inventory_btn.png b/textures/base/pack/inventory_btn.png
new file mode 100644 (file)
index 0000000..58f892b
Binary files /dev/null and b/textures/base/pack/inventory_btn.png differ
diff --git a/textures/base/pack/jump_btn.png b/textures/base/pack/jump_btn.png
new file mode 100644 (file)
index 0000000..573fd1a
Binary files /dev/null and b/textures/base/pack/jump_btn.png differ
diff --git a/textures/base/pack/ladder_down.png b/textures/base/pack/ladder_down.png
new file mode 100644 (file)
index 0000000..e3cb7db
Binary files /dev/null and b/textures/base/pack/ladder_down.png differ
diff --git a/textures/base/pack/left_arrow.png b/textures/base/pack/left_arrow.png
new file mode 100644 (file)
index 0000000..30ec327
Binary files /dev/null and b/textures/base/pack/left_arrow.png differ
diff --git a/textures/base/pack/right_arrow.png b/textures/base/pack/right_arrow.png
new file mode 100644 (file)
index 0000000..04594b6
Binary files /dev/null and b/textures/base/pack/right_arrow.png differ
diff --git a/textures/base/pack/up_arrow.png b/textures/base/pack/up_arrow.png
new file mode 100644 (file)
index 0000000..070529a
Binary files /dev/null and b/textures/base/pack/up_arrow.png differ