Browse Source

Merge branch 'master' of github.com:panda3d/panda3d into input-overhaul

rdb 7 years ago
parent
commit
09bd662919

+ 1 - 4
dtool/src/dtoolutil/executionEnvironment.cxx

@@ -540,7 +540,6 @@ read_environment_variables() {
  */
  */
 void ExecutionEnvironment::
 void ExecutionEnvironment::
 read_args() {
 read_args() {
-#ifndef ANDROID
   // First, we need to fill in _dtool_name.  This contains the full path to
   // First, we need to fill in _dtool_name.  This contains the full path to
   // the p3dtool library.
   // the p3dtool library.
 
 
@@ -578,7 +577,7 @@ read_args() {
   }
   }
 #endif
 #endif
 
 
-#if defined(IS_FREEBSD) || defined(IS_LINUX)
+#if defined(IS_FREEBSD) || (defined(IS_LINUX) && !defined(__ANDROID__))
   // FreeBSD and Linux have a function to get the origin of a loaded library.
   // FreeBSD and Linux have a function to get the origin of a loaded library.
 
 
   char origin[PATH_MAX + 1];
   char origin[PATH_MAX + 1];
@@ -833,8 +832,6 @@ read_args() {
   }
   }
 #endif  // _WIN32
 #endif  // _WIN32
 
 
-#endif  // ANDROID
-
   if (_dtool_name.empty()) {
   if (_dtool_name.empty()) {
     _dtool_name = _binary_name;
     _dtool_name = _binary_name;
   }
   }

+ 5 - 0
dtool/src/dtoolutil/filename.cxx

@@ -48,6 +48,11 @@
 #include <unistd.h>
 #include <unistd.h>
 #endif
 #endif
 
 
+#if defined(__ANDROID__) && !defined(HAVE_LOCKF)
+// Needed for flock.
+#include <sys/file.h>
+#endif
+
 TextEncoder::Encoding Filename::_filesystem_encoding = TextEncoder::E_utf8;
 TextEncoder::Encoding Filename::_filesystem_encoding = TextEncoder::E_utf8;
 
 
 TVOLATILE AtomicAdjust::Pointer Filename::_home_directory;
 TVOLATILE AtomicAdjust::Pointer Filename::_home_directory;

+ 41 - 29
makepanda/makepanda.py

@@ -944,7 +944,7 @@ if (COMPILER=="GCC"):
 
 
     if GetTarget() == 'android':
     if GetTarget() == 'android':
         LibName("ALWAYS", '-llog')
         LibName("ALWAYS", '-llog')
-        LibName("ALWAYS", '-landroid')
+        LibName("ANDROID", '-landroid')
         LibName("JNIGRAPHICS", '-ljnigraphics')
         LibName("JNIGRAPHICS", '-ljnigraphics')
 
 
     for pkg in MAYAVERSIONS:
     for pkg in MAYAVERSIONS:
@@ -1705,8 +1705,11 @@ def CompileLink(dll, obj, opts):
 
 
     if COMPILER == "GCC":
     if COMPILER == "GCC":
         cxx = GetCXX()
         cxx = GetCXX()
-        if GetOrigExt(dll) == ".exe" and GetTarget() != 'android':
+        if GetOrigExt(dll) == ".exe":
             cmd = cxx + ' -o ' + dll + ' -L' + GetOutputDir() + '/lib -L' + GetOutputDir() + '/tmp'
             cmd = cxx + ' -o ' + dll + ' -L' + GetOutputDir() + '/lib -L' + GetOutputDir() + '/tmp'
+            if GetTarget() == "android":
+                # Necessary to work around an issue with libandroid depending on vendor libraries
+                cmd += ' -Wl,--allow-shlib-undefined'
         else:
         else:
             if (GetTarget() == "darwin"):
             if (GetTarget() == "darwin"):
                 cmd = cxx + ' -undefined dynamic_lookup'
                 cmd = cxx + ' -undefined dynamic_lookup'
@@ -1719,6 +1722,7 @@ def CompileLink(dll, obj, opts):
                 cmd += ' -o ' + dll + ' -L' + GetOutputDir() + '/lib -L' + GetOutputDir() + '/tmp'
                 cmd += ' -o ' + dll + ' -L' + GetOutputDir() + '/lib -L' + GetOutputDir() + '/tmp'
             else:
             else:
                 cmd = cxx + ' -shared'
                 cmd = cxx + ' -shared'
+                # Always set soname on Android to avoid a linker warning when loading the library.
                 if "MODULE" not in opts or GetTarget() == 'android':
                 if "MODULE" not in opts or GetTarget() == 'android':
                     cmd += " -Wl,-soname=" + os.path.basename(dll)
                     cmd += " -Wl,-soname=" + os.path.basename(dll)
                 cmd += ' -o ' + dll + ' -L' + GetOutputDir() + '/lib -L' + GetOutputDir() + '/tmp'
                 cmd += ' -o ' + dll + ' -L' + GetOutputDir() + '/lib -L' + GetOutputDir() + '/tmp'
@@ -1775,14 +1779,7 @@ def CompileLink(dll, obj, opts):
 
 
         oscmd(cmd)
         oscmd(cmd)
 
 
-        if GetTarget() == 'android':
-            # Copy the library to built/libs/$ANDROID_ABI and strip it.
-            # This is the format that Android NDK projects should use.
-            new_path = '%s/libs/%s/%s' % (GetOutputDir(), SDK["ANDROID_ABI"], os.path.basename(dll))
-            CopyFile(new_path, dll)
-            oscmd('%s --strip-unneeded %s' % (GetStrip(), BracketNameWithQuotes(new_path)))
-
-        elif (GetOptimizeOption(opts)==4 and GetTarget() == 'linux'):
+        if GetOptimizeOption(opts) == 4 and GetTarget() in ('linux', 'android'):
             oscmd(GetStrip() + " --strip-unneeded " + BracketNameWithQuotes(dll))
             oscmd(GetStrip() + " --strip-unneeded " + BracketNameWithQuotes(dll))
 
 
         os.system("chmod +x " + BracketNameWithQuotes(dll))
         os.system("chmod +x " + BracketNameWithQuotes(dll))
@@ -1891,6 +1888,25 @@ def CompileRsrc(target, src, opts):
     cmd += " " + BracketNameWithQuotes(src)
     cmd += " " + BracketNameWithQuotes(src)
     oscmd(cmd)
     oscmd(cmd)
 
 
+##########################################################################################
+#
+# CompileJava (Android only)
+#
+##########################################################################################
+
+def CompileJava(target, src, opts):
+    """Compiles a .java file into a .class file."""
+    cmd = "ecj "
+
+    optlevel = GetOptimizeOption(opts)
+    if optlevel >= 4:
+        cmd += "-debug:none "
+
+    cmd += "-cp " + GetOutputDir() + "/classes "
+    cmd += "-d " + GetOutputDir() + "/classes "
+    cmd += BracketNameWithQuotes(src)
+    oscmd(cmd)
+
 ##########################################################################################
 ##########################################################################################
 #
 #
 # FreezePy
 # FreezePy
@@ -2144,6 +2160,9 @@ def CompileAnything(target, inputs, opts, progress = None):
     elif (origsuffix==".rsrc"):
     elif (origsuffix==".rsrc"):
         ProgressOutput(progress, "Building resource object", target)
         ProgressOutput(progress, "Building resource object", target)
         return CompileRsrc(target, infile, opts)
         return CompileRsrc(target, infile, opts)
+    elif (origsuffix==".class"):
+        ProgressOutput(progress, "Building Java class", target)
+        return CompileJava(target, infile, opts)
     elif (origsuffix==".obj"):
     elif (origsuffix==".obj"):
         if (infile.endswith(".cxx")):
         if (infile.endswith(".cxx")):
             ProgressOutput(progress, "Building C++ object", target)
             ProgressOutput(progress, "Building C++ object", target)
@@ -2354,7 +2373,7 @@ def WriteConfigSettings():
         dtool_config["HAVE_CGGL"] = '1'
         dtool_config["HAVE_CGGL"] = '1'
         dtool_config["HAVE_CGDX9"] = '1'
         dtool_config["HAVE_CGDX9"] = '1'
 
 
-    if (GetTarget() != "linux"):
+    if GetTarget() not in ("linux", "android"):
         dtool_config["HAVE_PROC_SELF_EXE"] = 'UNDEF'
         dtool_config["HAVE_PROC_SELF_EXE"] = 'UNDEF'
         dtool_config["HAVE_PROC_SELF_MAPS"] = 'UNDEF'
         dtool_config["HAVE_PROC_SELF_MAPS"] = 'UNDEF'
         dtool_config["HAVE_PROC_SELF_CMDLINE"] = 'UNDEF'
         dtool_config["HAVE_PROC_SELF_CMDLINE"] = 'UNDEF'
@@ -3235,15 +3254,6 @@ if (PkgSkip("CONTRIB")==0):
     CopyAllHeaders('contrib/src/contribbase')
     CopyAllHeaders('contrib/src/contribbase')
     CopyAllHeaders('contrib/src/ai')
     CopyAllHeaders('contrib/src/ai')
 
 
-########################################################################
-#
-# Copy Java files, if applicable
-#
-########################################################################
-
-if GetTarget() == 'android':
-    CopyAllJavaSources('panda/src/android')
-
 ########################################################################
 ########################################################################
 #
 #
 # These definitions are syntactic shorthand.  They make it easy
 # These definitions are syntactic shorthand.  They make it easy
@@ -3563,7 +3573,7 @@ TargetAdd('libpandaexpress.dll', input='p3express_composite1.obj')
 TargetAdd('libpandaexpress.dll', input='p3express_composite2.obj')
 TargetAdd('libpandaexpress.dll', input='p3express_composite2.obj')
 TargetAdd('libpandaexpress.dll', input='p3pandabase_pandabase.obj')
 TargetAdd('libpandaexpress.dll', input='p3pandabase_pandabase.obj')
 TargetAdd('libpandaexpress.dll', input=COMMON_DTOOL_LIBS)
 TargetAdd('libpandaexpress.dll', input=COMMON_DTOOL_LIBS)
-TargetAdd('libpandaexpress.dll', opts=['ADVAPI', 'WINSOCK2',  'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER'])
+TargetAdd('libpandaexpress.dll', opts=['ADVAPI', 'WINSOCK2',  'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER', 'ANDROID'])
 
 
 #
 #
 # DIRECTORY: panda/src/pipeline/
 # DIRECTORY: panda/src/pipeline/
@@ -5062,6 +5072,8 @@ if (not RTDIST and not RUNTIME and PkgSkip("PVIEW")==0 and GetTarget() != 'andro
 
 
 if (not RUNTIME and GetTarget() == 'android'):
 if (not RUNTIME and GetTarget() == 'android'):
   OPTS=['DIR:panda/src/android']
   OPTS=['DIR:panda/src/android']
+  TargetAdd('org/panda3d/android/NativeIStream.class', opts=OPTS, input='NativeIStream.java')
+  TargetAdd('org/panda3d/android/PandaActivity.class', opts=OPTS, input='PandaActivity.java', dep='org/panda3d/android/NativeIStream.class')
 
 
   TargetAdd('p3android_composite1.obj', opts=OPTS, input='p3android_composite1.cxx')
   TargetAdd('p3android_composite1.obj', opts=OPTS, input='p3android_composite1.cxx')
   TargetAdd('libp3android.dll', input='p3android_composite1.obj')
   TargetAdd('libp3android.dll', input='p3android_composite1.obj')
@@ -5073,15 +5085,15 @@ if (not RUNTIME and GetTarget() == 'android'):
 
 
   if (not RTDIST and PkgSkip("PVIEW")==0):
   if (not RTDIST and PkgSkip("PVIEW")==0):
     TargetAdd('pview_pview.obj', opts=OPTS, input='pview.cxx')
     TargetAdd('pview_pview.obj', opts=OPTS, input='pview.cxx')
-    TargetAdd('pview.exe', input='android_native_app_glue.obj')
-    TargetAdd('pview.exe', input='android_main.obj')
-    TargetAdd('pview.exe', input='pview_pview.obj')
-    TargetAdd('pview.exe', input='libp3framework.dll')
+    TargetAdd('libpview.dll', input='android_native_app_glue.obj')
+    TargetAdd('libpview.dll', input='android_main.obj')
+    TargetAdd('libpview.dll', input='pview_pview.obj')
+    TargetAdd('libpview.dll', input='libp3framework.dll')
     if not PkgSkip("EGG"):
     if not PkgSkip("EGG"):
-      TargetAdd('pview.exe', input='libpandaegg.dll')
-    TargetAdd('pview.exe', input='libp3android.dll')
-    TargetAdd('pview.exe', input=COMMON_PANDA_LIBS)
-    TargetAdd('AndroidManifest.xml', opts=OPTS, input='pview_manifest.xml')
+      TargetAdd('libpview.dll', input='libpandaegg.dll')
+    TargetAdd('libpview.dll', input='libp3android.dll')
+    TargetAdd('libpview.dll', input=COMMON_PANDA_LIBS)
+    TargetAdd('libpview.dll', opts=['MODULE', 'ANDROID'])
 
 
 #
 #
 # DIRECTORY: panda/src/androiddisplay/
 # DIRECTORY: panda/src/androiddisplay/

+ 3 - 23
makepanda/makepandacore.py

@@ -1145,12 +1145,7 @@ def MakeBuildTree():
         MakeDirectory(OUTPUTDIR + "/Frameworks")
         MakeDirectory(OUTPUTDIR + "/Frameworks")
 
 
     elif GetTarget() == 'android':
     elif GetTarget() == 'android':
-        MakeDirectory(OUTPUTDIR + "/libs")
-        MakeDirectory(OUTPUTDIR + "/libs/" + ANDROID_ABI)
-        MakeDirectory(OUTPUTDIR + "/src")
-        MakeDirectory(OUTPUTDIR + "/src/org")
-        MakeDirectory(OUTPUTDIR + "/src/org/panda3d")
-        MakeDirectory(OUTPUTDIR + "/src/org/panda3d/android")
+        MakeDirectory(OUTPUTDIR + "/classes")
 
 
 ########################################################################
 ########################################################################
 #
 #
@@ -2892,14 +2887,6 @@ def CopyAllHeaders(dir, skip=[]):
             WriteBinaryFile(dstfile, ReadBinaryFile(srcfile))
             WriteBinaryFile(dstfile, ReadBinaryFile(srcfile))
             JustBuilt([dstfile], [srcfile])
             JustBuilt([dstfile], [srcfile])
 
 
-def CopyAllJavaSources(dir, skip=[]):
-    for filename in GetDirectoryContents(dir, ["*.java"], skip):
-        srcfile = dir + "/" + filename
-        dstfile = OUTPUTDIR + "/src/org/panda3d/android/" + filename
-        if (NeedsBuild([dstfile], [srcfile])):
-            WriteBinaryFile(dstfile, ReadBinaryFile(srcfile))
-            JustBuilt([dstfile], [srcfile])
-
 def CopyTree(dstdir, srcdir, omitVCS=True):
 def CopyTree(dstdir, srcdir, omitVCS=True):
     if os.path.isdir(dstdir):
     if os.path.isdir(dstdir):
         source_entries = os.listdir(srcdir)
         source_entries = os.listdir(srcdir)
@@ -3147,6 +3134,7 @@ def CalcLocation(fn, ipath):
     if fn.startswith("panda3d/") and fn.endswith(".py"):
     if fn.startswith("panda3d/") and fn.endswith(".py"):
         return OUTPUTDIR + "/" + fn
         return OUTPUTDIR + "/" + fn
 
 
+    if (fn.endswith(".class")):return OUTPUTDIR+"/classes/"+fn
     if (fn.count("/")): return fn
     if (fn.count("/")): return fn
     dllext = ""
     dllext = ""
     target = GetTarget()
     target = GetTarget()
@@ -3162,6 +3150,7 @@ def CalcLocation(fn, ipath):
     if (fn.endswith(".lxx")): return CxxFindSource(fn, ipath)
     if (fn.endswith(".lxx")): return CxxFindSource(fn, ipath)
     if (fn.endswith(".pdef")):return CxxFindSource(fn, ipath)
     if (fn.endswith(".pdef")):return CxxFindSource(fn, ipath)
     if (fn.endswith(".xml")): return CxxFindSource(fn, ipath)
     if (fn.endswith(".xml")): return CxxFindSource(fn, ipath)
+    if (fn.endswith(".java")):return CxxFindSource(fn, ipath)
     if (fn.endswith(".egg")): return OUTPUTDIR+"/models/"+fn
     if (fn.endswith(".egg")): return OUTPUTDIR+"/models/"+fn
     if (fn.endswith(".egg.pz")):return OUTPUTDIR+"/models/"+fn
     if (fn.endswith(".egg.pz")):return OUTPUTDIR+"/models/"+fn
     if (fn.endswith(".pyd")): return OUTPUTDIR+"/panda3d/"+fn[:-4]+GetExtensionSuffix()
     if (fn.endswith(".pyd")): return OUTPUTDIR+"/panda3d/"+fn[:-4]+GetExtensionSuffix()
@@ -3197,15 +3186,6 @@ def CalcLocation(fn, ipath):
         if (fn.endswith(".rsrc")):  return OUTPUTDIR+"/tmp/"+fn
         if (fn.endswith(".rsrc")):  return OUTPUTDIR+"/tmp/"+fn
         if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn
         if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn
         if (fn.endswith(".app")):   return OUTPUTDIR+"/bin/"+fn
         if (fn.endswith(".app")):   return OUTPUTDIR+"/bin/"+fn
-    elif (target == 'android'):
-        # On Android, we build the libraries into built/tmp, then copy them.
-        if (fn.endswith(".obj")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
-        if (fn.endswith(".dll")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".so"
-        if (fn.endswith(".mll")):   return OUTPUTDIR+"/plugins/"+fn
-        if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".so"
-        if (fn.endswith(".exe")):   return OUTPUTDIR+"/tmp/lib"+fn[:-4]+".so"
-        if (fn.endswith(".lib")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
-        if (fn.endswith(".ilb")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
     else:
     else:
         if (fn.endswith(".obj")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
         if (fn.endswith(".obj")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
         if (fn.endswith(".dll")):   return OUTPUTDIR+"/lib/"+fn[:-4]+".so"
         if (fn.endswith(".dll")):   return OUTPUTDIR+"/lib/"+fn[:-4]+".so"

+ 74 - 20
panda/src/android/android_main.cxx

@@ -52,48 +52,102 @@ void android_main(struct android_app* app) {
   // Fetch the path to the data directory.
   // Fetch the path to the data directory.
   jfieldID datadir_field = env->GetFieldID(appinfo_class, "dataDir", "Ljava/lang/String;");
   jfieldID datadir_field = env->GetFieldID(appinfo_class, "dataDir", "Ljava/lang/String;");
   jstring datadir = (jstring) env->GetObjectField(appinfo, datadir_field);
   jstring datadir = (jstring) env->GetObjectField(appinfo, datadir_field);
-  const char *data_path = env->GetStringUTFChars(datadir, NULL);
+  const char *data_path = env->GetStringUTFChars(datadir, nullptr);
 
 
-  Filename::_internal_data_dir = data_path;
-  android_cat.info() << "Path to data: " << data_path << "\n";
+  if (data_path != nullptr) {
+    Filename::_internal_data_dir = data_path;
+    android_cat.info() << "Path to data: " << data_path << "\n";
 
 
-  env->ReleaseStringUTFChars(datadir, data_path);
+    env->ReleaseStringUTFChars(datadir, data_path);
+  }
 
 
   // Fetch the path to the library directory.
   // Fetch the path to the library directory.
-  jfieldID libdir_field = env->GetFieldID(appinfo_class, "nativeLibraryDir", "Ljava/lang/String;");
-  jstring libdir = (jstring) env->GetObjectField(appinfo, libdir_field);
-  const char *lib_path = env->GetStringUTFChars(libdir, NULL);
-
-  string dtool_name = string(lib_path) + "/libp3dtool.so";
-  ExecutionEnvironment::set_dtool_name(dtool_name);
-  android_cat.info() << "Path to dtool: " << dtool_name << "\n";
-
-  env->ReleaseStringUTFChars(libdir, lib_path);
+  if (ExecutionEnvironment::get_dtool_name().empty()) {
+    jfieldID libdir_field = env->GetFieldID(appinfo_class, "nativeLibraryDir", "Ljava/lang/String;");
+    jstring libdir = (jstring) env->GetObjectField(appinfo, libdir_field);
+    const char *lib_path = env->GetStringUTFChars(libdir, nullptr);
+
+    if (lib_path != nullptr) {
+      string dtool_name = string(lib_path) + "/libp3dtool.so";
+      ExecutionEnvironment::set_dtool_name(dtool_name);
+      android_cat.info() << "Path to dtool: " << dtool_name << "\n";
+
+      env->ReleaseStringUTFChars(libdir, lib_path);
+    }
+  }
 
 
   // Get the path to the APK.
   // Get the path to the APK.
   jmethodID methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;");
   jmethodID methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;");
   jstring code_path = (jstring) env->CallObjectMethod(activity->clazz, methodID);
   jstring code_path = (jstring) env->CallObjectMethod(activity->clazz, methodID);
 
 
   const char* apk_path;
   const char* apk_path;
-  apk_path = env->GetStringUTFChars(code_path, NULL);
+  apk_path = env->GetStringUTFChars(code_path, nullptr);
+
+  // We're going to set this as binary name, which is better than the
+  // default (which refers to the zygote).  Or should we set it to the
+  // native library?  How do we get the path to that?
   android_cat.info() << "Path to APK: " << apk_path << "\n";
   android_cat.info() << "Path to APK: " << apk_path << "\n";
+  ExecutionEnvironment::set_binary_name(apk_path);
 
 
   // Mount the assets directory.
   // Mount the assets directory.
+  Filename apk_fn(apk_path);
   PT(VirtualFileMountAndroidAsset) asset_mount;
   PT(VirtualFileMountAndroidAsset) asset_mount;
-  asset_mount = new VirtualFileMountAndroidAsset(app->activity->assetManager, apk_path);
+  asset_mount = new VirtualFileMountAndroidAsset(app->activity->assetManager, apk_fn);
   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  vfs->mount(asset_mount, "/android_asset", 0);
+
+  Filename asset_dir(apk_fn.get_dirname(), "assets");
+  vfs->mount(asset_mount, asset_dir, 0);
 
 
   // Release the apk_path.
   // Release the apk_path.
   env->ReleaseStringUTFChars(code_path, apk_path);
   env->ReleaseStringUTFChars(code_path, apk_path);
 
 
   // Now add the asset directory to the model-path.
   // Now add the asset directory to the model-path.
-  get_model_path().append_directory("/android_asset");
+  //TODO: prevent it from adding the directory multiple times.
+  get_model_path().append_directory(asset_dir);
 
 
-  // Create bogus argc and argv, then call our main function.
-  char *argv[] = {NULL};
+  // Create bogus argc and argv for calling the main function.
+  char *argv[] = {nullptr};
   int argc = 0;
   int argc = 0;
-  main(argc, argv);
+
+  while (!app->destroyRequested) {
+    // Call the main function.  This will not return until the app is done.
+    android_cat.info() << "Calling main()\n";
+    main(argc, argv);
+
+    if (app->destroyRequested) {
+      // The app closed responding to a destroy request.
+      break;
+    }
+
+    // Ask Android to clean up the activity.
+    android_cat.info() << "Exited from main(), finishing activity\n";
+    ANativeActivity_finish(activity);
+
+    // We still need to keep an event loop going until Android gives us leave
+    // to end the process.
+    int looper_id;
+    int events;
+    struct android_poll_source *source;
+    while ((looper_id = ALooper_pollAll(-1, nullptr, &events, (void**)&source)) >= 0) {
+      // Process this event, but intercept application command events.
+      if (looper_id == LOOPER_ID_MAIN) {
+        int8_t cmd = android_app_read_cmd(app);
+        android_app_pre_exec_cmd(app, cmd);
+        android_app_post_exec_cmd(app, cmd);
+
+        // I don't think we can get a resume command after we call finish(),
+        // but let's handle it just in case.
+        if (cmd == APP_CMD_RESUME ||
+            cmd == APP_CMD_DESTROY) {
+          break;
+        }
+      } else if (source != nullptr) {
+        source->process(app, source);
+      }
+    }
+  }
+
+  android_cat.info() << "Destroy requested, exiting from android_main\n";
 
 
   // Detach the thread before exiting.
   // Detach the thread before exiting.
   activity->vm->DetachCurrentThread();
   activity->vm->DetachCurrentThread();

+ 89 - 6
panda/src/androiddisplay/androidGraphicsWindow.cxx

@@ -44,7 +44,8 @@ AndroidGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
                       int flags,
                       int flags,
                       GraphicsStateGuardian *gsg,
                       GraphicsStateGuardian *gsg,
                       GraphicsOutput *host) :
                       GraphicsOutput *host) :
-  GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
+  GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host),
+  _mouse_button_state(0)
 {
 {
   AndroidGraphicsPipe *android_pipe;
   AndroidGraphicsPipe *android_pipe;
   DCAST_INTO_V(android_pipe, _pipe);
   DCAST_INTO_V(android_pipe, _pipe);
@@ -246,6 +247,13 @@ close_window() {
   }
   }
 
 
   GraphicsWindow::close_window();
   GraphicsWindow::close_window();
+
+  nassertv(_app != nullptr);
+  if (_app->userData == this) {
+    _app->userData = nullptr;
+    _app->onAppCmd = nullptr;
+    _app->onInputEvent = nullptr;
+  }
 }
 }
 
 
 /**
 /**
@@ -526,10 +534,29 @@ handle_motion_event(const AInputEvent *event) {
   int32_t action = AMotionEvent_getAction(event);
   int32_t action = AMotionEvent_getAction(event);
   action &= AMOTION_EVENT_ACTION_MASK;
   action &= AMOTION_EVENT_ACTION_MASK;
 
 
-  if (action == AMOTION_EVENT_ACTION_DOWN) {
-    _input_devices[0].button_down(MouseButton::one());
-  } else if (action == AMOTION_EVENT_ACTION_UP) {
-    _input_devices[0].button_up(MouseButton::one());
+  if (action == AMOTION_EVENT_ACTION_DOWN ||
+      action == AMOTION_EVENT_ACTION_UP) {
+    // The up event doesn't let us know which button is up, so we need to
+    // keep track of the button state ourselves.
+    int32_t button_state = AMotionEvent_getButtonState(event);
+    int32_t changed = _mouse_button_state ^ button_state;
+    if (changed != 0) {
+      if (changed & AMOTION_EVENT_BUTTON_PRIMARY) {
+        if (button_state & AMOTION_EVENT_BUTTON_PRIMARY) {
+          _input_devices[0].button_down(MouseButton::one());
+        } else {
+          _input_devices[0].button_up(MouseButton::one());
+        }
+      }
+      if (changed & AMOTION_EVENT_BUTTON_SECONDARY) {
+        if (button_state & AMOTION_EVENT_BUTTON_SECONDARY) {
+          _input_devices[0].button_down(MouseButton::three());
+        } else {
+          _input_devices[0].button_up(MouseButton::three());
+        }
+      }
+      _mouse_button_state = button_state;
+    }
   }
   }
 
 
   float x = AMotionEvent_getX(event, 0) - _app->contentRect.left;
   float x = AMotionEvent_getX(event, 0) - _app->contentRect.left;
@@ -668,7 +695,7 @@ map_button(int32_t keycode) {
     case AKEYCODE_ENTER:
     case AKEYCODE_ENTER:
       return KeyboardButton::enter();
       return KeyboardButton::enter();
     case AKEYCODE_DEL:
     case AKEYCODE_DEL:
-      return KeyboardButton::del();
+      return KeyboardButton::backspace();
     case AKEYCODE_GRAVE:
     case AKEYCODE_GRAVE:
       return KeyboardButton::ascii_key('`');
       return KeyboardButton::ascii_key('`');
     case AKEYCODE_MINUS:
     case AKEYCODE_MINUS:
@@ -696,6 +723,7 @@ map_button(int32_t keycode) {
     case AKEYCODE_PLUS:
     case AKEYCODE_PLUS:
       return KeyboardButton::ascii_key('+');
       return KeyboardButton::ascii_key('+');
     case AKEYCODE_MENU:
     case AKEYCODE_MENU:
+      return KeyboardButton::menu();
     case AKEYCODE_NOTIFICATION:
     case AKEYCODE_NOTIFICATION:
     case AKEYCODE_SEARCH:
     case AKEYCODE_SEARCH:
     case AKEYCODE_MEDIA_PLAY_PAUSE:
     case AKEYCODE_MEDIA_PLAY_PAUSE:
@@ -727,6 +755,61 @@ map_button(int32_t keycode) {
     case AKEYCODE_BUTTON_START:
     case AKEYCODE_BUTTON_START:
     case AKEYCODE_BUTTON_SELECT:
     case AKEYCODE_BUTTON_SELECT:
     case AKEYCODE_BUTTON_MODE:
     case AKEYCODE_BUTTON_MODE:
+      break;
+    case AKEYCODE_ESCAPE:
+      return KeyboardButton::escape();
+    case AKEYCODE_FORWARD_DEL:
+      return KeyboardButton::del();
+    case AKEYCODE_CTRL_LEFT:
+      return KeyboardButton::lcontrol();
+    case AKEYCODE_CTRL_RIGHT:
+      return KeyboardButton::rcontrol();
+    case AKEYCODE_CAPS_LOCK:
+      return KeyboardButton::caps_lock();
+    case AKEYCODE_SCROLL_LOCK:
+      return KeyboardButton::scroll_lock();
+    case AKEYCODE_META_LEFT:
+      return KeyboardButton::lmeta();
+    case AKEYCODE_META_RIGHT:
+      return KeyboardButton::rmeta();
+    case AKEYCODE_FUNCTION:
+      break;
+    case AKEYCODE_SYSRQ:
+      return KeyboardButton::print_screen();
+    case AKEYCODE_BREAK:
+      return KeyboardButton::pause();
+    case AKEYCODE_MOVE_HOME:
+      return KeyboardButton::home();
+    case AKEYCODE_MOVE_END:
+      return KeyboardButton::end();
+    case AKEYCODE_INSERT:
+      return KeyboardButton::insert();
+    case AKEYCODE_F1:
+      return KeyboardButton::f1();
+    case AKEYCODE_F2:
+      return KeyboardButton::f2();
+    case AKEYCODE_F3:
+      return KeyboardButton::f3();
+    case AKEYCODE_F4:
+      return KeyboardButton::f4();
+    case AKEYCODE_F5:
+      return KeyboardButton::f5();
+    case AKEYCODE_F6:
+      return KeyboardButton::f6();
+    case AKEYCODE_F7:
+      return KeyboardButton::f7();
+    case AKEYCODE_F8:
+      return KeyboardButton::f8();
+    case AKEYCODE_F9:
+      return KeyboardButton::f9();
+    case AKEYCODE_F10:
+      return KeyboardButton::f10();
+    case AKEYCODE_F11:
+      return KeyboardButton::f11();
+    case AKEYCODE_F12:
+      return KeyboardButton::f12();
+    case AKEYCODE_NUM_LOCK:
+      return KeyboardButton::num_lock();
     default:
     default:
       break;
       break;
   }
   }

+ 2 - 0
panda/src/androiddisplay/androidGraphicsWindow.h

@@ -71,6 +71,8 @@ private:
   EGLDisplay _egl_display;
   EGLDisplay _egl_display;
   EGLSurface _egl_surface;
   EGLSurface _egl_surface;
 
 
+  int32_t _mouse_button_state;
+
   const ARect *rect;
   const ARect *rect;
 
 
 public:
 public:

+ 2 - 0
panda/src/audiotraits/config_fmodAudio.cxx

@@ -51,6 +51,8 @@ init_libFmodAudio() {
   FmodAudioManager::init_type();
   FmodAudioManager::init_type();
   FmodAudioSound::init_type();
   FmodAudioSound::init_type();
 
 
+  AudioManager::register_AudioManager_creator(&Create_FmodAudioManager);
+
   PandaSystem *ps = PandaSystem::get_global_ptr();
   PandaSystem *ps = PandaSystem::get_global_ptr();
   ps->add_system("FMOD");
   ps->add_system("FMOD");
   ps->add_system("audio");
   ps->add_system("audio");

+ 1 - 1
panda/src/audiotraits/config_fmodAudio.h

@@ -24,7 +24,7 @@ NotifyCategoryDecl(fmodAudio, EXPCL_FMOD_AUDIO, EXPTP_FMOD_AUDIO);
 
 
 extern ConfigVariableInt fmod_audio_preload_threshold;
 extern ConfigVariableInt fmod_audio_preload_threshold;
 
 
-extern EXPCL_FMOD_AUDIO void init_libFmodAudio();
+extern "C" EXPCL_FMOD_AUDIO void init_libFmodAudio();
 extern "C" EXPCL_FMOD_AUDIO Create_AudioManager_proc *get_audio_manager_func_fmod_audio();
 extern "C" EXPCL_FMOD_AUDIO Create_AudioManager_proc *get_audio_manager_func_fmod_audio();
 
 
 #endif // CONFIG_FMODAUDIO_H
 #endif // CONFIG_FMODAUDIO_H

+ 3 - 3
panda/src/bullet/bulletBoxShape.cxx

@@ -64,9 +64,9 @@ make_from_solid(const CollisionBox *solid) {
   LPoint3 p0 = solid->get_min();
   LPoint3 p0 = solid->get_min();
   LPoint3 p1 = solid->get_max();
   LPoint3 p1 = solid->get_max();
 
 
-  LVecBase3 extents(p1.get_x() - p0.get_x() / 2.0,
-                     p1.get_y() - p0.get_y() / 2.0,
-                     p1.get_z() - p0.get_z() / 2.0);
+  LVecBase3 extents((p1.get_x() - p0.get_x()) / 2.0,
+                    (p1.get_y() - p0.get_y()) / 2.0,
+                    (p1.get_z() - p0.get_z()) / 2.0);
 
 
   return new BulletBoxShape(extents);
   return new BulletBoxShape(extents);
 }
 }

+ 3 - 1
panda/src/bullet/bulletDebugNode.cxx

@@ -31,8 +31,10 @@ PStatCollector BulletDebugNode::_pstat_debug("App:Bullet:DoPhysics:Debug");
  *
  *
  */
  */
 BulletDebugNode::
 BulletDebugNode::
-BulletDebugNode(const char *name) : PandaNode(name), _debug_stale(true) {
+BulletDebugNode(const char *name) : PandaNode(name) {
 
 
+  _debug_stale = false;
+  _debug_world = nullptr;
   _wireframe = true;
   _wireframe = true;
   _constraints = true;
   _constraints = true;
   _bounds = false;
   _bounds = false;

+ 1 - 0
panda/src/bullet/bulletWorld.cxx

@@ -134,6 +134,7 @@ void BulletWorld::
 clear_debug_node() {
 clear_debug_node() {
   if (_debug != nullptr) {
   if (_debug != nullptr) {
     LightMutexHolder holder(_debug->_lock);
     LightMutexHolder holder(_debug->_lock);
+    _debug->_debug_stale = false;
     _debug->_debug_world = nullptr;
     _debug->_debug_world = nullptr;
     _world->setDebugDrawer(nullptr);
     _world->setDebugDrawer(nullptr);
     _debug = nullptr;
     _debug = nullptr;

+ 5 - 0
panda/src/gobj/vertexDataSaveFile.cxx

@@ -23,6 +23,11 @@
 #include <errno.h>
 #include <errno.h>
 #endif  // _WIN32
 #endif  // _WIN32
 
 
+#if defined(__ANDROID__) && !defined(HAVE_LOCKF)
+// Needed for flock.
+#include <sys/file.h>
+#endif
+
 /**
 /**
  *
  *
  */
  */

+ 2 - 2
panda/src/net/connectionManager.cxx

@@ -26,7 +26,7 @@
 #elif defined(WIN32_VC) || defined(WIN64_VC)
 #elif defined(WIN32_VC) || defined(WIN64_VC)
 #include <winsock2.h>  // For gethostname()
 #include <winsock2.h>  // For gethostname()
 #include <Iphlpapi.h> // For GetAdaptersAddresses()
 #include <Iphlpapi.h> // For GetAdaptersAddresses()
-#elif defined(ANDROID)
+#elif defined(__ANDROID__)
 #include <net/if.h>
 #include <net/if.h>
 #else
 #else
 #include <net/if.h>
 #include <net/if.h>
@@ -538,7 +538,7 @@ scan_interfaces() {
     PANDA_FREE_ARRAY(addresses);
     PANDA_FREE_ARRAY(addresses);
   }
   }
 
 
-#elif defined(ANDROID)
+#elif defined(__ANDROID__)
   // TODO: implementation using netlink_socket?
   // TODO: implementation using netlink_socket?
 
 
 #else  // WIN32_VC
 #else  // WIN32_VC