diff --git a/README.md b/README.md index 59d4570c0e..36300f0a87 100644 --- a/README.md +++ b/README.md @@ -185,16 +185,19 @@ from the Play Store. Many of the dependencies can be installed by running the following command in the Termux shell: ```bash -pkg install python-dev termux-tools ndk-stl ndk-sysroot clang libvorbis-dev libopus-dev opusfile-dev openal-soft-dev freetype-dev harfbuzz-dev libpng-dev ecj4.6 dx patchelf aapt apksigner libcrypt-dev +pkg install python ndk-sysroot clang bison freetype harfbuzz libpng eigen openal-soft opusfile libvorbis assimp libopus ecj dx patchelf aapt apksigner libcrypt openssl pkg-config ``` -Then, you can build and install the .apk right away using these commands: +Then, you can build the .apk using this command: ```bash python makepanda/makepanda.py --everything --target android-21 --no-tiff --installer -xdg-open panda3d.apk ``` +You can install the generated panda3d.apk by browsing to the panda3d folder +using a file manager. You may need to copy it to `/sdcard` to be able to +access it from other apps. + To launch a Python program from Termux, you can use the `run_python.sh` script inside the `panda/src/android` directory. It will launch Python in a separate activity, load it with the Python script you passed as argument, and use a diff --git a/direct/src/actor/Actor.py b/direct/src/actor/Actor.py index f99a18bbdf..11d4990351 100644 --- a/direct/src/actor/Actor.py +++ b/direct/src/actor/Actor.py @@ -5,6 +5,7 @@ __all__ = ['Actor'] from panda3d.core import * from panda3d.core import Loader as PandaLoader from direct.showbase.DirectObject import DirectObject +from direct.showbase.Loader import Loader from direct.directnotify import DirectNotifyGlobal @@ -1886,6 +1887,9 @@ class Actor(DirectObject, NodePath): else: loaderOptions.setFlags(loaderOptions.getFlags() | LoaderOptions.LFReportErrors) + # Ensure that custom Python loader hooks are initialized. + Loader._loadPythonFileTypes() + # Pass loaderOptions to specify that we want to # get the skeleton model. This only matters to model # files (like .mb) for which we can choose to extract diff --git a/direct/src/dcparser/dcPackerInterface.I b/direct/src/dcparser/dcPackerInterface.I index 160cc4fdd9..c9daac2c7e 100644 --- a/direct/src/dcparser/dcPackerInterface.I +++ b/direct/src/dcparser/dcPackerInterface.I @@ -253,8 +253,9 @@ do_unpack_int64(const char *buffer) { ((uint64_t)(unsigned char)buffer[4] << 32) | ((uint64_t)(unsigned char)buffer[5] << 40) | ((uint64_t)(unsigned char)buffer[6] << 48) | - ((int64_t)(signed char)buffer[7] << 54)); + ((int64_t)(signed char)buffer[7] << 56)); } + /** * */ @@ -295,10 +296,9 @@ do_unpack_uint64(const char *buffer) { ((uint64_t)(unsigned char)buffer[4] << 32) | ((uint64_t)(unsigned char)buffer[5] << 40) | ((uint64_t)(unsigned char)buffer[6] << 48) | - ((int64_t)(signed char)buffer[7] << 54)); + ((uint64_t)(unsigned char)buffer[7] << 56)); } - /** * */ diff --git a/direct/src/dcparser/dcPackerInterface.h b/direct/src/dcparser/dcPackerInterface.h index 0b2f81d2fe..1f4ac4a47f 100644 --- a/direct/src/dcparser/dcPackerInterface.h +++ b/direct/src/dcparser/dcPackerInterface.h @@ -16,7 +16,6 @@ #include "dcbase.h" #include "dcSubatomicType.h" -#include "vector_uchar.h" class DCFile; class DCField; diff --git a/direct/src/dcparser/dcParserDefs.h b/direct/src/dcparser/dcParserDefs.h index f6f7aacd71..9c7c589753 100644 --- a/direct/src/dcparser/dcParserDefs.h +++ b/direct/src/dcparser/dcParserDefs.h @@ -16,7 +16,6 @@ #include "dcbase.h" #include "dcSubatomicType.h" -#include "vector_uchar.h" class DCFile; class DCClass; diff --git a/direct/src/dcparser/dcbase.h b/direct/src/dcparser/dcbase.h index 64ef117158..3a3fbbd13e 100644 --- a/direct/src/dcparser/dcbase.h +++ b/direct/src/dcparser/dcbase.h @@ -32,6 +32,7 @@ #include "pvector.h" #include "pmap.h" #include "pset.h" +#include "vector_uchar.h" #else // WITHIN_PANDA @@ -81,6 +82,7 @@ #define nassertr_always(condition, return_value) assert(condition) #define nassertv(condition) assert(condition) #define nassertv_always(condition) assert(condition) +#define nassert_raise(message) {std::cerr << message << std::endl; abort();} // Panda defines a special Filename class. We'll use an ordinary string // instead. @@ -97,8 +99,10 @@ typedef std::string Filename; #define pvector std::vector #define pmap std::map #define pset std::set +#define vector_uchar std::vector #include +#include typedef std::ifstream pifstream; typedef std::ofstream pofstream; diff --git a/direct/src/dcparser/hashGenerator.h b/direct/src/dcparser/hashGenerator.h index 472e858cb0..7779dda735 100644 --- a/direct/src/dcparser/hashGenerator.h +++ b/direct/src/dcparser/hashGenerator.h @@ -16,7 +16,6 @@ #include "dcbase.h" #include "primeNumberGenerator.h" -#include "vector_uchar.h" /** * This class generates an arbitrary hash number from a sequence of ints. diff --git a/direct/src/gui/DirectGuiGlobals.py b/direct/src/gui/DirectGuiGlobals.py index 59fd474900..dce1d38b18 100644 --- a/direct/src/gui/DirectGuiGlobals.py +++ b/direct/src/gui/DirectGuiGlobals.py @@ -70,6 +70,8 @@ B3PRESS = PGButton.getPressPrefix() + MouseButton.three().getName() + '-' B1RELEASE = PGButton.getReleasePrefix() + MouseButton.one().getName() + '-' B2RELEASE = PGButton.getReleasePrefix() + MouseButton.two().getName() + '-' B3RELEASE = PGButton.getReleasePrefix() + MouseButton.three().getName() + '-' +WHEELUP = PGButton.getReleasePrefix() + MouseButton.wheelUp().getName() + '-' +WHEELDOWN = PGButton.getReleasePrefix() + MouseButton.wheelDown().getName() + '-' # For DirectEntry widgets OVERFLOW = PGEntry.getOverflowPrefix() ACCEPT = PGEntry.getAcceptPrefix() + KeyboardButton.enter().getName() + '-' diff --git a/direct/src/showbase/Loader.py b/direct/src/showbase/Loader.py index f0afbc06ec..80aa5f09e2 100644 --- a/direct/src/showbase/Loader.py +++ b/direct/src/showbase/Loader.py @@ -22,6 +22,8 @@ class Loader(DirectObject): notify = directNotify.newCategory("Loader") loaderIndex = 0 + _loadedPythonFileTypes = False + class Callback: """Returned by loadModel when used asynchronously. This class is modelled after Future, and can be awaited.""" @@ -149,8 +151,7 @@ class Loader(DirectObject): Loader.loaderIndex += 1 self.accept(self.hook, self.__gotAsyncObject) - if ConfigVariableBool('loader-support-entry-points', True): - self._loadPythonFileTypes() + self._loadPythonFileTypes() def destroy(self): self.ignore(self.hook) @@ -158,7 +159,14 @@ class Loader(DirectObject): del self.base del self.loader - def _loadPythonFileTypes(self): + @classmethod + def _loadPythonFileTypes(cls): + if cls._loadedPythonFileTypes: + return + + if not ConfigVariableBool('loader-support-entry-points', True): + return + import importlib try: pkg_resources = importlib.import_module('pkg_resources') @@ -171,6 +179,8 @@ class Loader(DirectObject): for entry_point in pkg_resources.iter_entry_points('panda3d.loaders'): registry.register_deferred_type(entry_point) + cls._loadedPythonFileTypes = True + # model loading funcs def loadModel(self, modelPath, loaderOptions = None, noCache = None, allowInstance = False, okMissing = None, diff --git a/direct/src/stdpy/threading.py b/direct/src/stdpy/threading.py index 8903a2fbc0..911f35daeb 100644 --- a/direct/src/stdpy/threading.py +++ b/direct/src/stdpy/threading.py @@ -87,7 +87,7 @@ class Thread(ThreadBase): object. The wrapper is designed to emulate Python's own threading.Thread object. """ - def __init__(self, group=None, target=None, name=None, args=(), kwargs={}): + def __init__(self, group=None, target=None, name=None, args=(), kwargs={}, daemon=None): ThreadBase.__init__(self) assert group is None @@ -99,7 +99,10 @@ class Thread(ThreadBase): name = _newname() current = current_thread() - self.__dict__['daemon'] = current.daemon + if daemon is not None: + self.__dict__['daemon'] = daemon + else: + self.__dict__['daemon'] = current.daemon self.__dict__['name'] = name def call_run(): diff --git a/direct/src/stdpy/threading2.py b/direct/src/stdpy/threading2.py index 6e25f5208c..6630bb1738 100644 --- a/direct/src/stdpy/threading2.py +++ b/direct/src/stdpy/threading2.py @@ -14,6 +14,7 @@ module, and so it is therefore layered on top of Panda's thread implementation. """ import sys as _sys +import atexit as _atexit from direct.stdpy import thread as _thread from direct.stdpy.thread import stack_size, _newname, _local as local @@ -395,8 +396,12 @@ class Thread(_Verbose): # operation on/with a NoneType __exc_info = _sys.exc_info + # Set to True when the _shutdown handler is registered as atexit function. + # Protected by _active_limbo_lock. + __registered_atexit = False + def __init__(self, group=None, target=None, name=None, - args=(), kwargs=None, verbose=None): + args=(), kwargs=None, verbose=None, daemon=None): assert group is None, "group argument must be None for now" _Verbose.__init__(self, verbose) if kwargs is None: @@ -405,7 +410,10 @@ class Thread(_Verbose): self.__name = str(name or _newname()) self.__args = args self.__kwargs = kwargs - self.__daemonic = self._set_daemon() + if daemon is not None: + self.__daemonic = daemon + else: + self.__daemonic = self._set_daemon() self.__started = False self.__stopped = False self.__block = Condition(Lock()) @@ -436,6 +444,14 @@ class Thread(_Verbose): self._note("%s.start(): starting thread", self) _active_limbo_lock.acquire() _limbo[self] = self + + # If we are starting a non-daemon thread, we need to call join() on it + # when the interpreter exits. Python will call _shutdown() on the + # built-in threading module automatically, but not on our module. + if not self.__daemonic and not Thread.__registered_atexit: + _atexit.register(_shutdown) + Thread.__registered_atexit = True + _active_limbo_lock.release() _start_new_thread(self.__bootstrap, ()) self.__started = True @@ -599,6 +615,9 @@ class Thread(_Verbose): assert not self.__started, "cannot set daemon status of active thread" self.__daemonic = daemonic + name = property(getName, setName) + daemon = property(isDaemon, setDaemon) + # The timer class was contributed by Itamar Shtull-Trauring def Timer(*args, **kwargs): @@ -670,7 +689,7 @@ class _MainThread(Thread): class _DummyThread(Thread): def __init__(self): - Thread.__init__(self, name=_newname("Dummy-%d")) + Thread.__init__(self, name=_newname("Dummy-%d"), daemon=True) # Thread.__block consumes an OS-level locking primitive, which # can never be used by a _DummyThread. Since a _DummyThread diff --git a/dtool/src/interrogatedb/py_compat.h b/dtool/src/interrogatedb/py_compat.h index a0d52c7fdc..5d0f3bf4ea 100644 --- a/dtool/src/interrogatedb/py_compat.h +++ b/dtool/src/interrogatedb/py_compat.h @@ -141,16 +141,13 @@ typedef long Py_hash_t; #if PY_VERSION_HEX < 0x03080000 && !defined(_PyObject_CallNoArg) INLINE PyObject *_PyObject_CallNoArg(PyObject *func) { - static PyTupleObject empty_tuple = {PyVarObject_HEAD_INIT(nullptr, 0)}; -#ifdef Py_TRACE_REFS - _Py_AddToAllObjects((PyObject *)&empty_tuple, 0); -#endif - return PyObject_Call(func, (PyObject *)&empty_tuple, nullptr); + static PyObject *empty_tuple = PyTuple_New(0); + return PyObject_Call(func, empty_tuple, nullptr); } # define _PyObject_CallNoArg _PyObject_CallNoArg #endif -#ifndef _PyObject_FastCall +#if PY_VERSION_HEX < 0x03080000 && !defined(_PyObject_FastCall) INLINE PyObject *_PyObject_FastCall(PyObject *func, PyObject **args, Py_ssize_t nargs) { PyObject *tuple = PyTuple_New(nargs); for (Py_ssize_t i = 0; i < nargs; ++i) { diff --git a/makepanda/makepackage.py b/makepanda/makepackage.py index 966795c32e..2e383538d6 100755 --- a/makepanda/makepackage.py +++ b/makepanda/makepackage.py @@ -537,7 +537,10 @@ def MakeInstallerOSX(version, python_versions=[], **kwargs): dist.write('\n') dist.write('\n') dist.write(' Panda3D SDK %s\n' % (version)) - dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') dist.write(' %s\n' % ReadFile("doc/LICENSE")) dist.write('