From 4e6395e07af0e9d140bdb11eba09f4f32066a85c Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 24 Dec 2020 19:04:36 +0100 Subject: [PATCH 1/8] dist: Fix line of code that mysteriously disappeared --- direct/src/dist/commands.py | 1 + 1 file changed, 1 insertion(+) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index 26f33a3e07..d29f9dad87 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -995,6 +995,7 @@ class build_apps(setuptools.Command): not pattern.pattern.endswith('/**'): continue + pattern_dir = p3d.Filename(pattern.pattern).get_dirname() if abspath.startswith(pattern_dir + '/'): return True From d2a74811818477fd991354cd200cac8147d8e4fe Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 24 Dec 2020 20:46:13 +0100 Subject: [PATCH 2/8] dist: handle SyntaxError in imported module --- direct/src/dist/FreezeTool.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/direct/src/dist/FreezeTool.py b/direct/src/dist/FreezeTool.py index a6a5ab63b9..99413d5922 100644 --- a/direct/src/dist/FreezeTool.py +++ b/direct/src/dist/FreezeTool.py @@ -2410,6 +2410,9 @@ class PandaModuleFinder(modulefinder.ModuleFinder): except ImportError as msg: self.msg(2, "ImportError:", str(msg)) self._add_badmodule(name, caller) + except SyntaxError as msg: + self.msg(2, "SyntaxError:", str(msg)) + self._add_badmodule(name, caller) else: if fromlist: for sub in fromlist: From 4b7c11059d3284b91cacd7f754f443764af8fcbe Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 24 Dec 2020 20:46:33 +0100 Subject: [PATCH 3/8] dist: Ignore some missing modules --- direct/src/dist/FreezeTool.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/direct/src/dist/FreezeTool.py b/direct/src/dist/FreezeTool.py index 99413d5922..71b664271e 100644 --- a/direct/src/dist/FreezeTool.py +++ b/direct/src/dist/FreezeTool.py @@ -649,7 +649,9 @@ okMissing = [ 'EasyDialogs', 'SOCKS', 'ic', 'rourl2path', 'termios', 'vms_lib', 'OverrideFrom23._Res', 'email', 'email.Utils', 'email.Generator', 'email.Iterators', '_subprocess', 'gestalt', 'java.lang', - 'direct.extensions_native.extensions_darwin', + 'direct.extensions_native.extensions_darwin', '_manylinux', + 'collections.Iterable', 'collections.Mapping', 'collections.MutableMapping', + 'collections.Sequence', 'numpy_distutils', ] class Freezer: From 030bdd1d34dbe5da79a1171b084c5c336fba85b6 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 24 Dec 2020 20:48:09 +0100 Subject: [PATCH 4/8] dist: Work around stdlib bug causing erroneous missing module warnings --- direct/src/dist/FreezeTool.py | 70 +++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/direct/src/dist/FreezeTool.py b/direct/src/dist/FreezeTool.py index 71b664271e..1977d98ae1 100644 --- a/direct/src/dist/FreezeTool.py +++ b/direct/src/dist/FreezeTool.py @@ -2428,6 +2428,76 @@ class PandaModuleFinder(modulefinder.ModuleFinder): self.msg(2, "ImportError:", str(msg)) self._add_badmodule(fullname, caller) + def scan_code(self, co, m): + code = co.co_code + # This was renamed to scan_opcodes in Python 3.6 + if hasattr(self, 'scan_opcodes_25'): + scanner = self.scan_opcodes_25 + else: + scanner = self.scan_opcodes + + for what, args in scanner(co): + if what == "store": + name, = args + m.globalnames[name] = 1 + elif what in ("import", "absolute_import"): + fromlist, name = args + have_star = 0 + if fromlist is not None: + if "*" in fromlist: + have_star = 1 + fromlist = [f for f in fromlist if f != "*"] + if what == "absolute_import": level = 0 + else: level = -1 + self._safe_import_hook(name, m, fromlist, level=level) + if have_star: + # We've encountered an "import *". If it is a Python module, + # the code has already been parsed and we can suck out the + # global names. + mm = None + if m.__path__: + # At this point we don't know whether 'name' is a + # submodule of 'm' or a global module. Let's just try + # the full name first. + mm = self.modules.get(m.__name__ + "." + name) + if mm is None: + mm = self.modules.get(name) + if mm is not None: + m.globalnames.update(mm.globalnames) + m.starimports.update(mm.starimports) + if mm.__code__ is None: + m.starimports[name] = 1 + else: + m.starimports[name] = 1 + elif what == "relative_import": + level, fromlist, name = args + parent = self.determine_parent(m, level=level) + if name: + self._safe_import_hook(name, m, fromlist, level=level) + else: + self._safe_import_hook(parent.__name__, None, fromlist, level=0) + + if fromlist and "*" in fromlist: + if name: + mm = self.modules.get(parent.__name__ + "." + name) + else: + mm = self.modules.get(parent.__name__) + + if mm is not None: + m.globalnames.update(mm.globalnames) + m.starimports.update(mm.starimports) + if mm.__code__ is None: + m.starimports[name] = 1 + else: + m.starimports[name] = 1 + else: + # We don't expect anything else from the generator. + raise RuntimeError(what) + + for c in co.co_consts: + if isinstance(c, type(co)): + self.scan_code(c, m) + def find_module(self, name, path=None, parent=None): """ Finds a module with the indicated name on the given search path (or self.path if None). Returns a tuple like (fp, path, stuff), where From 84a6e900af1fc8af4b9872dfa7db512a741aa6f4 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 25 Dec 2020 00:24:01 +0100 Subject: [PATCH 5/8] dist: Fix error using build_apps in Python 2.7 --- direct/src/dist/commands.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index d29f9dad87..c610a9c679 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -459,7 +459,8 @@ class build_apps(setuptools.Command): abi_tag += 'u' whldir = os.path.join(whlcache, '_'.join((platform, abi_tag))) - os.makedirs(whldir, exist_ok=True) + if not os.path.isdir(whldir): + os.makedirs(whldir) # Remove any .zip files. These are built from a VCS and block for an # interactive prompt on subsequent downloads. From 5fd6436df571a822611f78e8e446f06dc9e20eff Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 25 Dec 2020 00:25:33 +0100 Subject: [PATCH 6/8] doc: Update release notes for 1.10.8 [skip ci] --- doc/ReleaseNotes | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index 0c5428e652..376bbc8f63 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -4,7 +4,7 @@ Recommended maintenance release. * Support building for macOS 11 "Big Sur" and "Apple Silicon" (arm64) * Fix a memory leak, particularly noticeable with multithreaded pipeline (#1077) -* Fix problem building Windows binaries using deployment system +* Fix error with build_apps not working with certain versions of Python * Fix DirectEntry/PGEntry flickering in the multithreaded pipeline (#1070) * Fix sounds resuming on reactivation if stop() was called while inactive (#559) * Collision traverser now releases GIL during traversal (#1033) @@ -16,6 +16,7 @@ Recommended maintenance release. * Fix compilation error with Bullet 2.90+ * Assimp library was updated in Windows thirdparty packages (#1020) * libCg is now shipped as library instead of framework on macOS (#1079) +* Fix some erroneous warnings about missing modules in build_apps * Add warnings to build_apps when forgetting dependencies in requirements.txt * Add experimental TextureStage::M_emission mode * Add experimental p3d_TextureNormal, p3d_TextureEmission, etc. GLSL inputs From 55d43a1d57369dd689db523156b0801eff8d0a05 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 26 Dec 2020 12:45:13 +0100 Subject: [PATCH 7/8] device: Fix macOS crash when unplugging device in threaded pipeline Fixes #1082 --- doc/ReleaseNotes | 1 + panda/src/device/inputDeviceManager.cxx | 2 ++ 2 files changed, 3 insertions(+) diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index 376bbc8f63..83fe5b1bd5 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -4,6 +4,7 @@ Recommended maintenance release. * Support building for macOS 11 "Big Sur" and "Apple Silicon" (arm64) * Fix a memory leak, particularly noticeable with multithreaded pipeline (#1077) +* Fix crash on macOS when unplugging device with threading active (#1082) * Fix error with build_apps not working with certain versions of Python * Fix DirectEntry/PGEntry flickering in the multithreaded pipeline (#1070) * Fix sounds resuming on reactivation if stop() was called while inactive (#559) diff --git a/panda/src/device/inputDeviceManager.cxx b/panda/src/device/inputDeviceManager.cxx index 8cd470e271..9f4f81c833 100644 --- a/panda/src/device/inputDeviceManager.cxx +++ b/panda/src/device/inputDeviceManager.cxx @@ -111,6 +111,8 @@ add_device(InputDevice *device) { */ void InputDeviceManager:: remove_device(InputDevice *device) { + // We need to hold a reference, since remove_device decrements the refcount. + PT(InputDevice) device_ref = device; { LightMutexHolder holder(_lock); _connected_devices.remove_device(device); From d5c2dc64478feb8824c3f076f81a837e7808f859 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 26 Dec 2020 13:59:19 +0100 Subject: [PATCH 8/8] dgui: Add setTextPos, etc. aliases for setPos setPos will be deprecated in a future version. See #1041 --- direct/src/gui/OnscreenText.py | 106 +++++++++++++++++--- tests/gui/test_OnscreenText.py | 171 +++++++++++++++++++++++++++++++++ 2 files changed, 265 insertions(+), 12 deletions(-) create mode 100644 tests/gui/test_OnscreenText.py diff --git a/direct/src/gui/OnscreenText.py b/direct/src/gui/OnscreenText.py index 1a35152f40..13a284622f 100644 --- a/direct/src/gui/OnscreenText.py +++ b/direct/src/gui/OnscreenText.py @@ -313,43 +313,98 @@ class OnscreenText(NodePath): text = property(getText, setText) + def setTextX(self, x): + self.setTextPos(x, self.__pos[1]) + def setX(self, x): - self.setPos(x, self.__pos[1]) + """ + .. deprecated:: 1.11.0 + Use `.setTextX()` method instead. + """ + self.setTextPos(x, self.__pos[1]) + + def setTextY(self, y): + self.setTextPos(self.__pos[0], y) def setY(self, y): - self.setPos(self.__pos[0], y) + """ + .. deprecated:: 1.11.0 + Use `.setTextY()` method instead. + """ + self.setTextPos(self.__pos[0], y) + + def setTextPos(self, x, y=None): + """ + Position the onscreen text in 2d screen space + """ + if y is None: + self.__pos = tuple(x) + else: + self.__pos = (x, y) + self.updateTransformMat() + + def getTextPos(self): + return self.__pos + + text_pos = property(getTextPos, setTextPos) def setPos(self, x, y): """setPos(self, float, float) Position the onscreen text in 2d screen space + + .. deprecated:: 1.11.0 + Use `.setTextPos()` method or `.text_pos` property instead. """ self.__pos = (x, y) self.updateTransformMat() def getPos(self): + """ + .. deprecated:: 1.11.0 + Use `.getTextPos()` method or `.text_pos` property instead. + """ return self.__pos - pos = property(getPos, setPos) + pos = property(getPos) + + def setTextR(self, r): + """setTextR(self, float) + Rotates the text around the screen's normal. + """ + self.__roll = -r + self.updateTransformMat() + + def getTextR(self): + return -self.__roll + + text_r = property(getTextR, setTextR) def setRoll(self, roll): """setRoll(self, float) - Rotate the onscreen text around the screen's normal + Rotate the onscreen text around the screen's normal. + + .. deprecated:: 1.11.0 + Use ``setTextR(-roll)`` instead (note the negated sign). """ self.__roll = roll self.updateTransformMat() def getRoll(self): + """ + .. deprecated:: 1.11.0 + Use ``-getTextR()`` instead (note the negated sign). + """ return self.__roll roll = property(getRoll, setRoll) - def setScale(self, sx, sy = None): - """setScale(self, float, float) + def setTextScale(self, sx, sy = None): + """setTextScale(self, float, float) Scale the text in 2d space. You may specify either a single uniform scale, or two scales, or a tuple of two scales. """ - if sy == None: + if sy is None: if isinstance(sx, tuple): self.__scale = sx else: @@ -358,6 +413,38 @@ class OnscreenText(NodePath): self.__scale = (sx, sy) self.updateTransformMat() + def getTextScale(self): + return self.__scale + + text_scale = property(getTextScale, setTextScale) + + def setScale(self, sx, sy = None): + """setScale(self, float, float) + Scale the text in 2d space. You may specify either a single + uniform scale, or two scales, or a tuple of two scales. + + .. deprecated:: 1.11.0 + Use `.setTextScale()` method or `.text_scale` property instead. + """ + + if sy is None: + if isinstance(sx, tuple): + self.__scale = sx + else: + self.__scale = (sx, sx) + else: + self.__scale = (sx, sy) + self.updateTransformMat() + + def getScale(self): + """ + .. deprecated:: 1.11.0 + Use `.getTextScale()` method or `.text_scale` property instead. + """ + return self.__scale + + scale = property(getScale, setScale) + def updateTransformMat(self): assert(isinstance(self.textNode, TextNode)) mat = ( @@ -367,11 +454,6 @@ class OnscreenText(NodePath): ) self.textNode.setTransform(mat) - def getScale(self): - return self.__scale - - scale = property(getScale, setScale) - def setWordwrap(self, wordwrap): self.__wordwrap = wordwrap diff --git a/tests/gui/test_OnscreenText.py b/tests/gui/test_OnscreenText.py new file mode 100644 index 0000000000..2ec74b9f14 --- /dev/null +++ b/tests/gui/test_OnscreenText.py @@ -0,0 +1,171 @@ +from direct.gui.OnscreenText import OnscreenText + + +def test_onscreentext_text_pos(): + text = OnscreenText(pos=(1, 2)) + assert text['pos'] == (1, 2) + assert text.pos == (1, 2) + assert text.getPos() == (1, 2) + assert text.text_pos == (1, 2) + assert text.getTextPos() == (1, 2) + assert text.get_pos() == (0, 0, 0) + + text.setTextPos(3, 4) + assert text['pos'] == (3, 4) + assert text.pos == (3, 4) + assert text.getPos() == (3, 4) + assert text.text_pos == (3, 4) + assert text.getTextPos() == (3, 4) + assert text.get_pos() == (0, 0, 0) + + text.text_pos = (7, 8) + assert text['pos'] == (7, 8) + assert text.pos == (7, 8) + assert text.getPos() == (7, 8) + assert text.text_pos == (7, 8) + assert text.getTextPos() == (7, 8) + assert text.get_pos() == (0, 0, 0) + + text.setPos(9, 10) + assert text['pos'] == (9, 10) + assert text.pos == (9, 10) + assert text.getPos() == (9, 10) + assert text.text_pos == (9, 10) + assert text.getTextPos() == (9, 10) + assert text.get_pos() == (0, 0, 0) + + text['pos'] = (11, 12) + assert text['pos'] == (11, 12) + assert text.pos == (11, 12) + assert text.getPos() == (11, 12) + assert text.text_pos == (11, 12) + assert text.getTextPos() == (11, 12) + assert text.get_pos() == (0, 0, 0) + + +def test_onscreentext_node_pos(): + text = OnscreenText() + + text.set_pos(1, 2, 3) + assert text['pos'] == (0, 0) + assert text.pos == (0, 0) + assert text.getPos() == (0, 0) + assert text.text_pos == (0, 0) + assert text.getTextPos() == (0, 0) + assert text.get_pos() == (1, 2, 3) + + +def test_onscreentext_text_roll(): + text = OnscreenText(roll=1) + assert text['roll'] == 1 + assert text.roll == 1 + assert text.getRoll() == 1 + assert text.text_r == -1 + assert text.getTextR() == -1 + assert text.get_r() == 0 + + text.setTextR(2) + assert text['roll'] == -2 + assert text.roll == -2 + assert text.getRoll() == -2 + assert text.text_r == 2 + assert text.getTextR() == 2 + assert text.get_r() == 0 + + text.text_r = 3 + assert text['roll'] == -3 + assert text.roll == -3 + assert text.getRoll() == -3 + assert text.text_r == 3 + assert text.getTextR() == 3 + assert text.get_r() == 0 + + text.setRoll(4) + assert text['roll'] == 4 + assert text.roll == 4 + assert text.getRoll() == 4 + assert text.text_r == -4 + assert text.getTextR() == -4 + assert text.get_r() == 0 + + text['roll'] = 5 + assert text['roll'] == 5 + assert text.roll == 5 + assert text.getRoll() == 5 + assert text.text_r == -5 + assert text.getTextR() == -5 + assert text.get_r() == 0 + + +def test_onscreentext_node_roll(): + text = OnscreenText() + + text.set_r(45) + assert text['roll'] == 0 + assert text.roll == 0 + assert text.getRoll() == 0 + assert text.text_r == 0 + assert text.getTextR() == 0 + assert text.get_r() == 45 + + +def test_onscreentext_text_scale(): + text = OnscreenText(scale=(1, 2)) + assert text['scale'] == (1, 2) + assert text.scale == (1, 2) + assert text.getScale() == (1, 2) + assert text.text_scale == (1, 2) + assert text.getTextScale() == (1, 2) + assert text.get_scale() == (1, 1, 1) + + text.setTextScale(3, 4) + assert text['scale'] == (3, 4) + assert text.scale == (3, 4) + assert text.getScale() == (3, 4) + assert text.text_scale == (3, 4) + assert text.getTextScale() == (3, 4) + assert text.get_scale() == (1, 1, 1) + + text.text_scale = (7, 8) + assert text['scale'] == (7, 8) + assert text.scale == (7, 8) + assert text.getScale() == (7, 8) + assert text.text_scale == (7, 8) + assert text.getTextScale() == (7, 8) + assert text.get_scale() == (1, 1, 1) + + text.setScale(9, 10) + assert text['scale'] == (9, 10) + assert text.scale == (9, 10) + assert text.getScale() == (9, 10) + assert text.text_scale == (9, 10) + assert text.getTextScale() == (9, 10) + assert text.get_scale() == (1, 1, 1) + + text['scale'] = (11, 12) + assert text['scale'] == (11, 12) + assert text.scale == (11, 12) + assert text.getScale() == (11, 12) + assert text.text_scale == (11, 12) + assert text.getTextScale() == (11, 12) + assert text.get_scale() == (1, 1, 1) + + text.scale = 13 + assert text['scale'] == (13, 13) + assert text.scale == (13, 13) + assert text.getScale() == (13, 13) + assert text.text_scale == (13, 13) + assert text.getTextScale() == (13, 13) + assert text.get_scale() == (1, 1, 1) + + +def test_onscreentext_node_scale(): + text = OnscreenText() + + text.set_scale(1, 2, 3) + assert text['scale'] == (0.07, 0.07) + assert text.scale == (0.07, 0.07) + assert text.getScale() == (0.07, 0.07) + assert text.text_scale == (0.07, 0.07) + assert text.getTextScale() == (0.07, 0.07) + assert text.get_scale() == (1, 2, 3)