diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..f8d79052bf --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +open_collective: panda3d + diff --git a/.gitignore b/.gitignore index 0b40b42f79..de3e59aef4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,11 +4,12 @@ /targetroot/ /dstroot/ -# Core dumps +# Core dumps and traces core core.* vgcore.* *.core +*.trace # Editor files/directories *.save @@ -26,6 +27,7 @@ vgcore.* /+DESC /+MANIFEST /pkg-plist +/debug.ks # Produced installer/executables /*.exe @@ -36,6 +38,7 @@ vgcore.* /*.dmg /*.whl /*.txz +/*.apk # CMake /build/ diff --git a/.travis.yml b/.travis.yml index 2c4efe8a65..ee9b09d37e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,7 +44,6 @@ install: - $PYTHONV -m pip install pytest script: - $PYTHONV makepanda/makepanda.py --everything --git-commit $TRAVIS_COMMIT $FLAGS --threads 4 - - test -n "$SKIP_TESTS" || LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV makepanda/test_imports.py - test -n "$SKIP_TESTS" || LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV -m pytest -v tests notifications: irc: @@ -54,3 +53,8 @@ notifications: on_failure: always use_notice: true skip_join: false + webhooks: + urls: + - https://www.panda3d.org/webhooks/travis-ci.php + on_success: change + on_failure: always diff --git a/BACKERS.md b/BACKERS.md new file mode 100644 index 0000000000..d3dcf17084 --- /dev/null +++ b/BACKERS.md @@ -0,0 +1,35 @@ +# Panda3D Backers + +This is a list of all the people who are contributing financially to Panda3D. If you'd like to join them, visit [our campaign on OpenCollective](https://opencollective.com/panda3d)! + +## Gold Sponsors + +![Gold Sponsors](https://opencollective.com/panda3d/tiers/gold-sponsor.svg?avatarHeight=48&width=600) + +* [tcdude](https://opencollective.com/tizilogic) + +## Bronze Sponsors + +![Bronze Sponsors](https://opencollective.com/panda3d/tiers/bronze-sponsor.svg?avatarHeight=48&width=600) + +* [Mitchell Stokes](https://opencollective.com/mitchell-stokes) +* [Daniel Stokes](https://opencollective.com/daniel-stokes) +* [David Rose](https://opencollective.com/david-rose) +* [Carnetsoft](https://cs-driving-simulator.com/) + +## Benefactors + +![Benefactors](https://opencollective.com/panda3d/tiers/benefactor.svg?avatarHeight=48&width=600) + +* Sam Edwards +* Max Voss + +## Enthusiasts + +![Benefactors](https://opencollective.com/panda3d/tiers/enthusiast.svg?avatarHeight=48&width=600) + +* Eric Thomson + +## Backers + +![Backers](https://opencollective.com/panda3d/tiers/backer.svg?avatarHeight=48&width=600) diff --git a/README.md b/README.md index a9de69e51b..1a5af7deb8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ [![Build Status](https://travis-ci.org/panda3d/panda3d.svg?branch=master)](https://travis-ci.org/panda3d/panda3d) +[![OpenCollective](https://opencollective.com/panda3d/backers/badge.svg)](https://opencollective.com/panda3d) +[![OpenCollective](https://opencollective.com/panda3d/sponsors/badge.svg)](https://opencollective.com/panda3d) - + Panda3D ======= @@ -8,12 +10,12 @@ Panda3D Panda3D is a game engine, a framework for 3D rendering and game development for Python and C++ programs. Panda3D is open-source and free for any purpose, including commercial ventures, thanks to its -[liberal license](https://www.panda3d.org/license.php). To learn more about -Panda3D's capabilities, visit the [gallery](https://www.panda3d.org/gallery.php) -and the [feature list](https://www.panda3d.org/features.php). To learn how to -use Panda3D, check the [documentation](https://www.panda3d.org/documentation.php) +[liberal license](https://www.panda3d.org/license/). To learn more about +Panda3D's capabilities, visit the [gallery](https://www.panda3d.org/gallery/) +and the [feature list](https://www.panda3d.org/features/). To learn how to +use Panda3D, check the [documentation](https://www.panda3d.org/documentation/) resources. If you get stuck, ask for help from our active -[community](https://www.panda3d.org/community.php). +[community](https://discourse.panda3d.org). Panda3D is licensed under the Modified BSD License. See the LICENSE file for more details. @@ -21,7 +23,16 @@ more details. Installing Panda3D ================== -By far, the easiest way to install the latest development build of Panda3D +The latest Panda3D SDK can be downloaded from +[this page](https://www.panda3d.org/download/sdk-1-10-4-1/). +If you are familiar with installing Python packages, you can use +the following comand: + +```bash +pip install panda3d +``` + +The easiest way to install the latest development build of Panda3D into an existing Python installation is using the following command: ```bash @@ -31,9 +42,7 @@ pip install --pre --extra-index-url https://archive.panda3d.org/ panda3d If this command fails, please make sure your version of pip is up-to-date. If you prefer to install the full SDK with all tools, the latest development -builds can be obtained from this page: - -https://www.panda3d.org/download.php?sdk&version=devel +builds can be obtained from [this page](https://www.panda3d.org/download.php?version=devel&sdk). These are automatically kept up-to-date with the latest GitHub version of Panda. @@ -55,8 +64,8 @@ depending on whether you are on a 32-bit or 64-bit system, or you can [click here](https://github.com/rdb/panda3d-thirdparty) for instructions on building them from source. -https://www.panda3d.org/download/panda3d-1.10.0/panda3d-1.10.0-tools-win64.zip -https://www.panda3d.org/download/panda3d-1.10.0/panda3d-1.10.0-tools-win32.zip +https://www.panda3d.org/download/panda3d-1.10.4.1/panda3d-1.10.4.1-tools-win64.zip +https://www.panda3d.org/download/panda3d-1.10.4.1/panda3d-1.10.4.1-tools-win32.zip After acquiring these dependencies, you may simply build Panda3D from the command prompt using the following command. (Change `14.1` to `14` if you are @@ -96,14 +105,14 @@ python makepanda/makepanda.py --everything --installer --no-egl --no-gles --no-g You will probably see some warnings saying that it's unable to find several dependency packages. You should determine which ones you want to include in your build and install the respective development packages. You may visit -[this manual page](https://www.panda3d.org/manual/index.php/Dependencies) +[this manual page](https://www.panda3d.org/manual/?title=Third-party_dependencies_and_license_info) for an overview of the various dependencies. If you are on Ubuntu, this command should cover the most frequently used third-party packages: ```bash -sudo apt-get install build-essential pkg-config python-dev libpng-dev libjpeg-dev libtiff-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev bison flex libfreetype6-dev libvorbis-dev libeigen3-dev libopenal-dev libode-dev libbullet-dev nvidia-cg-toolkit libgtk2.0-dev libassimp-dev libopenexr-dev +sudo apt-get install build-essential pkg-config fakeroot python-dev libpng-dev libjpeg-dev libtiff-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev bison flex libfreetype6-dev libvorbis-dev libeigen3-dev libopenal-dev libode-dev libbullet-dev nvidia-cg-toolkit libgtk2.0-dev libassimp-dev libopenexr-dev ``` Once Panda3D has built, you can either install the .deb or .rpm package that @@ -126,7 +135,7 @@ macOS ----- On macOS, you will need to download a set of precompiled thirdparty packages in order to -compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.9.4/panda3d-1.9.4-tools-mac.tar.gz). +compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.4.1/panda3d-1.10.4.1-tools-mac.tar.gz). After placing the thirdparty directory inside the panda3d source directory, you may build Panda3D using a command like the following: @@ -135,9 +144,8 @@ you may build Panda3D using a command like the following: python makepanda/makepanda.py --everything --installer ``` -In order to make a universal build, pass the --universal flag. You may also -target a specific minimum macOS version using the --osxtarget flag followed -by the release number, eg. 10.6 or 10.7. +You may target a specific minimum macOS version using the --osxtarget flag +followed by the release number, eg. 10.7 or 10.9. If the build was successful, makepanda will have generated a .dmg file in the source directory containing the installer. Simply open it and run the @@ -151,7 +159,7 @@ install the requisite packages using the system package manager. To install the recommended set of dependencies, you can use this command: ```bash -pkg install pkgconf png jpeg-turbo tiff freetype2 eigen squish openal opusfile libvorbis libX11 libGL ode bullet assimp openexr +pkg install pkgconf bison png jpeg-turbo tiff freetype2 harfbuzz eigen squish openal opusfile libvorbis libX11 mesa-libs ode bullet assimp openexr ``` You will also need to choose which version of Python you want to use. @@ -183,7 +191,7 @@ pkg install python-dev termux-tools ndk-stl ndk-sysroot clang libvorbis-dev libo Then, you can build and install the .apk right away using these commands: ```bash -python makepanda/makepanda.py --everything --target android-21 --installer +python makepanda/makepanda.py --everything --target android-21 --no-tiff --installer xdg-open panda3d.apk ``` @@ -222,3 +230,25 @@ models that are necessary for the developers to reproduce the issue. If you're not sure whether you've encountered a bug, feel free to ask about it in the forums or the IRC channel first. + +Supporting the Project +====================== + +If you would like to support the project financially, visit +[our campaign on OpenCollective](https://opencollective.com/panda3d). Your +contributions help us accelerate the development of Panda3D. + +For the list of backers, see the [BACKERS.md](BACKERS.md) file or visit the +[Sponsors page](https://www.panda3d.org/sponsors) on our web site. Thank you +to everyone who has donated! + + + + + +### Gold Sponsors +[![](https://opencollective.com/panda3d/tiers/gold-sponsor/0/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/0/website) +[![](https://opencollective.com/panda3d/tiers/gold-sponsor/1/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/1/website) +[![](https://opencollective.com/panda3d/tiers/gold-sponsor/2/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/2/website) +[![](https://opencollective.com/panda3d/tiers/gold-sponsor/3/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/3/website) +[![](https://opencollective.com/panda3d/tiers/gold-sponsor/4/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/4/website) diff --git a/contrib/src/ai/aiBehaviors.cxx b/contrib/src/ai/aiBehaviors.cxx index 1ace436e5b..62f577241b 100644 --- a/contrib/src/ai/aiBehaviors.cxx +++ b/contrib/src/ai/aiBehaviors.cxx @@ -13,6 +13,17 @@ #include "aiBehaviors.h" +#include "arrival.h" +#include "evade.h" +#include "flee.h" +#include "flock.h" +#include "obstacleAvoidance.h" +#include "pathFind.h" +#include "pathFollow.h" +#include "pursue.h" +#include "seek.h" +#include "wander.h" + using std::cout; using std::endl; using std::string; diff --git a/contrib/src/ai/aiBehaviors.h b/contrib/src/ai/aiBehaviors.h index 3524a32b63..eb70906e4a 100644 --- a/contrib/src/ai/aiBehaviors.h +++ b/contrib/src/ai/aiBehaviors.h @@ -28,6 +28,9 @@ class PathFollow; class PathFind; class ObstacleAvoidance; +#include "flee.h" +#include "evade.h" + typedef std::list > ListFlee; typedef std::list > ListEvade; diff --git a/contrib/src/ai/aiGlobals.h b/contrib/src/ai/aiGlobals.h index 1d8e8a9c41..f068bd4594 100644 --- a/contrib/src/ai/aiGlobals.h +++ b/contrib/src/ai/aiGlobals.h @@ -15,7 +15,6 @@ #define _AI_GLOBALS_H #include "config_ai.h" -#include "pandaFramework.h" #include "textNode.h" #include "pandaSystem.h" diff --git a/contrib/src/ai/arrival.cxx b/contrib/src/ai/arrival.cxx index 966a6b2611..3ce0f706da 100644 --- a/contrib/src/ai/arrival.cxx +++ b/contrib/src/ai/arrival.cxx @@ -13,6 +13,9 @@ #include "arrival.h" +#include "pursue.h" +#include "seek.h" + Arrival::Arrival(AICharacter *ai_ch, double distance) { _ai_char = ai_ch; diff --git a/contrib/src/ai/obstacleAvoidance.cxx b/contrib/src/ai/obstacleAvoidance.cxx index 87ce79026b..42c301e033 100644 --- a/contrib/src/ai/obstacleAvoidance.cxx +++ b/contrib/src/ai/obstacleAvoidance.cxx @@ -13,6 +13,8 @@ #include "obstacleAvoidance.h" +#include "aiWorld.h" + ObstacleAvoidance:: ObstacleAvoidance(AICharacter *ai_char, float feeler_length) { _ai_char = ai_char; diff --git a/contrib/src/ai/pathFind.cxx b/contrib/src/ai/pathFind.cxx index ee58f4940d..5813c9d503 100644 --- a/contrib/src/ai/pathFind.cxx +++ b/contrib/src/ai/pathFind.cxx @@ -13,6 +13,8 @@ #include "pathFind.h" +#include "pathFollow.h" + using std::cout; using std::endl; using std::string; diff --git a/contrib/src/ai/pathFollow.cxx b/contrib/src/ai/pathFollow.cxx index dd7720e318..942d7c7daa 100644 --- a/contrib/src/ai/pathFollow.cxx +++ b/contrib/src/ai/pathFollow.cxx @@ -1,6 +1,20 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file pathFind.cxx + * @author Deepak, John, Navin + * @date 2009-10-24 + */ #include "pathFollow.h" +#include "pathFind.h" + PathFollow::PathFollow(AICharacter *ai_ch, float follow_wt) { _follow_weight = follow_wt; _curr_path_waypoint = -1; diff --git a/contrib/src/rplight/shadowAtlas.h b/contrib/src/rplight/shadowAtlas.h index e3f0958a50..d6296e7658 100644 --- a/contrib/src/rplight/shadowAtlas.h +++ b/contrib/src/rplight/shadowAtlas.h @@ -28,7 +28,7 @@ #define SHADOWATLAS_H #include "pandabase.h" -#include "lvecBase4.h" +#include "luse.h" NotifyCategoryDecl(shadowatlas, EXPORT_CLASS, EXPORT_TEMPL); diff --git a/direct/src/actor/Actor.py b/direct/src/actor/Actor.py index b7d48f2d87..902c6830d7 100644 --- a/direct/src/actor/Actor.py +++ b/direct/src/actor/Actor.py @@ -67,7 +67,7 @@ class Actor(DirectObject, NodePath): def __init__(self, filename = None, animBundle = None): self.filename = filename - self.animBundle = None + self.animBundle = animBundle self.animControl = None def makeCopy(self): @@ -1947,6 +1947,7 @@ class Actor(DirectObject, NodePath): animName = acc.getAnimName(i) animDef = Actor.AnimDef() + animDef.animBundle = animControl.getAnim() animDef.animControl = animControl self.__animControlDict[lodName][partName][animName] = animDef @@ -2507,7 +2508,7 @@ class Actor(DirectObject, NodePath): else: animNames = [animName] for animName in animNames: - if animName is 'nothing': + if animName == 'nothing': continue thisAnim = '' totalEffect = 0. diff --git a/direct/src/controls/GravityWalker.py b/direct/src/controls/GravityWalker.py index 32c18c05f3..bf3ea8a9bd 100755 --- a/direct/src/controls/GravityWalker.py +++ b/direct/src/controls/GravityWalker.py @@ -71,90 +71,6 @@ class GravityWalker(DirectObject.DirectObject): self.isAirborne = 0 self.highMark = 0 - """ - def spawnTest(self): - assert self.notify.debugStateCall(self) - if not self.wantDebugIndicator: - return - from pandac.PandaModules import * - from direct.interval.IntervalGlobal import * - from toontown.coghq import MovingPlatform - - if hasattr(self, "platform"): - # Remove the prior instantiation: - self.moveIval.pause() - del self.moveIval - self.platform.destroy() - del self.platform - self.platform2.destroy() - del self.platform2 - - model = loader.loadModel('phase_9/models/cogHQ/platform1') - fakeId = id(self) - self.platform = MovingPlatform.MovingPlatform() - self.platform.setupCopyModel(fakeId, model, 'platformcollision') - self.platformRoot = render.attachNewNode("GravityWalker-spawnTest-%s"%fakeId) - self.platformRoot.setPos(base.localAvatar, Vec3(0.0, 0.0, 1.0)) - self.platformRoot.setHpr(base.localAvatar, Vec3.zero()) - self.platform.reparentTo(self.platformRoot) - - self.platform2 = MovingPlatform.MovingPlatform() - self.platform2.setupCopyModel(1+fakeId, model, 'platformcollision') - self.platform2Root = render.attachNewNode("GravityWalker-spawnTest2-%s"%fakeId) - self.platform2Root.setPos(base.localAvatar, Vec3(-16.0, 30.0, 1.0)) - self.platform2Root.setHpr(base.localAvatar, Vec3.zero()) - self.platform2.reparentTo(self.platform2Root) - - duration = 5 - self.moveIval = Parallel( - Sequence( - WaitInterval(0.3), - LerpPosInterval(self.platform, duration, - Vec3(0.0, 30.0, 0.0), - name='platformOut%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform, duration, - Vec3(0.0, 0.0, 0.0), - name='platformBack%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform, duration, - Vec3(0.0, 0.0, 30.0), - name='platformUp%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform, duration, - Vec3(0.0, 0.0, 0.0), - name='platformDown%s' % fakeId, - fluid = 1), - ), - Sequence( - WaitInterval(0.3), - LerpPosInterval(self.platform2, duration, - Vec3(0.0, -30.0, 0.0), - name='platform2Out%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform2, duration, - Vec3(0.0, 30.0, 30.0), - name='platform2Back%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform2, duration, - Vec3(0.0, -30.0, 0.0), - name='platform2Up%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform2, duration, - Vec3(0.0, 0.0, 0.0), - name='platformDown%s' % fakeId, - fluid = 1), - ), - name='platformIval%s' % fakeId, - ) - self.moveIval.loop() - """ def setWalkSpeed(self, forward, jump, reverse, rotate): assert self.notify.debugStateCall(self) self.avatarControlForwardSpeed=forward diff --git a/direct/src/controls/PhysicsWalker.py b/direct/src/controls/PhysicsWalker.py index fc6fe1ca70..de6ebcefa8 100755 --- a/direct/src/controls/PhysicsWalker.py +++ b/direct/src/controls/PhysicsWalker.py @@ -67,51 +67,6 @@ class PhysicsWalker(DirectObject.DirectObject): self.isAirborne = 0 self.highMark = 0 - """ - def spawnTest(self): - assert self.debugPrint("\n\nspawnTest()\n") - if not self.wantDebugIndicator: - return - from pandac.PandaModules import * - from direct.interval.IntervalGlobal import * - from toontown.coghq import MovingPlatform - - if hasattr(self, "platform"): - # Remove the prior instantiation: - self.moveIval.pause() - del self.moveIval - self.platform.destroy() - del self.platform - - model = loader.loadModel('phase_9/models/cogHQ/platform1') - fakeId = id(self) - self.platform = MovingPlatform.MovingPlatform() - self.platform.setupCopyModel(fakeId, model, 'platformcollision') - self.platformRoot = render.attachNewNode("physicsWalker-spawnTest-%s"%fakeId) - self.platformRoot.setPos(base.localAvatar, Vec3(0.0, 3.0, 1.0)) - self.platformRoot.setHpr(base.localAvatar, Vec3.zero()) - self.platform.reparentTo(self.platformRoot) - - startPos = Vec3(0.0, -15.0, 0.0) - endPos = Vec3(0.0, 15.0, 0.0) - distance = Vec3(startPos-endPos).length() - duration = distance/4 - self.moveIval = Sequence( - WaitInterval(0.3), - LerpPosInterval(self.platform, duration, - endPos, startPos=startPos, - name='platformOut%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform, duration, - startPos, startPos=endPos, - name='platformBack%s' % fakeId, - fluid = 1), - name='platformIval%s' % fakeId, - ) - self.moveIval.loop() - """ - def setWalkSpeed(self, forward, jump, reverse, rotate): assert self.debugPrint("setWalkSpeed()") self.avatarControlForwardSpeed=forward diff --git a/direct/src/dcparser/dcClass.cxx b/direct/src/dcparser/dcClass.cxx index 9487c5e323..f1d6955981 100644 --- a/direct/src/dcparser/dcClass.cxx +++ b/direct/src/dcparser/dcClass.cxx @@ -16,22 +16,13 @@ #include "dcAtomicField.h" #include "hashGenerator.h" #include "dcindent.h" -#include "dcmsgtypes.h" #include "dcClassParameter.h" #include -#ifdef HAVE_PYTHON -#include "py_panda.h" -#endif - -using std::ostream; -using std::ostringstream; using std::string; #ifdef WITHIN_PANDA -#include "pStatTimer.h" - #ifndef CPPPARSER PStatCollector DCClass::_update_pcollector("App:Show code:readerPollTask:Update"); PStatCollector DCClass::_generate_pcollector("App:Show code:readerPollTask:Generate"); @@ -86,10 +77,7 @@ DCClass(DCFile *dc_file, const string &name, bool is_struct, bool bogus_class) : _number = -1; _constructor = nullptr; -#ifdef HAVE_PYTHON - _class_def = nullptr; - _owner_class_def = nullptr; -#endif + _python_class_defs = nullptr; } /** @@ -105,11 +93,6 @@ DCClass:: for (fi = _fields.begin(); fi != _fields.end(); ++fi) { delete (*fi); } - -#ifdef HAVE_PYTHON - Py_XDECREF(_class_def); - Py_XDECREF(_owner_class_def); -#endif } /** @@ -335,7 +318,7 @@ inherits_from_bogus_class() const { * Write a string representation of this instance to . */ void DCClass:: -output(ostream &out) const { +output(std::ostream &out) const { if (_is_struct) { out << "struct"; } else { @@ -346,678 +329,11 @@ output(ostream &out) const { } } -#ifdef HAVE_PYTHON -/** - * Returns true if the DCClass object has an associated Python class - * definition, false otherwise. - */ -bool DCClass:: -has_class_def() const { - return (_class_def != nullptr); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Sets the class object associated with this DistributedClass. This object - * will be used to construct new instances of the class. - */ -void DCClass:: -set_class_def(PyObject *class_def) { - Py_XINCREF(class_def); - Py_XDECREF(_class_def); - _class_def = class_def; -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Returns the class object that was previously associated with this - * DistributedClass. This will return a new reference to the object. - */ -PyObject *DCClass:: -get_class_def() const { - if (_class_def == nullptr) { - Py_INCREF(Py_None); - return Py_None; - } - - Py_INCREF(_class_def); - return _class_def; -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Returns true if the DCClass object has an associated Python owner class - * definition, false otherwise. - */ -bool DCClass:: -has_owner_class_def() const { - return (_owner_class_def != nullptr); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Sets the owner class object associated with this DistributedClass. This - * object will be used to construct new owner instances of the class. - */ -void DCClass:: -set_owner_class_def(PyObject *owner_class_def) { - Py_XINCREF(owner_class_def); - Py_XDECREF(_owner_class_def); - _owner_class_def = owner_class_def; -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Returns the owner class object that was previously associated with this - * DistributedClass. This will return a new reference to the object. - */ -PyObject *DCClass:: -get_owner_class_def() const { - if (_owner_class_def == nullptr) { - Py_INCREF(Py_None); - return Py_None; - } - - Py_INCREF(_owner_class_def); - return _owner_class_def; -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Extracts the update message out of the packer and applies it to the - * indicated object by calling the appropriate method. - */ -void DCClass:: -receive_update(PyObject *distobj, DatagramIterator &di) const { -#ifdef WITHIN_PANDA - PStatTimer timer(((DCClass *)this)->_class_update_pcollector); -#endif - DCPacker packer; - const char *data = (const char *)di.get_datagram().get_data(); - packer.set_unpack_data(data + di.get_current_index(), - di.get_remaining_size(), false); - - int field_id = packer.raw_unpack_uint16(); - DCField *field = get_field_by_index(field_id); - if (field == nullptr) { - ostringstream strm; - strm - << "Received update for field " << field_id << ", not in class " - << get_name(); - nassert_raise(strm.str()); - return; - } - - packer.begin_unpack(field); - field->receive_update(packer, distobj); - packer.end_unpack(); - - di.skip_bytes(packer.get_num_unpacked_bytes()); - -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Processes a big datagram that includes all of the "required" fields that - * are sent along with a normal "generate with required" message. This is all - * of the atomic fields that are marked "broadcast required". - */ -void DCClass:: -receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const { -#ifdef WITHIN_PANDA - PStatTimer timer(((DCClass *)this)->_class_update_pcollector); -#endif - DCPacker packer; - const char *data = (const char *)di.get_datagram().get_data(); - packer.set_unpack_data(data + di.get_current_index(), - di.get_remaining_size(), false); - - int num_fields = get_num_inherited_fields(); - for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { - DCField *field = get_inherited_field(i); - if (field->as_molecular_field() == nullptr && - field->is_required() && field->is_broadcast()) { - packer.begin_unpack(field); - field->receive_update(packer, distobj); - if (!packer.end_unpack()) { - break; - } - } - } - - di.skip_bytes(packer.get_num_unpacked_bytes()); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Processes a big datagram that includes all of the "required" fields that - * are sent along with a normal "generate with required" message. This is all - * of the atomic fields that are marked "broadcast ownrecv". Should be used - * for 'owner-view' objects. - */ -void DCClass:: -receive_update_broadcast_required_owner(PyObject *distobj, - DatagramIterator &di) const { -#ifdef WITHIN_PANDA - PStatTimer timer(((DCClass *)this)->_class_update_pcollector); -#endif - DCPacker packer; - const char *data = (const char *)di.get_datagram().get_data(); - packer.set_unpack_data(data + di.get_current_index(), - di.get_remaining_size(), false); - - int num_fields = get_num_inherited_fields(); - for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { - DCField *field = get_inherited_field(i); - if (field->as_molecular_field() == nullptr && - field->is_required() && (field->is_ownrecv() || field->is_broadcast())) { - packer.begin_unpack(field); - field->receive_update(packer, distobj); - if (!packer.end_unpack()) { - break; - } - } - } - - di.skip_bytes(packer.get_num_unpacked_bytes()); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Processes a big datagram that includes all of the "required" fields that - * are sent when an avatar is created. This is all of the atomic fields that - * are marked "required", whether they are broadcast or not. - */ -void DCClass:: -receive_update_all_required(PyObject *distobj, DatagramIterator &di) const { -#ifdef WITHIN_PANDA - PStatTimer timer(((DCClass *)this)->_class_update_pcollector); -#endif - DCPacker packer; - const char *data = (const char *)di.get_datagram().get_data(); - packer.set_unpack_data(data + di.get_current_index(), - di.get_remaining_size(), false); - - int num_fields = get_num_inherited_fields(); - for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { - DCField *field = get_inherited_field(i); - if (field->as_molecular_field() == nullptr && - field->is_required()) { - packer.begin_unpack(field); - field->receive_update(packer, distobj); - if (!packer.end_unpack()) { - break; - } - } - } - - di.skip_bytes(packer.get_num_unpacked_bytes()); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Processes a datagram that lists some additional fields that are broadcast - * in one chunk. - */ -void DCClass:: -receive_update_other(PyObject *distobj, DatagramIterator &di) const { -#ifdef WITHIN_PANDA - PStatTimer timer(((DCClass *)this)->_class_update_pcollector); -#endif - int num_fields = di.get_uint16(); - for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { - receive_update(distobj, di); - } -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Processes an update for a named field from a packed value blob. - */ -void DCClass:: -direct_update(PyObject *distobj, const string &field_name, - const vector_uchar &value_blob) { - DCField *field = get_field_by_name(field_name); - nassertv_always(field != nullptr); - - DCPacker packer; - packer.set_unpack_data(value_blob); - packer.begin_unpack(field); - field->receive_update(packer, distobj); - packer.end_unpack(); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Processes an update for a named field from a packed datagram. - */ -void DCClass:: -direct_update(PyObject *distobj, const string &field_name, - const Datagram &datagram) { - DCField *field = get_field_by_name(field_name); - nassertv_always(field != nullptr); - - DCPacker packer; - packer.set_unpack_data((const char *)datagram.get_data(), datagram.get_length(), false); - packer.begin_unpack(field); - field->receive_update(packer, distobj); - packer.end_unpack(); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Looks up the current value of the indicated field by calling the - * appropriate get*() function, then packs that value into the datagram. This - * field is presumably either a required field or a specified optional field, - * and we are building up a datagram for the generate-with-required message. - * - * Returns true on success, false on failure. - */ -bool DCClass:: -pack_required_field(Datagram &datagram, PyObject *distobj, - const DCField *field) const { - DCPacker packer; - packer.begin_pack(field); - if (!pack_required_field(packer, distobj, field)) { - return false; - } - if (!packer.end_pack()) { - return false; - } - - datagram.append_data(packer.get_data(), packer.get_length()); - return true; -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Looks up the current value of the indicated field by calling the - * appropriate get*() function, then packs that value into the packer. This - * field is presumably either a required field or a specified optional field, - * and we are building up a datagram for the generate-with-required message. - * - * Returns true on success, false on failure. - */ -bool DCClass:: -pack_required_field(DCPacker &packer, PyObject *distobj, - const DCField *field) const { - const DCParameter *parameter = field->as_parameter(); - if (parameter != nullptr) { - // This is the easy case: to pack a parameter, we just look on the class - // object for the data element. - string field_name = field->get_name(); - - if (!PyObject_HasAttrString(distobj, (char *)field_name.c_str())) { - // If the attribute is not defined, but the field has a default value - // specified, quietly pack the default value. - if (field->has_default_value()) { - packer.pack_default_value(); - return true; - } - - // If there is no default value specified, it's an error. - ostringstream strm; - strm << "Data element " << field_name - << ", required by dc file for dclass " << get_name() - << ", not defined on object"; - nassert_raise(strm.str()); - return false; - } - PyObject *result = - PyObject_GetAttrString(distobj, (char *)field_name.c_str()); - nassertr(result != nullptr, false); - - // Now pack the value into the datagram. - bool pack_ok = parameter->pack_args(packer, result); - Py_DECREF(result); - - return pack_ok; - } - - if (field->as_molecular_field() != nullptr) { - ostringstream strm; - strm << "Cannot pack molecular field " << field->get_name() - << " for generate"; - nassert_raise(strm.str()); - return false; - } - - const DCAtomicField *atom = field->as_atomic_field(); - nassertr(atom != nullptr, false); - - // We need to get the initial value of this field. There isn't a good, - // robust way to get this; presently, we just mangle the "setFoo()" name of - // the required field into "getFoo()" and call that. - string setter_name = atom->get_name(); - - if (setter_name.empty()) { - ostringstream strm; - strm << "Required field is unnamed!"; - nassert_raise(strm.str()); - return false; - } - - if (atom->get_num_elements() == 0) { - // It sure doesn't make sense to have a required field with no parameters. - // What data, exactly, is required? - ostringstream strm; - strm << "Required field " << setter_name << " has no parameters!"; - nassert_raise(strm.str()); - return false; - } - - string getter_name = setter_name; - if (setter_name.substr(0, 3) == "set") { - // If the original method started with "set", we mangle this directly to - // "get". - getter_name[0] = 'g'; - - } else { - // Otherwise, we add a "get" prefix, and capitalize the next letter. - getter_name = "get" + setter_name; - getter_name[3] = toupper(getter_name[3]); - } - - // Now we have to look up the getter on the distributed object and call it. - if (!PyObject_HasAttrString(distobj, (char *)getter_name.c_str())) { - // As above, if there's no getter but the field has a default value - // specified, quietly pack the default value. - if (field->has_default_value()) { - packer.pack_default_value(); - return true; - } - - // Otherwise, with no default value it's an error. - ostringstream strm; - strm << "Distributed class " << get_name() - << " doesn't have getter named " << getter_name - << " to match required field " << setter_name; - nassert_raise(strm.str()); - return false; - } - PyObject *func = - PyObject_GetAttrString(distobj, (char *)getter_name.c_str()); - nassertr(func != nullptr, false); - - PyObject *empty_args = PyTuple_New(0); - PyObject *result = PyObject_CallObject(func, empty_args); - Py_DECREF(empty_args); - Py_DECREF(func); - if (result == nullptr) { - // We don't set this as an exception, since presumably the Python method - // itself has already triggered a Python exception. - std::cerr << "Error when calling " << getter_name << "\n"; - return false; - } - - if (atom->get_num_elements() == 1) { - // In this case, we expect the getter to return one object, which we wrap - // up in a tuple. - PyObject *tuple = PyTuple_New(1); - PyTuple_SET_ITEM(tuple, 0, result); - result = tuple; - - } else { - // Otherwise, it had better already be a sequence or tuple of some sort. - if (!PySequence_Check(result)) { - ostringstream strm; - strm << "Since dclass " << get_name() << " method " << setter_name - << " is declared to have multiple parameters, Python function " - << getter_name << " must return a list or tuple.\n"; - nassert_raise(strm.str()); - return false; - } - } - - // Now pack the arguments into the datagram. - bool pack_ok = atom->pack_args(packer, result); - Py_DECREF(result); - - return pack_ok; -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Generates a datagram containing the message necessary to send an update for - * the indicated distributed object from the client. - */ -Datagram DCClass:: -client_format_update(const string &field_name, DOID_TYPE do_id, - PyObject *args) const { - DCField *field = get_field_by_name(field_name); - if (field == nullptr) { - ostringstream strm; - strm << "No field named " << field_name << " in class " << get_name() - << "\n"; - nassert_raise(strm.str()); - return Datagram(); - } - - return field->client_format_update(do_id, args); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Generates a datagram containing the message necessary to send an update for - * the indicated distributed object from the AI. - */ -Datagram DCClass:: -ai_format_update(const string &field_name, DOID_TYPE do_id, - CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const { - DCField *field = get_field_by_name(field_name); - if (field == nullptr) { - ostringstream strm; - strm << "No field named " << field_name << " in class " << get_name() - << "\n"; - nassert_raise(strm.str()); - return Datagram(); - } - - return field->ai_format_update(do_id, to_id, from_id, args); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Generates a datagram containing the message necessary to send an update, - * using the indicated msg type for the indicated distributed object from the - * AI. - */ -Datagram DCClass:: -ai_format_update_msg_type(const string &field_name, DOID_TYPE do_id, - CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const { - DCField *field = get_field_by_name(field_name); - if (field == nullptr) { - ostringstream strm; - strm << "No field named " << field_name << " in class " << get_name() - << "\n"; - nassert_raise(strm.str()); - return Datagram(); - } - - return field->ai_format_update_msg_type(do_id, to_id, from_id, msg_type, args); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Generates a datagram containing the message necessary to generate a new - * distributed object from the client. This requires querying the object for - * the initial value of its required fields. - * - * optional_fields is a list of fieldNames to generate in addition to the - * normal required fields. - * - * This method is only called by the CMU implementation. - */ -Datagram DCClass:: -client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id, - ZONEID_TYPE zone_id, - PyObject *optional_fields) const { - DCPacker packer; - - packer.raw_pack_uint16(CLIENT_OBJECT_GENERATE_CMU); - - packer.raw_pack_uint32(zone_id); - packer.raw_pack_uint16(_number); - packer.raw_pack_uint32(do_id); - - // Specify all of the required fields. - int num_fields = get_num_inherited_fields(); - for (int i = 0; i < num_fields; ++i) { - DCField *field = get_inherited_field(i); - if (field->is_required() && field->as_molecular_field() == nullptr) { - packer.begin_pack(field); - if (!pack_required_field(packer, distobj, field)) { - return Datagram(); - } - packer.end_pack(); - } - } - - // Also specify the optional fields. - int num_optional_fields = 0; - if (PyObject_IsTrue(optional_fields)) { - num_optional_fields = PySequence_Size(optional_fields); - } - packer.raw_pack_uint16(num_optional_fields); - - for (int i = 0; i < num_optional_fields; i++) { - PyObject *py_field_name = PySequence_GetItem(optional_fields, i); -#if PY_MAJOR_VERSION >= 3 - string field_name = PyUnicode_AsUTF8(py_field_name); -#else - string field_name = PyString_AsString(py_field_name); -#endif - Py_XDECREF(py_field_name); - - DCField *field = get_field_by_name(field_name); - if (field == nullptr) { - ostringstream strm; - strm << "No field named " << field_name << " in class " << get_name() - << "\n"; - nassert_raise(strm.str()); - return Datagram(); - } - packer.raw_pack_uint16(field->get_number()); - packer.begin_pack(field); - if (!pack_required_field(packer, distobj, field)) { - return Datagram(); - } - packer.end_pack(); - } - - return Datagram(packer.get_data(), packer.get_length()); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Generates a datagram containing the message necessary to generate a new - * distributed object from the AI. This requires querying the object for the - * initial value of its required fields. - * - * optional_fields is a list of fieldNames to generate in addition to the - * normal required fields. - */ -Datagram DCClass:: -ai_format_generate(PyObject *distobj, DOID_TYPE do_id, - DOID_TYPE parent_id, ZONEID_TYPE zone_id, - CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id, - PyObject *optional_fields) const { - DCPacker packer; - - packer.raw_pack_uint8(1); - packer.RAW_PACK_CHANNEL(district_channel_id); - packer.RAW_PACK_CHANNEL(from_channel_id); - // packer.raw_pack_uint8('A'); - - bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0); - - if (has_optional_fields) { - packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER); - } else { - packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED); - } - - packer.raw_pack_uint32(do_id); - // Parent is a bit overloaded; this parent is not about inheritance, this - // one is about the visibility container parent, i.e. the zone parent: - packer.raw_pack_uint32(parent_id); - packer.raw_pack_uint32(zone_id); - packer.raw_pack_uint16(_number); - - // Specify all of the required fields. - int num_fields = get_num_inherited_fields(); - for (int i = 0; i < num_fields; ++i) { - DCField *field = get_inherited_field(i); - if (field->is_required() && field->as_molecular_field() == nullptr) { - packer.begin_pack(field); - if (!pack_required_field(packer, distobj, field)) { - return Datagram(); - } - packer.end_pack(); - } - } - - // Also specify the optional fields. - if (has_optional_fields) { - int num_optional_fields = PySequence_Size(optional_fields); - packer.raw_pack_uint16(num_optional_fields); - - for (int i = 0; i < num_optional_fields; ++i) { - PyObject *py_field_name = PySequence_GetItem(optional_fields, i); -#if PY_MAJOR_VERSION >= 3 - string field_name = PyUnicode_AsUTF8(py_field_name); -#else - string field_name = PyString_AsString(py_field_name); -#endif - Py_XDECREF(py_field_name); - - DCField *field = get_field_by_name(field_name); - if (field == nullptr) { - ostringstream strm; - strm << "No field named " << field_name << " in class " << get_name() - << "\n"; - nassert_raise(strm.str()); - return Datagram(); - } - - packer.raw_pack_uint16(field->get_number()); - - packer.begin_pack(field); - if (!pack_required_field(packer, distobj, field)) { - return Datagram(); - } - packer.end_pack(); - } - } - - return Datagram(packer.get_data(), packer.get_length()); -} -#endif // HAVE_PYTHON - /** * Write a string representation of this instance to . */ void DCClass:: -output(ostream &out, bool brief) const { +output(std::ostream &out, bool brief) const { output_instance(out, brief, "", "", ""); } @@ -1026,7 +342,7 @@ output(ostream &out, bool brief) const { * stream. */ void DCClass:: -write(ostream &out, bool brief, int indent_level) const { +write(std::ostream &out, bool brief, int indent_level) const { indent(out, indent_level); if (_is_struct) { out << "struct"; @@ -1086,7 +402,7 @@ write(ostream &out, bool brief, int indent_level) const { * stream. */ void DCClass:: -output_instance(ostream &out, bool brief, const string &prename, +output_instance(std::ostream &out, bool brief, const string &prename, const string &name, const string &postname) const { if (_is_struct) { out << "struct"; diff --git a/direct/src/dcparser/dcClass.h b/direct/src/dcparser/dcClass.h index 94feae1c19..2dcc6bc560 100644 --- a/direct/src/dcparser/dcClass.h +++ b/direct/src/dcparser/dcClass.h @@ -17,11 +17,12 @@ #include "dcbase.h" #include "dcField.h" #include "dcDeclaration.h" -#include "dcPython.h" #ifdef WITHIN_PANDA #include "pStatCollector.h" #include "configVariableBool.h" +#include "extension.h" +#include "datagramIterator.h" extern ConfigVariableBool dc_multiple_inheritance; extern ConfigVariableBool dc_virtual_inheritance; @@ -80,44 +81,52 @@ PUBLISHED: virtual void output(std::ostream &out) const; -#ifdef HAVE_PYTHON - bool has_class_def() const; - void set_class_def(PyObject *class_def); - PyObject *get_class_def() const; - bool has_owner_class_def() const; - void set_owner_class_def(PyObject *owner_class_def); - PyObject *get_owner_class_def() const; + EXTENSION(bool has_class_def() const); + EXTENSION(void set_class_def(PyObject *class_def)); + EXTENSION(PyObject *get_class_def() const); + EXTENSION(bool has_owner_class_def() const); + EXTENSION(void set_owner_class_def(PyObject *owner_class_def)); + EXTENSION(PyObject *get_owner_class_def() const); - void receive_update(PyObject *distobj, DatagramIterator &di) const; - void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const; - void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const; - void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const; - void receive_update_other(PyObject *distobj, DatagramIterator &di) const; + EXTENSION(void receive_update(PyObject *distobj, DatagramIterator &di) const); + EXTENSION(void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const); + EXTENSION(void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const); + EXTENSION(void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const); + EXTENSION(void receive_update_other(PyObject *distobj, DatagramIterator &di) const); - void direct_update(PyObject *distobj, const std::string &field_name, - const vector_uchar &value_blob); - void direct_update(PyObject *distobj, const std::string &field_name, - const Datagram &datagram); - bool pack_required_field(Datagram &datagram, PyObject *distobj, - const DCField *field) const; - bool pack_required_field(DCPacker &packer, PyObject *distobj, - const DCField *field) const; + EXTENSION(void direct_update(PyObject *distobj, const std::string &field_name, + const vector_uchar &value_blob)); + EXTENSION(void direct_update(PyObject *distobj, const std::string &field_name, + const Datagram &datagram)); + EXTENSION(bool pack_required_field(Datagram &datagram, PyObject *distobj, + const DCField *field) const); + EXTENSION(bool pack_required_field(DCPacker &packer, PyObject *distobj, + const DCField *field) const); - Datagram client_format_update(const std::string &field_name, - DOID_TYPE do_id, PyObject *args) const; - Datagram ai_format_update(const std::string &field_name, DOID_TYPE do_id, - CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const; - Datagram ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id, - CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const; - Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id, ZONEID_TYPE parent_id, ZONEID_TYPE zone_id, - CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id, - PyObject *optional_fields) const; - Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id, - ZONEID_TYPE zone_id, PyObject *optional_fields) const; + EXTENSION(Datagram client_format_update(const std::string &field_name, + DOID_TYPE do_id, PyObject *args) const); + EXTENSION(Datagram ai_format_update(const std::string &field_name, + DOID_TYPE do_id, + CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, + PyObject *args) const); + EXTENSION(Datagram ai_format_update_msg_type(const std::string &field_name, + DOID_TYPE do_id, + CHANNEL_TYPE to_id, + CHANNEL_TYPE from_id, + int msg_type, + PyObject *args) const); + EXTENSION(Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id, + ZONEID_TYPE parent_id, + ZONEID_TYPE zone_id, + CHANNEL_TYPE district_channel_id, + CHANNEL_TYPE from_channel_id, + PyObject *optional_fields) const); + EXTENSION(Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id, + ZONEID_TYPE zone_id, + PyObject *optional_fields) const); -#endif public: virtual void output(std::ostream &out, bool brief) const; @@ -136,8 +145,8 @@ private: void shadow_inherited_field(const std::string &name); #ifdef WITHIN_PANDA - PStatCollector _class_update_pcollector; - PStatCollector _class_generate_pcollector; + mutable PStatCollector _class_update_pcollector; + mutable PStatCollector _class_generate_pcollector; static PStatCollector _update_pcollector; static PStatCollector _generate_pcollector; #endif @@ -163,12 +172,17 @@ private: typedef pmap FieldsByIndex; FieldsByIndex _fields_by_index; -#ifdef HAVE_PYTHON - PyObject *_class_def; - PyObject *_owner_class_def; -#endif + // See pandaNode.h for an explanation of this trick + class PythonClassDefs : public ReferenceCount { + public: + virtual ~PythonClassDefs() {}; + }; + PT(PythonClassDefs) _python_class_defs; friend class DCField; +#ifdef WITHIN_PANDA + friend class Extension; +#endif }; #include "dcClass.I" diff --git a/direct/src/dcparser/dcClass_ext.cxx b/direct/src/dcparser/dcClass_ext.cxx new file mode 100644 index 0000000000..4e9ffb9aa9 --- /dev/null +++ b/direct/src/dcparser/dcClass_ext.cxx @@ -0,0 +1,665 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file dcClass_ext.cxx + * @author CFSworks + * @date 2019-07-03 + */ + +#include "dcClass_ext.h" +#include "dcField_ext.h" +#include "dcAtomicField.h" +#include "dcPacker.h" +#include "dcmsgtypes.h" + +#include "datagram.h" +#include "datagramIterator.h" +#include "pStatTimer.h" + +#ifdef HAVE_PYTHON + +/** + * Returns true if the DCClass object has an associated Python class + * definition, false otherwise. + */ +bool Extension:: +has_class_def() const { + return _this->_python_class_defs != nullptr + && ((PythonClassDefsImpl *)_this->_python_class_defs.p())->_class_def != nullptr; +} + +/** + * Sets the class object associated with this DistributedClass. This object + * will be used to construct new instances of the class. + */ +void Extension:: +set_class_def(PyObject *class_def) { + PythonClassDefsImpl *defs = do_get_defs(); + + Py_XINCREF(class_def); + Py_XDECREF(defs->_class_def); + defs->_class_def = class_def; +} + +/** + * Returns the class object that was previously associated with this + * DistributedClass. This will return a new reference to the object. + */ +PyObject *Extension:: +get_class_def() const { + if (!has_class_def()) { + Py_INCREF(Py_None); + return Py_None; + } + + PythonClassDefsImpl *defs = do_get_defs(); + Py_INCREF(defs->_class_def); + return defs->_class_def; +} + +/** + * Returns true if the DCClass object has an associated Python owner class + * definition, false otherwise. + */ +bool Extension:: +has_owner_class_def() const { + return _this->_python_class_defs != nullptr + && ((PythonClassDefsImpl *)_this->_python_class_defs.p())->_owner_class_def != nullptr; +} + +/** + * Sets the owner class object associated with this DistributedClass. This + * object will be used to construct new owner instances of the class. + */ +void Extension:: +set_owner_class_def(PyObject *owner_class_def) { + PythonClassDefsImpl *defs = do_get_defs(); + + Py_XINCREF(owner_class_def); + Py_XDECREF(defs->_owner_class_def); + defs->_owner_class_def = owner_class_def; +} + +/** + * Returns the owner class object that was previously associated with this + * DistributedClass. This will return a new reference to the object. + */ +PyObject *Extension:: +get_owner_class_def() const { + if (!has_owner_class_def()) { + Py_INCREF(Py_None); + return Py_None; + } + + PythonClassDefsImpl *defs = do_get_defs(); + Py_INCREF(defs->_owner_class_def); + return defs->_owner_class_def; +} + +/** + * Extracts the update message out of the packer and applies it to the + * indicated object by calling the appropriate method. + */ +void Extension:: +receive_update(PyObject *distobj, DatagramIterator &di) const { + PStatTimer timer(_this->_class_update_pcollector); + DCPacker packer; + const char *data = (const char *)di.get_datagram().get_data(); + packer.set_unpack_data(data + di.get_current_index(), + di.get_remaining_size(), false); + + int field_id = packer.raw_unpack_uint16(); + DCField *field = _this->get_field_by_index(field_id); + if (field == nullptr) { + ostringstream strm; + strm + << "Received update for field " << field_id << ", not in class " + << _this->get_name(); + nassert_raise(strm.str()); + return; + } + + packer.begin_unpack(field); + invoke_extension(field).receive_update(packer, distobj); + packer.end_unpack(); + + di.skip_bytes(packer.get_num_unpacked_bytes()); +} + +/** + * Processes a big datagram that includes all of the "required" fields that + * are sent along with a normal "generate with required" message. This is all + * of the atomic fields that are marked "broadcast required". + */ +void Extension:: +receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const { + PStatTimer timer(_this->_class_update_pcollector); + DCPacker packer; + const char *data = (const char *)di.get_datagram().get_data(); + packer.set_unpack_data(data + di.get_current_index(), + di.get_remaining_size(), false); + + int num_fields = _this->get_num_inherited_fields(); + for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { + DCField *field = _this->get_inherited_field(i); + if (field->as_molecular_field() == nullptr && + field->is_required() && field->is_broadcast()) { + packer.begin_unpack(field); + invoke_extension(field).receive_update(packer, distobj); + if (!packer.end_unpack()) { + break; + } + } + } + + di.skip_bytes(packer.get_num_unpacked_bytes()); +} + +/** + * Processes a big datagram that includes all of the "required" fields that + * are sent along with a normal "generate with required" message. This is all + * of the atomic fields that are marked "broadcast ownrecv". Should be used + * for 'owner-view' objects. + */ +void Extension:: +receive_update_broadcast_required_owner(PyObject *distobj, + DatagramIterator &di) const { + PStatTimer timer(_this->_class_update_pcollector); + DCPacker packer; + const char *data = (const char *)di.get_datagram().get_data(); + packer.set_unpack_data(data + di.get_current_index(), + di.get_remaining_size(), false); + + int num_fields = _this->get_num_inherited_fields(); + for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { + DCField *field = _this->get_inherited_field(i); + if (field->as_molecular_field() == nullptr && + field->is_required() && (field->is_ownrecv() || field->is_broadcast())) { + packer.begin_unpack(field); + invoke_extension(field).receive_update(packer, distobj); + if (!packer.end_unpack()) { + break; + } + } + } + + di.skip_bytes(packer.get_num_unpacked_bytes()); +} + +/** + * Processes a big datagram that includes all of the "required" fields that + * are sent when an avatar is created. This is all of the atomic fields that + * are marked "required", whether they are broadcast or not. + */ +void Extension:: +receive_update_all_required(PyObject *distobj, DatagramIterator &di) const { + PStatTimer timer(_this->_class_update_pcollector); + DCPacker packer; + const char *data = (const char *)di.get_datagram().get_data(); + packer.set_unpack_data(data + di.get_current_index(), + di.get_remaining_size(), false); + + int num_fields = _this->get_num_inherited_fields(); + for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { + DCField *field = _this->get_inherited_field(i); + if (field->as_molecular_field() == nullptr && + field->is_required()) { + packer.begin_unpack(field); + invoke_extension(field).receive_update(packer, distobj); + if (!packer.end_unpack()) { + break; + } + } + } + + di.skip_bytes(packer.get_num_unpacked_bytes()); +} + +/** + * Processes a datagram that lists some additional fields that are broadcast + * in one chunk. + */ +void Extension:: +receive_update_other(PyObject *distobj, DatagramIterator &di) const { + PStatTimer timer(_this->_class_update_pcollector); + int num_fields = di.get_uint16(); + for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { + receive_update(distobj, di); + } +} + +/** + * Processes an update for a named field from a packed value blob. + */ +void Extension:: +direct_update(PyObject *distobj, const std::string &field_name, + const vector_uchar &value_blob) { + DCField *field = _this->get_field_by_name(field_name); + nassertv_always(field != nullptr); + + DCPacker packer; + packer.set_unpack_data(value_blob); + packer.begin_unpack(field); + invoke_extension(field).receive_update(packer, distobj); + packer.end_unpack(); +} + +/** + * Processes an update for a named field from a packed datagram. + */ +void Extension:: +direct_update(PyObject *distobj, const std::string &field_name, + const Datagram &datagram) { + DCField *field = _this->get_field_by_name(field_name); + nassertv_always(field != nullptr); + + DCPacker packer; + packer.set_unpack_data((const char *)datagram.get_data(), datagram.get_length(), false); + packer.begin_unpack(field); + invoke_extension(field).receive_update(packer, distobj); + packer.end_unpack(); +} + +/** + * Looks up the current value of the indicated field by calling the + * appropriate get*() function, then packs that value into the datagram. This + * field is presumably either a required field or a specified optional field, + * and we are building up a datagram for the generate-with-required message. + * + * Returns true on success, false on failure. + */ +bool Extension:: +pack_required_field(Datagram &datagram, PyObject *distobj, + const DCField *field) const { + DCPacker packer; + packer.begin_pack(field); + if (!pack_required_field(packer, distobj, field)) { + return false; + } + if (!packer.end_pack()) { + return false; + } + + datagram.append_data(packer.get_data(), packer.get_length()); + return true; +} + +/** + * Looks up the current value of the indicated field by calling the + * appropriate get*() function, then packs that value into the packer. This + * field is presumably either a required field or a specified optional field, + * and we are building up a datagram for the generate-with-required message. + * + * Returns true on success, false on failure. + */ +bool Extension:: +pack_required_field(DCPacker &packer, PyObject *distobj, + const DCField *field) const { + using std::ostringstream; + + const DCParameter *parameter = field->as_parameter(); + if (parameter != nullptr) { + // This is the easy case: to pack a parameter, we just look on the class + // object for the data element. + std::string field_name = field->get_name(); + + if (!PyObject_HasAttrString(distobj, (char *)field_name.c_str())) { + // If the attribute is not defined, but the field has a default value + // specified, quietly pack the default value. + if (field->has_default_value()) { + packer.pack_default_value(); + return true; + } + + // If there is no default value specified, it's an error. + ostringstream strm; + strm << "Data element " << field_name + << ", required by dc file for dclass " << _this->get_name() + << ", not defined on object"; + nassert_raise(strm.str()); + return false; + } + PyObject *result = + PyObject_GetAttrString(distobj, (char *)field_name.c_str()); + nassertr(result != nullptr, false); + + // Now pack the value into the datagram. + bool pack_ok = invoke_extension((DCField *)parameter).pack_args(packer, result); + Py_DECREF(result); + + return pack_ok; + } + + if (field->as_molecular_field() != nullptr) { + ostringstream strm; + strm << "Cannot pack molecular field " << field->get_name() + << " for generate"; + nassert_raise(strm.str()); + return false; + } + + const DCAtomicField *atom = field->as_atomic_field(); + nassertr(atom != nullptr, false); + + // We need to get the initial value of this field. There isn't a good, + // robust way to get this; presently, we just mangle the "setFoo()" name of + // the required field into "getFoo()" and call that. + std::string setter_name = atom->get_name(); + + if (setter_name.empty()) { + ostringstream strm; + strm << "Required field is unnamed!"; + nassert_raise(strm.str()); + return false; + } + + if (atom->get_num_elements() == 0) { + // It sure doesn't make sense to have a required field with no parameters. + // What data, exactly, is required? + ostringstream strm; + strm << "Required field " << setter_name << " has no parameters!"; + nassert_raise(strm.str()); + return false; + } + + std::string getter_name = setter_name; + if (setter_name.substr(0, 3) == "set") { + // If the original method started with "set", we mangle this directly to + // "get". + getter_name[0] = 'g'; + + } else { + // Otherwise, we add a "get" prefix, and capitalize the next letter. + getter_name = "get" + setter_name; + getter_name[3] = toupper(getter_name[3]); + } + + // Now we have to look up the getter on the distributed object and call it. + if (!PyObject_HasAttrString(distobj, (char *)getter_name.c_str())) { + // As above, if there's no getter but the field has a default value + // specified, quietly pack the default value. + if (field->has_default_value()) { + packer.pack_default_value(); + return true; + } + + // Otherwise, with no default value it's an error. + ostringstream strm; + strm << "Distributed class " << _this->get_name() + << " doesn't have getter named " << getter_name + << " to match required field " << setter_name; + nassert_raise(strm.str()); + return false; + } + PyObject *func = + PyObject_GetAttrString(distobj, (char *)getter_name.c_str()); + nassertr(func != nullptr, false); + + PyObject *empty_args = PyTuple_New(0); + PyObject *result = PyObject_CallObject(func, empty_args); + Py_DECREF(empty_args); + Py_DECREF(func); + if (result == nullptr) { + // We don't set this as an exception, since presumably the Python method + // itself has already triggered a Python exception. + std::cerr << "Error when calling " << getter_name << "\n"; + return false; + } + + if (atom->get_num_elements() == 1) { + // In this case, we expect the getter to return one object, which we wrap + // up in a tuple. + PyObject *tuple = PyTuple_New(1); + PyTuple_SET_ITEM(tuple, 0, result); + result = tuple; + + } else { + // Otherwise, it had better already be a sequence or tuple of some sort. + if (!PySequence_Check(result)) { + ostringstream strm; + strm << "Since dclass " << _this->get_name() << " method " << setter_name + << " is declared to have multiple parameters, Python function " + << getter_name << " must return a list or tuple.\n"; + nassert_raise(strm.str()); + return false; + } + } + + // Now pack the arguments into the datagram. + bool pack_ok = invoke_extension((DCField *)atom).pack_args(packer, result); + Py_DECREF(result); + + return pack_ok; +} + +/** + * Generates a datagram containing the message necessary to send an update for + * the indicated distributed object from the client. + */ +Datagram Extension:: +client_format_update(const std::string &field_name, DOID_TYPE do_id, + PyObject *args) const { + DCField *field = _this->get_field_by_name(field_name); + if (field == nullptr) { + std::ostringstream strm; + strm << "No field named " << field_name << " in class " << _this->get_name() + << "\n"; + nassert_raise(strm.str()); + return Datagram(); + } + + return invoke_extension(field).client_format_update(do_id, args); +} + +/** + * Generates a datagram containing the message necessary to send an update for + * the indicated distributed object from the AI. + */ +Datagram Extension:: +ai_format_update(const std::string &field_name, DOID_TYPE do_id, + CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const { + DCField *field = _this->get_field_by_name(field_name); + if (field == nullptr) { + std::ostringstream strm; + strm << "No field named " << field_name << " in class " << _this->get_name() + << "\n"; + nassert_raise(strm.str()); + return Datagram(); + } + + return invoke_extension(field).ai_format_update(do_id, to_id, from_id, args); +} + +/** + * Generates a datagram containing the message necessary to send an update, + * using the indicated msg type for the indicated distributed object from the + * AI. + */ +Datagram Extension:: +ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id, + CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, + int msg_type, PyObject *args) const { + DCField *field = _this->get_field_by_name(field_name); + if (field == nullptr) { + std::ostringstream strm; + strm << "No field named " << field_name << " in class " << _this->get_name() + << "\n"; + nassert_raise(strm.str()); + return Datagram(); + } + + return invoke_extension(field).ai_format_update_msg_type(do_id, to_id, from_id, msg_type, args); +} + +/** + * Generates a datagram containing the message necessary to generate a new + * distributed object from the client. This requires querying the object for + * the initial value of its required fields. + * + * optional_fields is a list of fieldNames to generate in addition to the + * normal required fields. + * + * This method is only called by the CMU implementation. + */ +Datagram Extension:: +client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id, + ZONEID_TYPE zone_id, + PyObject *optional_fields) const { + DCPacker packer; + + packer.raw_pack_uint16(CLIENT_OBJECT_GENERATE_CMU); + + packer.raw_pack_uint32(zone_id); + packer.raw_pack_uint16(_this->_number); + packer.raw_pack_uint32(do_id); + + // Specify all of the required fields. + int num_fields = _this->get_num_inherited_fields(); + for (int i = 0; i < num_fields; ++i) { + DCField *field = _this->get_inherited_field(i); + if (field->is_required() && field->as_molecular_field() == nullptr) { + packer.begin_pack(field); + if (!pack_required_field(packer, distobj, field)) { + return Datagram(); + } + packer.end_pack(); + } + } + + // Also specify the optional fields. + int num_optional_fields = 0; + if (PyObject_IsTrue(optional_fields)) { + num_optional_fields = PySequence_Size(optional_fields); + } + packer.raw_pack_uint16(num_optional_fields); + + for (int i = 0; i < num_optional_fields; i++) { + PyObject *py_field_name = PySequence_GetItem(optional_fields, i); +#if PY_MAJOR_VERSION >= 3 + std::string field_name = PyUnicode_AsUTF8(py_field_name); +#else + std::string field_name = PyString_AsString(py_field_name); +#endif + Py_XDECREF(py_field_name); + + DCField *field = _this->get_field_by_name(field_name); + if (field == nullptr) { + std::ostringstream strm; + strm << "No field named " << field_name << " in class " << _this->get_name() + << "\n"; + nassert_raise(strm.str()); + return Datagram(); + } + packer.raw_pack_uint16(field->get_number()); + packer.begin_pack(field); + if (!pack_required_field(packer, distobj, field)) { + return Datagram(); + } + packer.end_pack(); + } + + return Datagram(packer.get_data(), packer.get_length()); +} + +/** + * Generates a datagram containing the message necessary to generate a new + * distributed object from the AI. This requires querying the object for the + * initial value of its required fields. + * + * optional_fields is a list of fieldNames to generate in addition to the + * normal required fields. + */ +Datagram Extension:: +ai_format_generate(PyObject *distobj, DOID_TYPE do_id, + DOID_TYPE parent_id, ZONEID_TYPE zone_id, + CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id, + PyObject *optional_fields) const { + DCPacker packer; + + packer.raw_pack_uint8(1); + packer.RAW_PACK_CHANNEL(district_channel_id); + packer.RAW_PACK_CHANNEL(from_channel_id); + // packer.raw_pack_uint8('A'); + + bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0); + + if (has_optional_fields) { + packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER); + } else { + packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED); + } + + packer.raw_pack_uint32(do_id); + // Parent is a bit overloaded; this parent is not about inheritance, this + // one is about the visibility container parent, i.e. the zone parent: + packer.raw_pack_uint32(parent_id); + packer.raw_pack_uint32(zone_id); + packer.raw_pack_uint16(_this->_number); + + // Specify all of the required fields. + int num_fields = _this->get_num_inherited_fields(); + for (int i = 0; i < num_fields; ++i) { + DCField *field = _this->get_inherited_field(i); + if (field->is_required() && field->as_molecular_field() == nullptr) { + packer.begin_pack(field); + if (!pack_required_field(packer, distobj, field)) { + return Datagram(); + } + packer.end_pack(); + } + } + + // Also specify the optional fields. + if (has_optional_fields) { + int num_optional_fields = PySequence_Size(optional_fields); + packer.raw_pack_uint16(num_optional_fields); + + for (int i = 0; i < num_optional_fields; ++i) { + PyObject *py_field_name = PySequence_GetItem(optional_fields, i); +#if PY_MAJOR_VERSION >= 3 + std::string field_name = PyUnicode_AsUTF8(py_field_name); +#else + std::string field_name = PyString_AsString(py_field_name); +#endif + Py_XDECREF(py_field_name); + + DCField *field = _this->get_field_by_name(field_name); + if (field == nullptr) { + std::ostringstream strm; + strm << "No field named " << field_name << " in class " + << _this->get_name() << "\n"; + nassert_raise(strm.str()); + return Datagram(); + } + + packer.raw_pack_uint16(field->get_number()); + + packer.begin_pack(field); + if (!pack_required_field(packer, distobj, field)) { + return Datagram(); + } + packer.end_pack(); + } + } + + return Datagram(packer.get_data(), packer.get_length()); +} + +/** + * Returns the PythonClassDefsImpl object stored on the DCClass object, + * creating it if it didn't yet exist. + */ +Extension::PythonClassDefsImpl *Extension:: +do_get_defs() const { + if (!_this->_python_class_defs) { + _this->_python_class_defs = new PythonClassDefsImpl(); + } + return (PythonClassDefsImpl *)_this->_python_class_defs.p(); +} + +#endif // HAVE_PYTHON diff --git a/direct/src/dcparser/dcClass_ext.h b/direct/src/dcparser/dcClass_ext.h new file mode 100644 index 0000000000..9f81b7b722 --- /dev/null +++ b/direct/src/dcparser/dcClass_ext.h @@ -0,0 +1,93 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file dcClass_ext.h + * @author CFSworks + * @date 2019-07-03 + */ + +#ifndef DCCLASS_EXT_H +#define DCCLASS_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "dcClass.h" +#include "py_panda.h" + +/** + * This class defines the extension methods for DCClass, which are called + * instead of any C++ methods with the same prototype. + */ +template<> +class Extension : public ExtensionBase { +public: + bool has_class_def() const; + void set_class_def(PyObject *class_def); + PyObject *get_class_def() const; + bool has_owner_class_def() const; + void set_owner_class_def(PyObject *owner_class_def); + PyObject *get_owner_class_def() const; + + void receive_update(PyObject *distobj, DatagramIterator &di) const; + void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const; + void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const; + void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const; + void receive_update_other(PyObject *distobj, DatagramIterator &di) const; + + void direct_update(PyObject *distobj, const std::string &field_name, + const vector_uchar &value_blob); + void direct_update(PyObject *distobj, const std::string &field_name, + const Datagram &datagram); + bool pack_required_field(Datagram &datagram, PyObject *distobj, + const DCField *field) const; + bool pack_required_field(DCPacker &packer, PyObject *distobj, + const DCField *field) const; + + + + Datagram client_format_update(const std::string &field_name, + DOID_TYPE do_id, PyObject *args) const; + Datagram ai_format_update(const std::string &field_name, DOID_TYPE do_id, + CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const; + Datagram ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id, + CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const; + Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id, + ZONEID_TYPE parent_id, ZONEID_TYPE zone_id, + CHANNEL_TYPE district_channel_id, + CHANNEL_TYPE from_channel_id, + PyObject *optional_fields) const; + Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id, + ZONEID_TYPE zone_id, + PyObject *optional_fields) const; + +private: + /** + * Implementation of DCClass::PythonClassDefs which actually stores the + * Python pointers. This needs to be defined here rather than on DCClass + * itself, since DCClass cannot include Python.h or call Python functions. + */ + class PythonClassDefsImpl : public DCClass::PythonClassDefs { + public: + virtual ~PythonClassDefsImpl() { + Py_XDECREF(_class_def); + Py_XDECREF(_owner_class_def); + } + + PyObject *_class_def = nullptr; + PyObject *_owner_class_def = nullptr; + }; + + PythonClassDefsImpl *do_get_defs() const; +}; + +#endif // HAVE_PYTHON + +#endif // DCCLASS_EXT_H diff --git a/direct/src/dcparser/dcField.cxx b/direct/src/dcparser/dcField.cxx index 5880fd5097..9265160b28 100644 --- a/direct/src/dcparser/dcField.cxx +++ b/direct/src/dcparser/dcField.cxx @@ -18,16 +18,6 @@ #include "hashGenerator.h" #include "dcmsgtypes.h" -#ifdef HAVE_PYTHON -#include "py_panda.h" -#endif - -#ifdef WITHIN_PANDA -#include "pStatTimer.h" -#endif - -using std::string; - /** * */ @@ -58,7 +48,7 @@ DCField() : * */ DCField:: -DCField(const string &name, DCClass *dclass) : +DCField(const std::string &name, DCClass *dclass) : DCPackerInterface(name), _dclass(dclass) #ifdef WITHIN_PANDA @@ -161,14 +151,14 @@ as_parameter() const { * string formatting it for human consumption. Returns empty string if there * is an error. */ -string DCField:: +std::string DCField:: format_data(const vector_uchar &packed_data, bool show_field_names) { DCPacker packer; packer.set_unpack_data(packed_data); packer.begin_unpack(this); - string result = packer.unpack_and_format(show_field_names); + std::string result = packer.unpack_and_format(show_field_names); if (!packer.end_unpack()) { - return string(); + return std::string(); } return result; } @@ -179,7 +169,7 @@ format_data(const vector_uchar &packed_data, bool show_field_names) { * the corresponding packed data. Returns empty string if there is an error. */ vector_uchar DCField:: -parse_string(const string &formatted_string) { +parse_string(const std::string &formatted_string) { DCPacker packer; packer.begin_pack(this); if (!packer.parse_and_pack(formatted_string)) { @@ -212,254 +202,6 @@ validate_ranges(const vector_uchar &packed_data) const { return (packer.get_num_unpacked_bytes() == packed_data.size()); } -#ifdef HAVE_PYTHON -/** - * Packs the Python arguments from the indicated tuple into the packer. - * Returns true on success, false on failure. - * - * It is assumed that the packer is currently positioned on this field. - */ -bool DCField:: -pack_args(DCPacker &packer, PyObject *sequence) const { - nassertr(!packer.had_error(), false); - nassertr(packer.get_current_field() == this, false); - - packer.pack_object(sequence); - if (!packer.had_error()) { - /* - cerr << "pack " << get_name() << get_pystr(sequence) << "\n"; - */ - - return true; - } - - if (!Notify::ptr()->has_assert_failed()) { - std::ostringstream strm; - PyObject *exc_type = PyExc_Exception; - - if (as_parameter() != nullptr) { - // If it's a parameter-type field, the value may or may not be a - // sequence. - if (packer.had_pack_error()) { - strm << "Incorrect arguments to field: " << get_name() - << " = " << get_pystr(sequence); - exc_type = PyExc_TypeError; - } else { - strm << "Value out of range on field: " << get_name() - << " = " << get_pystr(sequence); - exc_type = PyExc_ValueError; - } - - } else { - // If it's a molecular or atomic field, the value should be a sequence. - PyObject *tuple = PySequence_Tuple(sequence); - if (tuple == nullptr) { - strm << "Value for " << get_name() << " not a sequence: " \ - << get_pystr(sequence); - exc_type = PyExc_TypeError; - - } else { - if (packer.had_pack_error()) { - strm << "Incorrect arguments to field: " << get_name() - << get_pystr(sequence); - exc_type = PyExc_TypeError; - } else { - strm << "Value out of range on field: " << get_name() - << get_pystr(sequence); - exc_type = PyExc_ValueError; - } - - Py_DECREF(tuple); - } - } - - string message = strm.str(); - PyErr_SetString(exc_type, message.c_str()); - } - return false; -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Unpacks the values from the packer, beginning at the current point in the - * unpack_buffer, into a Python tuple and returns the tuple. - * - * It is assumed that the packer is currently positioned on this field. - */ -PyObject *DCField:: -unpack_args(DCPacker &packer) const { - nassertr(!packer.had_error(), nullptr); - nassertr(packer.get_current_field() == this, nullptr); - - size_t start_byte = packer.get_num_unpacked_bytes(); - PyObject *object = packer.unpack_object(); - - if (!packer.had_error()) { - // Successfully unpacked. - /* - cerr << "recv " << get_name() << get_pystr(object) << "\n"; - */ - - return object; - } - - if (!Notify::ptr()->has_assert_failed()) { - std::ostringstream strm; - PyObject *exc_type = PyExc_Exception; - - if (packer.had_pack_error()) { - strm << "Data error unpacking field "; - output(strm, true); - size_t length = packer.get_unpack_length() - start_byte; - strm << "\nGot data (" << (int)length << " bytes):\n"; - Datagram dg(packer.get_unpack_data() + start_byte, length); - dg.dump_hex(strm); - size_t error_byte = packer.get_num_unpacked_bytes() - start_byte; - strm << "Error detected on byte " << error_byte - << " (" << std::hex << error_byte << std::dec << " hex)"; - - exc_type = PyExc_RuntimeError; - } else { - strm << "Value outside specified range when unpacking field " - << get_name() << ": " << get_pystr(object); - exc_type = PyExc_ValueError; - } - - string message = strm.str(); - PyErr_SetString(exc_type, message.c_str()); - } - - Py_XDECREF(object); - return nullptr; -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Extracts the update message out of the datagram and applies it to the - * indicated object by calling the appropriate method. - */ -void DCField:: -receive_update(DCPacker &packer, PyObject *distobj) const { - if (as_parameter() != nullptr) { - // If it's a parameter-type field, just store a new value on the object. - PyObject *value = unpack_args(packer); - if (value != nullptr) { - PyObject_SetAttrString(distobj, (char *)_name.c_str(), value); - } - Py_DECREF(value); - - } else { - // Otherwise, it must be an atomic or molecular field, so call the - // corresponding method. - - if (!PyObject_HasAttrString(distobj, (char *)_name.c_str())) { - // If there's no Python method to receive this message, don't bother - // unpacking it to a Python tuple--just skip past the message. - packer.unpack_skip(); - - } else { - // Otherwise, get a Python tuple from the args and call the Python - // method. - PyObject *args = unpack_args(packer); - - if (args != nullptr) { - PyObject *func = PyObject_GetAttrString(distobj, (char *)_name.c_str()); - nassertv(func != nullptr); - - PyObject *result; - { -#ifdef WITHIN_PANDA - PStatTimer timer(((DCField *)this)->_field_update_pcollector); -#endif - result = PyObject_CallObject(func, args); - } - Py_XDECREF(result); - Py_DECREF(func); - Py_DECREF(args); - } - } - } -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Generates a datagram containing the message necessary to send an update for - * the indicated distributed object from the client. - */ -Datagram DCField:: -client_format_update(DOID_TYPE do_id, PyObject *args) const { - DCPacker packer; - - packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD); - packer.raw_pack_uint32(do_id); - packer.raw_pack_uint16(_number); - - packer.begin_pack(this); - pack_args(packer, args); - if (!packer.end_pack()) { - return Datagram(); - } - - return Datagram(packer.get_data(), packer.get_length()); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Generates a datagram containing the message necessary to send an update for - * the indicated distributed object from the AI. - */ -Datagram DCField:: -ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const { - DCPacker packer; - - packer.raw_pack_uint8(1); - packer.RAW_PACK_CHANNEL(to_id); - packer.RAW_PACK_CHANNEL(from_id); - packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD); - packer.raw_pack_uint32(do_id); - packer.raw_pack_uint16(_number); - - packer.begin_pack(this); - pack_args(packer, args); - if (!packer.end_pack()) { - return Datagram(); - } - - return Datagram(packer.get_data(), packer.get_length()); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Generates a datagram containing the message necessary to send an update, - * with the msg type, for the indicated distributed object from the AI. - */ -Datagram DCField:: -ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const { - DCPacker packer; - - packer.raw_pack_uint8(1); - packer.RAW_PACK_CHANNEL(to_id); - packer.RAW_PACK_CHANNEL(from_id); - packer.raw_pack_uint16(msg_type); - packer.raw_pack_uint32(do_id); - packer.raw_pack_uint16(_number); - - packer.begin_pack(this); - pack_args(packer, args); - if (!packer.end_pack()) { - return Datagram(); - } - - return Datagram(packer.get_data(), packer.get_length()); -} -#endif // HAVE_PYTHON - - /** * Accumulates the properties of this field into the hash. */ @@ -499,62 +241,13 @@ pack_default_value(DCPackData &pack_data, bool &) const { * Sets the name of this field. */ void DCField:: -set_name(const string &name) { +set_name(const std::string &name) { DCPackerInterface::set_name(name); if (_dclass != nullptr) { _dclass->_dc_file->mark_inherited_fields_stale(); } } -#ifdef HAVE_PYTHON -/** - * Returns the string representation of the indicated Python object. - */ -string DCField:: -get_pystr(PyObject *value) { - if (value == nullptr) { - return "(null)"; - } - - PyObject *str = PyObject_Str(value); - if (str != nullptr) { -#if PY_MAJOR_VERSION >= 3 - string result = PyUnicode_AsUTF8(str); -#else - string result = PyString_AsString(str); -#endif - Py_DECREF(str); - return result; - } - - PyObject *repr = PyObject_Repr(value); - if (repr != nullptr) { -#if PY_MAJOR_VERSION >= 3 - string result = PyUnicode_AsUTF8(repr); -#else - string result = PyString_AsString(repr); -#endif - Py_DECREF(repr); - return result; - } - - if (value->ob_type != nullptr) { - PyObject *typestr = PyObject_Str((PyObject *)(value->ob_type)); - if (typestr != nullptr) { -#if PY_MAJOR_VERSION >= 3 - string result = PyUnicode_AsUTF8(typestr); -#else - string result = PyString_AsString(typestr); -#endif - Py_DECREF(typestr); - return result; - } - } - - return "(invalid object)"; -} -#endif // HAVE_PYTHON - /** * Recomputes the default value of the field by repacking it. */ diff --git a/direct/src/dcparser/dcField.h b/direct/src/dcparser/dcField.h index 96b7da4487..1c6fa8ce5e 100644 --- a/direct/src/dcparser/dcField.h +++ b/direct/src/dcparser/dcField.h @@ -17,10 +17,11 @@ #include "dcbase.h" #include "dcPackerInterface.h" #include "dcKeywordList.h" -#include "dcPython.h" #ifdef WITHIN_PANDA #include "pStatCollector.h" +#include "extension.h" +#include "datagram.h" #endif class DCPacker; @@ -76,18 +77,17 @@ PUBLISHED: INLINE void output(std::ostream &out) const; INLINE void write(std::ostream &out, int indent_level) const; -#ifdef HAVE_PYTHON - bool pack_args(DCPacker &packer, PyObject *sequence) const; - PyObject *unpack_args(DCPacker &packer) const; + EXTENSION(bool pack_args(DCPacker &packer, PyObject *sequence) const); + EXTENSION(PyObject *unpack_args(DCPacker &packer) const); - void receive_update(DCPacker &packer, PyObject *distobj) const; + EXTENSION(void receive_update(DCPacker &packer, PyObject *distobj) const); - Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const; - Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, - PyObject *args) const; - Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, - int msg_type, PyObject *args) const; -#endif + EXTENSION(Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const); + EXTENSION(Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, + CHANNEL_TYPE from_id, PyObject *args) const); + EXTENSION(Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, + CHANNEL_TYPE from_id, int msg_type, + PyObject *args) const); public: virtual void output(std::ostream &out, bool brief) const=0; @@ -100,10 +100,6 @@ public: INLINE void set_class(DCClass *dclass); INLINE void set_default_value(vector_uchar default_value); -#ifdef HAVE_PYTHON - static std::string get_pystr(PyObject *value); -#endif - protected: void refresh_default_value(); @@ -119,6 +115,8 @@ private: #ifdef WITHIN_PANDA PStatCollector _field_update_pcollector; + + friend class Extension; #endif }; diff --git a/direct/src/dcparser/dcField_ext.cxx b/direct/src/dcparser/dcField_ext.cxx new file mode 100644 index 0000000000..3d229b2906 --- /dev/null +++ b/direct/src/dcparser/dcField_ext.cxx @@ -0,0 +1,305 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file dcField_ext.cxx + * @author CFSworks + * @date 2019-07-03 + */ + +#include "dcField_ext.h" +#include "dcPacker_ext.h" +#include "dcmsgtypes.h" + +#include "datagram.h" +#include "pStatTimer.h" + +#ifdef HAVE_PYTHON + +/** + * Packs the Python arguments from the indicated tuple into the packer. + * Returns true on success, false on failure. + * + * It is assumed that the packer is currently positioned on this field. + */ +bool Extension:: +pack_args(DCPacker &packer, PyObject *sequence) const { + nassertr(!packer.had_error(), false); + nassertr(packer.get_current_field() == _this, false); + + invoke_extension(&packer).pack_object(sequence); + if (!packer.had_error()) { + /* + cerr << "pack " << _this->get_name() << get_pystr(sequence) << "\n"; + */ + + return true; + } + + if (!Notify::ptr()->has_assert_failed()) { + std::ostringstream strm; + PyObject *exc_type = PyExc_Exception; + + if (_this->as_parameter() != nullptr) { + // If it's a parameter-type field, the value may or may not be a + // sequence. + if (packer.had_pack_error()) { + strm << "Incorrect arguments to field: " << _this->get_name() + << " = " << get_pystr(sequence); + exc_type = PyExc_TypeError; + } else { + strm << "Value out of range on field: " << _this->get_name() + << " = " << get_pystr(sequence); + exc_type = PyExc_ValueError; + } + + } else { + // If it's a molecular or atomic field, the value should be a sequence. + PyObject *tuple = PySequence_Tuple(sequence); + if (tuple == nullptr) { + strm << "Value for " << _this->get_name() << " not a sequence: " \ + << get_pystr(sequence); + exc_type = PyExc_TypeError; + + } else { + if (packer.had_pack_error()) { + strm << "Incorrect arguments to field: " << _this->get_name() + << get_pystr(sequence); + exc_type = PyExc_TypeError; + } else { + strm << "Value out of range on field: " << _this->get_name() + << get_pystr(sequence); + exc_type = PyExc_ValueError; + } + + Py_DECREF(tuple); + } + } + + std::string message = strm.str(); + PyErr_SetString(exc_type, message.c_str()); + } + return false; +} + +/** + * Unpacks the values from the packer, beginning at the current point in the + * unpack_buffer, into a Python tuple and returns the tuple. + * + * It is assumed that the packer is currently positioned on this field. + */ +PyObject *Extension:: +unpack_args(DCPacker &packer) const { + nassertr(!packer.had_error(), nullptr); + nassertr(packer.get_current_field() == _this, nullptr); + + size_t start_byte = packer.get_num_unpacked_bytes(); + PyObject *object = invoke_extension(&packer).unpack_object(); + + if (!packer.had_error()) { + // Successfully unpacked. + /* + cerr << "recv " << _this->get_name() << get_pystr(object) << "\n"; + */ + + return object; + } + + if (!Notify::ptr()->has_assert_failed()) { + std::ostringstream strm; + PyObject *exc_type = PyExc_Exception; + + if (packer.had_pack_error()) { + strm << "Data error unpacking field "; + _this->output(strm, true); + size_t length = packer.get_unpack_length() - start_byte; + strm << "\nGot data (" << (int)length << " bytes):\n"; + Datagram dg(packer.get_unpack_data() + start_byte, length); + dg.dump_hex(strm); + size_t error_byte = packer.get_num_unpacked_bytes() - start_byte; + strm << "Error detected on byte " << error_byte + << " (" << std::hex << error_byte << std::dec << " hex)"; + + exc_type = PyExc_RuntimeError; + } else { + strm << "Value outside specified range when unpacking field " + << _this->get_name() << ": " << get_pystr(object); + exc_type = PyExc_ValueError; + } + + std::string message = strm.str(); + PyErr_SetString(exc_type, message.c_str()); + } + + Py_XDECREF(object); + return nullptr; +} + +/** + * Extracts the update message out of the datagram and applies it to the + * indicated object by calling the appropriate method. + */ +void Extension:: +receive_update(DCPacker &packer, PyObject *distobj) const { + if (_this->as_parameter() != nullptr) { + // If it's a parameter-type field, just store a new value on the object. + PyObject *value = unpack_args(packer); + if (value != nullptr) { + PyObject_SetAttrString(distobj, (char *)_this->_name.c_str(), value); + } + Py_DECREF(value); + + } else { + // Otherwise, it must be an atomic or molecular field, so call the + // corresponding method. + + if (!PyObject_HasAttrString(distobj, (char *)_this->_name.c_str())) { + // If there's no Python method to receive this message, don't bother + // unpacking it to a Python tuple--just skip past the message. + packer.unpack_skip(); + + } else { + // Otherwise, get a Python tuple from the args and call the Python + // method. + PyObject *args = unpack_args(packer); + + if (args != nullptr) { + PyObject *func = PyObject_GetAttrString(distobj, (char *)_this->_name.c_str()); + nassertv(func != nullptr); + + PyObject *result; + { +#ifdef WITHIN_PANDA + PStatTimer timer(_this->_field_update_pcollector); +#endif + result = PyObject_CallObject(func, args); + } + Py_XDECREF(result); + Py_DECREF(func); + Py_DECREF(args); + } + } + } +} + +/** + * Generates a datagram containing the message necessary to send an update for + * the indicated distributed object from the client. + */ +Datagram Extension:: +client_format_update(DOID_TYPE do_id, PyObject *args) const { + DCPacker packer; + + packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD); + packer.raw_pack_uint32(do_id); + packer.raw_pack_uint16(_this->_number); + + packer.begin_pack(_this); + pack_args(packer, args); + if (!packer.end_pack()) { + return Datagram(); + } + + return Datagram(packer.get_data(), packer.get_length()); +} + +/** + * Generates a datagram containing the message necessary to send an update for + * the indicated distributed object from the AI. + */ +Datagram Extension:: +ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const { + DCPacker packer; + + packer.raw_pack_uint8(1); + packer.RAW_PACK_CHANNEL(to_id); + packer.RAW_PACK_CHANNEL(from_id); + packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD); + packer.raw_pack_uint32(do_id); + packer.raw_pack_uint16(_this->_number); + + packer.begin_pack(_this); + pack_args(packer, args); + if (!packer.end_pack()) { + return Datagram(); + } + + return Datagram(packer.get_data(), packer.get_length()); +} + +/** + * Generates a datagram containing the message necessary to send an update, + * with the msg type, for the indicated distributed object from the AI. + */ +Datagram Extension:: +ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const { + DCPacker packer; + + packer.raw_pack_uint8(1); + packer.RAW_PACK_CHANNEL(to_id); + packer.RAW_PACK_CHANNEL(from_id); + packer.raw_pack_uint16(msg_type); + packer.raw_pack_uint32(do_id); + packer.raw_pack_uint16(_this->_number); + + packer.begin_pack(_this); + pack_args(packer, args); + if (!packer.end_pack()) { + return Datagram(); + } + + return Datagram(packer.get_data(), packer.get_length()); +} + +/** + * Returns the string representation of the indicated Python object. + */ +std::string Extension:: +get_pystr(PyObject *value) { + if (value == nullptr) { + return "(null)"; + } + + PyObject *str = PyObject_Str(value); + if (str != nullptr) { +#if PY_MAJOR_VERSION >= 3 + std::string result = PyUnicode_AsUTF8(str); +#else + std::string result = PyString_AsString(str); +#endif + Py_DECREF(str); + return result; + } + + PyObject *repr = PyObject_Repr(value); + if (repr != nullptr) { +#if PY_MAJOR_VERSION >= 3 + std::string result = PyUnicode_AsUTF8(repr); +#else + std::string result = PyString_AsString(repr); +#endif + Py_DECREF(repr); + return result; + } + + if (value->ob_type != nullptr) { + PyObject *typestr = PyObject_Str((PyObject *)(value->ob_type)); + if (typestr != nullptr) { +#if PY_MAJOR_VERSION >= 3 + std::string result = PyUnicode_AsUTF8(typestr); +#else + std::string result = PyString_AsString(typestr); +#endif + Py_DECREF(typestr); + return result; + } + } + + return "(invalid object)"; +} + +#endif // HAVE_PYTHON diff --git a/direct/src/dcparser/dcField_ext.h b/direct/src/dcparser/dcField_ext.h new file mode 100644 index 0000000000..45350b4596 --- /dev/null +++ b/direct/src/dcparser/dcField_ext.h @@ -0,0 +1,48 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file dcField_ext.h + * @author CFSworks + * @date 2019-07-03 + */ + +#ifndef DCFIELD_EXT_H +#define DCFIELD_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "dcField.h" +#include "py_panda.h" + +/** + * This class defines the extension methods for DCField, which are called + * instead of any C++ methods with the same prototype. + */ +template<> +class Extension : public ExtensionBase { +public: + bool pack_args(DCPacker &packer, PyObject *sequence) const; + PyObject *unpack_args(DCPacker &packer) const; + + void receive_update(DCPacker &packer, PyObject *distobj) const; + + Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const; + Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, + PyObject *args) const; + Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, + int msg_type, PyObject *args) const; + + static std::string get_pystr(PyObject *value); +}; + +#endif // HAVE_PYTHON + +#endif // DCFIELD_EXT_H diff --git a/direct/src/dcparser/dcPacker.cxx b/direct/src/dcparser/dcPacker.cxx index b6dbd60855..ce67a570bf 100644 --- a/direct/src/dcparser/dcPacker.cxx +++ b/direct/src/dcparser/dcPacker.cxx @@ -19,10 +19,6 @@ #include "dcSwitchParameter.h" #include "dcClass.h" -#ifdef HAVE_PYTHON -#include "py_panda.h" -#endif - using std::istream; using std::istringstream; using std::ostream; @@ -622,335 +618,6 @@ unpack_skip() { } } -#ifdef HAVE_PYTHON -/** - * Packs the Python object of whatever type into the packer. Each numeric - * object and string object maps to the corresponding pack_value() call; a - * tuple or sequence maps to a push() followed by all of the tuple's contents - * followed by a pop(). - */ -void DCPacker:: -pack_object(PyObject *object) { - nassertv(_mode == M_pack || _mode == M_repack); - DCPackType pack_type = get_pack_type(); - - // had to add this for basic 64 and unsigned data to get packed right .. Not - // sure if we can just do the rest this way.. - - switch(pack_type) - { - case PT_int64: - if(PyLong_Check(object)) - { - pack_int64(PyLong_AsLongLong(object)); - return; - } -#if PY_MAJOR_VERSION < 3 - else if (PyInt_Check(object)) - { - pack_int64(PyInt_AsLong(object)); - return; - } -#endif - break; - case PT_uint64: - if(PyLong_Check(object)) - { - pack_uint64(PyLong_AsUnsignedLongLong(object)); - return; - } -#if PY_MAJOR_VERSION < 3 - else if(PyInt_Check(object)) - { - PyObject *obj1 = PyNumber_Long(object); - pack_int(PyLong_AsUnsignedLongLong(obj1)); - Py_DECREF(obj1); - return; - } -#endif - break; - case PT_int: - if(PyLong_Check(object)) - { - pack_int(PyLong_AsLong(object)); - return; - } -#if PY_MAJOR_VERSION < 3 - else if (PyInt_Check(object)) - { - pack_int(PyInt_AsLong(object)); - return; - } -#endif - break; - case PT_uint: - if(PyLong_Check(object)) - { - pack_uint(PyLong_AsUnsignedLong(object)); - return; - } -#if PY_MAJOR_VERSION < 3 - else if (PyInt_Check(object)) - { - PyObject *obj1 = PyNumber_Long(object); - pack_uint(PyLong_AsUnsignedLong(obj1)); - Py_DECREF(obj1); - return; - } -#endif - break; - default: - break; - } - if (PyLong_Check(object)) { - pack_int(PyLong_AsLong(object)); -#if PY_MAJOR_VERSION < 3 - } else if (PyInt_Check(object)) { - pack_int(PyInt_AS_LONG(object)); -#endif - } else if (PyFloat_Check(object)) { - pack_double(PyFloat_AS_DOUBLE(object)); - } else if (PyLong_Check(object)) { - pack_int64(PyLong_AsLongLong(object)); -#if PY_MAJOR_VERSION >= 3 - } else if (PyUnicode_Check(object)) { - const char *buffer; - Py_ssize_t length; - buffer = PyUnicode_AsUTF8AndSize(object, &length); - if (buffer) { - pack_string(string(buffer, length)); - } - } else if (PyBytes_Check(object)) { - const unsigned char *buffer; - Py_ssize_t length; - PyBytes_AsStringAndSize(object, (char **)&buffer, &length); - if (buffer) { - pack_blob(vector_uchar(buffer, buffer + length)); - } -#else - } else if (PyString_Check(object) || PyUnicode_Check(object)) { - char *buffer; - Py_ssize_t length; - PyString_AsStringAndSize(object, &buffer, &length); - if (buffer) { - pack_string(string(buffer, length)); - } -#endif - } else { - // For some reason, PySequence_Check() is incorrectly reporting that a - // class instance is a sequence, even if it doesn't provide __len__, so we - // double-check by testing for __len__ explicitly. - bool is_sequence = - (PySequence_Check(object) != 0) && - (PyObject_HasAttrString(object, "__len__") != 0); - bool is_instance = false; - - const DCClass *dclass = nullptr; - const DCPackerInterface *current_field = get_current_field(); - if (current_field != nullptr) { - const DCClassParameter *class_param = get_current_field()->as_class_parameter(); - if (class_param != nullptr) { - dclass = class_param->get_class(); - - if (dclass->has_class_def()) { - PyObject *class_def = dclass->get_class_def(); - is_instance = (PyObject_IsInstance(object, dclass->get_class_def()) != 0); - Py_DECREF(class_def); - } - } - } - - // If dclass is not NULL, the packer is expecting a class object. There - // are then two cases: (1) the user has supplied a matching class object, - // or (2) the user has supplied a sequence object. Unfortunately, it may - // be difficult to differentiate these two cases, since a class object may - // also be a sequence object. - - // The rule to differentiate them is: - - // (1) If the supplied class object is an instance of the expected class - // object, it is considered to be a class object. - - // (2) Otherwise, if the supplied class object has a __len__() method - // (i.e. PySequence_Check() returns true), then it is considered to be a - // sequence. - - // (3) Otherwise, it is considered to be a class object. - - if (dclass != nullptr && (is_instance || !is_sequence)) { - // The supplied object is either an instance of the expected class - // object, or it is not a sequence--this is case (1) or (3). - pack_class_object(dclass, object); - } else if (is_sequence) { - // The supplied object is not an instance of the expected class object, - // but it is a sequence. This is case (2). - push(); - int size = PySequence_Size(object); - for (int i = 0; i < size; ++i) { - PyObject *element = PySequence_GetItem(object, i); - if (element != nullptr) { - pack_object(element); - Py_DECREF(element); - } else { - std::cerr << "Unable to extract item " << i << " from sequence.\n"; - } - } - pop(); - } else { - // The supplied object is not a sequence, and we weren't expecting a - // class parameter. This is none of the above, an error. - ostringstream strm; - strm << "Don't know how to pack object: " - << DCField::get_pystr(object); - nassert_raise(strm.str()); - _pack_error = true; - } - } -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Unpacks a Python object of the appropriate type from the stream for the - * current field. This may be an integer or a string for a simple field - * object; if the current field represents a list of fields it will be a - * tuple. - */ -PyObject *DCPacker:: -unpack_object() { - PyObject *object = nullptr; - - DCPackType pack_type = get_pack_type(); - - switch (pack_type) { - case PT_invalid: - object = Py_None; - Py_INCREF(object); - unpack_skip(); - break; - - case PT_double: - { - double value = unpack_double(); - object = PyFloat_FromDouble(value); - } - break; - - case PT_int: - { - int value = unpack_int(); -#if PY_MAJOR_VERSION >= 3 - object = PyLong_FromLong(value); -#else - object = PyInt_FromLong(value); -#endif - } - break; - - case PT_uint: - { - unsigned int value = unpack_uint(); -#if PY_MAJOR_VERSION >= 3 - object = PyLong_FromLong(value); -#else - if (value & 0x80000000) { - object = PyLong_FromUnsignedLong(value); - } else { - object = PyInt_FromLong(value); - } -#endif - } - break; - - case PT_int64: - { - int64_t value = unpack_int64(); - object = PyLong_FromLongLong(value); - } - break; - - case PT_uint64: - { - uint64_t value = unpack_uint64(); - object = PyLong_FromUnsignedLongLong(value); - } - break; - - case PT_blob: -#if PY_MAJOR_VERSION >= 3 - { - string str; - unpack_string(str); - object = PyBytes_FromStringAndSize(str.data(), str.size()); - } - break; -#endif - // On Python 2, fall through to below. - - case PT_string: - { - string str; - unpack_string(str); -#if PY_MAJOR_VERSION >= 3 - object = PyUnicode_FromStringAndSize(str.data(), str.size()); -#else - object = PyString_FromStringAndSize(str.data(), str.size()); -#endif - } - break; - - case PT_class: - { - const DCClassParameter *class_param = get_current_field()->as_class_parameter(); - if (class_param != nullptr) { - const DCClass *dclass = class_param->get_class(); - if (dclass->has_class_def()) { - // If we know what kind of class object this is and it has a valid - // constructor, create the class object instead of just a tuple. - object = unpack_class_object(dclass); - if (object == nullptr) { - std::cerr << "Unable to construct object of class " - << dclass->get_name() << "\n"; - } else { - break; - } - } - } - } - // Fall through (if no constructor) - - // If we don't know what kind of class object it is, or it doesn't have a - // constructor, fall through and make a tuple. - default: - { - // First, build up a list from the nested objects. - object = PyList_New(0); - - push(); - while (more_nested_fields()) { - PyObject *element = unpack_object(); - PyList_Append(object, element); - Py_DECREF(element); - } - pop(); - - if (pack_type != PT_array) { - // For these other kinds of objects, we'll convert the list into a - // tuple. - PyObject *tuple = PyList_AsTuple(object); - Py_DECREF(object); - object = tuple; - } - } - break; - } - - nassertr(object != nullptr, nullptr); - return object; -} -#endif // HAVE_PYTHON - - /** * Parses an object's value according to the DC file syntax (e.g. as a * default value string) and packs it. Returns true on success, false on a @@ -1206,178 +873,3 @@ clear_stack() { _stack = next; } } - -#ifdef HAVE_PYTHON -/** - * Given that the current element is a ClassParameter for a Python class - * object, try to extract the appropriate values from the class object and - * pack in. - */ -void DCPacker:: -pack_class_object(const DCClass *dclass, PyObject *object) { - push(); - while (more_nested_fields() && !_pack_error) { - const DCField *field = get_current_field()->as_field(); - nassertv(field != nullptr); - get_class_element(dclass, object, field); - } - pop(); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -/** - * Given that the current element is a ClassParameter for a Python class for - * which we have a valid constructor, unpack it and fill in its values. - */ -PyObject *DCPacker:: -unpack_class_object(const DCClass *dclass) { - PyObject *class_def = dclass->get_class_def(); - nassertr(class_def != nullptr, nullptr); - - PyObject *object = nullptr; - - if (!dclass->has_constructor()) { - // If the class uses a default constructor, go ahead and create the Python - // object for it now. - object = PyObject_CallObject(class_def, nullptr); - if (object == nullptr) { - return nullptr; - } - } - - push(); - if (object == nullptr && more_nested_fields()) { - // The first nested field will be the constructor. - const DCField *field = get_current_field()->as_field(); - nassertr(field != nullptr, object); - nassertr(field == dclass->get_constructor(), object); - - set_class_element(class_def, object, field); - - // By now, the object should have been constructed. - if (object == nullptr) { - return nullptr; - } - } - while (more_nested_fields()) { - const DCField *field = get_current_field()->as_field(); - nassertr(field != nullptr, object); - - set_class_element(class_def, object, field); - } - pop(); - - return object; -} -#endif // HAVE_PYTHON - - -#ifdef HAVE_PYTHON -/** - * Unpacks the current element and stuffs it on the Python class object in - * whatever way is appropriate. - */ -void DCPacker:: -set_class_element(PyObject *class_def, PyObject *&object, - const DCField *field) { - string field_name = field->get_name(); - DCPackType pack_type = get_pack_type(); - - if (field_name.empty()) { - switch (pack_type) { - case PT_class: - case PT_switch: - // If the field has no name, but it is one of these container objects, - // we want to unpack its nested objects directly into the class. - push(); - while (more_nested_fields()) { - const DCField *field = get_current_field()->as_field(); - nassertv(field != nullptr); - nassertv(object != nullptr); - set_class_element(class_def, object, field); - } - pop(); - break; - - default: - // Otherwise, we just skip over the field. - unpack_skip(); - } - - } else { - // If the field does have a name, we will want to store it on the class, - // either by calling a method (for a PT_field pack_type) or by setting a - // value (for any other kind of pack_type). - - PyObject *element = unpack_object(); - - if (pack_type == PT_field) { - if (object == nullptr) { - // If the object hasn't been constructed yet, assume this is the - // constructor. - object = PyObject_CallObject(class_def, element); - - } else { - if (PyObject_HasAttrString(object, (char *)field_name.c_str())) { - PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str()); - if (func != nullptr) { - PyObject *result = PyObject_CallObject(func, element); - Py_XDECREF(result); - Py_DECREF(func); - } - } - } - - } else { - nassertv(object != nullptr); - PyObject_SetAttrString(object, (char *)field_name.c_str(), element); - } - - Py_DECREF(element); - } -} -#endif // HAVE_PYTHON - - -#ifdef HAVE_PYTHON -/** - * Gets the current element from the Python object and packs it. - */ -void DCPacker:: -get_class_element(const DCClass *dclass, PyObject *object, - const DCField *field) { - string field_name = field->get_name(); - DCPackType pack_type = get_pack_type(); - - if (field_name.empty()) { - switch (pack_type) { - case PT_class: - case PT_switch: - // If the field has no name, but it is one of these container objects, - // we want to get its nested objects directly from the class. - push(); - while (more_nested_fields() && !_pack_error) { - const DCField *field = get_current_field()->as_field(); - nassertv(field != nullptr); - get_class_element(dclass, object, field); - } - pop(); - break; - - default: - // Otherwise, we just pack the default value. - pack_default_value(); - } - - } else { - // If the field does have a name, we will want to get it from the class - // and pack it. It just so happens that there's already a method that - // does this on DCClass. - - if (!dclass->pack_required_field(*this, object, field)) { - _pack_error = true; - } - } -} -#endif // HAVE_PYTHON diff --git a/direct/src/dcparser/dcPacker.h b/direct/src/dcparser/dcPacker.h index dce28dbc30..32f7e57de4 100644 --- a/direct/src/dcparser/dcPacker.h +++ b/direct/src/dcparser/dcPacker.h @@ -19,7 +19,10 @@ #include "dcSubatomicType.h" #include "dcPackData.h" #include "dcPackerCatalog.h" -#include "dcPython.h" + +#ifdef WITHIN_PANDA +#include "extension.h" +#endif class DCClass; class DCSwitchParameter; @@ -104,10 +107,8 @@ public: PUBLISHED: -#ifdef HAVE_PYTHON - void pack_object(PyObject *object); - PyObject *unpack_object(); -#endif + EXTENSION(void pack_object(PyObject *object)); + EXTENSION(PyObject *unpack_object()); bool parse_and_pack(const std::string &formatted_object); bool parse_and_pack(std::istream &in); @@ -195,14 +196,12 @@ private: void clear(); void clear_stack(); -#ifdef HAVE_PYTHON - void pack_class_object(const DCClass *dclass, PyObject *object); - PyObject *unpack_class_object(const DCClass *dclass); - void set_class_element(PyObject *class_def, PyObject *&object, - const DCField *field); - void get_class_element(const DCClass *dclass, PyObject *object, - const DCField *field); -#endif + EXTENSION(void pack_class_object(const DCClass *dclass, PyObject *object)); + EXTENSION(PyObject *unpack_class_object(const DCClass *dclass)); + EXTENSION(void set_class_element(PyObject *class_def, PyObject *&object, + const DCField *field)); + EXTENSION(void get_class_element(const DCClass *dclass, PyObject *object, + const DCField *field)); private: enum Mode { @@ -223,7 +222,7 @@ private: const DCPackerCatalog *_catalog; const DCPackerCatalog::LiveCatalog *_live_catalog; - class StackElement { + class EXPCL_DIRECT_DCPARSER StackElement { public: // As an optimization, we implement operator new and delete here to // minimize allocation overhead during push() and pop(). @@ -258,6 +257,10 @@ private: bool _parse_error; bool _pack_error; bool _range_error; + +#ifdef WITHIN_PANDA + friend class Extension; +#endif }; #include "dcPacker.I" diff --git a/direct/src/dcparser/dcPacker_ext.cxx b/direct/src/dcparser/dcPacker_ext.cxx new file mode 100644 index 0000000000..8e7eb1ec5f --- /dev/null +++ b/direct/src/dcparser/dcPacker_ext.cxx @@ -0,0 +1,508 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file dcPacker_ext.cxx + * @author CFSworks + * @date 2019-07-03 + */ + +#include "dcPacker_ext.h" +#include "dcClass_ext.h" +#include "dcField_ext.h" + +#include "dcClassParameter.h" + +#ifdef HAVE_PYTHON + +/** + * Packs the Python object of whatever type into the packer. Each numeric + * object and string object maps to the corresponding pack_value() call; a + * tuple or sequence maps to a push() followed by all of the tuple's contents + * followed by a pop(). + */ +void Extension:: +pack_object(PyObject *object) { + nassertv(_this->_mode == DCPacker::Mode::M_pack || + _this->_mode == DCPacker::Mode::M_repack); + DCPackType pack_type = _this->get_pack_type(); + + // had to add this for basic 64 and unsigned data to get packed right .. Not + // sure if we can just do the rest this way.. + + switch(pack_type) { + case PT_int64: + if (PyLong_Check(object)) { + _this->pack_int64(PyLong_AsLongLong(object)); + return; + } +#if PY_MAJOR_VERSION < 3 + else if (PyInt_Check(object)) { + _this->pack_int64(PyInt_AsLong(object)); + return; + } +#endif + break; + + case PT_uint64: + if (PyLong_Check(object)) { + _this->pack_uint64(PyLong_AsUnsignedLongLong(object)); + return; + } +#if PY_MAJOR_VERSION < 3 + else if (PyInt_Check(object)) { + PyObject *obj1 = PyNumber_Long(object); + _this->pack_int(PyLong_AsUnsignedLongLong(obj1)); + Py_DECREF(obj1); + return; + } +#endif + break; + + case PT_int: + if (PyLong_Check(object)) { + _this->pack_int(PyLong_AsLong(object)); + return; + } +#if PY_MAJOR_VERSION < 3 + else if (PyInt_Check(object)) { + _this->pack_int(PyInt_AsLong(object)); + return; + } +#endif + break; + + case PT_uint: + if (PyLong_Check(object)) { + _this->pack_uint(PyLong_AsUnsignedLong(object)); + return; + } +#if PY_MAJOR_VERSION < 3 + else if (PyInt_Check(object)) { + PyObject *obj1 = PyNumber_Long(object); + _this->pack_uint(PyLong_AsUnsignedLong(obj1)); + Py_DECREF(obj1); + return; + } +#endif + break; + + default: + break; + } + + if (PyLong_Check(object)) { + _this->pack_int(PyLong_AsLong(object)); +#if PY_MAJOR_VERSION < 3 + } else if (PyInt_Check(object)) { + _this->pack_int(PyInt_AS_LONG(object)); +#endif + } else if (PyFloat_Check(object)) { + _this->pack_double(PyFloat_AS_DOUBLE(object)); + } else if (PyLong_Check(object)) { + _this->pack_int64(PyLong_AsLongLong(object)); +#if PY_MAJOR_VERSION >= 3 + } else if (PyUnicode_Check(object)) { + const char *buffer; + Py_ssize_t length; + buffer = PyUnicode_AsUTF8AndSize(object, &length); + if (buffer) { + _this->pack_string(std::string(buffer, length)); + } + } else if (PyBytes_Check(object)) { + const unsigned char *buffer; + Py_ssize_t length; + PyBytes_AsStringAndSize(object, (char **)&buffer, &length); + if (buffer) { + _this->pack_blob(vector_uchar(buffer, buffer + length)); + } +#else + } else if (PyString_Check(object) || PyUnicode_Check(object)) { + char *buffer; + Py_ssize_t length; + PyString_AsStringAndSize(object, &buffer, &length); + if (buffer) { + _this->pack_string(std::string(buffer, length)); + } +#endif + } else { + // For some reason, PySequence_Check() is incorrectly reporting that a + // class instance is a sequence, even if it doesn't provide __len__, so we + // double-check by testing for __len__ explicitly. + bool is_sequence = + (PySequence_Check(object) != 0) && + (PyObject_HasAttrString(object, "__len__") != 0); + bool is_instance = false; + + const DCClass *dclass = nullptr; + const DCPackerInterface *current_field = _this->get_current_field(); + if (current_field != nullptr) { + const DCClassParameter *class_param = _this->get_current_field()->as_class_parameter(); + if (class_param != nullptr) { + dclass = class_param->get_class(); + + if (invoke_extension(dclass).has_class_def()) { + PyObject *class_def = invoke_extension(dclass).get_class_def(); + is_instance = (PyObject_IsInstance(object, invoke_extension(dclass).get_class_def()) != 0); + Py_DECREF(class_def); + } + } + } + + // If dclass is not NULL, the packer is expecting a class object. There + // are then two cases: (1) the user has supplied a matching class object, + // or (2) the user has supplied a sequence object. Unfortunately, it may + // be difficult to differentiate these two cases, since a class object may + // also be a sequence object. + + // The rule to differentiate them is: + + // (1) If the supplied class object is an instance of the expected class + // object, it is considered to be a class object. + + // (2) Otherwise, if the supplied class object has a __len__() method + // (i.e. PySequence_Check() returns true), then it is considered to be a + // sequence. + + // (3) Otherwise, it is considered to be a class object. + + if (dclass != nullptr && (is_instance || !is_sequence)) { + // The supplied object is either an instance of the expected class + // object, or it is not a sequence--this is case (1) or (3). + pack_class_object(dclass, object); + } else if (is_sequence) { + // The supplied object is not an instance of the expected class object, + // but it is a sequence. This is case (2). + _this->push(); + int size = PySequence_Size(object); + for (int i = 0; i < size; ++i) { + PyObject *element = PySequence_GetItem(object, i); + if (element != nullptr) { + pack_object(element); + Py_DECREF(element); + } else { + std::cerr << "Unable to extract item " << i << " from sequence.\n"; + } + } + _this->pop(); + } else { + // The supplied object is not a sequence, and we weren't expecting a + // class parameter. This is none of the above, an error. + std::ostringstream strm; + strm << "Don't know how to pack object: " + << Extension::get_pystr(object); + nassert_raise(strm.str()); + _this->_pack_error = true; + } + } +} + +/** + * Unpacks a Python object of the appropriate type from the stream for the + * current field. This may be an integer or a string for a simple field + * object; if the current field represents a list of fields it will be a + * tuple. + */ +PyObject *Extension:: +unpack_object() { + PyObject *object = nullptr; + + DCPackType pack_type = _this->get_pack_type(); + + switch (pack_type) { + case PT_invalid: + object = Py_None; + Py_INCREF(object); + _this->unpack_skip(); + break; + + case PT_double: + { + double value = _this->unpack_double(); + object = PyFloat_FromDouble(value); + } + break; + + case PT_int: + { + int value = _this->unpack_int(); +#if PY_MAJOR_VERSION >= 3 + object = PyLong_FromLong(value); +#else + object = PyInt_FromLong(value); +#endif + } + break; + + case PT_uint: + { + unsigned int value = _this->unpack_uint(); +#if PY_MAJOR_VERSION >= 3 + object = PyLong_FromLong(value); +#else + if (value & 0x80000000) { + object = PyLong_FromUnsignedLong(value); + } else { + object = PyInt_FromLong(value); + } +#endif + } + break; + + case PT_int64: + { + int64_t value = _this->unpack_int64(); + object = PyLong_FromLongLong(value); + } + break; + + case PT_uint64: + { + uint64_t value = _this->unpack_uint64(); + object = PyLong_FromUnsignedLongLong(value); + } + break; + + case PT_blob: +#if PY_MAJOR_VERSION >= 3 + { + std::string str; + _this->unpack_string(str); + object = PyBytes_FromStringAndSize(str.data(), str.size()); + } + break; +#endif + // On Python 2, fall through to below. + + case PT_string: + { + std::string str; + _this->unpack_string(str); +#if PY_MAJOR_VERSION >= 3 + object = PyUnicode_FromStringAndSize(str.data(), str.size()); +#else + object = PyString_FromStringAndSize(str.data(), str.size()); +#endif + } + break; + + case PT_class: + { + const DCClassParameter *class_param = _this->get_current_field()->as_class_parameter(); + if (class_param != nullptr) { + const DCClass *dclass = class_param->get_class(); + if (invoke_extension(dclass).has_class_def()) { + // If we know what kind of class object this is and it has a valid + // constructor, create the class object instead of just a tuple. + object = unpack_class_object(dclass); + if (object == nullptr) { + std::cerr << "Unable to construct object of class " + << dclass->get_name() << "\n"; + } else { + break; + } + } + } + } + // Fall through (if no constructor) + + // If we don't know what kind of class object it is, or it doesn't have a + // constructor, fall through and make a tuple. + default: + { + // First, build up a list from the nested objects. + object = PyList_New(0); + + _this->push(); + while (_this->more_nested_fields()) { + PyObject *element = unpack_object(); + PyList_Append(object, element); + Py_DECREF(element); + } + _this->pop(); + + if (pack_type != PT_array) { + // For these other kinds of objects, we'll convert the list into a + // tuple. + PyObject *tuple = PyList_AsTuple(object); + Py_DECREF(object); + object = tuple; + } + } + break; + } + + nassertr(object != nullptr, nullptr); + return object; +} + +/** + * Given that the current element is a ClassParameter for a Python class + * object, try to extract the appropriate values from the class object and + * pack in. + */ +void Extension:: +pack_class_object(const DCClass *dclass, PyObject *object) { + _this->push(); + while (_this->more_nested_fields() && !_this->_pack_error) { + const DCField *field = _this->get_current_field()->as_field(); + nassertv(field != nullptr); + get_class_element(dclass, object, field); + } + _this->pop(); +} + +/** + * Given that the current element is a ClassParameter for a Python class for + * which we have a valid constructor, unpack it and fill in its values. + */ +PyObject *Extension:: +unpack_class_object(const DCClass *dclass) { + PyObject *class_def = invoke_extension(dclass).get_class_def(); + nassertr(class_def != nullptr, nullptr); + + PyObject *object = nullptr; + + if (!dclass->has_constructor()) { + // If the class uses a default constructor, go ahead and create the Python + // object for it now. + object = PyObject_CallObject(class_def, nullptr); + if (object == nullptr) { + return nullptr; + } + } + + _this->push(); + if (object == nullptr && _this->more_nested_fields()) { + // The first nested field will be the constructor. + const DCField *field = _this->get_current_field()->as_field(); + nassertr(field != nullptr, object); + nassertr(field == dclass->get_constructor(), object); + + set_class_element(class_def, object, field); + + // By now, the object should have been constructed. + if (object == nullptr) { + return nullptr; + } + } + while (_this->more_nested_fields()) { + const DCField *field = _this->get_current_field()->as_field(); + nassertr(field != nullptr, object); + + set_class_element(class_def, object, field); + } + _this->pop(); + + return object; +} + +/** + * Unpacks the current element and stuffs it on the Python class object in + * whatever way is appropriate. + */ +void Extension:: +set_class_element(PyObject *class_def, PyObject *&object, + const DCField *field) { + std::string field_name = field->get_name(); + DCPackType pack_type = _this->get_pack_type(); + + if (field_name.empty()) { + switch (pack_type) { + case PT_class: + case PT_switch: + // If the field has no name, but it is one of these container objects, + // we want to unpack its nested objects directly into the class. + _this->push(); + while (_this->more_nested_fields()) { + const DCField *field = _this->get_current_field()->as_field(); + nassertv(field != nullptr); + nassertv(object != nullptr); + set_class_element(class_def, object, field); + } + _this->pop(); + break; + + default: + // Otherwise, we just skip over the field. + _this->unpack_skip(); + } + + } else { + // If the field does have a name, we will want to store it on the class, + // either by calling a method (for a PT_field pack_type) or by setting a + // value (for any other kind of pack_type). + + PyObject *element = unpack_object(); + + if (pack_type == PT_field) { + if (object == nullptr) { + // If the object hasn't been constructed yet, assume this is the + // constructor. + object = PyObject_CallObject(class_def, element); + + } else { + if (PyObject_HasAttrString(object, (char *)field_name.c_str())) { + PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str()); + if (func != nullptr) { + PyObject *result = PyObject_CallObject(func, element); + Py_XDECREF(result); + Py_DECREF(func); + } + } + } + + } else { + nassertv(object != nullptr); + PyObject_SetAttrString(object, (char *)field_name.c_str(), element); + } + + Py_DECREF(element); + } +} + +/** + * Gets the current element from the Python object and packs it. + */ +void Extension:: +get_class_element(const DCClass *dclass, PyObject *object, + const DCField *field) { + std::string field_name = field->get_name(); + DCPackType pack_type = _this->get_pack_type(); + + if (field_name.empty()) { + switch (pack_type) { + case PT_class: + case PT_switch: + // If the field has no name, but it is one of these container objects, + // we want to get its nested objects directly from the class. + _this->push(); + while (_this->more_nested_fields() && !_this->_pack_error) { + const DCField *field = _this->get_current_field()->as_field(); + nassertv(field != nullptr); + get_class_element(dclass, object, field); + } + _this->pop(); + break; + + default: + // Otherwise, we just pack the default value. + _this->pack_default_value(); + } + + } else { + // If the field does have a name, we will want to get it from the class + // and pack it. It just so happens that there's already a method that + // does this on DCClass. + + if (!invoke_extension(dclass).pack_required_field(*_this, object, field)) { + _this->_pack_error = true; + } + } +} + +#endif // HAVE_PYTHON diff --git a/direct/src/dcparser/dcPacker_ext.h b/direct/src/dcparser/dcPacker_ext.h new file mode 100644 index 0000000000..c7e8333a5f --- /dev/null +++ b/direct/src/dcparser/dcPacker_ext.h @@ -0,0 +1,45 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file dcPacker_ext.h + * @author CFSworks + * @date 2019-07-03 + */ + +#ifndef DCPACKER_EXT_H +#define DCPACKER_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "dcPacker.h" +#include "py_panda.h" + +/** + * This class defines the extension methods for DCPacker, which are called + * instead of any C++ methods with the same prototype. + */ +template<> +class Extension : public ExtensionBase { +public: + void pack_object(PyObject *object); + PyObject *unpack_object(); + + void pack_class_object(const DCClass *dclass, PyObject *object); + PyObject *unpack_class_object(const DCClass *dclass); + void set_class_element(PyObject *class_def, PyObject *&object, + const DCField *field); + void get_class_element(const DCClass *dclass, PyObject *object, + const DCField *field); +}; + +#endif // HAVE_PYTHON + +#endif // DCPACKER_EXT_H diff --git a/direct/src/dcparser/dcPython.h b/direct/src/dcparser/dcPython.h deleted file mode 100644 index 513f1b008e..0000000000 --- a/direct/src/dcparser/dcPython.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file dcPython.h - * @author drose - * @date 2004-06-22 - */ - -#ifndef DCPYTHON_H -#define DCPYTHON_H - -// The only purpose of this file is to serve as a common place to put the -// nonsense associated with #including . - -#ifdef HAVE_PYTHON - -#define PY_SSIZE_T_CLEAN 1 - -#undef _POSIX_C_SOURCE -#undef _XOPEN_SOURCE -#include - -// Python 2.5 adds Py_ssize_t; earlier versions don't have it. -#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) -typedef int Py_ssize_t; -#define PY_SSIZE_T_MAX INT_MAX -#define PY_SSIZE_T_MIN INT_MIN -#endif - -// Several interfaces in this module that use Python also require these header -// files, so we might as well pick them up too. -#include "datagram.h" -#include "datagramIterator.h" - -#endif // HAVE_PYTHON - -#endif diff --git a/direct/src/dcparser/p3dcparser_ext_composite.cxx b/direct/src/dcparser/p3dcparser_ext_composite.cxx new file mode 100644 index 0000000000..3d05bfd964 --- /dev/null +++ b/direct/src/dcparser/p3dcparser_ext_composite.cxx @@ -0,0 +1,3 @@ +#include "dcClass_ext.cxx" +#include "dcField_ext.cxx" +#include "dcPacker_ext.cxx" diff --git a/direct/src/directscripts/DetectPanda3D.js b/direct/src/directscripts/DetectPanda3D.js deleted file mode 100644 index 52c57cc034..0000000000 --- a/direct/src/directscripts/DetectPanda3D.js +++ /dev/null @@ -1,99 +0,0 @@ -// Based on Apple sample code at -// http://developer.apple.com/internet/webcontent/examples/detectplugins_source.html - - -// initialize global variables -var detectableWithVB = false; -var pluginFound = false; - -function goURL(daURL) { - // Assume we have Javascript 1.1 functionality. - window.location.replace(daURL); - return; -} - -function redirectCheck(pluginFound, redirectURL, redirectIfFound) { - // check for redirection - if( redirectURL && ((pluginFound && redirectIfFound) || - (!pluginFound && !redirectIfFound)) ) { - // go away - goURL(redirectURL); - return pluginFound; - } else { - // stay here and return result of plugin detection - return pluginFound; - } -} - -function canDetectPlugins() { - if( detectableWithVB || (navigator.plugins && navigator.plugins.length > 0) ) { - return true; - } else { - return false; - } -} - -function detectPanda3D(redirectURL, redirectIfFound) { - pluginFound = detectPlugin('Panda3D'); - // if not found, try to detect with VisualBasic - if(!pluginFound && detectableWithVB) { - pluginFound = detectActiveXControl('P3DACTIVEX.P3DActiveXCtrl.1'); - } - // check for redirection - return redirectCheck(pluginFound, redirectURL, redirectIfFound); -} - -function detectPlugin() { - // allow for multiple checks in a single pass - var daPlugins = detectPlugin.arguments; - // consider pluginFound to be false until proven true - var pluginFound = false; - // if plugins array is there and not fake - if (navigator.plugins && navigator.plugins.length > 0) { - var pluginsArrayLength = navigator.plugins.length; - // for each plugin... - for (pluginsArrayCounter=0; pluginsArrayCounter < pluginsArrayLength; pluginsArrayCounter++ ) { - // loop through all desired names and check each against the current plugin name - var numFound = 0; - for(namesCounter=0; namesCounter < daPlugins.length; namesCounter++) { - // if desired plugin name is found in either plugin name or description - if( (navigator.plugins[pluginsArrayCounter].name.indexOf(daPlugins[namesCounter]) >= 0) || - (navigator.plugins[pluginsArrayCounter].description.indexOf(daPlugins[namesCounter]) >= 0) ) { - // this name was found - numFound++; - } - } - // now that we have checked all the required names against this one plugin, - // if the number we found matches the total number provided then we were successful - if(numFound == daPlugins.length) { - pluginFound = true; - // if we've found the plugin, we can stop looking through at the rest of the plugins - break; - } - } - } - return pluginFound; -} // detectPlugin - - -// Here we write out the VBScript block for MSIE Windows -if ((navigator.userAgent.indexOf('MSIE') != -1) && (navigator.userAgent.indexOf('Win') != -1)) { - document.writeln(''); -} diff --git a/direct/src/directscripts/Doxyfile.cxx b/direct/src/directscripts/Doxyfile.cxx index 6c81025960..db71c81b3b 100644 --- a/direct/src/directscripts/Doxyfile.cxx +++ b/direct/src/directscripts/Doxyfile.cxx @@ -432,7 +432,7 @@ EXTRACT_STATIC = NO # for Java sources. # The default value is: YES. -EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local methods, # which are defined in the implementation section but not in the interface are @@ -802,6 +802,11 @@ EXCLUDE = dtool/src/parser-inc \ panda/src/iphone \ panda/src/tinydisplay \ panda/src/movies/dr_flac.h \ + panda/src/windisplay/winDetectDx.h \ + panda/src/wgldisplay/wglext.h \ + panda/src/glxdisplay/panda_glxext.h \ + pandatool/src/gtk-stats \ + dtool/src/dtoolbase/fakestringstream.h \ dtool/src/dtoolbase/pdtoa.cxx \ dtool/src/dtoolutil/panda_getopt_long.h \ dtool/src/dtoolutil/panda_getopt_impl.h \ @@ -840,7 +845,10 @@ EXCLUDE_PATTERNS = */Opt*-*/* \ # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = InterrogateFunctionWrapper::Parameter \ + CollisionFloorMesh::TriangleIndices \ + tagTOUCHINPUT \ + WINDOW_METRICS # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include @@ -1018,7 +1026,7 @@ ALPHABETICAL_INDEX = YES # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. -COLS_IN_ALPHA_INDEX = 5 +COLS_IN_ALPHA_INDEX = 3 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag @@ -1972,6 +1980,7 @@ INCLUDE_FILE_PATTERNS = PREDEFINED = TVOLATILE= \ INLINE=inline \ + ALWAYS_INLINE=inline \ PUBLISHED=public \ protected=private \ INLINE_LINMATH=inline \ @@ -1986,7 +1995,8 @@ PREDEFINED = TVOLATILE= \ MAKE_SEQ(x)= \ MAKE_SEQ_PROPERTY(x)= \ MAKE_MAP_PROPERTY(x)= \ - MAKE_MAP_KEYS_SEQ(x)= + MAKE_MAP_KEYS_SEQ(x)= \ + RETURNS_ALIGNED(x)= # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/direct/src/directscripts/Doxyfile.python b/direct/src/directscripts/Doxyfile.python index 2119f7b321..170c3917c1 100644 --- a/direct/src/directscripts/Doxyfile.python +++ b/direct/src/directscripts/Doxyfile.python @@ -805,7 +805,7 @@ ALPHABETICAL_INDEX = YES # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) -COLS_IN_ALPHA_INDEX = 5 +COLS_IN_ALPHA_INDEX = 2 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. diff --git a/direct/src/directscripts/RunPanda3D.js b/direct/src/directscripts/RunPanda3D.js deleted file mode 100644 index b26d3b99c3..0000000000 --- a/direct/src/directscripts/RunPanda3D.js +++ /dev/null @@ -1,132 +0,0 @@ -// This script injects the appropriate syntax into the document to -// embed Panda3D, either for the ActiveX or Mozilla-based plugin. - -// It is also possible to write browser-independent code by nesting -// tags, but this causes problems when you need to reference -// the particular object that is actually running (which object is -// it?) for scripting purposes. - -// This script writes only a single tag, and it can be -// assigned the id you specify, avoiding this ambiguity. - -var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false; -var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false; -var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false; - - -function P3D_Generateobj(objAttrs, params, embedAttrs, imageAttrs) -{ - var str = ''; - - if (isIE && isWin && !isOpera) - { - str += ' '; - } - } - else - { - str += ''; - } - str += ' required_size: + break + + if from_size > required_size: + Icon.notify.warning("Generating %dx%d icon by scaling down %dx%d image" % (required_size, required_size, from_size, from_size)) + + image = PNMImage(required_size, required_size) + if self.images[from_size].hasAlpha(): + image.addAlpha() + image.quickFilterFrom(self.images[from_size]) + self.images[required_size] = image + else: + Icon.notify.warning("Cannot generate %dx%d icon; no higher resolution image available" % (required_size, required_size)) + + def _write_bitmap(self, fp, image, size, bpp): + """ Writes the bitmap header and data of an .ico file. """ + + fp.write(struct.pack('= 256: + # Find closest pixel instead. + index = closest_indices[index - 256] + fp.write(struct.pack('> 3) & 3) + for y in xrange(size): + mask = 0 + num_bits = 7 + for x in xrange(size): + a = image.get_alpha_val(x, size - y - 1) + if a <= 1: + mask |= (1 << num_bits) + num_bits -= 1 + if num_bits < 0: + fp.write(struct.pack('> 3 + if andsize % 4 != 0: + andsize += 4 - (andsize % 4) + fp.write(b'\x00' * (andsize * size)) + + def makeICO(self, fn): + """ Writes the images to a Windows ICO file. Returns True on success. """ + + if not isinstance(fn, Filename): + fn = Filename.fromOsSpecific(fn) + fn.setBinary() + + # ICO files only support resolutions up to 256x256. + count = 0 + for size in self.images.keys(): + if size < 256: + count += 1 + if size <= 256: + count += 1 + dataoffs = 6 + count * 16 + + ico = open(fn, 'wb') + ico.write(struct.pack('= 256: + continue + ico.write(struct.pack('> 3 + if andsize % 4 != 0: + andsize += 4 - (andsize % 4) + datasize = 40 + 256 * 4 + (xorsize + andsize) * size + + ico.write(struct.pack(' 256: + continue + elif size == 256: + ico.write(b'\0\0') + else: + ico.write(struct.pack('> 3 + if andsize % 4 != 0: + andsize += 4 - (andsize % 4) + datasize = 40 + (xorsize + andsize) * size + + ico.write(struct.pack('I', len(pngdata))) + icns.write(pngdata) + + elif size in icon_types: + # If it has an alpha channel, we write out a mask too. + if image.hasAlpha(): + icns.write(mask_types[size]) + icns.write(struct.pack('>I', size * size + 8)) + + for y in xrange(size): + for x in xrange(size): + icns.write(struct.pack('I', size * size * 4 + 8)) + + for y in xrange(size): + for x in xrange(size): + r, g, b = image.getXel(x, y) + icns.write(struct.pack('>BBBB', 0, int(r * 255), int(g * 255), int(b * 255))) + + length = icns.tell() + icns.seek(4) + icns.write(struct.pack('>I', length)) + icns.close() + + return True + + diff --git a/direct/src/distributed/cConnectionRepository.cxx b/direct/src/distributed/cConnectionRepository.cxx index 16aea6e8dd..59617330f5 100644 --- a/direct/src/distributed/cConnectionRepository.cxx +++ b/direct/src/distributed/cConnectionRepository.cxx @@ -26,6 +26,7 @@ #ifdef HAVE_PYTHON #include "py_panda.h" +#include "dcClass_ext.h" #endif using std::endl; @@ -736,7 +737,7 @@ handle_update_field() { // get into trouble if it tried to delete the object from the doId2do // map. Py_INCREF(distobj); - dclass->receive_update(distobj, _di); + invoke_extension(dclass).receive_update(distobj, _di); Py_DECREF(distobj); if (PyErr_Occurred()) { @@ -820,7 +821,7 @@ handle_update_field_owner() { // make a copy of the datagram iterator so that we can use the main // iterator for the non-owner update DatagramIterator _odi(_di); - dclass->receive_update(distobjOV, _odi); + invoke_extension(dclass).receive_update(distobjOV, _odi); Py_DECREF(distobjOV); if (PyErr_Occurred()) { @@ -861,7 +862,7 @@ handle_update_field_owner() { // get into trouble if it tried to delete the object from the doId2do // map. Py_INCREF(distobj); - dclass->receive_update(distobj, _di); + invoke_extension(dclass).receive_update(distobj, _di); Py_DECREF(distobj); if (PyErr_Occurred()) { diff --git a/direct/src/distributed/cConnectionRepository.h b/direct/src/distributed/cConnectionRepository.h index c67d4aad23..2db877215b 100644 --- a/direct/src/distributed/cConnectionRepository.h +++ b/direct/src/distributed/cConnectionRepository.h @@ -53,7 +53,7 @@ class SocketStream; * the C++ layer, while server messages that are not understood by the C++ * layer are returned up to the Python layer for processing. */ -class EXPCL_DIRECT_DISTRIBUTED CConnectionRepository { +class CConnectionRepository { PUBLISHED: explicit CConnectionRepository(bool has_owner_view = false, bool threaded_net = false); diff --git a/direct/src/distributed/cDistributedSmoothNodeBase.cxx b/direct/src/distributed/cDistributedSmoothNodeBase.cxx index 67e273644f..564ac6ac9b 100644 --- a/direct/src/distributed/cDistributedSmoothNodeBase.cxx +++ b/direct/src/distributed/cDistributedSmoothNodeBase.cxx @@ -18,6 +18,10 @@ #include "dcmsgtypes.h" #include "config_distributed.h" +#ifdef HAVE_PYTHON +#include "py_panda.h" +#endif + static const PN_stdfloat smooth_node_epsilon = 0.01; static const double network_time_precision = 100.0; // Matches ClockDelta.py diff --git a/direct/src/distributed/cDistributedSmoothNodeBase.h b/direct/src/distributed/cDistributedSmoothNodeBase.h index 6c57f6a2bf..bebe55fcdb 100644 --- a/direct/src/distributed/cDistributedSmoothNodeBase.h +++ b/direct/src/distributed/cDistributedSmoothNodeBase.h @@ -18,7 +18,6 @@ #include "nodePath.h" #include "dcbase.h" #include "dcPacker.h" -#include "dcPython.h" // to pick up Python.h #include "clockObject.h" class DCClass; @@ -28,7 +27,7 @@ class CConnectionRepository; * This class defines some basic methods of DistributedSmoothNodeBase which * have been moved into C++ as a performance optimization. */ -class EXPCL_DIRECT_DISTRIBUTED CDistributedSmoothNodeBase { +class CDistributedSmoothNodeBase { PUBLISHED: CDistributedSmoothNodeBase(); ~CDistributedSmoothNodeBase(); diff --git a/direct/src/distributed/config_distributed.h b/direct/src/distributed/config_distributed.h index 22814bfa13..1690295e5a 100644 --- a/direct/src/distributed/config_distributed.h +++ b/direct/src/distributed/config_distributed.h @@ -23,10 +23,10 @@ NotifyCategoryDecl(distributed, EXPCL_DIRECT_DISTRIBUTED, EXPTP_DIRECT_DISTRIBUTED); -extern ConfigVariableInt game_server_timeout_ms; -extern ConfigVariableDouble min_lag; -extern ConfigVariableDouble max_lag; -extern ConfigVariableBool handle_datagrams_internally; +extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableInt game_server_timeout_ms; +extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableDouble min_lag; +extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableDouble max_lag; +extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableBool handle_datagrams_internally; extern EXPCL_DIRECT_DISTRIBUTED void init_libdistributed(); diff --git a/direct/src/filter/FilterManager.py b/direct/src/filter/FilterManager.py index 4150cba568..c20cbed139 100644 --- a/direct/src/filter/FilterManager.py +++ b/direct/src/filter/FilterManager.py @@ -124,7 +124,7 @@ class FilterManager(DirectObject): return winx,winy - def renderSceneInto(self, depthtex=None, colortex=None, auxtex=None, auxbits=0, textures=None): + def renderSceneInto(self, depthtex=None, colortex=None, auxtex=None, auxbits=0, textures=None, fbprops=None): """ Causes the scene to be rendered into the supplied textures instead of into the original window. Puts a fullscreen quad @@ -185,7 +185,10 @@ class FilterManager(DirectObject): # Choose the size of the offscreen buffer. (winx, winy) = self.getScaledSize(1,1,1) - buffer = self.createBuffer("filter-base", winx, winy, texgroup) + if fbprops is not None: + buffer = self.createBuffer("filter-base", winx, winy, texgroup, fbprops=fbprops) + else: + buffer = self.createBuffer("filter-base", winx, winy, texgroup) if (buffer == None): return None @@ -236,7 +239,7 @@ class FilterManager(DirectObject): return quad - def renderQuadInto(self, name="filter-stage", mul=1, div=1, align=1, depthtex=None, colortex=None, auxtex0=None, auxtex1=None): + def renderQuadInto(self, name="filter-stage", mul=1, div=1, align=1, depthtex=None, colortex=None, auxtex0=None, auxtex1=None, fbprops=None): """ Creates an offscreen buffer for an intermediate computation. Installs a quad into the buffer. Returns @@ -250,7 +253,10 @@ class FilterManager(DirectObject): depthbits = bool(depthtex != None) - buffer = self.createBuffer(name, winx, winy, texgroup, depthbits) + if fbprops is not None: + buffer = self.createBuffer(name, winx, winy, texgroup, depthbits, fbprops=fbprops) + else: + buffer = self.createBuffer(name, winx, winy, texgroup, depthbits) if (buffer == None): return None @@ -287,7 +293,7 @@ class FilterManager(DirectObject): return quad - def createBuffer(self, name, xsize, ysize, texgroup, depthbits=1): + def createBuffer(self, name, xsize, ysize, texgroup, depthbits=1, fbprops=None): """ Low-level buffer creation. Not intended for public use. """ winprops = WindowProperties() @@ -297,6 +303,9 @@ class FilterManager(DirectObject): props.setRgbColor(1) props.setDepthBits(depthbits) props.setStereo(self.win.isStereo()) + if fbprops is not None: + props.addProperties(fbprops) + depthtex, colortex, auxtex0, auxtex1 = texgroup if (auxtex0 != None): props.setAuxRgba(1) diff --git a/direct/src/gui/DirectDialog.py b/direct/src/gui/DirectDialog.py index 33852dc504..ba56fdbfa0 100644 --- a/direct/src/gui/DirectDialog.py +++ b/direct/src/gui/DirectDialog.py @@ -3,6 +3,7 @@ __all__ = ['findDialog', 'cleanupDialog', 'DirectDialog', 'OkDialog', 'OkCancelDialog', 'YesNoDialog', 'YesNoCancelDialog', 'RetryCancelDialog'] from panda3d.core import * +from direct.showbase import ShowBaseGlobal from . import DirectGuiGlobals as DGG from .DirectFrame import * from .DirectButton import * @@ -207,7 +208,7 @@ class DirectDialog(DirectFrame): image = None # Get size of text/geom without image (for state 0) if image: - image.reparentTo(hidden) + image.reparentTo(ShowBaseGlobal.hidden) bounds = self.stateNodePath[0].getTightBounds() if image: image.reparentTo(self.stateNodePath[0]) diff --git a/direct/src/gui/DirectEntry.py b/direct/src/gui/DirectEntry.py index e345c526e9..d614a3825a 100644 --- a/direct/src/gui/DirectEntry.py +++ b/direct/src/gui/DirectEntry.py @@ -4,6 +4,7 @@ text entered using the keyboard.""" __all__ = ['DirectEntry'] from panda3d.core import * +from direct.showbase import ShowBaseGlobal from . import DirectGuiGlobals as DGG from .DirectFrame import * from .OnscreenText import OnscreenText @@ -94,7 +95,7 @@ class DirectEntry(DirectFrame): self.onscreenText = self.createcomponent( 'text', (), None, OnscreenText, - (), parent = hidden, + (), parent = ShowBaseGlobal.hidden, # Pass in empty text to avoid extra work, since its really # The PGEntry which will use the TextNode to generate geometry text = '', @@ -215,11 +216,11 @@ class DirectEntry(DirectFrame): self._autoCapitalize() def _autoCapitalize(self): - name = self.get().decode('utf-8') + name = self.guiItem.getWtext() # capitalize each word, allowing for things like McMutton - capName = '' + capName = u'' # track each individual word to detect prefixes like Mc - wordSoFar = '' + wordSoFar = u'' # track whether the previous character was part of a word or not wasNonWordChar = True for i, character in enumerate(name): @@ -228,9 +229,9 @@ class DirectEntry(DirectFrame): # This assumes that string.lower and string.upper will return different # values for all unicode letters. # - Don't count apostrophes as a break between words - if character.lower() == character.upper() and character != "'": + if character.lower() == character.upper() and character != u"'": # we are between words - wordSoFar = '' + wordSoFar = u'' wasNonWordChar = True else: capitalize = False @@ -254,7 +255,8 @@ class DirectEntry(DirectFrame): wordSoFar += character wasNonWordChar = False capName += character - self.enterText(capName.encode('utf-8')) + self.guiItem.setWtext(capName) + self.guiItem.setCursorPosition(self.guiItem.getNumCharacters()) def focusOutCommandFunc(self): if self['focusOutCommand']: diff --git a/direct/src/gui/DirectEntryScroll.py b/direct/src/gui/DirectEntryScroll.py index 0cd7c3ea21..fd369b2716 100644 --- a/direct/src/gui/DirectEntryScroll.py +++ b/direct/src/gui/DirectEntryScroll.py @@ -29,19 +29,41 @@ class DirectEntryScroll(DirectFrame): # frameSize = (-0.006, 3.2, -0.015, 0.036), # if you need to scale the entry scale it's parent instead - self.entry = entry self.canvas = NodePath(self.guiItem.getCanvasNode()) - self.entry.reparentTo(self.canvas) self.canvas.setPos(0,0,0) - self.entry.bind(DGG.CURSORMOVE,self.cursorMove) + self.entry = None + if entry is not None: + self.entry = entry + self.entry.reparentTo(self.canvas) + self.entry.bind(DGG.CURSORMOVE, self.cursorMove) self.canvas.node().setBounds(OmniBoundingVolume()) self.canvas.node().setFinal(1) self.resetCanvas() + def setEntry(self, entry): + """ + Sets a DirectEntry element for this scroll frame. A DirectEntryScroll + can only hold one entry at a time, so make sure to not call this + function twice or call clearEntry before to make sure no entry + is already set. + """ + assert self.entry is None, "An entry was already set for this DirectEntryScroll element" + self.entry = entry + self.entry.reparentTo(self.canvas) + self.entry.bind(DGG.CURSORMOVE, self.cursorMove) + def clearEntry(self): + """ + detaches and unbinds the entry from the scroll frame and its + events. You'll be responsible for destroying it. + """ + if self.entry is None: return + self.entry.unbind(DGG.CURSORMOVE) + self.entry.detachNode() + self.entry = None def cursorMove(self, cursorX, cursorY): cursorX = self.entry.guiItem.getCursorX() * self.entry['text_scale'][0] diff --git a/direct/src/gui/DirectGuiBase.py b/direct/src/gui/DirectGuiBase.py index 3180d0dfce..3b47ac1973 100644 --- a/direct/src/gui/DirectGuiBase.py +++ b/direct/src/gui/DirectGuiBase.py @@ -634,7 +634,7 @@ class DirectGuiBase(DirectObject.DirectObject): """ # Need to tack on gui item specific id gEvent = event + self.guiId - if ShowBase.config.GetBool('debug-directgui-msgs', False): + if ShowBaseGlobal.config.GetBool('debug-directgui-msgs', False): from direct.showbase.PythonUtil import StackTrace print(gEvent) print(StackTrace()) @@ -663,7 +663,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath): # Determine the default initial state for inactive (or # unclickable) components. If we are in edit mode, these are # actually clickable by default. - guiEdit = ShowBase.config.GetBool('direct-gui-edit', False) + guiEdit = ShowBaseGlobal.config.GetBool('direct-gui-edit', False) if guiEdit: inactiveInitState = DGG.NORMAL else: @@ -729,7 +729,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath): guiObjectCollector.addLevel(1) guiObjectCollector.flushLevel() # track gui items by guiId for tracking down leaks - if ShowBase.config.GetBool('track-gui-items', False): + if ShowBaseGlobal.config.GetBool('track-gui-items', False): if not hasattr(ShowBase, 'guiItems'): ShowBase.guiItems = {} if self.guiId in ShowBase.guiItems: diff --git a/direct/src/gui/DirectOptionMenu.py b/direct/src/gui/DirectOptionMenu.py index 40d48f7672..cbf3a2974e 100644 --- a/direct/src/gui/DirectOptionMenu.py +++ b/direct/src/gui/DirectOptionMenu.py @@ -22,10 +22,12 @@ class DirectOptionMenu(DirectButton): # List of items to display on the popup menu ('items', [], self.setItems), # Initial item to display on menu button - # Can be an interger index or the same string as the button + # Can be an integer index or the same string as the button ('initialitem', None, DGG.INITOPT), # Amount of padding to place around popup button indicator ('popupMarkerBorder', (.1, .1), None), + # The initial position of the popup marker + ('popupMarker_pos', None, None), # Background color to use to highlight popup menu items ('highlightColor', (.5, .5, .5, 1), None), # Extra scale to use on highlight popup menu items @@ -50,6 +52,8 @@ class DirectOptionMenu(DirectButton): frameSize = (-0.5, 0.5, -0.2, 0.2), scale = 0.4, relief = DGG.RAISED) + # Record any user specified popup marker position + self.initPopupMarkerPos = self['popupMarker_pos'] # This needs to popup the menu too self.popupMarker.bind(DGG.B1PRESS, self.showPopupMenu) # Check if item is highlighted on release and select it if it is @@ -73,6 +77,7 @@ class DirectOptionMenu(DirectButton): state = 'normal') # Make sure this is on top of all the other widgets self.cancelFrame.setBin('gui-popup', 0) + self.cancelFrame.node().setBounds(OmniBoundingVolume()) self.cancelFrame.bind(DGG.B1PRESS, self.hidePopupMenu) # Default action on press is to show popup menu self.bind(DGG.B1PRESS, self.showPopupMenu) @@ -168,8 +173,13 @@ class DirectOptionMenu(DirectButton): else: # Or base it upon largest item bounds = [self.minX, self.maxX, self.minZ, self.maxZ] - pm.setPos(bounds[1] + pmw/2.0, 0, - bounds[2] + (bounds[3] - bounds[2])/2.0) + if self.initPopupMarkerPos: + # Use specified position + pmPos = list(self.initPopupMarkerPos) + else: + # Or base the position on the frame size. + pmPos = [bounds[1] + pmw/2.0, 0, bounds[2] + (bounds[3] - bounds[2])/2.0] + pm.setPos(pmPos[0], pmPos[1], pmPos[2]) # Adjust popup menu button to fit all items (or use user specified # frame size bounds[1] += pmw @@ -184,6 +194,12 @@ class DirectOptionMenu(DirectButton): Adjust popup position if default position puts it outside of visible screen region """ + + # Needed attributes (such as minZ) won't be set unless the user has specified + # items to display. Let's assert that we've given items to work with. + items = self['items'] + assert items and len(items) > 0, 'Cannot show an empty popup menu! You must add items!' + # Show the menu self.popupMenu.show() # Make sure its at the right scale diff --git a/direct/src/gui/DirectScrolledFrame.py b/direct/src/gui/DirectScrolledFrame.py index c44bc7a0fb..2c60ed950f 100644 --- a/direct/src/gui/DirectScrolledFrame.py +++ b/direct/src/gui/DirectScrolledFrame.py @@ -33,7 +33,7 @@ class DirectScrolledFrame(DirectFrame): ('canvasSize', (-1, 1, -1, 1), self.setCanvasSize), ('manageScrollBars', 1, self.setManageScrollBars), ('autoHideScrollBars', 1, self.setAutoHideScrollBars), - ('scrollBarWidth', 0.08, None), + ('scrollBarWidth', 0.08, self.setScrollBarWidth), ('borderWidth', (0.01, 0.01), self.setBorderWidth), ) @@ -72,6 +72,11 @@ class DirectScrolledFrame(DirectFrame): # Call option initialization functions self.initialiseoptions(DirectScrolledFrame) + def setScrollBarWidth(self): + w = self['scrollBarWidth'] + self.verticalScroll["frameSize"] = (-w / 2.0, w / 2.0, -1, 1) + self.horizontalScroll["frameSize"] = (-1, 1, -w / 2.0, w / 2.0) + def setCanvasSize(self): f = self['canvasSize'] self.guiItem.setVirtualFrame(f[0], f[1], f[2], f[3]) @@ -100,8 +105,10 @@ class DirectScrolledFrame(DirectFrame): simpleChildGui = self.guiDict.get(parts[-1]) if simpleChildGui: simpleChildGui.destroy() - self.verticalScroll.destroy() - self.horizontalScroll.destroy() - del self.verticalScroll - del self.horizontalScroll + if self.verticalScroll: + self.verticalScroll.destroy() + if self.horizontalScroll: + self.horizontalScroll.destroy() + self.verticalScroll = None + self.horizontalScroll = None DirectFrame.destroy(self) diff --git a/direct/src/gui/DirectScrolledList.py b/direct/src/gui/DirectScrolledList.py index a89b691576..661b09f13d 100644 --- a/direct/src/gui/DirectScrolledList.py +++ b/direct/src/gui/DirectScrolledList.py @@ -3,6 +3,7 @@ __all__ = ['DirectScrolledListItem', 'DirectScrolledList'] from panda3d.core import * +from direct.showbase import ShowBaseGlobal from . import DirectGuiGlobals as DGG from direct.directnotify import DirectNotifyGlobal from direct.task.Task import Task @@ -369,7 +370,7 @@ class DirectScrolledList(DirectFrame): del self.currentSelected self["items"].remove(item) if type(item) != type(''): - item.reparentTo(hidden) + item.reparentTo(ShowBaseGlobal.hidden) self.refresh() return 1 else: @@ -387,7 +388,7 @@ class DirectScrolledList(DirectFrame): item.destroy() self["items"].remove(item) if type(item) != type(''): - item.reparentTo(hidden) + item.reparentTo(ShowBaseGlobal.hidden) self.refresh() return 1 else: @@ -410,7 +411,7 @@ class DirectScrolledList(DirectFrame): self["items"].remove(item) if type(item) != type(''): #RAU possible leak here, let's try to do the right thing - #item.reparentTo(hidden) + #item.reparentTo(ShowBaseGlobal.hidden) item.removeNode() retval = 1 @@ -435,7 +436,7 @@ class DirectScrolledList(DirectFrame): self["items"].remove(item) if type(item) != type(''): #RAU possible leak here, let's try to do the right thing - #item.reparentTo(hidden) + #item.reparentTo(ShowBaseGlobal.hidden) item.removeNode() retval = 1 if (refresh): diff --git a/direct/src/interval/SoundInterval.py b/direct/src/interval/SoundInterval.py index 5927d14703..e5c5a04467 100644 --- a/direct/src/interval/SoundInterval.py +++ b/direct/src/interval/SoundInterval.py @@ -27,8 +27,7 @@ class SoundInterval(Interval.Interval): # than explicitly restarting the sound every time around. This # prevents a skip in the sound at every repetition (the gap in # the sound is caused by the delay between the end of the sound - # and the next taskMgr cycle). There still seems to be a skip - # in Miles when looping MP3s. =( + # and the next taskMgr cycle). # RAU 03/01/07 add listenerNode in case we don't want to # use base.camera as the listener, node must not be None def __init__(self, sound, loop = 0, duration = 0.0, name = None, @@ -62,23 +61,6 @@ class SoundInterval(Interval.Interval): #if (duration == 0): # self.notify.warning('zero length duration!') - # MPG - hack for Miles bug - #duration += 1.5 - - # DCR - hack for Miles bug - adding 1.5 seconds caused - # problems for MG_neg_buzzer.wav - - # DCR - what this is all about: Miles is under-reporting the - # length of MP3 files, and they're getting cut off too early. - # This is a temporary hack. We could: - # - hack Miles to fix its MP3 length calculation - # - complain louder about this to RAD - # - precompute MP3 durations and store them in a table - - # drose - ok, I've put in a lower-level workaround in the - # MilesAudioManager. This is no longer necessary up here, - # where it pollutes SoundInterval for everyone. - #duration += min(duration * 2.4, 1.5) # Generate unique name if necessary if (name == None): diff --git a/direct/src/interval/cLerpNodePathInterval.I b/direct/src/interval/cLerpNodePathInterval.I index 03c90753cb..5d4337f9df 100644 --- a/direct/src/interval/cLerpNodePathInterval.I +++ b/direct/src/interval/cLerpNodePathInterval.I @@ -106,6 +106,8 @@ set_end_hpr(const LQuaternion &quat) { * if either set_end_quat() or set_end_hpr() is also called. This parameter * is optional; if unspecified, the value will be taken from the node's actual * rotation at the time the lerp is performed. + * + * The given quaternion needs to be normalized. */ INLINE void CLerpNodePathInterval:: set_start_quat(const LQuaternion &quat) { @@ -143,6 +145,8 @@ set_end_quat(const LVecBase3 &hpr) { * This replaces a previous call to set_end_hpr(). If neither set_end_quat() * nor set_end_hpr() is called, the node's rotation will not be affected by * the lerp. + * + * The given quaternion needs to be normalized. */ INLINE void CLerpNodePathInterval:: set_end_quat(const LQuaternion &quat) { diff --git a/direct/src/interval/cLerpNodePathInterval.cxx b/direct/src/interval/cLerpNodePathInterval.cxx index 2fbc8a7e75..97babae49e 100644 --- a/direct/src/interval/cLerpNodePathInterval.cxx +++ b/direct/src/interval/cLerpNodePathInterval.cxx @@ -174,14 +174,14 @@ priv_step(double t) { setup_slerp(); } else if ((_flags & F_bake_in_start) != 0) { - set_start_quat(transform->get_quat()); + set_start_quat(transform->get_norm_quat()); setup_slerp(); } else { if (_prev_d == 1.0) { _start_quat = _end_quat; } else { - LQuaternion prev_value = transform->get_quat(); + LQuaternion prev_value = transform->get_norm_quat(); _start_quat = (prev_value - _prev_d * _end_quat) / (1.0 - _prev_d); } setup_slerp(); diff --git a/direct/src/leveleditor/ActionMgr.py b/direct/src/leveleditor/ActionMgr.py index 29d9ba3742..dcc5b7ff91 100755 --- a/direct/src/leveleditor/ActionMgr.py +++ b/direct/src/leveleditor/ActionMgr.py @@ -1,4 +1,4 @@ -from pandac.PandaModules import * +from panda3d.core import * from . import ObjectGlobals as OG class ActionMgr: diff --git a/direct/src/leveleditor/CurveEditor.py b/direct/src/leveleditor/CurveEditor.py index b1acc04525..57cc119378 100755 --- a/direct/src/leveleditor/CurveEditor.py +++ b/direct/src/leveleditor/CurveEditor.py @@ -2,7 +2,7 @@ This is the module for curve edit """ -from pandac.PandaModules import * +from panda3d.core import * from direct.wxwidgets.WxPandaShell import * from direct.showbase.DirectObject import * from direct.directtools.DirectSelection import SelectionRay diff --git a/direct/src/leveleditor/FileMgr.py b/direct/src/leveleditor/FileMgr.py index a0b00d1a04..1f11dc7133 100755 --- a/direct/src/leveleditor/FileMgr.py +++ b/direct/src/leveleditor/FileMgr.py @@ -11,7 +11,7 @@ class FileMgr: def saveToFile(self, fileName): try: f = open(fileName, 'w') - f.write("from pandac.PandaModules import *\n") + f.write("from panda3d.core import *\n") f.write("\nif hasattr(base, 'le'):\n") f.write(" objectMgr = base.le.objectMgr\n") f.write(" animMgr = base.le.animMgr\n") diff --git a/direct/src/leveleditor/LayerEditorUI.py b/direct/src/leveleditor/LayerEditorUI.py index d77f8779bb..e30dc343b4 100644 --- a/direct/src/leveleditor/LayerEditorUI.py +++ b/direct/src/leveleditor/LayerEditorUI.py @@ -2,7 +2,7 @@ Defines Layer UI """ import wx -from pandac.PandaModules import * +from panda3d.core import * from . import ObjectGlobals as OG diff --git a/direct/src/leveleditor/LevelEditor.py b/direct/src/leveleditor/LevelEditor.py index c1322c455d..36b1dd86e0 100644 --- a/direct/src/leveleditor/LevelEditor.py +++ b/direct/src/leveleditor/LevelEditor.py @@ -31,7 +31,7 @@ class LevelEditor(LevelEditorBase): # Populating uderlined data-structures self.ui = LevelEditorUI(self) - self.ui.SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) + self.ui.SetCursor(wx.Cursor(wx.CURSOR_WAIT)) self.objectPalette.populate() self.protoPalette.populate() @@ -42,4 +42,4 @@ class LevelEditor(LevelEditorBase): # When you define your own LevelEditor class inheriting LevelEditorBase # you should call self.initialize() at the end of __init__() function self.initialize() - self.ui.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) + self.ui.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) diff --git a/direct/src/leveleditor/LevelEditorBase.py b/direct/src/leveleditor/LevelEditorBase.py index ad351a7e39..16ff696744 100755 --- a/direct/src/leveleditor/LevelEditorBase.py +++ b/direct/src/leveleditor/LevelEditorBase.py @@ -305,7 +305,7 @@ class LevelEditorBase(DirectObject): if self.settingsFile is None: return - self.ui.SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) + self.ui.SetCursor(wx.Cursor(wx.CURSOR_WAIT)) try: f = open(self.settingsFile, 'r') configLines = f.readlines() @@ -342,7 +342,7 @@ class LevelEditorBase(DirectObject): self.ui.updateMenu() except: pass - self.ui.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) + self.ui.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) def convertMaya(self, modelname, callBack, obj=None, isAnim=False): if obj and isAnim: diff --git a/direct/src/leveleditor/LevelEditorStart.py b/direct/src/leveleditor/LevelEditorStart.py index 4ec577106c..bcb8800c94 100644 --- a/direct/src/leveleditor/LevelEditorStart.py +++ b/direct/src/leveleditor/LevelEditorStart.py @@ -1,4 +1,4 @@ -from . import LevelEditor +from direct.leveleditor import LevelEditor if __name__ == '__main__': base.le = LevelEditor.LevelEditor() diff --git a/direct/src/leveleditor/LevelEditorUIBase.py b/direct/src/leveleditor/LevelEditorUIBase.py index f457f838d6..33101dff22 100755 --- a/direct/src/leveleditor/LevelEditorUIBase.py +++ b/direct/src/leveleditor/LevelEditorUIBase.py @@ -2,7 +2,7 @@ ## import os ## from wx.lib.agw import fourwaysplitter as FWS -from pandac.PandaModules import * +from panda3d.core import * from direct.wxwidgets.WxPandaShell import * from direct.directtools.DirectSelection import SelectionRay @@ -478,7 +478,7 @@ class LevelEditorUIBase(WxPandaShell): self.editor.reset() def onOpen(self, evt=None): - dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", wx.OPEN) + dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) if dialog.ShowModal() == wx.ID_OK: self.editor.load(dialog.GetPath()) self.editor.setTitleWithFilename(dialog.GetPath()) @@ -492,7 +492,7 @@ class LevelEditorUIBase(WxPandaShell): self.editor.save() def onSaveAs(self, evt): - dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", wx.SAVE) + dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) result = True if dialog.ShowModal() == wx.ID_OK: self.editor.saveAs(dialog.GetPath()) @@ -503,7 +503,7 @@ class LevelEditorUIBase(WxPandaShell): return result def onExportToMaya(self, evt): - dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.mb", wx.SAVE) + dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.mb", style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) if dialog.ShowModal() == wx.ID_OK: self.editor.exportToMaya(dialog.GetPath()) dialog.Destroy() diff --git a/direct/src/leveleditor/ObjectMgrBase.py b/direct/src/leveleditor/ObjectMgrBase.py index 3a65d6a998..2e9cc2a17c 100755 --- a/direct/src/leveleditor/ObjectMgrBase.py +++ b/direct/src/leveleditor/ObjectMgrBase.py @@ -6,7 +6,7 @@ import os, time, copy from direct.task import Task from direct.actor.Actor import Actor -from pandac.PandaModules import * +from panda3d.core import * from .ActionMgr import * from . import ObjectGlobals as OG diff --git a/direct/src/leveleditor/ObjectPaletteUI.py b/direct/src/leveleditor/ObjectPaletteUI.py index bf84e3d492..ffd168a258 100755 --- a/direct/src/leveleditor/ObjectPaletteUI.py +++ b/direct/src/leveleditor/ObjectPaletteUI.py @@ -69,4 +69,4 @@ class ObjectPaletteUI(wx.Panel): return cmp(index1, index2) def getSelected(self): - return self.tree.GetItemPyData(self.tree.GetSelection()) + return self.tree.GetItemData(self.tree.GetSelection()) diff --git a/direct/src/leveleditor/ObjectPropertyUI.py b/direct/src/leveleditor/ObjectPropertyUI.py index 08adb8b784..4b76cb8879 100755 --- a/direct/src/leveleditor/ObjectPropertyUI.py +++ b/direct/src/leveleditor/ObjectPropertyUI.py @@ -9,7 +9,7 @@ from wx.lib.embeddedimage import PyEmbeddedImage from wx.lib.scrolledpanel import ScrolledPanel from wx.lib.agw.cubecolourdialog import * from direct.wxwidgets.WxSlider import * -from pandac.PandaModules import * +from panda3d.core import * from . import ObjectGlobals as OG from . import AnimGlobals as AG diff --git a/direct/src/leveleditor/PaletteTreeCtrl.py b/direct/src/leveleditor/PaletteTreeCtrl.py index c4a5d46fce..75c752f0fc 100644 --- a/direct/src/leveleditor/PaletteTreeCtrl.py +++ b/direct/src/leveleditor/PaletteTreeCtrl.py @@ -41,7 +41,7 @@ class PaletteTreeCtrl(wx.TreeCtrl): roots.append(key) for root in roots: newItem = self.AppendItem(parentItem, root) - self.SetItemPyData(newItem, root) + self.SetItemData(newItem, root) rootItems.append(newItem) itemKeys.remove(root) for rootItem in rootItems: @@ -108,7 +108,7 @@ class PaletteTreeCtrl(wx.TreeCtrl): item, cookie = self.GetFirstChild(parent) while item: itemName = self.GetItemText(item) - itemData = self.GetItemPyData(item) + itemData = self.GetItemData(item) newItem = self.AppendItem(newParent, itemName) self.SetItemPyData(newItem, itemData) diff --git a/direct/src/leveleditor/ProtoObjsUI.py b/direct/src/leveleditor/ProtoObjsUI.py index 63222e3a2d..a4e09af9f3 100755 --- a/direct/src/leveleditor/ProtoObjsUI.py +++ b/direct/src/leveleditor/ProtoObjsUI.py @@ -4,7 +4,7 @@ Defines ProtoObjs List UI import wx import os -from pandac.PandaModules import * +from panda3d.core import * from .ProtoObjs import * class ProtoDropTarget(wx.PyDropTarget): diff --git a/direct/src/leveleditor/ProtoPaletteUI.py b/direct/src/leveleditor/ProtoPaletteUI.py index d9f047b55f..6736311821 100755 --- a/direct/src/leveleditor/ProtoPaletteUI.py +++ b/direct/src/leveleditor/ProtoPaletteUI.py @@ -3,13 +3,13 @@ Defines ProtoPalette tree UI """ import wx import os -from pandac.PandaModules import * +from panda3d.core import * from .PaletteTreeCtrl import * -class UniversalDropTarget(wx.PyDropTarget): +class UniversalDropTarget(wx.DropTarget): """Implements drop target functionality to receive files, bitmaps and text""" def __init__(self, editor): - wx.PyDropTarget.__init__(self) + wx.DropTarget.__init__(self) self.editor = editor self.do = wx.DataObjectComposite() # the dataobject that gets filled with the appropriate data self.filedo = wx.FileDataObject() diff --git a/direct/src/leveleditor/SceneGraphUIBase.py b/direct/src/leveleditor/SceneGraphUIBase.py index c98cf8505c..66537fbbdf 100755 --- a/direct/src/leveleditor/SceneGraphUIBase.py +++ b/direct/src/leveleditor/SceneGraphUIBase.py @@ -2,7 +2,7 @@ Defines Scene Graph tree UI Base """ import wx -from pandac.PandaModules import * +from panda3d.core import * from .ActionMgr import * from . import ObjectGlobals as OG @@ -26,7 +26,7 @@ class SceneGraphUIBase(wx.Panel): size=wx.DefaultSize, style=wx.TR_MULTIPLE|wx.TR_DEFAULT_STYLE, validator=wx.DefaultValidator, name="treeCtrl") self.root = self.tree.AddRoot('render') - self.tree.SetItemPyData(self.root, "render") + self.tree.SetItemData(self.root, "render") self.shouldShowPandaObjChildren = False @@ -61,7 +61,7 @@ class SceneGraphUIBase(wx.Panel): self.tree.Delete(item) def traversePandaObjects(self, parent, objNodePath): - itemId = self.tree.GetItemPyData(parent) + itemId = self.tree.GetItemData(parent) i = 0 for child in objNodePath.getChildren(): if child.hasTag('OBJRoot'): @@ -78,7 +78,7 @@ class SceneGraphUIBase(wx.Panel): def addPandaObjectChildren(self, parent): # first, find Panda Object's NodePath of the item - itemId = self.tree.GetItemPyData(parent) + itemId = self.tree.GetItemData(parent) if itemId == "render": return obj = self.editor.objectMgr.findObjectById(itemId) @@ -96,7 +96,7 @@ class SceneGraphUIBase(wx.Panel): def removePandaObjectChildren(self, parent): # first, find Panda Object's NodePath of the item - itemId = self.tree.GetItemPyData(parent) + itemId = self.tree.GetItemData(parent) if itemId == "render": return obj = self.editor.objectMgr.findObjectById(itemId) @@ -142,14 +142,14 @@ class SceneGraphUIBase(wx.Panel): def traverse(self, parent, itemId): # prevent from traversing into self - if itemId == self.tree.GetItemPyData(parent): + if itemId == self.tree.GetItemData(parent): return None # main loop - serching for an item with an itemId item, cookie = self.tree.GetFirstChild(parent) while item: # if the item was found - return it - if itemId == self.tree.GetItemPyData(item): + if itemId == self.tree.GetItemData(item): return item # the tem was not found - checking if it has children @@ -168,7 +168,7 @@ class SceneGraphUIBase(wx.Panel): item, cookie = self.tree.GetFirstChild(parent) while item: data = self.tree.GetItemText(item) - itemId = self.tree.GetItemPyData(item) + itemId = self.tree.GetItemData(item) newItem = self.tree.AppendItem(newParent, data) self.tree.SetItemPyData(newItem, itemId) @@ -186,13 +186,13 @@ class SceneGraphUIBase(wx.Panel): def reParent(self, oldParent, newParent, child): if newParent is None: newParent = self.root - itemId = self.tree.GetItemPyData(oldParent) + itemId = self.tree.GetItemData(oldParent) newItem = self.tree.AppendItem(newParent, child) self.tree.SetItemPyData(newItem, itemId) self.reParentTree(oldParent, newItem) obj = self.editor.objectMgr.findObjectById(itemId) - itemId = self.tree.GetItemPyData(newParent) + itemId = self.tree.GetItemData(newParent) if itemId != "render": newParentObj = self.editor.objectMgr.findObjectById(itemId) self.reParentData(newParentObj[OG.OBJ_NP], obj[OG.OBJ_NP]) @@ -207,7 +207,7 @@ class SceneGraphUIBase(wx.Panel): self.addPandaObjectChildren(newpParent) def isChildOrGrandChild(self, parent, child): - childId = self.tree.GetItemPyData(child) + childId = self.tree.GetItemData(child) return self.traverse(parent, childId) def changeHierarchy(self, data, x, y): @@ -226,7 +226,7 @@ class SceneGraphUIBase(wx.Panel): return # undo function setup... - action = ActionChangeHierarchy(self.editor, self.tree.GetItemPyData(self.tree.GetItemParent(item)), self.tree.GetItemPyData(item), self.tree.GetItemPyData(dragToItem), data) + action = ActionChangeHierarchy(self.editor, self.tree.GetItemData(self.tree.GetItemParent(item)), self.tree.GetItemData(item), self.tree.GetItemData(dragToItem), data) self.editor.actionMgr.push(action) action() @@ -283,7 +283,7 @@ class SceneGraphUIBase(wx.Panel): def onSelected(self, event): item = event.GetItem(); if item: - itemId = self.tree.GetItemPyData(item) + itemId = self.tree.GetItemData(item) if itemId: obj = self.editor.objectMgr.findObjectById(itemId); if obj: @@ -313,7 +313,7 @@ class SceneGraphUIBase(wx.Panel): if not item.IsOk(): return self.currItem = item - itemId = self.tree.GetItemPyData(item) + itemId = self.tree.GetItemData(item) if not itemId: return self.currObj = self.editor.objectMgr.findObjectById(itemId); diff --git a/direct/src/leveleditor/testData.py b/direct/src/leveleditor/testData.py index 28d4a04d1c..d35c01b103 100755 --- a/direct/src/leveleditor/testData.py +++ b/direct/src/leveleditor/testData.py @@ -1,4 +1,4 @@ -from pandac.PandaModules import * +from panda3d.core import * if hasattr(base, 'le'): objectMgr = base.le.objectMgr diff --git a/direct/src/p3d/AppRunner.py b/direct/src/p3d/AppRunner.py deleted file mode 100644 index aae03754e6..0000000000 --- a/direct/src/p3d/AppRunner.py +++ /dev/null @@ -1,1245 +0,0 @@ -""" -This module is intended to be compiled into the Panda3D runtime -distributable, to execute a packaged p3d application, but it can also -be run directly via the Python interpreter (if the current Panda3D and -Python versions match the version expected by the application). See -runp3d.py for a command-line tool to invoke this module. - -The global AppRunner instance may be imported as follows:: - - from direct.showbase.AppRunnerGlobal import appRunner - -This will be None if Panda was not run from the runtime environment. -""" - -__all__ = ["AppRunner", "dummyAppRunner", "ArgumentError"] - -import sys -import os - -if sys.version_info >= (3, 0): - import builtins -else: - import __builtin__ as builtins - -from direct.showbase import VFSImporter -from direct.showbase.DirectObject import DirectObject -from panda3d.core import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, WindowProperties, ExecutionEnvironment, PandaSystem, Notify, StreamWriter, ConfigVariableString, ConfigPageManager -from panda3d.direct import init_app_for_gui -from panda3d import core -from direct.stdpy import file, glob -from direct.task.TaskManagerGlobal import taskMgr -from direct.showbase.MessengerGlobal import messenger -from direct.showbase import AppRunnerGlobal -from direct.directnotify.DirectNotifyGlobal import directNotify -from direct.p3d.HostInfo import HostInfo -from direct.p3d.ScanDirectoryNode import ScanDirectoryNode -from direct.p3d.InstalledHostData import InstalledHostData -from direct.p3d.InstalledPackageData import InstalledPackageData - -# These imports are read by the C++ wrapper in p3dPythonRun.cxx. -from direct.p3d.JavaScript import Undefined, ConcreteStruct - -class ArgumentError(AttributeError): - pass - -class ScriptAttributes: - """ This dummy class serves as the root object for the scripting - interface. The Python code can store objects and functions here - for direct inspection by the browser's JavaScript code. """ - pass - -class AppRunner(DirectObject): - - """ This class is intended to be compiled into the Panda3D runtime - distributable, to execute a packaged p3d application. It also - provides some useful runtime services while running in that - packaged environment. - - It does not usually exist while running Python directly, but you - can use dummyAppRunner() to create one at startup for testing or - development purposes. """ - - notify = directNotify.newCategory("AppRunner") - - ConfigBasename = 'config.xml' - - # Default values for parameters that are absent from the config file: - maxDiskUsage = 2048 * 1048576 # 2 GB - - # Values for verifyContents, from p3d_plugin.h - P3DVCNone = 0 - P3DVCNormal = 1 - P3DVCForce = 2 - P3DVCNever = 3 - - # Also from p3d_plugin.h - P3D_CONTENTS_DEFAULT_MAX_AGE = 5 - - def __init__(self): - DirectObject.__init__(self) - - # We direct both our stdout and stderr objects onto Panda's - # Notify stream. This ensures that unadorned print statements - # made within Python will get routed into the log properly. - stream = StreamWriter(Notify.out(), False) - sys.stdout = stream - sys.stderr = stream - - # This is set true by dummyAppRunner(), below. - self.dummy = False - - # These will be set from the application flags when - # setP3DFilename() is called. - self.allowPythonDev = False - self.guiApp = False - self.interactiveConsole = False - self.initialAppImport = False - self.trueFileIO = False - self.respectPerPlatform = None - - self.verifyContents = self.P3DVCNone - - self.sessionId = 0 - self.packedAppEnvironmentInitialized = False - self.gotWindow = False - self.gotP3DFilename = False - self.p3dFilename = None - self.p3dUrl = None - self.started = False - self.windowOpened = False - self.windowPrc = None - - self.http = None - if hasattr(core, 'HTTPClient'): - self.http = core.HTTPClient.getGlobalPtr() - - self.Undefined = Undefined - self.ConcreteStruct = ConcreteStruct - - # This is per session. - self.nextScriptId = 0 - - # TODO: we need one of these per instance, not per session. - self.instanceId = None - - # The root Panda3D install directory. This is filled in when - # the instance starts up. - self.rootDir = None - - # The log directory. Also filled in when the instance starts. - self.logDirectory = None - - # self.superMirrorUrl, if nonempty, is the "super mirror" URL - # that should be contacted first before trying the actual - # host. This is primarily used for "downloading" from a - # locally-stored Panda3D installation. This is also filled in - # when the instance starts up. - self.superMirrorUrl = None - - # A list of the Panda3D packages that have been loaded. - self.installedPackages = [] - - # A list of the Panda3D packages that in the queue to be - # downloaded. - self.downloadingPackages = [] - - # A dictionary of HostInfo objects for the various download - # hosts we have imported packages from. - self.hosts = {} - - # The altHost string that is in effect from the HTML tokens, - # if any, and the dictionary of URL remapping: orig host url - # -> alt host url. - self.altHost = None - self.altHostMap = {} - - # The URL from which Panda itself should be downloaded. - self.pandaHostUrl = PandaSystem.getPackageHostUrl() - - # Application code can assign a callable object here; if so, - # it will be invoked when an uncaught exception propagates to - # the top of the TaskMgr.run() loop. - self.exceptionHandler = None - - # Managing packages for runtime download. - self.downloadingPackages = [] - self.downloadTask = None - - # The mount point for the multifile. For now, this is always - # the current working directory, for convenience; but when we - # move to multiple-instance sessions, it may have to be - # different for each instance. - self.multifileRoot = str(ExecutionEnvironment.getCwd()) - - # The "main" object will be exposed to the DOM as a property - # of the plugin object; that is, document.pluginobject.main in - # JavaScript will be appRunner.main here. This may be - # replaced with a direct reference to the JavaScript object - # later, in setInstanceInfo(). - self.main = ScriptAttributes() - - # By default, we publish a stop() method so the browser can - # easy stop the plugin. A particular application can remove - # this if it chooses. - self.main.stop = self.stop - - # This will be the browser's toplevel window DOM object; - # e.g. self.dom.document will be the document. - self.dom = None - - # This is the list of expressions we will evaluate when - # self.dom gets assigned. - self.deferredEvals = [] - - # This is the default requestFunc that is installed if we - # never call setRequestFunc(). - def defaultRequestFunc(*args): - if args[1] == 'notify': - # Quietly ignore notifies. - return - self.notify.info("Ignoring request: %s" % (args,)) - self.requestFunc = defaultRequestFunc - - # This will be filled in with the default WindowProperties for - # this instance, e.g. the WindowProperties necessary to - # re-embed a window in the browser frame. - self.windowProperties = None - - # Store our pointer so DirectStart-based apps can find us. - if AppRunnerGlobal.appRunner is None: - AppRunnerGlobal.appRunner = self - - # We use this messenger hook to dispatch this __startIfReady() - # call back to the main thread. - self.accept('AppRunner_startIfReady', self.__startIfReady) - - def getToken(self, tokenName): - """ Returns the value of the indicated web token as a string, - if it was set, or None if it was not. """ - - return self.tokenDict.get(tokenName.lower(), None) - - def getTokenInt(self, tokenName): - """ Returns the value of the indicated web token as an integer - value, if it was set, or None if it was not, or not an - integer. """ - - value = self.getToken(tokenName) - if value is not None: - try: - value = int(value) - except ValueError: - value = None - return value - - def getTokenFloat(self, tokenName): - """ Returns the value of the indicated web token as a - floating-point value value, if it was set, or None if it was - not, or not a number. """ - - value = self.getToken(tokenName) - if value is not None: - try: - value = float(value) - except ValueError: - value = None - return value - - def getTokenBool(self, tokenName): - """ Returns the value of the indicated web token as a boolean - value, if it was set, or None if it was not. """ - - value = self.getTokenInt(tokenName) - if value is not None: - value = bool(value) - return value - - - - def installPackage(self, packageName, version = None, hostUrl = None): - - """ Installs the named package, downloading it first if - necessary. Returns true on success, false on failure. This - method runs synchronously, and will block until it is - finished; see the PackageInstaller class if you want this to - happen asynchronously instead. """ - - host = self.getHostWithAlt(hostUrl) - if not host.downloadContentsFile(self.http): - return False - - # All right, get the package info now. - package = host.getPackage(packageName, version) - if not package: - self.notify.warning("Package %s %s not known on %s" % ( - packageName, version, hostUrl)) - return False - - return self.__rInstallPackage(package, []) - - def __rInstallPackage(self, package, nested): - """ The recursive implementation of installPackage(). The new - parameter, nested, is a list of packages that we are - recursively calling this from, to avoid recursive loops. """ - - package.checkStatus() - if not package.downloadDescFile(self.http): - return False - - # Now that we've downloaded and read the desc file, we can - # install all of the required packages first. - nested = nested[:] + [self] - for packageName, version, host in package.requires: - if host.downloadContentsFile(self.http): - p2 = host.getPackage(packageName, version) - if not p2: - self.notify.warning("Couldn't find %s %s on %s" % (packageName, version, host.hostUrl)) - else: - if p2 not in nested: - self.__rInstallPackage(p2, nested) - - # Now that all of the required packages are installed, carry - # on to download and install this package. - if not package.downloadPackage(self.http): - return False - - if not package.installPackage(self): - return False - - self.notify.info("Package %s %s installed." % ( - package.packageName, package.packageVersion)) - return True - - def getHostWithAlt(self, hostUrl): - """ Returns a suitable HostInfo object for downloading - contents from the indicated URL. This is almost always the - same thing as getHost(), except in the rare case when we have - an alt_host specified in the HTML tokens; in this case, we may - actually want to download the contents from a different URL - than the one given, for instance to download a version in - testing. """ - - if hostUrl is None: - hostUrl = self.pandaHostUrl - - altUrl = self.altHostMap.get(hostUrl, None) - if altUrl: - # We got an alternate host. Use it. - return self.getHost(altUrl) - - # We didn't get an aternate host, use the original. - host = self.getHost(hostUrl) - - # But we might need to consult the host itself to see if *it* - # recommends an altHost. - if self.altHost: - # This means forcing the host to download its contents - # file on the spot, a blocking operation. This is a - # little unfortunate, but since alt_host is so rarely - # used, probably not really a problem. - host.downloadContentsFile(self.http) - altUrl = host.altHosts.get(self.altHost, None) - if altUrl: - return self.getHost(altUrl) - - # No shenanigans, just return the requested host. - return host - - def getHost(self, hostUrl, hostDir = None): - """ Returns a new HostInfo object corresponding to the - indicated host URL. If we have already seen this URL - previously, returns the same object. - - This returns the literal referenced host. To return the - mapped host, which is the one we should actually download - from, see getHostWithAlt(). """ - - if not hostUrl: - hostUrl = self.pandaHostUrl - - host = self.hosts.get(hostUrl, None) - if not host: - host = HostInfo(hostUrl, appRunner = self, hostDir = hostDir) - self.hosts[hostUrl] = host - return host - - def getHostWithDir(self, hostDir): - """ Returns the HostInfo object that corresponds to the - indicated on-disk host directory. This would be used when - reading a host directory from disk, instead of downloading it - from a server. Supply the full path to the host directory, as - a Filename. Returns None if the contents.xml in the indicated - host directory cannot be read or doesn't seem consistent. """ - - host = HostInfo(None, hostDir = hostDir, appRunner = self) - if not host.hasContentsFile: - if not host.readContentsFile(): - # Couldn't read the contents.xml file - return None - - if not host.hostUrl: - # The contents.xml file there didn't seem to indicate the - # same host directory. - return None - - host2 = self.hosts.get(host.hostUrl) - if host2 is None: - # No such host already; store this one. - self.hosts[host.hostUrl] = host - return host - - if host2.hostDir != host.hostDir: - # Hmm, we already have that host somewhere else. - return None - - # We already have that host, and it's consistent. - return host2 - - def deletePackages(self, packages): - """ Removes all of the indicated packages from the disk, - uninstalling them and deleting all of their files. The - packages parameter must be a list of one or more PackageInfo - objects, for instance as returned by getHost().getPackage(). - Returns the list of packages that were NOT found. """ - - for hostUrl, host in self.hosts.items(): - packages = host.deletePackages(packages) - - if not host.packages: - # If that's all of the packages for this host, delete - # the host directory too. - del self.hosts[hostUrl] - self.__deleteHostFiles(host) - - return packages - - def __deleteHostFiles(self, host): - """ Called by deletePackages(), this removes all the files for - the indicated host (for which we have presumably already - removed all of the packages). """ - - self.notify.info("Deleting host %s: %s" % (host.hostUrl, host.hostDir)) - self.rmtree(host.hostDir) - - self.sendRequest('forget_package', host.hostUrl, '', '') - - - def freshenFile(self, host, fileSpec, localPathname): - """ Ensures that the localPathname is the most current version - of the file defined by fileSpec, as offered by host. If not, - it downloads a new version on-the-spot. Returns true on - success, false on failure. """ - - assert self.http - return host.freshenFile(self.http, fileSpec, localPathname) - - def scanInstalledPackages(self): - """ Scans the hosts and packages already installed locally on - the system. Returns a list of InstalledHostData objects, each - of which contains a list of InstalledPackageData objects. """ - - result = [] - hostsFilename = Filename(self.rootDir, 'hosts') - hostsDir = ScanDirectoryNode(hostsFilename) - for dirnode in hostsDir.nested: - host = self.getHostWithDir(dirnode.pathname) - hostData = InstalledHostData(host, dirnode) - - if host: - for package in host.getAllPackages(includeAllPlatforms = True): - packageDir = package.getPackageDir() - if not packageDir.exists(): - continue - - subdir = dirnode.extractSubdir(packageDir) - if not subdir: - # This package, while defined by the host, isn't installed - # locally; ignore it. - continue - - packageData = InstalledPackageData(package, subdir) - hostData.packages.append(packageData) - - # Now that we've examined all of the packages for the host, - # anything left over is junk. - for subdir in dirnode.nested: - packageData = InstalledPackageData(None, subdir) - hostData.packages.append(packageData) - - result.append(hostData) - - return result - - def readConfigXml(self): - """ Reads the config.xml file that may be present in the root - directory. """ - - if not hasattr(core, 'TiXmlDocument'): - return - - filename = Filename(self.rootDir, self.ConfigBasename) - doc = core.TiXmlDocument(filename.toOsSpecific()) - if not doc.LoadFile(): - return - - xconfig = doc.FirstChildElement('config') - if xconfig: - maxDiskUsage = xconfig.Attribute('max_disk_usage') - try: - self.maxDiskUsage = int(maxDiskUsage or '') - except ValueError: - pass - - def writeConfigXml(self): - """ Rewrites the config.xml to the root directory. This isn't - called automatically; an application may call this after - adjusting some parameters (such as self.maxDiskUsage). """ - - from panda3d.core import TiXmlDocument, TiXmlDeclaration, TiXmlElement - - filename = Filename(self.rootDir, self.ConfigBasename) - doc = TiXmlDocument(filename.toOsSpecific()) - decl = TiXmlDeclaration("1.0", "utf-8", "") - doc.InsertEndChild(decl) - - xconfig = TiXmlElement('config') - xconfig.SetAttribute('max_disk_usage', str(self.maxDiskUsage)) - doc.InsertEndChild(xconfig) - - # Write the file to a temporary filename, then atomically move - # it to its actual filename, to avoid race conditions when - # updating this file. - tfile = Filename.temporary(str(self.rootDir), '.xml') - if doc.SaveFile(tfile.toOsSpecific()): - tfile.renameTo(filename) - - - def checkDiskUsage(self): - """ Checks the total disk space used by all packages, and - removes old packages if necessary. """ - - totalSize = 0 - hosts = self.scanInstalledPackages() - for hostData in hosts: - for packageData in hostData.packages: - totalSize += packageData.totalSize - self.notify.info("Total Panda3D disk space used: %s MB" % ( - (totalSize + 524288) // 1048576)) - - if self.verifyContents == self.P3DVCNever: - # We're not allowed to delete anything anyway. - return - - self.notify.info("Configured max usage is: %s MB" % ( - (self.maxDiskUsage + 524288) // 1048576)) - if totalSize <= self.maxDiskUsage: - # Still within budget; no need to clean up anything. - return - - # OK, we're over budget. Now we have to remove old packages. - usedPackages = [] - for hostData in hosts: - for packageData in hostData.packages: - if packageData.package and packageData.package.installed: - # Don't uninstall any packages we're currently using. - continue - - usedPackages.append((packageData.lastUse, packageData)) - - # Sort the packages into oldest-first order. - usedPackages.sort() - - # Delete packages until we free up enough space. - packages = [] - for lastUse, packageData in usedPackages: - if totalSize <= self.maxDiskUsage: - break - totalSize -= packageData.totalSize - - if packageData.package: - packages.append(packageData.package) - else: - # If it's an unknown package, just delete it directly. - print("Deleting unknown package %s" % (packageData.pathname)) - self.rmtree(packageData.pathname) - - packages = self.deletePackages(packages) - if packages: - print("Unable to delete %s packages" % (len(packages))) - - return - - def stop(self): - """ This method can be called by JavaScript to stop the - application. """ - - # We defer the actual exit for a few frames, so we don't raise - # an exception and invalidate the JavaScript call; and also to - # help protect against race conditions as the application - # shuts down. - taskMgr.doMethodLater(0.5, sys.exit, 'exit') - - def run(self): - """ This method calls taskMgr.run(), with an optional - exception handler. This is generally the program's main loop - when running in a p3d environment (except on unusual platforms - like the iPhone, which have to hand the main loop off to the - OS, and don't use this interface). """ - - try: - taskMgr.run() - - except SystemExit as err: - # Presumably the window has already been shut down here, but shut - # it down again for good measure. - if hasattr(builtins, "base"): - base.destroy() - - self.notify.info("Normal exit with status %s." % repr(err.code)) - raise - - except: - # Some unexpected Python exception; pass it to the - # optional handler, if it is defined. - if self.exceptionHandler and not self.interactiveConsole: - self.exceptionHandler() - else: - raise - - def rmtree(self, filename): - """ This is like shutil.rmtree(), but it can remove read-only - files on Windows. It receives a Filename, the root directory - to delete. """ - if filename.isDirectory(): - for child in filename.scanDirectory(): - self.rmtree(Filename(filename, child)) - if not filename.rmdir(): - print("could not remove directory %s" % (filename)) - else: - if not filename.unlink(): - print("could not delete %s" % (filename)) - - def setSessionId(self, sessionId): - """ This message should come in at startup. """ - self.sessionId = sessionId - self.nextScriptId = self.sessionId * 1000 + 10000 - - def initPackedAppEnvironment(self): - """ This function sets up the Python environment suitably for - running a packed app. It should only run once in any given - session (and it includes logic to ensure this). """ - - if self.packedAppEnvironmentInitialized: - return - - self.packedAppEnvironmentInitialized = True - - vfs = VirtualFileSystem.getGlobalPtr() - - # Now set up Python to import this stuff. - VFSImporter.register() - sys.path.append(self.multifileRoot) - - # Make sure that $MAIN_DIR is set to the p3d root before we - # start executing the code in this file. - ExecutionEnvironment.setEnvironmentVariable("MAIN_DIR", Filename(self.multifileRoot).toOsSpecific()) - - # Put our root directory on the model-path, too. - getModelPath().appendDirectory(self.multifileRoot) - - if not self.trueFileIO: - # Replace the builtin open and file symbols so user code will get - # our versions by default, which can open and read files out of - # the multifile. - builtins.open = file.open - if sys.version_info < (3, 0): - builtins.file = file.open - builtins.execfile = file.execfile - os.listdir = file.listdir - os.walk = file.walk - os.path.join = file.join - os.path.isfile = file.isfile - os.path.isdir = file.isdir - os.path.exists = file.exists - os.path.lexists = file.lexists - os.path.getmtime = file.getmtime - os.path.getsize = file.getsize - sys.modules['glob'] = glob - - self.checkDiskUsage() - - def __startIfReady(self): - """ Called internally to start the application. """ - if self.started: - return - - if self.gotWindow and self.gotP3DFilename: - self.started = True - - # Now we can ignore future calls to startIfReady(). - self.ignore('AppRunner_startIfReady') - - # Hang a hook so we know when the window is actually opened. - self.acceptOnce('window-event', self.__windowEvent) - - # Look for the startup Python file. This might be a magic - # filename (like "__main__", or any filename that contains - # invalid module characters), so we can't just import it - # directly; instead, we go through the low-level importer. - - # If there's no p3d_info.xml file, we look for "main". - moduleName = 'main' - if self.p3dPackage: - mainName = self.p3dPackage.Attribute('main_module') - if mainName: - moduleName = mainName - - # Temporarily set this flag while we import the app, so - # that if the app calls run() within its own main.py, it - # will properly get ignored by ShowBase. - self.initialAppImport = True - - # Python won't let us import a module named __main__. So, - # we have to do that manually, via the VFSImporter. - if moduleName == '__main__': - dirName = Filename(self.multifileRoot).toOsSpecific() - importer = VFSImporter.VFSImporter(dirName) - loader = importer.find_module('__main__') - if loader is None: - raise ImportError('No module named __main__') - - mainModule = loader.load_module('__main__') - else: - __import__(moduleName) - mainModule = sys.modules[moduleName] - - # Check if it has a main() function. If so, call it. - if hasattr(mainModule, 'main') and hasattr(mainModule.main, '__call__'): - mainModule.main(self) - - # Now clear this flag. - self.initialAppImport = False - - if self.interactiveConsole: - # At this point, we have successfully loaded the app. - # If the interactive_console flag is enabled, stop the - # main loop now and give the user a Python prompt. - taskMgr.stop() - - def getPandaScriptObject(self): - """ Called by the browser to query the Panda instance's - toplevel scripting object, for querying properties in the - Panda instance. The attributes on this object are mapped to - document.pluginobject.main within the DOM. """ - - return self.main - - def setBrowserScriptObject(self, dom): - """ Called by the browser to supply the browser's toplevel DOM - object, for controlling the JavaScript and the document in the - same page with the Panda3D plugin. """ - - self.dom = dom - - # Now evaluate any deferred expressions. - for expression in self.deferredEvals: - self.scriptRequest('eval', self.dom, value = expression, - needsResponse = False) - self.deferredEvals = [] - - def setInstanceInfo(self, rootDir, logDirectory, superMirrorUrl, - verifyContents, main, respectPerPlatform): - """ Called by the browser to set some global information about - the instance. """ - - # rootDir is the root Panda3D install directory on the local - # machine. - self.rootDir = Filename.fromOsSpecific(rootDir) - - # logDirectory is the directory name where all log files end - # up. - if logDirectory: - self.logDirectory = Filename.fromOsSpecific(logDirectory) - else: - self.logDirectory = Filename(rootDir, 'log') - - # The "super mirror" URL, generally used only by panda3d.exe. - self.superMirrorUrl = superMirrorUrl - - # How anxious should we be about contacting the server for - # the latest code? - self.verifyContents = verifyContents - - # The initial "main" object, if specified. - if main is not None: - self.main = main - - self.respectPerPlatform = respectPerPlatform - #self.notify.info("respectPerPlatform = %s" % (self.respectPerPlatform)) - - # Now that we have rootDir, we can read the config file. - self.readConfigXml() - - - def addPackageInfo(self, name, platform, version, hostUrl, hostDir = None, - recurse = False): - """ Called by the browser for each one of the "required" - packages that were preloaded before starting the application. - If for some reason the package isn't already downloaded, this - will download it on the spot. Raises OSError on failure. """ - - host = self.getHost(hostUrl, hostDir = hostDir) - - if not host.hasContentsFile: - # Always pre-read these hosts' contents.xml files, even if - # we have P3DVCForce in effect, since presumably we've - # already forced them on the plugin side. - host.readContentsFile() - - if not host.downloadContentsFile(self.http): - # Couldn't download? Must have failed to download in the - # plugin as well. But since we launched, we probably have - # a copy already local; let's use it. - message = "Host %s cannot be downloaded, cannot preload %s." % (hostUrl, name) - if not host.hasContentsFile: - # This is weird. How did we launch without having - # this file at all? - raise OSError(message) - - # Just make it a warning and continue. - self.notify.warning(message) - - if name == 'panda3d' and not self.pandaHostUrl: - # A special case: in case we don't have the PackageHostUrl - # compiled in, infer it from the first package we - # installed named "panda3d". - self.pandaHostUrl = hostUrl - - if not platform: - platform = None - package = host.getPackage(name, version, platform = platform) - if not package: - if not recurse: - # Maybe the contents.xml file isn't current. Re-fetch it. - if host.redownloadContentsFile(self.http): - return self.addPackageInfo(name, platform, version, hostUrl, hostDir = hostDir, recurse = True) - - message = "Couldn't find %s %s on %s" % (name, version, hostUrl) - raise OSError(message) - - package.checkStatus() - if not package.downloadDescFile(self.http): - message = "Couldn't get desc file for %s" % (name) - raise OSError(message) - - if not package.downloadPackage(self.http): - message = "Couldn't download %s" % (name) - raise OSError(message) - - if not package.installPackage(self): - message = "Couldn't install %s" % (name) - raise OSError(message) - - if package.guiApp: - self.guiApp = True - init_app_for_gui() - - def setP3DFilename(self, p3dFilename, tokens, argv, instanceId, - interactiveConsole, p3dOffset = 0, p3dUrl = None): - """ Called by the browser to specify the p3d file that - contains the application itself, along with the web tokens - and/or command-line arguments. Once this method has been - called, the application is effectively started. """ - - # One day we will have support for multiple instances within a - # Python session. Against that day, we save the instance ID - # for this instance. - self.instanceId = instanceId - - self.tokens = tokens - self.argv = argv - - # We build up a token dictionary with care, so that if a given - # token appears twice in the token list, we record only the - # first value, not the second or later. This is consistent - # with the internal behavior of the core API. - self.tokenDict = {} - for token, keyword in tokens: - self.tokenDict.setdefault(token, keyword) - - # Also store the arguments on sys, for applications that - # aren't instance-ready. - sys.argv = argv - - # That means we now know the altHost in effect. - self.altHost = self.tokenDict.get('alt_host', None) - - # Tell the browser that Python is up and running, and ready to - # respond to queries. - self.notifyRequest('onpythonload') - - # Now go load the applet. - fname = Filename.fromOsSpecific(p3dFilename) - vfs = VirtualFileSystem.getGlobalPtr() - - if not vfs.exists(fname): - raise ArgumentError("No such file: %s" % (p3dFilename)) - - fname.makeAbsolute() - fname.setBinary() - mf = Multifile() - if p3dOffset == 0: - if not mf.openRead(fname): - raise ArgumentError("Not a Panda3D application: %s" % (p3dFilename)) - else: - if not mf.openRead(fname, p3dOffset): - raise ArgumentError("Not a Panda3D application: %s at offset: %s" % (p3dFilename, p3dOffset)) - - # Now load the p3dInfo file. - self.p3dInfo = None - self.p3dPackage = None - self.p3dConfig = None - self.allowPythonDev = False - - i = mf.findSubfile('p3d_info.xml') - if i >= 0 and hasattr(core, 'readXmlStream'): - stream = mf.openReadSubfile(i) - self.p3dInfo = core.readXmlStream(stream) - mf.closeReadSubfile(stream) - if self.p3dInfo: - self.p3dPackage = self.p3dInfo.FirstChildElement('package') - if self.p3dPackage: - self.p3dConfig = self.p3dPackage.FirstChildElement('config') - - xhost = self.p3dPackage.FirstChildElement('host') - while xhost: - self.__readHostXml(xhost) - xhost = xhost.NextSiblingElement('host') - - if self.p3dConfig: - allowPythonDev = self.p3dConfig.Attribute('allow_python_dev') - if allowPythonDev: - self.allowPythonDev = int(allowPythonDev) - guiApp = self.p3dConfig.Attribute('gui_app') - if guiApp: - self.guiApp = int(guiApp) - - trueFileIO = self.p3dConfig.Attribute('true_file_io') - if trueFileIO: - self.trueFileIO = int(trueFileIO) - - # The interactiveConsole flag can only be set true if the - # application has allow_python_dev set. - if not self.allowPythonDev and interactiveConsole: - raise Exception("Impossible, interactive_console set without allow_python_dev.") - self.interactiveConsole = interactiveConsole - - if self.allowPythonDev: - # Set the fps text to remind the user that - # allow_python_dev is enabled. - ConfigVariableString('frame-rate-meter-text-pattern').setValue('allow_python_dev %0.1f fps') - - if self.guiApp: - init_app_for_gui() - - self.initPackedAppEnvironment() - - # Mount the Multifile under self.multifileRoot. - vfs.mount(mf, self.multifileRoot, vfs.MFReadOnly) - self.p3dMultifile = mf - VFSImporter.reloadSharedPackages() - - self.loadMultifilePrcFiles(mf, self.multifileRoot) - self.gotP3DFilename = True - self.p3dFilename = fname - if p3dUrl: - # The url from which the p3d file was downloaded is - # provided if available. It is only for documentation - # purposes; the actual p3d file has already been - # downloaded to p3dFilename. - self.p3dUrl = core.URLSpec(p3dUrl) - - # Send this call to the main thread; don't call it directly. - messenger.send('AppRunner_startIfReady', taskChain = 'default') - - def __readHostXml(self, xhost): - """ Reads the data in the indicated entry. """ - - url = xhost.Attribute('url') - host = self.getHost(url) - host.readHostXml(xhost) - - # Scan for a matching . If found, it means we - # should use the alternate URL instead of the original URL. - if self.altHost: - xalthost = xhost.FirstChildElement('alt_host') - while xalthost: - keyword = xalthost.Attribute('keyword') - if keyword == self.altHost: - origUrl = xhost.Attribute('url') - newUrl = xalthost.Attribute('url') - self.altHostMap[origUrl] = newUrl - break - - xalthost = xalthost.NextSiblingElement('alt_host') - - def loadMultifilePrcFiles(self, mf, root): - """ Loads any prc files in the root of the indicated - Multifile, which is presumed to have been mounted already - under root. """ - - # We have to load these prc files explicitly, since the - # ConfigPageManager can't directly look inside the vfs. Use - # the Multifile interface to find the prc files, rather than - # vfs.scanDirectory(), so we only pick up the files in this - # particular multifile. - cpMgr = ConfigPageManager.getGlobalPtr() - for f in mf.getSubfileNames(): - fn = Filename(f) - if fn.getDirname() == '' and fn.getExtension() == 'prc': - pathname = '%s/%s' % (root, f) - - alreadyLoaded = False - for cpi in range(cpMgr.getNumImplicitPages()): - if cpMgr.getImplicitPage(cpi).getName() == pathname: - # No need to load this file twice. - alreadyLoaded = True - break - - if not alreadyLoaded: - data = file.open(Filename(pathname), 'r').read() - cp = loadPrcFileData(pathname, data) - # Set it to sort value 20, behind the implicit pages. - cp.setSort(20) - - - def __clearWindowProperties(self): - """ Clears the windowPrc file that was created in a previous - call to setupWindow(), if any. """ - - if self.windowPrc: - unloadPrcFile(self.windowPrc) - self.windowPrc = None - WindowProperties.clearDefault() - - # However, we keep the self.windowProperties object around, in - # case an application wants to return the window to the - # browser frame. - - def setupWindow(self, windowType, x, y, width, height, - parent): - """ Applies the indicated window parameters to the prc - settings, for future windows; or applies them directly to the - main window if the window has already been opened. This is - called by the browser. """ - - if self.started and base.win: - # If we've already got a window, this must be a - # resize/reposition request. - wp = WindowProperties() - if x or y or windowType == 'embedded': - wp.setOrigin(x, y) - if width or height: - wp.setSize(width, height) - if windowType == 'embedded': - wp.setParentWindow(parent) - wp.setFullscreen(False) - base.win.requestProperties(wp) - self.windowProperties = wp - return - - # If we haven't got a window already, start 'er up. Apply the - # requested setting to the prc file, and to the default - # WindowProperties structure. - - self.__clearWindowProperties() - - if windowType == 'hidden': - data = 'window-type none\n' - else: - data = 'window-type onscreen\n' - - wp = WindowProperties.getDefault() - - wp.clearParentWindow() - wp.clearOrigin() - wp.clearSize() - - wp.setFullscreen(False) - if windowType == 'fullscreen': - wp.setFullscreen(True) - - if windowType == 'embedded': - wp.setParentWindow(parent) - - if x or y or windowType == 'embedded': - wp.setOrigin(x, y) - - if width or height: - wp.setSize(width, height) - - self.windowProperties = wp - self.windowPrc = loadPrcFileData("setupWindow", data) - WindowProperties.setDefault(wp) - - self.gotWindow = True - - # Send this call to the main thread; don't call it directly. - messenger.send('AppRunner_startIfReady', taskChain = 'default') - - def setRequestFunc(self, func): - """ This method is called by the browser at startup to supply a - function that can be used to deliver requests upstream, to the - core API, and thereby to the browser. """ - self.requestFunc = func - - def sendRequest(self, request, *args): - """ Delivers a request to the browser via self.requestFunc. - This low-level function is not intended to be called directly - by user code. """ - - assert self.requestFunc - return self.requestFunc(self.instanceId, request, args) - - def __windowEvent(self, win): - """ This method is called when we get a window event. We - listen for this to detect when the window has been - successfully opened. """ - - if not self.windowOpened: - self.windowOpened = True - - # Now that the window is open, we don't need to keep those - # prc settings around any more. - self.__clearWindowProperties() - - # Inform the plugin and browser. - self.notifyRequest('onwindowopen') - - def notifyRequest(self, message): - """ Delivers a notify request to the browser. This is a "this - happened" type notification; it also triggers some JavaScript - code execution, if indicated in the HTML tags, and may also - trigger some internal automatic actions. (For instance, the - plugin takes down the splash window when it sees the - onwindowopen notification. """ - - self.sendRequest('notify', message.lower()) - - def evalScript(self, expression, needsResponse = False): - """ Evaluates an arbitrary JavaScript expression in the global - DOM space. This may be deferred if necessary if needsResponse - is False and self.dom has not yet been assigned. If - needsResponse is true, this waits for the value and returns - it, which means it cannot be deferred. """ - - if not self.dom: - # Defer the expression. - assert not needsResponse - self.deferredEvals.append(expression) - else: - # Evaluate it now. - return self.scriptRequest('eval', self.dom, value = expression, - needsResponse = needsResponse) - - def scriptRequest(self, operation, object, propertyName = '', - value = None, needsResponse = True): - """ Issues a new script request to the browser. This queries - or modifies one of the browser's DOM properties. This is a - low-level method that user code should not call directly; - instead, just operate on the Python wrapper objects that - shadow the DOM objects, beginning with appRunner.dom. - - operation may be one of [ 'get_property', 'set_property', - 'call', 'evaluate' ]. - - object is the browser object to manipulate, or the scope in - which to evaluate the expression. - - propertyName is the name of the property to manipulate, if - relevant (set to None for the default method name). - - value is the new value to assign to the property for - set_property, or the parameter list for call, or the string - expression for evaluate. - - If needsResponse is true, this method will block until the - return value is received from the browser, and then it returns - that value. Otherwise, it returns None immediately, without - waiting for the browser to process the request. - """ - uniqueId = self.nextScriptId - self.nextScriptId = (self.nextScriptId + 1) % 0xffffffff - self.sendRequest('script', operation, object, - propertyName, value, needsResponse, uniqueId) - - if needsResponse: - # Now wait for the response to come in. - result = self.sendRequest('wait_script_response', uniqueId) - return result - - def dropObject(self, objectId): - """ Inform the parent process that we no longer have an - interest in the P3D_object corresponding to the indicated - objectId. Not intended to be called by user code. """ - - self.sendRequest('drop_p3dobj', objectId) - -def dummyAppRunner(tokens = [], argv = None): - """ This function creates a dummy global AppRunner object, which - is useful for testing running in a packaged environment without - actually bothering to package up the application. Call this at - the start of your application to enable it. - - It places the current working directory under /mf, as if it were - mounted from a packed multifile. It doesn't convert egg files to - bam files, of course; and there are other minor differences from - running in an actual packaged environment. But it can be a useful - first-look sanity check. """ - - if AppRunnerGlobal.appRunner: - print("Already have AppRunner, not creating a new one.") - return AppRunnerGlobal.appRunner - - appRunner = AppRunner() - appRunner.dummy = True - AppRunnerGlobal.appRunner = appRunner - - platform = PandaSystem.getPlatform() - version = PandaSystem.getPackageVersionString() - hostUrl = PandaSystem.getPackageHostUrl() - - if platform.startswith('win'): - rootDir = Filename(Filename.getUserAppdataDirectory(), 'Panda3D') - elif platform.startswith('osx'): - rootDir = Filename(Filename.getHomeDirectory(), 'Library/Caches/Panda3D') - else: - rootDir = Filename(Filename.getHomeDirectory(), '.panda3d') - - appRunner.rootDir = rootDir - appRunner.logDirectory = Filename(rootDir, 'log') - - # Of course we will have the panda3d application loaded. - appRunner.addPackageInfo('panda3d', platform, version, hostUrl) - - appRunner.tokens = tokens - appRunner.tokenDict = dict(tokens) - if argv is None: - argv = sys.argv - appRunner.argv = argv - appRunner.altHost = appRunner.tokenDict.get('alt_host', None) - - appRunner.p3dInfo = None - appRunner.p3dPackage = None - - # Mount the current directory under the multifileRoot, as if it - # were coming from a multifile. - cwd = ExecutionEnvironment.getCwd() - vfs = VirtualFileSystem.getGlobalPtr() - vfs.mount(cwd, appRunner.multifileRoot, vfs.MFReadOnly) - - appRunner.initPackedAppEnvironment() - - return appRunner - diff --git a/direct/src/p3d/DWBPackageInstaller.py b/direct/src/p3d/DWBPackageInstaller.py deleted file mode 100644 index c4daa67cb1..0000000000 --- a/direct/src/p3d/DWBPackageInstaller.py +++ /dev/null @@ -1,96 +0,0 @@ -__all__ = ["DWBPackageInstaller"] - -from direct.p3d.PackageInstaller import PackageInstaller -from direct.gui.DirectWaitBar import DirectWaitBar -from direct.gui import DirectGuiGlobals as DGG - -class DWBPackageInstaller(DirectWaitBar, PackageInstaller): - """ This class presents a PackageInstaller that also inherits from - DirectWaitBar, so it updates its own GUI as it downloads. - - Specify perPackage = True to make the progress bar reset for each - package, or False (the default) to show one continuous progress - bar for all packages. - - Specify updateText = True (the default) to update the text label - with the name of the package or False to leave it up to you to set - it. - - You can specify a callback function with finished = func; this - function will be called, with one boolean parameter, when the - download has completed. The parameter will be true on success, or - false on failure. - """ - - def __init__(self, appRunner, parent = None, **kw): - PackageInstaller.__init__(self, appRunner) - - optiondefs = ( - ('borderWidth', (0.01, 0.01), None), - ('relief', DGG.SUNKEN, self.setRelief), - ('range', 1, self.setRange), - ('barBorderWidth', (0.01, 0.01), self.setBarBorderWidth), - ('barColor', (0.424, 0.647, 0.878, 1), self.setBarColor), - ('barRelief', DGG.RAISED, self.setBarRelief), - ('text', 'Starting', self.setText), - ('text_pos', (0, -0.025), None), - ('text_scale', 0.1, None), - ('perPackage', False, None), - ('updateText', True, None), - ('finished', None, None), - ) - self.defineoptions(kw, optiondefs) - DirectWaitBar.__init__(self, parent, **kw) - self.initialiseoptions(DWBPackageInstaller) - self.updateBarStyle() - - # Hidden by default until the download begins. - self.hide() - - def cleanup(self): - PackageInstaller.cleanup(self) - DirectWaitBar.destroy(self) - - def destroy(self): - PackageInstaller.cleanup(self) - DirectWaitBar.destroy(self) - - def packageStarted(self, package): - """ This callback is made for each package between - downloadStarted() and downloadFinished() to indicate the start - of a new package. """ - if self['updateText']: - self['text'] = 'Installing %s' % (package.getFormattedName()) - self.show() - - def packageProgress(self, package, progress): - """ This callback is made repeatedly between packageStarted() - and packageFinished() to update the current progress on the - indicated package only. The progress value ranges from 0 - (beginning) to 1 (complete). """ - - if self['perPackage']: - self['value'] = progress * self['range'] - - def downloadProgress(self, overallProgress): - """ This callback is made repeatedly between downloadStarted() - and downloadFinished() to update the current progress through - all packages. The progress value ranges from 0 (beginning) to - 1 (complete). """ - - if not self['perPackage']: - self['value'] = overallProgress * self['range'] - - def downloadFinished(self, success): - """ This callback is made when all of the packages have been - downloaded and installed (or there has been some failure). If - all packages where successfully installed, success is True. - - If there were no packages that required downloading, this - callback will be made immediately, *without* a corresponding - call to downloadStarted(). """ - - self.hide() - - if self['finished']: - self['finished'](success) diff --git a/direct/src/p3d/DeploymentTools.py b/direct/src/p3d/DeploymentTools.py deleted file mode 100644 index 9c390fbf07..0000000000 --- a/direct/src/p3d/DeploymentTools.py +++ /dev/null @@ -1,1379 +0,0 @@ -""" This module is used to build a graphical installer -or a standalone executable from a p3d file. It will try -to build for as many platforms as possible. """ - -__all__ = ["Standalone", "Installer"] - -import os, sys, subprocess, tarfile, shutil, time, zipfile, socket, getpass, struct -import gzip, plistlib -from direct.directnotify.DirectNotifyGlobal import * -from direct.showbase.AppRunnerGlobal import appRunner -from panda3d.core import PandaSystem, HTTPClient, Filename, VirtualFileSystem, Multifile -from panda3d.core import TiXmlDocument, TiXmlDeclaration, TiXmlElement, readXmlStream -from panda3d.core import PNMImage, PNMFileTypeRegistry -from direct.stdpy.file import * -from direct.p3d.HostInfo import HostInfo -# This is important for some reason -import encodings - -try: - import pwd -except ImportError: - pwd = None - -if sys.version_info >= (3, 0): - xrange = range - from io import BytesIO, TextIOWrapper -else: - from io import BytesIO - from StringIO import StringIO - -# Make sure this matches with the magic in p3dEmbedMain.cxx. -P3DEMBED_MAGIC = 0xFF3D3D00 - -# This filter function is used when creating -# an archive that should be owned by root. -def archiveFilter(info): - basename = os.path.basename(info.name) - if basename in [".DS_Store", "Thumbs.db"]: - return None - - info.uid = info.gid = 0 - info.uname = info.gname = "root" - - # All files without an extension get mode 0755, - # all other files are chmodded to 0644. - # Somewhat hacky, but it's the only way - # permissions can work on a Windows box. - if info.type != tarfile.DIRTYPE and '.' in info.name.rsplit('/', 1)[-1]: - info.mode = 0o644 - else: - info.mode = 0o755 - - return info - -# Hack to make all files in a tar file owned by root. -# The tarfile module doesn't have filter functionality until Python 2.7. -# Yay duck typing! -class TarInfoRoot(tarfile.TarInfo): - uid = property(lambda self: 0, lambda self, x: None) - gid = property(lambda self: 0, lambda self, x: None) - uname = property(lambda self: "root", lambda self, x: None) - gname = property(lambda self: "root", lambda self, x: None) - mode = property(lambda self: 0o644 if self.type != tarfile.DIRTYPE and \ - '.' in self.name.rsplit('/', 1)[-1] else 0o755, - lambda self, x: None) - -# On OSX, the root group is named "wheel". -class TarInfoRootOSX(TarInfoRoot): - gname = property(lambda self: "wheel", lambda self, x: None) - - -class Standalone: - """ This class creates a standalone executable from a given .p3d file. """ - notify = directNotify.newCategory("Standalone") - - def __init__(self, p3dfile, tokens = {}): - self.p3dfile = Filename(p3dfile) - self.basename = self.p3dfile.getBasenameWoExtension() - self.tokens = tokens - - self.tempDir = Filename.temporary("", self.basename, "") + "/" - self.tempDir.makeDir() - self.host = HostInfo(PandaSystem.getPackageHostUrl(), appRunner = appRunner, hostDir = self.tempDir, asMirror = False, perPlatform = True) - - self.http = HTTPClient.getGlobalPtr() - if not self.host.hasContentsFile: - if not self.host.readContentsFile(): - if not self.host.downloadContentsFile(self.http): - Standalone.notify.error("couldn't read host") - return - - def __del__(self): - try: - appRunner.rmtree(self.tempDir) - except: - try: shutil.rmtree(self.tempDir.toOsSpecific()) - except: pass - - def buildAll(self, outputDir = "."): - """ Builds standalone executables for every known platform, - into the specified output directory. """ - - platforms = set() - for package in self.host.getPackages(name = "p3dembed"): - platforms.add(package.platform) - if len(platforms) == 0: - Standalone.notify.warning("No platforms found to build for!") - - if 'win32' in platforms and 'win_i386' in platforms: - platforms.remove('win32') - - outputDir = Filename(outputDir + "/") - outputDir.makeDir() - for platform in platforms: - if platform.startswith("win"): - self.build(Filename(outputDir, platform + "/" + self.basename + ".exe"), platform) - else: - self.build(Filename(outputDir, platform + "/" + self.basename), platform) - - def build(self, output, platform = None, extraTokens = {}): - """ Builds a standalone executable and stores it into the path - indicated by the 'output' argument. You can specify to build for - a different platform by altering the 'platform' argument. """ - - if platform == None: - platform = PandaSystem.getPlatform() - - vfs = VirtualFileSystem.getGlobalPtr() - - for package in self.host.getPackages(name = "p3dembed", platform = platform): - if not package.downloadDescFile(self.http): - Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform)) - continue - if not package.downloadPackage(self.http): - Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform)) - continue - - # Figure out where p3dembed might be now. - if package.platform.startswith("win"): - # Use p3dembedw unless console_environment was set. - cEnv = extraTokens.get("console_environment", self.tokens.get("console_environment", 0)) - if cEnv != "" and int(cEnv) != 0: - p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembed.exe" % package.platform) - else: - p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembedw.exe" % package.platform) - # Fallback for older p3dembed versions - if not vfs.exists(p3dembed): - Filename(self.host.hostDir, "p3dembed/%s/p3dembed.exe" % package.platform) - else: - p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembed" % package.platform) - - if not vfs.exists(p3dembed): - Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform)) - continue - - return self.embed(output, p3dembed, extraTokens) - - Standalone.notify.error("Failed to build standalone for platform %s" % platform) - - def embed(self, output, p3dembed, extraTokens = {}): - """ Embeds the p3d file into the provided p3dembed executable. - This function is not really useful - use build() or buildAll() instead. """ - - # Load the p3dembed data into memory - size = p3dembed.getFileSize() - p3dembed_data = VirtualFileSystem.getGlobalPtr().readFile(p3dembed, True) - assert len(p3dembed_data) == size - - # Find the magic size string and replace it with the real size, - # regardless of the endianness of the p3dembed executable. - p3dembed_data = p3dembed_data.replace(struct.pack('>I', P3DEMBED_MAGIC), - struct.pack('>I', size)) - p3dembed_data = p3dembed_data.replace(struct.pack(' %s failed for platform %s" % (package.packageName, package.platform)) - return [] - if not package.downloadPackage(self.http): - Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform)) - return [] - - filenames = [] - vfs = VirtualFileSystem.getGlobalPtr() - for e in package.extracts: - if e.basename not in ["p3dembed", "p3dembed.exe", "p3dembed.exe.manifest", "p3dembedw.exe", "p3dembedw.exe.manifest"]: - filename = Filename(package.getPackageDir(), e.filename) - filename.makeAbsolute() - if vfs.exists(filename): - filenames.append(filename) - else: - Standalone.notify.error("%s mentioned in xml, but does not exist" % e.filename) - - return filenames - - -class PackageTree: - """ A class used internally to build a temporary package - tree for inclusion into an installer. """ - - def __init__(self, platform, hostDir, hostUrl): - self.platform = platform - self.hosts = {} - self.packages = {} - self.hostUrl = hostUrl - self.hostDir = Filename(hostDir) - self.hostDir.makeDir() - self.http = HTTPClient.getGlobalPtr() - - def getHost(self, hostUrl): - if hostUrl in self.hosts: - return self.hosts[hostUrl] - - host = HostInfo(hostUrl, appRunner = appRunner, hostDir = self.hostDir, asMirror = False, perPlatform = True) - if not host.hasContentsFile: - if not host.readContentsFile(): - if not host.downloadContentsFile(self.http): - Installer.notify.error("couldn't read host %s" % host.hostUrl) - return None - self.hosts[hostUrl] = host - return host - - def installPackage(self, name, version, hostUrl = None): - """ Installs the named package into the tree. """ - - if hostUrl is None: - hostUrl = self.hostUrl - - pkgIdent = (name, version) - if pkgIdent in self.packages: - return self.packages[pkgIdent] - - package = None - # Always try the super host first, if any. - if appRunner and appRunner.superMirrorUrl: - superHost = self.getHost(appRunner.superMirrorUrl) - if self.platform: - package = superHost.getPackage(name, version, self.platform) - if not package: - package = superHost.getPackage(name, version) - - if not package: - host = self.getHost(hostUrl) - if self.platform: - package = host.getPackage(name, version, self.platform) - if not package: - package = host.getPackage(name, version) - - if not package: - Installer.notify.error("Package %s %s for %s not known on %s" % ( - name, version, self.platform, hostUrl)) - return - - package.installed = True # Hack not to let it unnecessarily install itself - if not package.downloadDescFile(self.http): - Installer.notify.error(" -> %s failed for platform %s" % (package.packageName, package.platform)) - return - if not package.downloadPackage(self.http): - Installer.notify.error(" -> %s failed for platform %s" % (package.packageName, package.platform)) - return - - self.packages[pkgIdent] = package - - # Check for any dependencies. - for rname, rversion, rhost in package.requires: - self.installPackage(rname, rversion, rhost.hostUrl) - - return package - - -class Icon: - """ This class is used to create an icon for various platforms. """ - notify = directNotify.newCategory("Icon") - - def __init__(self): - self.images = {} - - def addImage(self, image): - """ Adds an image to the icon. Returns False on failure, True on success. - Only one image per size can be loaded, and the image size must be square. """ - - if not isinstance(image, PNMImage): - fn = image - if not isinstance(fn, Filename): - fn = Filename.fromOsSpecific(fn) - - image = PNMImage() - if not image.read(fn): - Icon.notify.warning("Image '%s' could not be read" % fn.getBasename()) - return False - - if image.getXSize() != image.getYSize(): - Icon.notify.warning("Ignoring image without square size") - return False - - self.images[image.getXSize()] = image - - return True - - def makeICO(self, fn): - """ Writes the images to a Windows ICO file. Returns True on success. """ - - if not isinstance(fn, Filename): - fn = Filename.fromOsSpecific(fn) - fn.setBinary() - - count = 0 - for size in self.images.keys(): - if size <= 256: - count += 1 - - ico = open(fn, 'wb') - ico.write(struct.pack('I', pngsize + 8)) - icns.seek(start + pngsize) - - elif size in icon_types: - icns.write(icon_types[size]) - icns.write(struct.pack('>I', size * size * 4 + 8)) - - for y in xrange(size): - for x in xrange(size): - r, g, b = image.getXel(x, y) - icns.write(struct.pack('>BBBB', 0, int(r * 255), int(g * 255), int(b * 255))) - - if not image.hasAlpha(): - continue - icns.write(mask_types[size]) - icns.write(struct.pack('>I', size * size + 8)) - - for y in xrange(size): - for x in xrange(size): - icns.write(struct.pack('I', length)) - icns.close() - - return True - - -class Installer: - """ This class creates a (graphical) installer from a given .p3d file. """ - notify = directNotify.newCategory("Installer") - - def __init__(self, p3dfile, shortname, fullname, version, tokens = {}): - self.p3dFilename = p3dfile - if not shortname: - shortname = p3dfile.getBasenameWoExtension() - self.shortname = shortname - self.fullname = fullname - self.version = str(version) - self.includeRequires = False - self.offerRun = True - self.offerDesktopShortcut = True - self.licensename = "" - self.licensefile = Filename() - self.authorid = "org.panda3d" - self.authorname = os.environ.get("DEBFULLNAME", "") - self.authoremail = os.environ.get("DEBEMAIL", "") - self.icon = None - - # Try to determine a default author name ourselves. - uname = None - if pwd is not None and hasattr(os, 'getuid'): - uinfo = pwd.getpwuid(os.getuid()) - if uinfo: - uname = uinfo.pw_name - if not self.authorname: - self.authorname = \ - uinfo.pw_gecos.split(',', 1)[0] - - # Fallbacks in case that didn't work or wasn't supported. - if not uname: - uname = getpass.getuser() - if not self.authorname: - self.authorname = uname - if not self.authoremail and ' ' not in uname: - self.authoremail = "%s@%s" % (uname, socket.gethostname()) - - # Load the p3d file to read out the required packages - mf = Multifile() - if not mf.openRead(self.p3dFilename): - Installer.notify.error("Not a Panda3D application: %s" % (p3dfile)) - return - - # Now load the p3dInfo file. - self.hostUrl = None - self.requires = [] - self.extracts = [] - i = mf.findSubfile('p3d_info.xml') - if i >= 0: - stream = mf.openReadSubfile(i) - p3dInfo = readXmlStream(stream) - mf.closeReadSubfile(stream) - if p3dInfo: - p3dPackage = p3dInfo.FirstChildElement('package') - p3dHost = p3dPackage.FirstChildElement('host') - if p3dHost.Attribute('url'): - self.hostUrl = p3dHost.Attribute('url') - p3dRequires = p3dPackage.FirstChildElement('requires') - while p3dRequires: - self.requires.append(( - p3dRequires.Attribute('name'), - p3dRequires.Attribute('version'), - p3dRequires.Attribute('host'))) - p3dRequires = p3dRequires.NextSiblingElement('requires') - - p3dExtract = p3dPackage.FirstChildElement('extract') - while p3dExtract: - filename = p3dExtract.Attribute('filename') - self.extracts.append(filename) - p3dExtract = p3dExtract.NextSiblingElement('extract') - - if not self.fullname: - p3dConfig = p3dPackage.FirstChildElement('config') - if p3dConfig: - self.fullname = p3dConfig.Attribute('display_name') - else: - Installer.notify.warning("No p3d_info.xml was found in .p3d archive.") - - mf.close() - - if not self.hostUrl: - self.hostUrl = PandaSystem.getPackageHostUrl() - if not self.hostUrl: - self.hostUrl = self.standalone.host.hostUrl - Installer.notify.warning("No host URL was specified by .p3d archive. Falling back to %s" % (self.hostUrl)) - - if not self.fullname: - self.fullname = self.shortname - - self.tempDir = Filename.temporary("", self.shortname, "") + "/" - self.tempDir.makeDir() - self.__tempRoots = {} - - if self.extracts: - # Copy .p3d to a temporary file so we can remove the extracts. - p3dfile = Filename(self.tempDir, self.p3dFilename.getBasename()) - shutil.copyfile(self.p3dFilename.toOsSpecific(), p3dfile.toOsSpecific()) - mf = Multifile() - if not mf.openReadWrite(p3dfile): - Installer.notify.error("Failure to open %s for writing." % (p3dfile)) - - # We don't really need this silly thing when embedding, anyway. - mf.setHeaderPrefix("") - - for fn in self.extracts: - if not mf.removeSubfile(fn): - Installer.notify.error("Failure to remove %s from multifile." % (p3dfile)) - - mf.repack() - mf.close() - - self.standalone = Standalone(p3dfile, tokens) - - def __del__(self): - try: - appRunner.rmtree(self.tempDir) - except: - try: shutil.rmtree(self.tempDir.toOsSpecific()) - except: pass - - def installPackagesInto(self, hostDir, platform): - """ Installs the packages required by the .p3d file into - the specified directory, for the given platform. """ - - if not self.includeRequires: - return - - # Write out the extracts from the original .p3d. - if self.extracts: - mf = Multifile() - if not mf.openRead(self.p3dFilename): - Installer.notify.error("Failed to open .p3d archive: %s" % (filename)) - - for filename in self.extracts: - i = mf.findSubfile(filename) - if i < 0: - Installer.notify.error("Cannot find extract in .p3d archive: %s" % (filename)) - continue - - if not mf.extractSubfile(i, Filename(hostDir, filename)): - Installer.notify.error("Failed to extract file from .p3d archive: %s" % (filename)) - mf.close() - - pkgTree = PackageTree(platform, hostDir, self.hostUrl) - pkgTree.installPackage("images", None, self.standalone.host.hostUrl) - - for name, version, hostUrl in self.requires: - pkgTree.installPackage(name, version, hostUrl) - - # Remove the extracted files from the compressed archive, to save space. - vfs = VirtualFileSystem.getGlobalPtr() - for package in pkgTree.packages.values(): - if package.uncompressedArchive: - archive = Filename(package.getPackageDir(), package.uncompressedArchive.filename) - if not archive.exists(): - continue - - mf = Multifile() - # Make sure that it isn't mounted before altering it, just to be safe - vfs.unmount(archive) - os.chmod(archive.toOsSpecific(), 0o644) - if not mf.openReadWrite(archive): - Installer.notify.warning("Failed to open archive %s" % (archive)) - continue - - # We don't iterate over getNumSubfiles because we're - # removing subfiles while we're iterating over them. - subfiles = mf.getSubfileNames() - for subfile in subfiles: - # We do *NOT* call vfs.exists here in case the package is mounted. - if Filename(package.getPackageDir(), subfile).exists(): - Installer.notify.debug("Removing already-extracted %s from multifile" % (subfile)) - mf.removeSubfile(subfile) - - # This seems essential for mf.close() not to crash later. - mf.repack() - - # If we have no subfiles left, we can just remove the multifile. - #XXX rdb: it seems that removing it causes trouble, so let's not. - #if mf.getNumSubfiles() == 0: - # Installer.notify.info("Removing empty archive %s" % (package.uncompressedArchive.filename)) - # mf.close() - # archive.unlink() - #else: - mf.close() - try: os.chmod(archive.toOsSpecific(), 0o444) - except: pass - - # Write out our own contents.xml file. - doc = TiXmlDocument() - decl = TiXmlDeclaration("1.0", "utf-8", "") - doc.InsertEndChild(decl) - - xcontents = TiXmlElement("contents") - for package in pkgTree.packages.values(): - xpackage = TiXmlElement('package') - xpackage.SetAttribute('name', package.packageName) - - filename = package.packageName + "/" - - if package.packageVersion: - xpackage.SetAttribute('version', package.packageVersion) - filename += package.packageVersion + "/" - - if package.platform: - xpackage.SetAttribute('platform', package.platform) - filename += package.platform + "/" - assert package.platform == platform - - xpackage.SetAttribute('per_platform', '1') - - filename += package.descFileBasename - xpackage.SetAttribute('filename', filename) - xcontents.InsertEndChild(xpackage) - - doc.InsertEndChild(xcontents) - doc.SaveFile(Filename(hostDir, "contents.xml").toOsSpecific()) - - def buildAll(self, outputDir = "."): - """ Creates a (graphical) installer for every known platform. - Call this after you have set the desired parameters. """ - - platforms = set() - for package in self.standalone.host.getPackages(name = "p3dembed"): - platforms.add(package.platform) - if len(platforms) == 0: - Installer.notify.warning("No platforms found to build for!") - - if 'win32' in platforms and 'win_i386' in platforms: - platforms.remove('win32') - - outputDir = Filename(outputDir + "/") - outputDir.makeDir() - for platform in platforms: - output = Filename(outputDir, platform + "/") - output.makeDir() - self.build(output, platform) - - def build(self, output, platform = None): - """ Builds (graphical) installers and stores it into the path - indicated by the 'output' argument. You can specify to build for - a different platform by altering the 'platform' argument. - If 'output' is a directory, the installer will be stored in it. """ - - if platform == None: - platform = PandaSystem.getPlatform() - - if platform.startswith("win"): - self.buildNSIS(output, platform) - return - elif "_" in platform: - osname, arch = platform.split("_", 1) - if osname == "linux": - self.buildDEB(output, platform) - self.buildArch(output, platform) - return - elif osname == "osx": - self.buildPKG(output, platform) - return - Installer.notify.info("Ignoring unknown platform " + platform) - - def __buildTempLinux(self, platform): - """ Builds a filesystem for Linux. Used so that buildDEB, - buildRPM and buildArch can share the same temp directory. """ - - if platform in self.__tempRoots: - return self.__tempRoots[platform] - - tempdir = Filename(self.tempDir, platform) - tempdir.makeDir() - - Filename(tempdir, "usr/bin/").makeDir() - if self.includeRequires: - extraTokens = {"host_dir" : "/usr/lib/" + self.shortname.lower(), - "start_dir" : "/usr/lib/" + self.shortname.lower()} - else: - extraTokens = {} - self.standalone.build(Filename(tempdir, "usr/bin/" + self.shortname.lower()), platform, extraTokens) - if not self.licensefile.empty(): - Filename(tempdir, "usr/share/doc/%s/" % self.shortname.lower()).makeDir() - shutil.copyfile(self.licensefile.toOsSpecific(), Filename(tempdir, "usr/share/doc/%s/copyright" % self.shortname.lower()).toOsSpecific()) - shutil.copyfile(self.licensefile.toOsSpecific(), Filename(tempdir, "usr/share/doc/%s/LICENSE" % self.shortname.lower()).toOsSpecific()) - - # Add an image file to /usr/share/pixmaps/ - iconFile = None - if self.icon is not None: - iconImage = None - if 48 in self.icon.images: - iconImage = self.icon.images[48] - elif 64 in self.icon.images: - iconImage = self.icon.images[64] - elif 32 in self.icon.images: - iconImage = self.icon.images[32] - else: - Installer.notify.warning("No suitable icon image for Linux provided, should preferably be 48x48 in size") - - if iconImage is not None: - iconFile = Filename(tempdir, "usr/share/pixmaps/%s.png" % self.shortname) - iconFile.setBinary() - iconFile.makeDir() - if not iconImage.write(iconFile): - Installer.notify.warning("Failed to write icon file for Linux") - iconFile.unlink() - iconFile = None - - # Write a .desktop file to /usr/share/applications/ - desktopFile = Filename(tempdir, "usr/share/applications/%s.desktop" % self.shortname.lower()) - desktopFile.setText() - desktopFile.makeDir() - desktop = open(desktopFile.toOsSpecific(), 'w') - desktop.write("[Desktop Entry]\n") - desktop.write("Name=%s\n" % self.fullname) - desktop.write("Exec=%s\n" % self.shortname.lower()) - if iconFile is not None: - desktop.write("Icon=%s\n" % iconFile.getBasename()) - - # Set the "Terminal" option based on whether or not a console env is requested - cEnv = self.standalone.tokens.get("console_environment", "") - if cEnv == "" or int(cEnv) == 0: - desktop.write("Terminal=false\n") - else: - desktop.write("Terminal=true\n") - - desktop.write("Type=Application\n") - desktop.close() - - if self.includeRequires or self.extracts: - hostDir = Filename(tempdir, "usr/lib/%s/" % self.shortname.lower()) - hostDir.makeDir() - self.installPackagesInto(hostDir, platform) - - totsize = 0 - for root, dirs, files in self.os_walk(tempdir.toOsSpecific()): - for name in files: - totsize += os.path.getsize(os.path.join(root, name)) - - self.__tempRoots[platform] = (tempdir, totsize) - return self.__tempRoots[platform] - - def buildDEB(self, output, platform): - """ Builds a .deb archive and stores it in the path indicated - by the 'output' argument. It will be built for the architecture - specified by the 'arch' argument. - If 'output' is a directory, the deb file will be stored in it. """ - - arch = platform.rsplit("_", 1)[-1] - output = Filename(output) - if output.isDirectory(): - output = Filename(output, "%s_%s_%s.deb" % (self.shortname.lower(), self.version, arch)) - Installer.notify.info("Creating %s..." % output) - modtime = int(time.time()) - - # Create a temporary directory and write the launcher and dependencies to it. - tempdir, totsize = self.__buildTempLinux(platform) - - # Create a control file in memory. - controlfile = BytesIO() - if sys.version_info >= (3, 0): - cout = TextIOWrapper(controlfile, encoding='utf-8', newline='') - else: - cout = StringIO() - - cout.write("Package: %s\n" % self.shortname.lower()) - cout.write("Version: %s\n" % self.version) - cout.write("Maintainer: %s <%s>\n" % (self.authorname, self.authoremail)) - cout.write("Section: games\n") - cout.write("Priority: optional\n") - cout.write("Architecture: %s\n" % arch) - cout.write("Installed-Size: %d\n" % -(-totsize // 1024)) - cout.write("Description: %s\n" % self.fullname) - cout.write("Depends: libc6, libgcc1, libstdc++6, libx11-6\n") - cout.flush() - if sys.version_info < (3, 0): - controlfile.write(cout.getvalue().encode('utf-8')) - - controlinfo = TarInfoRoot("control") - controlinfo.mtime = modtime - controlinfo.size = controlfile.tell() - controlfile.seek(0) - - # Open the deb file and write to it. It's actually - # just an AR file, which is very easy to make. - if output.exists(): - output.unlink() - debfile = open(output.toOsSpecific(), "wb") - debfile.write(b"!\x0A") - pad_mtime = str(modtime).encode().ljust(12, b' ') - - # The first entry is a special file that marks it a .deb. - debfile.write(b"debian-binary ") - debfile.write(pad_mtime) - debfile.write(b"0 0 100644 4 \x60\x0A") - debfile.write(b"2.0\x0A") - - # Write the control.tar.gz to the archive. We'll leave the - # size 0 for now, and go back and fill it in later. - debfile.write(b"control.tar.gz ") - debfile.write(pad_mtime) - debfile.write(b"0 0 100644 0 \x60\x0A") - ctaroffs = debfile.tell() - ctarfile = tarfile.open("control.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot) - ctarfile.addfile(controlinfo, controlfile) - ctarfile.close() - ctarsize = debfile.tell() - ctaroffs - if (ctarsize & 1): debfile.write(b"\x0A") - - # Write the data.tar.gz to the archive. Again, leave size 0. - debfile.write(b"data.tar.gz ") - debfile.write(pad_mtime) - debfile.write(b"0 0 100644 0 \x60\x0A") - dtaroffs = debfile.tell() - dtarfile = tarfile.open("data.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot) - dtarfile.add(Filename(tempdir, "usr").toOsSpecific(), "/usr") - dtarfile.close() - dtarsize = debfile.tell() - dtaroffs - if (dtarsize & 1): debfile.write(b"\x0A") - - # Write the correct sizes of the archives. - debfile.seek(ctaroffs - 12) - debfile.write(str(ctarsize).encode().ljust(10, b' ')) - debfile.seek(dtaroffs - 12) - debfile.write(str(dtarsize).encode().ljust(10, b' ')) - - debfile.close() - - return output - - def buildArch(self, output, platform): - """ Builds an ArchLinux package and stores it in the path - indicated by the 'output' argument. It will be built for the - architecture specified by the 'arch' argument. - If 'output' is a directory, the deb file will be stored in it. """ - - arch = platform.rsplit("_", 1)[-1] - assert arch in ("i386", "amd64") - arch = {"i386" : "i686", "amd64" : "x86_64"}[arch] - pkgver = self.version + "-1" - - output = Filename(output) - if output.isDirectory(): - output = Filename(output, "%s-%s-%s.pkg.tar.gz" % (self.shortname.lower(), pkgver, arch)) - Installer.notify.info("Creating %s..." % output) - modtime = int(time.time()) - - # Create a temporary directory and write the launcher and dependencies to it. - tempdir, totsize = self.__buildTempLinux(platform) - - # Create a pkginfo file in memory. - pkginfo = BytesIO() - if sys.version_info >= (3, 0): - pout = TextIOWrapper(pkginfo, encoding='utf-8', newline='') - else: - pout = StringIO() - - pout.write("# Generated using pdeploy\n") - pout.write("# %s\n" % time.ctime(modtime)) - pout.write("pkgname = %s\n" % self.shortname.lower()) - pout.write("pkgver = %s\n" % pkgver) - pout.write("pkgdesc = %s\n" % self.fullname) - pout.write("builddate = %s\n" % modtime) - pout.write("packager = %s <%s>\n" % (self.authorname, self.authoremail)) - pout.write("size = %d\n" % totsize) - pout.write("arch = %s\n" % arch) - if self.licensename != "": - pout.write("license = %s\n" % self.licensename) - pout.flush() - if sys.version_info < (3, 0): - pkginfo.write(pout.getvalue().encode('utf-8')) - - pkginfoinfo = TarInfoRoot(".PKGINFO") - pkginfoinfo.mtime = modtime - pkginfoinfo.size = pkginfo.tell() - pkginfo.seek(0) - - # Create the actual package now. - pkgfile = tarfile.open(output.toOsSpecific(), "w:gz", tarinfo = TarInfoRoot) - pkgfile.addfile(pkginfoinfo, pkginfo) - pkgfile.add(tempdir.toOsSpecific(), "/") - if not self.licensefile.empty(): - pkgfile.add(self.licensefile.toOsSpecific(), "/usr/share/licenses/%s/LICENSE" % self.shortname.lower()) - pkgfile.close() - - return output - - def buildAPP(self, output, platform): - - output = Filename(output) - if output.isDirectory() and output.getExtension() != 'app': - output = Filename(output, "%s.app" % self.fullname) - Installer.notify.info("Creating %s..." % output) - - # Create the executable for the application bundle - exefile = Filename(output, "Contents/MacOS/" + self.shortname) - exefile.makeDir() - if self.includeRequires: - extraTokens = {"host_dir": "../Resources", "start_dir": "../Resources"} - else: - extraTokens = {} - self.standalone.build(exefile, platform, extraTokens) - hostDir = Filename(output, "Contents/Resources/") - hostDir.makeDir() - self.installPackagesInto(hostDir, platform) - - hasIcon = False - if self.icon is not None: - Installer.notify.info("Generating %s.icns..." % self.shortname) - hasIcon = self.icon.makeICNS(Filename(hostDir, "%s.icns" % self.shortname)) - - # Create the application plist file using Python's plistlib module. - plist = { - 'CFBundleDevelopmentRegion': 'English', - 'CFBundleDisplayName': self.fullname, - 'CFBundleExecutable': exefile.getBasename(), - 'CFBundleIdentifier': '%s.%s' % (self.author, self.shortname), - 'CFBundleInfoDictionaryVersion': '6.0', - 'CFBundleName': self.shortname, - 'CFBundlePackageType': 'APPL', - 'CFBundleShortVersionString': self.version, - 'CFBundleVersion': self.version, - 'LSHasLocalizedDisplayName': False, - 'NSAppleScriptEnabled': False, - 'NSPrincipalClass': 'NSApplication', - } - if hasIcon: - plist['CFBundleIconFile'] = self.shortname + '.icns' - - plistlib.writePlist(plist, Filename(output, "Contents/Info.plist").toOsSpecific()) - return output - - def buildPKG(self, output, platform): - appfn = self.buildAPP(output, platform) - appname = "/Applications/" + appfn.getBasename() - output = Filename(output) - if output.isDirectory(): - output = Filename(output, "%s %s.pkg" % (self.fullname, self.version)) - Installer.notify.info("Creating %s..." % output) - - Filename(output, "Contents/Resources/en.lproj/").makeDir() - if self.licensefile: - shutil.copyfile(self.licensefile.toOsSpecific(), Filename(output, "Contents/Resources/License.txt").toOsSpecific()) - pkginfo = open(Filename(output, "Contents/PkgInfo").toOsSpecific(), "w") - pkginfo.write("pmkrpkg1") - pkginfo.close() - pkginfo = open(Filename(output, "Contents/Resources/package_version").toOsSpecific(), "w") - pkginfo.write("major: 1\nminor: 9") - pkginfo.close() - - # Although it might make more sense to use Python's plistlib here, - # it is not available on non-OSX systems before Python 2.6. - plist = open(Filename(output, "Contents/Info.plist").toOsSpecific(), "w") - plist.write('\n') - plist.write('\n') - plist.write('\n') - plist.write('\n') - plist.write('\tCFBundleIdentifier\n') - plist.write('\t%s.pkg.%s\n' % (self.authorid, self.shortname)) - plist.write('\tCFBundleShortVersionString\n') - plist.write('\t%s\n' % self.version) - plist.write('\tIFMajorVersion\n') - plist.write('\t1\n') - plist.write('\tIFMinorVersion\n') - plist.write('\t9\n') - plist.write('\tIFPkgFlagAllowBackRev\n') - plist.write('\t\n') - plist.write('\tIFPkgFlagAuthorizationAction\n') - plist.write('\tRootAuthorization\n') - plist.write('\tIFPkgFlagDefaultLocation\n') - plist.write('\t/\n') - plist.write('\tIFPkgFlagFollowLinks\n') - plist.write('\t\n') - plist.write('\tIFPkgFlagIsRequired\n') - plist.write('\t\n') - plist.write('\tIFPkgFlagOverwritePermissions\n') - plist.write('\t\n') - plist.write('\tIFPkgFlagRelocatable\n') - plist.write('\t\n') - plist.write('\tIFPkgFlagRestartAction\n') - plist.write('\tNone\n') - plist.write('\tIFPkgFlagRootVolumeOnly\n') - plist.write('\t\n') - plist.write('\tIFPkgFlagUpdateInstalledLanguages\n') - plist.write('\t\n') - plist.write('\tIFPkgFormatVersion\n') - plist.write('\t0.10000000149011612\n') - plist.write('\tIFPkgPathMappings\n') - plist.write('\t\n') - plist.write('\t\t%s\n' % appname) - plist.write('\t\t{pkmk-token-2}\n') - plist.write('\t\n') - plist.write('\n') - plist.write('\n') - plist.close() - - plist = open(Filename(output, "Contents/Resources/TokenDefinitions.plist").toOsSpecific(), "w") - plist.write('\n') - plist.write('\n') - plist.write('\n') - plist.write('\n') - plist.write('\tpkmk-token-2\n') - plist.write('\t\n') - plist.write('\t\t\n') - plist.write('\t\t\tidentifier\n') - plist.write('\t\t\t%s.%s\n' % (self.authorid, self.shortname)) - plist.write('\t\t\tpath\n') - plist.write('\t\t\t%s\n' % appname) - plist.write('\t\t\tsearchPlugin\n') - plist.write('\t\t\tCommonAppSearch\n') - plist.write('\t\t\n') - plist.write('\t\n') - plist.write('\n') - plist.write('\n') - plist.close() - - plist = open(Filename(output, "Contents/Resources/en.lproj/Description.plist").toOsSpecific(), "w") - plist.write('\n') - plist.write('\n') - plist.write('\n') - plist.write('\n') - plist.write('\tIFPkgDescriptionDescription\n') - plist.write('\t\n') - plist.write('\tIFPkgDescriptionTitle\n') - plist.write('\t%s\n' % self.fullname) - plist.write('\n') - plist.write('\n') - plist.close() - - # OS X El Capitan no longer accepts .pax archives - it must be a CPIO archive named .pax. - archive = gzip.open(Filename(output, "Contents/Archive.pax.gz").toOsSpecific(), 'wb') - self.__ino = 0 - self.__writeCPIO(archive, appfn, appname) - archive.write(b"0707070000000000000000000000000000000000010000000000000000000001300000000000TRAILER!!!\0") - archive.close() - - # Put the .pkg into a zipfile - zip_fn = Filename(output.getDirname(), "%s %s.pkg.zip" % (self.fullname, self.version)) - dir = Filename(output.getDirname()) - dir.makeAbsolute() - zip = zipfile.ZipFile(zip_fn.toOsSpecific(), 'w') - for root, dirs, files in self.os_walk(output.toOsSpecific()): - for name in files: - file = Filename.fromOsSpecific(os.path.join(root, name)) - file.makeAbsolute() - file.makeRelativeTo(dir) - zip.write(os.path.join(root, name), str(file)) - zip.close() - - return output - - def __writeCPIO(self, archive, fn, name): - """ Adds the given fn under the given name to the CPIO archive. """ - - st = os.lstat(fn.toOsSpecific()) - - archive.write(b"070707") # magic - archive.write(b"000000") # dev - - # Synthesize an inode number, different for each entry. - self.__ino += 1 - archive.write("%06o" % (self.__ino)) - - # Determine based on the type which mode to write. - if os.path.islink(fn.toOsSpecific()): - archive.write("%06o" % (st.st_mode)) - target = os.path.readlink(fn.toOsSpecific()).encode('utf-8') - size = len(target) - elif os.path.isdir(fn.toOsSpecific()): - archive.write(b"040755") - size = 0 - elif not fn.getExtension(): # Binary file? - archive.write(b"100755") - size = st.st_size - else: - archive.write(b"100644") - size = st.st_size - - archive.write("000000") # uid (root) - archive.write("000000") # gid (wheel) - archive.write("%06o" % (st.st_nlink)) - archive.write("000000") # rdev - archive.write("%011o" % (st.st_mtime)) - archive.write("%06o" % (len(name) + 1)) - archive.write("%011o" % (size)) - - # Write the filename, plus terminating NUL byte. - archive.write(name.encode('utf-8')) - archive.write(b"\0") - - # Copy the file data to the archive. - if os.path.islink(fn.toOsSpecific()): - archive.write(target) - elif size: - handle = open(fn.toOsSpecific(), 'rb') - data = handle.read(1024 * 1024) - while data: - archive.write(data) - data = handle.read(1024 * 1024) - handle.close() - - # If this is a directory, recurse. - if os.path.isdir(fn.toOsSpecific()): - for child in os.listdir(fn.toOsSpecific()): - self.__writeCPIO(archive, Filename(fn, child), name + "/" + child) - - def buildNSIS(self, output, platform): - # Check if we have makensis first - makensis = None - if (sys.platform.startswith("win")): - syspath = os.defpath.split(";") + os.environ["PATH"].split(";") - for p in set(syspath): - p1 = os.path.join(p, "makensis.exe") - p2 = os.path.join(os.path.dirname(p), "nsis", "makensis.exe") - if os.path.isfile(p1): - makensis = p1 - break - elif os.path.isfile(p2): - makensis = p2 - break - if not makensis: - import pandac - makensis = os.path.dirname(os.path.dirname(pandac.__file__)) - makensis = os.path.join(makensis, "nsis", "makensis.exe") - if not os.path.isfile(makensis): makensis = None - else: - for p in os.defpath.split(":") + os.environ["PATH"].split(":"): - if os.path.isfile(os.path.join(p, "makensis")): - makensis = os.path.join(p, "makensis") - - if makensis == None: - Installer.notify.warning("Makensis utility not found, no Windows installer will be built!") - return None - - output = Filename(output) - if output.isDirectory(): - output = Filename(output, "%s %s.exe" % (self.fullname, self.version)) - Installer.notify.info("Creating %s..." % output) - output.makeAbsolute() - extrafiles = self.standalone.getExtraFiles(platform) - - exefile = Filename(Filename.getTempDirectory(), self.shortname + ".exe") - exefile.unlink() - if self.includeRequires: - extraTokens = {"host_dir": ".", "start_dir": "."} - else: - extraTokens = {} - self.standalone.build(exefile, platform, extraTokens) - - # Temporary directory to store the hostdir in - hostDir = Filename(self.tempDir, platform + "/") - if not hostDir.exists(): - hostDir.makeDir() - self.installPackagesInto(hostDir, platform) - - # See if we can generate an icon - icofile = None - if self.icon is not None: - icofile = Filename(Filename.getTempDirectory(), self.shortname + ".ico") - icofile.unlink() - Installer.notify.info("Generating %s.ico..." % self.shortname) - if not self.icon.makeICO(icofile): - icofile = None - - # Create the .nsi installer script - nsifile = Filename(Filename.getTempDirectory(), self.shortname + ".nsi") - nsifile.unlink() - nsi = open(nsifile.toOsSpecific(), "w") - - # Some global info - nsi.write('Name "%s"\n' % self.fullname) - nsi.write('OutFile "%s"\n' % output.toOsSpecific()) - if platform == 'win_amd64': - nsi.write('InstallDir "$PROGRAMFILES64\\%s"\n' % self.fullname) - else: - nsi.write('InstallDir "$PROGRAMFILES\\%s"\n' % self.fullname) - nsi.write('SetCompress auto\n') - nsi.write('SetCompressor lzma\n') - nsi.write('ShowInstDetails nevershow\n') - nsi.write('ShowUninstDetails nevershow\n') - nsi.write('InstType "Typical"\n') - - # Tell Vista that we require admin rights - nsi.write('RequestExecutionLevel admin\n') - nsi.write('\n') - if self.offerRun: - nsi.write('Function launch\n') - nsi.write(' ExecShell "open" "$INSTDIR\\%s.exe"\n' % self.shortname) - nsi.write('FunctionEnd\n') - nsi.write('\n') - - if self.offerDesktopShortcut: - nsi.write('Function desktopshortcut\n') - if icofile is None: - nsi.write(' CreateShortcut "$DESKTOP\\%s.lnk" "$INSTDIR\\%s.exe"\n' % (self.fullname, self.shortname)) - else: - nsi.write(' CreateShortcut "$DESKTOP\\%s.lnk" "$INSTDIR\\%s.exe" "" "$INSTDIR\\%s.ico"\n' % (self.fullname, self.shortname, self.shortname)) - nsi.write('FunctionEnd\n') - nsi.write('\n') - - nsi.write('!include "MUI2.nsh"\n') - nsi.write('!define MUI_ABORTWARNING\n') - if self.offerRun: - nsi.write('!define MUI_FINISHPAGE_RUN\n') - nsi.write('!define MUI_FINISHPAGE_RUN_NOTCHECKED\n') - nsi.write('!define MUI_FINISHPAGE_RUN_FUNCTION launch\n') - nsi.write('!define MUI_FINISHPAGE_RUN_TEXT "Run %s"\n' % self.fullname) - if self.offerDesktopShortcut: - nsi.write('!define MUI_FINISHPAGE_SHOWREADME ""\n') - nsi.write('!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED\n') - nsi.write('!define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Desktop Shortcut"\n') - nsi.write('!define MUI_FINISHPAGE_SHOWREADME_FUNCTION desktopshortcut\n') - nsi.write('\n') - nsi.write('Var StartMenuFolder\n') - nsi.write('!insertmacro MUI_PAGE_WELCOME\n') - if not self.licensefile.empty(): - abs = Filename(self.licensefile) - abs.makeAbsolute() - nsi.write('!insertmacro MUI_PAGE_LICENSE "%s"\n' % abs.toOsSpecific()) - nsi.write('!insertmacro MUI_PAGE_DIRECTORY\n') - nsi.write('!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder\n') - nsi.write('!insertmacro MUI_PAGE_INSTFILES\n') - nsi.write('!insertmacro MUI_PAGE_FINISH\n') - nsi.write('!insertmacro MUI_UNPAGE_WELCOME\n') - nsi.write('!insertmacro MUI_UNPAGE_CONFIRM\n') - nsi.write('!insertmacro MUI_UNPAGE_INSTFILES\n') - nsi.write('!insertmacro MUI_UNPAGE_FINISH\n') - nsi.write('!insertmacro MUI_LANGUAGE "English"\n') - - # This section defines the installer. - nsi.write('Section "" SecCore\n') - nsi.write(' SetOutPath "$INSTDIR"\n') - nsi.write(' File "%s"\n' % exefile.toOsSpecific()) - if icofile is not None: - nsi.write(' File "%s"\n' % icofile.toOsSpecific()) - for f in extrafiles: - nsi.write(' File "%s"\n' % f.toOsSpecific()) - curdir = "" - for root, dirs, files in self.os_walk(hostDir.toOsSpecific()): - for name in files: - basefile = Filename.fromOsSpecific(os.path.join(root, name)) - file = Filename(basefile) - file.makeAbsolute() - file.makeRelativeTo(hostDir) - outdir = file.getDirname().replace('/', '\\') - if curdir != outdir: - nsi.write(' SetOutPath "$INSTDIR\\%s"\n' % outdir) - curdir = outdir - nsi.write(' File "%s"\n' % (basefile.toOsSpecific())) - nsi.write(' SetOutPath "$INSTDIR"\n') - nsi.write(' WriteUninstaller "$INSTDIR\\Uninstall.exe"\n') - nsi.write(' ; Start menu items\n') - nsi.write(' !insertmacro MUI_STARTMENU_WRITE_BEGIN Application\n') - nsi.write(' CreateDirectory "$SMPROGRAMS\\$StartMenuFolder"\n') - if icofile is None: - nsi.write(' CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\%s.lnk" "$INSTDIR\\%s.exe"\n' % (self.fullname, self.shortname)) - else: - nsi.write(' CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\%s.lnk" "$INSTDIR\\%s.exe" "" "$INSTDIR\\%s.ico"\n' % (self.fullname, self.shortname, self.shortname)) - nsi.write(' CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\Uninstall.lnk" "$INSTDIR\\Uninstall.exe"\n') - nsi.write(' !insertmacro MUI_STARTMENU_WRITE_END\n') - nsi.write('SectionEnd\n') - - # This section defines the uninstaller. - nsi.write('Section Uninstall\n') - nsi.write(' Delete "$INSTDIR\\%s.exe"\n' % self.shortname) - if icofile is not None: - nsi.write(' Delete "$INSTDIR\\%s.ico"\n' % self.shortname) - for f in extrafiles: - nsi.write(' Delete "%s"\n' % f.getBasename()) - nsi.write(' Delete "$INSTDIR\\Uninstall.exe"\n') - nsi.write(' RMDir /r "$INSTDIR"\n') - nsi.write(' ; Desktop icon\n') - nsi.write(' Delete "$DESKTOP\\%s.lnk"\n' % self.fullname) - nsi.write(' ; Start menu items\n') - nsi.write(' !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder\n') - nsi.write(' Delete "$SMPROGRAMS\\$StartMenuFolder\\%s.lnk"\n' % self.fullname) - nsi.write(' Delete "$SMPROGRAMS\\$StartMenuFolder\\Uninstall.lnk"\n') - nsi.write(' RMDir "$SMPROGRAMS\\$StartMenuFolder"\n') - nsi.write('SectionEnd\n') - nsi.close() - - cmd = [makensis] - for o in ["V2"]: - if sys.platform.startswith("win"): - cmd.append("/" + o) - else: - cmd.append("-" + o) - cmd.append(nsifile.toOsSpecific()) - print(cmd) - try: - retcode = subprocess.call(cmd, shell = False) - if retcode != 0: - self.notify.warning("Failure invoking NSIS command.") - else: - nsifile.unlink() - except OSError: - self.notify.warning("Unable to invoke NSIS command.") - - if icofile is not None: - icofile.unlink() - - return output - - def os_walk(self, top): - """ Re-implements os.walk(). For some reason the built-in - definition is failing on Windows when this is run within a p3d - environment!? """ - - dirnames = [] - filenames = [] - - dirlist = os.listdir(top) - if dirlist: - for file in dirlist: - path = os.path.join(top, file) - if os.path.isdir(path): - dirnames.append(file) - else: - filenames.append(file) - - yield (top, dirnames, filenames) - - for dir in dirnames: - next = os.path.join(top, dir) - for tuple in self.os_walk(next): - yield tuple diff --git a/direct/src/p3d/FileSpec.py b/direct/src/p3d/FileSpec.py deleted file mode 100644 index ddd1dafd6e..0000000000 --- a/direct/src/p3d/FileSpec.py +++ /dev/null @@ -1,246 +0,0 @@ -__all__ = ["FileSpec"] - -import os -import time -from panda3d.core import Filename, HashVal, VirtualFileSystem - -class FileSpec: - """ This class represents a disk file whose hash and size - etc. were read from an xml file. This class provides methods to - verify whether the file on disk matches the version demanded by - the xml. """ - - def __init__(self): - self.actualFile = None - self.filename = None - self.size = 0 - self.timestamp = 0 - self.hash = None - - def fromFile(self, packageDir, filename, pathname = None, st = None): - """ Reads the file information from the indicated file. If st - is supplied, it is the result of os.stat on the filename. """ - - vfs = VirtualFileSystem.getGlobalPtr() - - filename = Filename(filename) - if pathname is None: - pathname = Filename(packageDir, filename) - - self.filename = str(filename) - self.basename = filename.getBasename() - - if st is None: - st = os.stat(pathname.toOsSpecific()) - self.size = st.st_size - self.timestamp = int(st.st_mtime) - - self.readHash(pathname) - - def readHash(self, pathname): - """ Reads the hash only from the indicated pathname. """ - - hv = HashVal() - hv.hashFile(pathname) - self.hash = hv.asHex() - - - def loadXml(self, xelement): - """ Reads the file information from the indicated XML - element. """ - - self.filename = xelement.Attribute('filename') - self.basename = None - if self.filename: - self.basename = Filename(self.filename).getBasename() - - size = xelement.Attribute('size') - try: - self.size = int(size) - except: - self.size = 0 - - timestamp = xelement.Attribute('timestamp') - try: - self.timestamp = int(timestamp) - except: - self.timestamp = 0 - - self.hash = xelement.Attribute('hash') - - def storeXml(self, xelement): - """ Adds the file information to the indicated XML - element. """ - - if self.filename: - xelement.SetAttribute('filename', self.filename) - if self.size: - xelement.SetAttribute('size', str(self.size)) - if self.timestamp: - xelement.SetAttribute('timestamp', str(int(self.timestamp))) - if self.hash: - xelement.SetAttribute('hash', self.hash) - - def storeMiniXml(self, xelement): - """ Adds the just the "mini" file information--size and - hash--to the indicated XML element. """ - - if self.size: - xelement.SetAttribute('size', str(self.size)) - if self.hash: - xelement.SetAttribute('hash', self.hash) - - def quickVerify(self, packageDir = None, pathname = None, - notify = None, correctSelf = False): - """ Performs a quick test to ensure the file has not been - modified. This test is vulnerable to people maliciously - attempting to fool the program (by setting datestamps etc.). - - if correctSelf is True, then any discrepency is corrected by - updating the appropriate fields internally, making the - assumption that the file on disk is the authoritative version. - - Returns true if it is intact, false if it is incorrect. If - correctSelf is true, raises OSError if the self-update is - impossible (for instance, because the file does not exist).""" - - if not pathname: - pathname = Filename(packageDir, self.filename) - try: - st = os.stat(pathname.toOsSpecific()) - except OSError: - # If the file is missing, the file fails. - if notify: - notify.debug("file not found: %s" % (pathname)) - if correctSelf: - raise - return False - - if st.st_size != self.size: - # If the size is wrong, the file fails. - if notify: - notify.debug("size wrong: %s" % (pathname)) - if correctSelf: - self.__correctHash(packageDir, pathname, st, notify) - return False - - if int(st.st_mtime) == self.timestamp: - # If the size is right and the timestamp is right, the - # file passes. - if notify: - notify.debug("file ok: %s" % (pathname)) - return True - - if notify: - notify.debug("modification time wrong: %s" % (pathname)) - - # If the size is right but the timestamp is wrong, the file - # soft-fails. We follow this up with a hash check. - if not self.checkHash(packageDir, pathname, st): - # Hard fail, the hash is wrong. - if notify: - notify.debug("hash check wrong: %s" % (pathname)) - notify.debug(" found %s, expected %s" % (self.actualFile.hash, self.hash)) - if correctSelf: - self.__correctHash(packageDir, pathname, st, notify) - return False - - if notify: - notify.debug("hash check ok: %s" % (pathname)) - - # The hash is OK after all. Change the file's timestamp back - # to what we expect it to be, so we can quick-verify it - # successfully next time. - if correctSelf: - # Or update our own timestamp. - self.__correctTimestamp(pathname, st, notify) - return False - else: - self.__updateTimestamp(pathname, st) - - return True - - - def fullVerify(self, packageDir = None, pathname = None, notify = None): - """ Performs a more thorough test to ensure the file has not - been modified. This test is less vulnerable to malicious - attacks, since it reads and verifies the entire file. - - Returns true if it is intact, false if it needs to be - redownloaded. """ - - if not pathname: - pathname = Filename(packageDir, self.filename) - try: - st = os.stat(pathname.toOsSpecific()) - except OSError: - # If the file is missing, the file fails. - if notify: - notify.debug("file not found: %s" % (pathname)) - return False - - if st.st_size != self.size: - # If the size is wrong, the file fails; - if notify: - notify.debug("size wrong: %s" % (pathname)) - return False - - if not self.checkHash(packageDir, pathname, st): - # Hard fail, the hash is wrong. - if notify: - notify.debug("hash check wrong: %s" % (pathname)) - notify.debug(" found %s, expected %s" % (self.actualFile.hash, self.hash)) - return False - - if notify: - notify.debug("hash check ok: %s" % (pathname)) - - # The hash is OK. If the timestamp is wrong, change it back - # to what we expect it to be, so we can quick-verify it - # successfully next time. - if int(st.st_mtime) != self.timestamp: - self.__updateTimestamp(pathname, st) - - return True - - def __updateTimestamp(self, pathname, st): - # On Windows, we have to change the file to read-write before - # we can successfully update its timestamp. - try: - os.chmod(pathname.toOsSpecific(), 0o755) - os.utime(pathname.toOsSpecific(), (st.st_atime, self.timestamp)) - os.chmod(pathname.toOsSpecific(), 0o555) - except OSError: - pass - - def __correctTimestamp(self, pathname, st, notify): - """ Corrects the internal timestamp to match the one on - disk. """ - if notify: - notify.info("Correcting timestamp of %s to %d (%s)" % ( - self.filename, st.st_mtime, time.asctime(time.localtime(st.st_mtime)))) - self.timestamp = int(st.st_mtime) - - def checkHash(self, packageDir, pathname, st): - """ Returns true if the file has the expected md5 hash, false - otherwise. As a side effect, stores a FileSpec corresponding - to the on-disk file in self.actualFile. """ - - fileSpec = FileSpec() - fileSpec.fromFile(packageDir, self.filename, - pathname = pathname, st = st) - self.actualFile = fileSpec - - return (fileSpec.hash == self.hash) - - def __correctHash(self, packageDir, pathname, st, notify): - """ Corrects the internal hash to match the one on disk. """ - if not self.actualFile: - self.checkHash(packageDir, pathname, st) - - if notify: - notify.info("Correcting hash %s to %s" % ( - self.filename, self.actualFile.hash)) - self.hash = self.actualFile.hash - self.size = self.actualFile.size - self.timestamp = self.actualFile.timestamp diff --git a/direct/src/p3d/HostInfo.py b/direct/src/p3d/HostInfo.py deleted file mode 100644 index 4e5efdcd3e..0000000000 --- a/direct/src/p3d/HostInfo.py +++ /dev/null @@ -1,751 +0,0 @@ -__all__ = ["HostInfo"] - -from panda3d.core import HashVal, Filename, PandaSystem, DocumentSpec, Ramfile -from panda3d.core import HTTPChannel, ConfigVariableInt -from panda3d import core -from direct.p3d.PackageInfo import PackageInfo -from direct.p3d.FileSpec import FileSpec -from direct.directnotify.DirectNotifyGlobal import directNotify -import time - -class HostInfo: - """ This class represents a particular download host serving up - Panda3D packages. It is the Python equivalent of the P3DHost - class in the core API. """ - - notify = directNotify.newCategory("HostInfo") - - def __init__(self, hostUrl, appRunner = None, hostDir = None, - rootDir = None, asMirror = False, perPlatform = None): - - """ You must specify either an appRunner or a hostDir to the - HostInfo constructor. - - If you pass asMirror = True, it means that this HostInfo - object is to be used to populate a "mirror" folder, a - duplicate (or subset) of the contents hosted by a server. - This means when you use this HostInfo to download packages, it - will only download the compressed archive file and leave it - there. At the moment, mirror folders do not download old - patch files from the server. - - If you pass perPlatform = True, then files are unpacked into a - platform-specific directory, which is appropriate when you - might be downloading multiple platforms. The default is - perPlatform = False, which means all files are unpacked into - the host directory directly, without an intervening - platform-specific directory name. If asMirror is True, then - the default is perPlatform = True. - - Note that perPlatform is also restricted by the individual - package's specification. """ - - self.__setHostUrl(hostUrl) - self.appRunner = appRunner - self.rootDir = rootDir - if rootDir is None and appRunner: - self.rootDir = appRunner.rootDir - - if hostDir and not isinstance(hostDir, Filename): - hostDir = Filename.fromOsSpecific(hostDir) - - self.hostDir = hostDir - self.asMirror = asMirror - self.perPlatform = perPlatform - if perPlatform is None: - self.perPlatform = asMirror - - # Initially false, this is set true when the contents file is - # successfully read. - self.hasContentsFile = False - - # This is the time value at which the current contents file is - # no longer valid. - self.contentsExpiration = 0 - - # Contains the md5 hash of the original contents.xml file. - self.contentsSpec = FileSpec() - - # descriptiveName will be filled in later, when the - # contents file is read. - self.descriptiveName = None - - # A list of known mirrors for this host, all URL's guaranteed - # to end with a slash. - self.mirrors = [] - - # A map of keyword -> altHost URL's. An altHost is different - # than a mirror; an altHost is an alternate URL to download a - # different (e.g. testing) version of this host's contents. - # It is rarely used. - self.altHosts = {} - - # This is a dictionary of packages by (name, version). It - # will be filled in when the contents file is read. - self.packages = {} - - if self.appRunner and self.appRunner.verifyContents != self.appRunner.P3DVCForce: - # Attempt to pre-read the existing contents.xml; maybe it - # will be current enough for our purposes. - self.readContentsFile() - - def __setHostUrl(self, hostUrl): - """ Assigns self.hostUrl, and related values. """ - self.hostUrl = hostUrl - - if not self.hostUrl: - # A special case: the URL will be set later. - self.hostUrlPrefix = None - self.downloadUrlPrefix = None - else: - # hostUrlPrefix is the host URL, but it is guaranteed to end - # with a slash. - self.hostUrlPrefix = hostUrl - if self.hostUrlPrefix[-1] != '/': - self.hostUrlPrefix += '/' - - # downloadUrlPrefix is the URL prefix that should be used for - # everything other than the contents.xml file. It might be - # the same as hostUrlPrefix, but in the case of an - # https-protected hostUrl, it will be the cleartext channel. - self.downloadUrlPrefix = self.hostUrlPrefix - - def freshenFile(self, http, fileSpec, localPathname): - """ Ensures that the localPathname is the most current version - of the file defined by fileSpec, as offered by host. If not, - it downloads a new version on-the-spot. Returns true on - success, false on failure. """ - - if fileSpec.quickVerify(pathname = localPathname): - # It's good, keep it. - return True - - # It's stale, get a new one. - doc = None - if self.appRunner and self.appRunner.superMirrorUrl: - # Use the "super mirror" first. - url = core.URLSpec(self.appRunner.superMirrorUrl + fileSpec.filename) - self.notify.info("Freshening %s" % (url)) - doc = http.getDocument(url) - - if not doc or not doc.isValid(): - # Failing the super mirror, contact the actual host. - url = core.URLSpec(self.hostUrlPrefix + fileSpec.filename) - self.notify.info("Freshening %s" % (url)) - doc = http.getDocument(url) - if not doc.isValid(): - return False - - file = Filename.temporary('', 'p3d_') - if not doc.downloadToFile(file): - # Failed to download. - file.unlink() - return False - - # Successfully downloaded! - localPathname.makeDir() - if not file.renameTo(localPathname): - # Couldn't move it into place. - file.unlink() - return False - - if not fileSpec.fullVerify(pathname = localPathname, notify = self.notify): - # No good after download. - self.notify.info("%s is still no good after downloading." % (url)) - return False - - return True - - def downloadContentsFile(self, http, redownload = False, - hashVal = None): - """ Downloads the contents.xml file for this particular host, - synchronously, and then reads it. Returns true on success, - false on failure. If hashVal is not None, it should be a - HashVal object, which will be filled with the hash from the - new contents.xml file.""" - - if self.hasCurrentContentsFile(): - # We've already got one. - return True - - if self.appRunner and self.appRunner.verifyContents == self.appRunner.P3DVCNever: - # Not allowed to. - return False - - rf = None - if http: - if not redownload and self.appRunner and self.appRunner.superMirrorUrl: - # We start with the "super mirror", if it's defined. - url = self.appRunner.superMirrorUrl + 'contents.xml' - request = DocumentSpec(url) - self.notify.info("Downloading contents file %s" % (request)) - - rf = Ramfile() - channel = http.makeChannel(False) - channel.getDocument(request) - if not channel.downloadToRam(rf): - self.notify.warning("Unable to download %s" % (url)) - rf = None - - if not rf: - # Then go to the main host, if our super mirror let us - # down. - - url = self.hostUrlPrefix + 'contents.xml' - # Append a uniquifying query string to the URL to force the - # download to go all the way through any caches. We use the - # time in seconds; that's unique enough. - url += '?' + str(int(time.time())) - - # We might as well explicitly request the cache to be disabled - # too, since we have an interface for that via HTTPChannel. - request = DocumentSpec(url) - request.setCacheControl(DocumentSpec.CCNoCache) - - self.notify.info("Downloading contents file %s" % (request)) - statusCode = None - statusString = '' - for attempt in range(int(ConfigVariableInt('contents-xml-dl-attempts', 3))): - if attempt > 0: - self.notify.info("Retrying (%s)..."%(attempt,)) - rf = Ramfile() - channel = http.makeChannel(False) - channel.getDocument(request) - if channel.downloadToRam(rf): - self.notify.info("Successfully downloaded %s" % (url,)) - break - else: - rf = None - statusCode = channel.getStatusCode() - statusString = channel.getStatusString() - self.notify.warning("Could not contact download server at %s" % (url,)) - self.notify.warning("Status code = %s %s" % (statusCode, statusString)) - - if not rf: - self.notify.warning("Unable to download %s" % (url,)) - try: - # Something screwed up. - if statusCode == HTTPChannel.SCDownloadOpenError or \ - statusCode == HTTPChannel.SCDownloadWriteError: - launcher.setPandaErrorCode(2) - elif statusCode == 404: - # 404 not found - launcher.setPandaErrorCode(5) - elif statusCode < 100: - # statusCode < 100 implies the connection attempt itself - # failed. This is usually due to firewall software - # interfering. Apparently some firewall software might - # allow the first connection and disallow subsequent - # connections; how strange. - launcher.setPandaErrorCode(4) - else: - # There are other kinds of failures, but these will - # generally have been caught already by the first test; so - # if we get here there may be some bigger problem. Just - # give the generic "big problem" message. - launcher.setPandaErrorCode(6) - except NameError as e: - # no launcher - pass - except AttributeError as e: - self.notify.warning("%s" % (str(e),)) - pass - return False - - tempFilename = Filename.temporary('', 'p3d_', '.xml') - if rf: - f = open(tempFilename.toOsSpecific(), 'wb') - f.write(rf.getData()) - f.close() - if hashVal: - hashVal.hashString(rf.getData()) - - if not self.readContentsFile(tempFilename, freshDownload = True): - self.notify.warning("Failure reading %s" % (url)) - tempFilename.unlink() - return False - - tempFilename.unlink() - return True - - # Couldn't download the file. Maybe we should look for a - # previously-downloaded copy already on disk? - return False - - def redownloadContentsFile(self, http): - """ Downloads a new contents.xml file in case it has changed. - Returns true if the file has indeed changed, false if it has - not. """ - assert self.hasContentsFile - - if self.appRunner and self.appRunner.verifyContents == self.appRunner.P3DVCNever: - # Not allowed to. - return False - - url = self.hostUrlPrefix + 'contents.xml' - self.notify.info("Redownloading %s" % (url)) - - # Get the hash of the original file. - assert self.hostDir - hv1 = HashVal() - if self.contentsSpec.hash: - hv1.setFromHex(self.contentsSpec.hash) - else: - filename = Filename(self.hostDir, 'contents.xml') - hv1.hashFile(filename) - - # Now download it again. - self.hasContentsFile = False - hv2 = HashVal() - if not self.downloadContentsFile(http, redownload = True, - hashVal = hv2): - return False - - if hv2 == HashVal(): - self.notify.info("%s didn't actually redownload." % (url)) - return False - elif hv1 != hv2: - self.notify.info("%s has changed." % (url)) - return True - else: - self.notify.info("%s has not changed." % (url)) - return False - - def hasCurrentContentsFile(self): - """ Returns true if a contents.xml file has been successfully - read for this host and is still current, false otherwise. """ - if not self.appRunner \ - or self.appRunner.verifyContents == self.appRunner.P3DVCNone \ - or self.appRunner.verifyContents == self.appRunner.P3DVCNever: - # If we're not asking to verify contents, then - # contents.xml files never expires. - return self.hasContentsFile - - now = int(time.time()) - return now < self.contentsExpiration and self.hasContentsFile - - def readContentsFile(self, tempFilename = None, freshDownload = False): - """ Reads the contents.xml file for this particular host, once - it has been downloaded into the indicated temporary file. - Returns true on success, false if the contents file is not - already on disk or is unreadable. - - If tempFilename is specified, it is the filename read, and it - is copied the file into the standard location if it's not - there already. If tempFilename is not specified, the standard - filename is read if it is known. """ - - if not hasattr(core, 'TiXmlDocument'): - return False - - if not tempFilename: - if self.hostDir: - # If the filename is not specified, we can infer it - # if we already know our hostDir - hostDir = self.hostDir - else: - # Otherwise, we have to guess the hostDir. - hostDir = self.__determineHostDir(None, self.hostUrl) - - tempFilename = Filename(hostDir, 'contents.xml') - - doc = core.TiXmlDocument(tempFilename.toOsSpecific()) - if not doc.LoadFile(): - return False - - xcontents = doc.FirstChildElement('contents') - if not xcontents: - return False - - maxAge = xcontents.Attribute('max_age') - if maxAge: - try: - maxAge = int(maxAge) - except: - maxAge = None - if maxAge is None: - # Default max_age if unspecified (see p3d_plugin.h). - from direct.p3d.AppRunner import AppRunner - maxAge = AppRunner.P3D_CONTENTS_DEFAULT_MAX_AGE - - # Get the latest possible expiration time, based on the max_age - # indication. Any expiration time later than this is in error. - now = int(time.time()) - self.contentsExpiration = now + maxAge - - if freshDownload: - self.contentsSpec.readHash(tempFilename) - - # Update the XML with the new download information. - xorig = xcontents.FirstChildElement('orig') - while xorig: - xcontents.RemoveChild(xorig) - xorig = xcontents.FirstChildElement('orig') - - xorig = core.TiXmlElement('orig') - self.contentsSpec.storeXml(xorig) - xorig.SetAttribute('expiration', str(self.contentsExpiration)) - - xcontents.InsertEndChild(xorig) - - else: - # Read the download hash and expiration time from the XML. - expiration = None - xorig = xcontents.FirstChildElement('orig') - if xorig: - self.contentsSpec.loadXml(xorig) - expiration = xorig.Attribute('expiration') - if expiration: - try: - expiration = int(expiration) - except: - expiration = None - if not self.contentsSpec.hash: - self.contentsSpec.readHash(tempFilename) - - if expiration is not None: - self.contentsExpiration = min(self.contentsExpiration, expiration) - - # Look for our own entry in the hosts table. - if self.hostUrl: - self.__findHostXml(xcontents) - else: - assert self.hostDir - self.__findHostXmlForHostDir(xcontents) - - if self.rootDir and not self.hostDir: - self.hostDir = self.__determineHostDir(None, self.hostUrl) - - # Get the list of packages available for download and/or import. - xpackage = xcontents.FirstChildElement('package') - while xpackage: - name = xpackage.Attribute('name') - platform = xpackage.Attribute('platform') - version = xpackage.Attribute('version') - try: - solo = int(xpackage.Attribute('solo') or '') - except ValueError: - solo = False - try: - perPlatform = int(xpackage.Attribute('per_platform') or '') - except ValueError: - perPlatform = False - - package = self.__makePackage(name, platform, version, solo, perPlatform) - package.descFile = FileSpec() - package.descFile.loadXml(xpackage) - package.setupFilenames() - - package.importDescFile = None - ximport = xpackage.FirstChildElement('import') - if ximport: - package.importDescFile = FileSpec() - package.importDescFile.loadXml(ximport) - - xpackage = xpackage.NextSiblingElement('package') - - self.hasContentsFile = True - - # Now save the contents.xml file into the standard location. - if self.appRunner and self.appRunner.verifyContents != self.appRunner.P3DVCNever: - assert self.hostDir - filename = Filename(self.hostDir, 'contents.xml') - filename.makeDir() - if freshDownload: - doc.SaveFile(filename.toOsSpecific()) - else: - if filename != tempFilename: - tempFilename.copyTo(filename) - - return True - - def __findHostXml(self, xcontents): - """ Looks for the or entry in the - contents.xml that corresponds to the URL that we actually - downloaded from. """ - - xhost = xcontents.FirstChildElement('host') - while xhost: - url = xhost.Attribute('url') - if url == self.hostUrl: - self.readHostXml(xhost) - return - - xalthost = xhost.FirstChildElement('alt_host') - while xalthost: - url = xalthost.Attribute('url') - if url == self.hostUrl: - self.readHostXml(xalthost) - return - xalthost = xalthost.NextSiblingElement('alt_host') - - xhost = xhost.NextSiblingElement('host') - - def __findHostXmlForHostDir(self, xcontents): - """ Looks for the or entry in the - contents.xml that corresponds to the host dir that we read the - contents.xml from. This is used when reading a contents.xml - file found on disk, as opposed to downloading it from a - site. """ - - xhost = xcontents.FirstChildElement('host') - while xhost: - url = xhost.Attribute('url') - hostDirBasename = xhost.Attribute('host_dir') - hostDir = self.__determineHostDir(hostDirBasename, url) - if hostDir == self.hostDir: - self.__setHostUrl(url) - self.readHostXml(xhost) - return - - xalthost = xhost.FirstChildElement('alt_host') - while xalthost: - url = xalthost.Attribute('url') - hostDirBasename = xalthost.Attribute('host_dir') - hostDir = self.__determineHostDir(hostDirBasename, url) - if hostDir == self.hostDir: - self.__setHostUrl(url) - self.readHostXml(xalthost) - return - xalthost = xalthost.NextSiblingElement('alt_host') - - xhost = xhost.NextSiblingElement('host') - - def readHostXml(self, xhost): - """ Reads a or entry and applies the data to - this object. """ - - descriptiveName = xhost.Attribute('descriptive_name') - if descriptiveName and not self.descriptiveName: - self.descriptiveName = descriptiveName - - hostDirBasename = xhost.Attribute('host_dir') - if self.rootDir and not self.hostDir: - self.hostDir = self.__determineHostDir(hostDirBasename, self.hostUrl) - - # Get the "download" URL, which is the source from which we - # download everything other than the contents.xml file. - downloadUrl = xhost.Attribute('download_url') - if downloadUrl: - self.downloadUrlPrefix = downloadUrl - if self.downloadUrlPrefix[-1] != '/': - self.downloadUrlPrefix += '/' - else: - self.downloadUrlPrefix = self.hostUrlPrefix - - xmirror = xhost.FirstChildElement('mirror') - while xmirror: - url = xmirror.Attribute('url') - if url: - if url[-1] != '/': - url += '/' - if url not in self.mirrors: - self.mirrors.append(url) - xmirror = xmirror.NextSiblingElement('mirror') - - xalthost = xhost.FirstChildElement('alt_host') - while xalthost: - keyword = xalthost.Attribute('keyword') - url = xalthost.Attribute('url') - if url and keyword: - self.altHosts[keyword] = url - xalthost = xalthost.NextSiblingElement('alt_host') - - def __makePackage(self, name, platform, version, solo, perPlatform): - """ Creates a new PackageInfo entry for the given name, - version, and platform. If there is already a matching - PackageInfo, returns it. """ - - if not platform: - platform = None - - platforms = self.packages.setdefault((name, version or ""), {}) - package = platforms.get("", None) - if not package: - package = PackageInfo(self, name, version, platform = platform, - solo = solo, asMirror = self.asMirror, - perPlatform = perPlatform) - platforms[platform or ""] = package - - return package - - def getPackage(self, name, version, platform = None): - """ Returns a PackageInfo that matches the indicated name and - version and the indicated platform or the current runtime - platform, if one is provided by this host, or None if not. """ - - assert self.hasContentsFile - platforms = self.packages.get((name, version or ""), {}) - - if platform: - # In this case, we are looking for a specific platform - # only. - return platforms.get(platform, None) - - # We are looking for one matching the current runtime - # platform. First, look for a package matching the current - # platform exactly. - package = platforms.get(PandaSystem.getPlatform(), None) - - # If not found, look for one matching no particular platform. - if not package: - package = platforms.get("", None) - - return package - - def getPackages(self, name = None, platform = None): - """ Returns a list of PackageInfo objects that match the - indicated name and/or platform, with no particular regards to - version. If name is None, all packages are returned. """ - - assert self.hasContentsFile - - packages = [] - for (pn, version), platforms in self.packages.items(): - if name and pn != name: - continue - - if not platform: - for p2 in platforms: - package = self.getPackage(pn, version, platform = p2) - if package: - packages.append(package) - else: - package = self.getPackage(pn, version, platform = platform) - if package: - packages.append(package) - - return packages - - def getAllPackages(self, includeAllPlatforms = False): - """ Returns a list of all available packages provided by this - host. """ - - result = [] - - items = sorted(self.packages.items()) - for key, platforms in items: - if self.perPlatform or includeAllPlatforms: - # If we maintain a different answer per platform, - # return all of them. - pitems = sorted(platforms.items()) - for pkey, package in pitems: - result.append(package) - else: - # If we maintain a host for the current platform - # only (e.g. a client copy), then return only the - # current platform, or no particular platform. - package = platforms.get(PandaSystem.getPlatform(), None) - if not package: - package = platforms.get("", None) - - if package: - result.append(package) - - return result - - def deletePackages(self, packages): - """ Removes all of the indicated packages from the disk, - uninstalling them and deleting all of their files. The - packages parameter must be a list of one or more PackageInfo - objects, for instance as returned by getPackage(). Returns - the list of packages that were NOT found. """ - - packages = packages[:] - - for key, platforms in list(self.packages.items()): - for platform, package in list(platforms.items()): - if package in packages: - self.__deletePackageFiles(package) - del platforms[platform] - packages.remove(package) - - if not platforms: - # If we've removed all the platforms for a given - # package, remove the key from the toplevel map. - del self.packages[key] - - return packages - - def __deletePackageFiles(self, package): - """ Called by deletePackage(), this actually removes the files - for the indicated package. """ - - if self.appRunner: - self.notify.info("Deleting package %s: %s" % (package.packageName, package.getPackageDir())) - self.appRunner.rmtree(package.getPackageDir()) - - self.appRunner.sendRequest('forget_package', self.hostUrl, package.packageName, package.packageVersion or '') - - def __determineHostDir(self, hostDirBasename, hostUrl): - """ Hashes the host URL into a (mostly) unique directory - string, which will be the root of the host's install tree. - Returns the resulting path, as a Filename. - - This code is duplicated in C++, in - P3DHost::determine_host_dir(). """ - - if hostDirBasename: - # If the contents.xml specified a host_dir parameter, use - # it. - hostDir = str(self.rootDir) + '/hosts' - for component in hostDirBasename.split('/'): - if component: - if component[0] == '.': - # Forbid ".foo" or "..". - component = 'x' + component - hostDir += '/' - hostDir += component - return Filename(hostDir) - - hostDir = 'hosts/' - - # Look for a server name in the URL. Including this string in the - # directory name makes it friendlier for people browsing the - # directory. - - # We could use URLSpec, but we do it by hand instead, to make - # it more likely that our hash code will exactly match the - # similar logic in P3DHost. - p = hostUrl.find('://') - hostname = '' - if p != -1: - start = p + 3 - end = hostUrl.find('/', start) - # Now start .. end is something like "username@host:port". - - at = hostUrl.find('@', start) - if at != -1 and at < end: - start = at + 1 - - colon = hostUrl.find(':', start) - if colon != -1 and colon < end: - end = colon - - # Now start .. end is just the hostname. - hostname = hostUrl[start : end] - - # Now build a hash string of the whole URL. We'll use MD5 to - # get a pretty good hash, with a minimum chance of collision. - # Even if there is a hash collision, though, it's not the end - # of the world; it just means that both hosts will dump their - # packages into the same directory, and they'll fight over the - # toplevel contents.xml file. Assuming they use different - # version numbers (which should be safe since they have the - # same hostname), there will be minimal redownloading. - - hashSize = 16 - keepHash = hashSize - if hostname: - hostDir += hostname + '_' - - # If we successfully got a hostname, we don't really need the - # full hash. We'll keep half of it. - keepHash = keepHash // 2 - - md = HashVal() - md.hashString(hostUrl) - hostDir += md.asHex()[:keepHash * 2] - - hostDir = Filename(self.rootDir, hostDir) - return hostDir diff --git a/direct/src/p3d/InstalledHostData.py b/direct/src/p3d/InstalledHostData.py deleted file mode 100644 index d98abb6d18..0000000000 --- a/direct/src/p3d/InstalledHostData.py +++ /dev/null @@ -1,24 +0,0 @@ -__all__ = ["InstalledHostData"] - -from panda3d.core import URLSpec - -class InstalledHostData: - """ A list of instances of this class is returned by - AppRunner.scanInstalledPackages(). Each of these corresponds to a - particular host that has provided packages that have been - installed on the local client. """ - - def __init__(self, host, dirnode): - self.host = host - self.pathname = dirnode.pathname - self.totalSize = dirnode.getTotalSize() - self.packages = [] - - if self.host: - self.hostUrl = self.host.hostUrl - self.descriptiveName = self.host.descriptiveName - if not self.descriptiveName: - self.descriptiveName = URLSpec(self.hostUrl).getServer() - else: - self.hostUrl = 'unknown' - self.descriptiveName = 'unknown' diff --git a/direct/src/p3d/InstalledPackageData.py b/direct/src/p3d/InstalledPackageData.py deleted file mode 100644 index 09ee81cda6..0000000000 --- a/direct/src/p3d/InstalledPackageData.py +++ /dev/null @@ -1,30 +0,0 @@ -__all__ = ["InstalledPackageData"] - -class InstalledPackageData: - """ A list of instances of this class is maintained by - InstalledHostData (which is in turn returned by - AppRunner.scanInstalledPackages()). Each of these corresponds to - a particular package that has been installed on the local - client. """ - - def __init__(self, package, dirnode): - self.package = package - self.pathname = dirnode.pathname - self.totalSize = dirnode.getTotalSize() - self.lastUse = None - - if self.package: - self.displayName = self.package.getFormattedName() - xusage = self.package.getUsage() - - if xusage: - lastUse = xusage.Attribute('last_use') - try: - lastUse = int(lastUse or '') - except ValueError: - lastUse = None - self.lastUse = lastUse - - else: - self.displayName = dirnode.pathname.getBasename() - diff --git a/direct/src/p3d/JavaScript.py b/direct/src/p3d/JavaScript.py deleted file mode 100644 index b59deb1aa2..0000000000 --- a/direct/src/p3d/JavaScript.py +++ /dev/null @@ -1,298 +0,0 @@ -""" This module defines some simple classes and instances which are -useful when writing code that integrates with JavaScript, especially -code that runs in a browser via the web plugin. """ - -__all__ = ["UndefinedObject", "Undefined", "ConcreteStruct", "BrowserObject", "MethodWrapper"] - -class UndefinedObject: - """ This is a special object that is returned by the browser to - represent an "undefined" or "void" value, typically the value for - an uninitialized variable or undefined property. It has no - attributes, similar to None, but it is a slightly different - concept in JavaScript. """ - - def __bool__(self): - return False - - __nonzero__ = __bool__ # Python 2 - - def __str__(self): - return "Undefined" - -# In fact, we normally always return this precise instance of the -# UndefinedObject. -Undefined = UndefinedObject() - -class ConcreteStruct: - """ Python objects that inherit from this class are passed to - JavaScript as a concrete struct: a mapping from string -> value, - with no methods, passed by value. This can be more optimal than - traditional Python objects which are passed by reference, - especially for small objects which might be repeatedly referenced - on the JavaScript side. """ - - def __init__(self): - pass - - def getConcreteProperties(self): - """ Returns a list of 2-tuples of the (key, value) pairs that - are to be passed to the concrete instance. By default, this - returns all properties of the object. You can override this - to restrict the set of properties that are uploaded. """ - - return list(self.__dict__.items()) - -class BrowserObject: - """ This class provides the Python wrapper around some object that - actually exists in the plugin host's namespace, e.g. a JavaScript - or DOM object. """ - - def __init__(self, runner, objectId): - self.__dict__['_BrowserObject__runner'] = runner - self.__dict__['_BrowserObject__objectId'] = objectId - - # This element is filled in by __getattr__; it connects - # the object to its parent. - self.__dict__['_BrowserObject__childObject'] = (None, None) - - # This is a cache of method names to MethodWrapper objects in - # the parent object. - self.__dict__['_BrowserObject__methods'] = {} - - def __del__(self): - # When the BrowserObject destructs, tell the parent process it - # doesn't need to keep around its corresponding P3D_object any - # more. - self.__runner.dropObject(self.__objectId) - - def __cacheMethod(self, methodName): - """ Stores a pointer to the named method on this object, so - that the next time __getattr__ is called, it can retrieve the - method wrapper without having to query the browser. This - cache assumes that callable methods don't generally come and - go on and object. - - The return value is the MethodWrapper object. """ - - method = self.__methods.get(methodName, None) - if method is None: - method = MethodWrapper(self.__runner, self, methodName) - self.__methods[methodName] = method - return method - - def __str__(self): - return self.toString() - - def __bool__(self): - return True - - __nonzero__ = __bool__ # Python 2 - - def __call__(self, *args, **kw): - needsResponse = True - if 'needsResponse' in kw: - needsResponse = kw['needsResponse'] - del kw['needsResponse'] - if kw: - raise ArgumentError('Keyword arguments not supported') - - try: - parentObj, attribName = self.__childObject - if parentObj: - # Call it as a method. - if parentObj is self.__runner.dom and attribName == 'alert': - # As a special hack, we don't wait for the return - # value from the alert() call, since this is a - # blocking call, and waiting for this could cause - # problems. - needsResponse = False - - if parentObj is self.__runner.dom and attribName == 'eval' and len(args) == 1 and isinstance(args[0], str): - # As another special hack, we make dom.eval() a - # special case, and map it directly into an eval() - # call. If the string begins with 'void ', we further - # assume we're not waiting for a response. - if args[0].startswith('void '): - needsResponse = False - result = self.__runner.scriptRequest('eval', parentObj, value = args[0], needsResponse = needsResponse) - else: - # This is a normal method call. - try: - result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args, needsResponse = needsResponse) - except EnvironmentError: - # Problem on the call. Maybe no such method? - raise AttributeError - - # Hey, the method call appears to have succeeded. - # Cache the method object on the parent so we won't - # have to look up the method wrapper again next time. - parentObj.__cacheMethod(attribName) - - else: - # Call it as a plain function. - result = self.__runner.scriptRequest('call', self, value = args, needsResponse = needsResponse) - except EnvironmentError: - # Some odd problem on the call. - raise TypeError - - return result - - def __getattr__(self, name): - """ Remaps attempts to query an attribute, as in obj.attr, - into the appropriate calls to query the actual browser object - under the hood. """ - - # First check to see if there's a cached method wrapper from a - # previous call. - method = self.__methods.get(name, None) - if method: - return method - - # No cache. Go query the browser for the desired value. - try: - value = self.__runner.scriptRequest('get_property', self, - propertyName = name) - except EnvironmentError: - # Failed to retrieve the attribute. But maybe there's a - # method instead? - if self.__runner.scriptRequest('has_method', self, propertyName = name): - # Yes, so create a method wrapper for it. - return self.__cacheMethod(name) - - raise AttributeError(name) - - if isinstance(value, BrowserObject): - # Fill in the parent object association, so __call__ can - # properly call a method. (Javascript needs to know the - # method container at the time of the call, and doesn't - # store it on the function object.) - value.__dict__['_BrowserObject__childObject'] = (self, name) - - return value - - def __setattr__(self, name, value): - if name in self.__dict__: - self.__dict__[name] = value - return - - result = self.__runner.scriptRequest('set_property', self, - propertyName = name, - value = value) - if not result: - raise AttributeError(name) - - def __delattr__(self, name): - if name in self.__dict__: - del self.__dict__[name] - return - - result = self.__runner.scriptRequest('del_property', self, - propertyName = name) - if not result: - raise AttributeError(name) - - def __getitem__(self, key): - """ Remaps attempts to query an attribute, as in obj['attr'], - into the appropriate calls to query the actual browser object - under the hood. Following the JavaScript convention, we treat - obj['attr'] almost the same as obj.attr. """ - - try: - value = self.__runner.scriptRequest('get_property', self, - propertyName = str(key)) - except EnvironmentError: - # Failed to retrieve the property. We return IndexError - # for numeric keys so we can properly support Python's - # iterators, but we return KeyError for string keys to - # emulate mapping objects. - if isinstance(key, str): - raise KeyError(key) - else: - raise IndexError(key) - - return value - - def __setitem__(self, key, value): - result = self.__runner.scriptRequest('set_property', self, - propertyName = str(key), - value = value) - if not result: - if isinstance(key, str): - raise KeyError(key) - else: - raise IndexError(key) - - def __delitem__(self, key): - result = self.__runner.scriptRequest('del_property', self, - propertyName = str(key)) - if not result: - if isinstance(key, str): - raise KeyError(key) - else: - raise IndexError(key) - -class MethodWrapper: - """ This is a Python wrapper around a property of a BrowserObject - that doesn't appear to be a first-class object in the Python - sense, but is nonetheless a callable method. """ - - def __init__(self, runner, parentObj, objectId): - self.__dict__['_MethodWrapper__runner'] = runner - self.__dict__['_MethodWrapper__childObject'] = (parentObj, objectId) - - def __str__(self): - parentObj, attribName = self.__childObject - return "%s.%s" % (parentObj, attribName) - - def __bool__(self): - return True - - __nonzero__ = __bool__ # Python 2 - - def __call__(self, *args, **kw): - needsResponse = True - if 'needsResponse' in kw: - needsResponse = kw['needsResponse'] - del kw['needsResponse'] - if kw: - raise ArgumentError('Keyword arguments not supported') - - try: - parentObj, attribName = self.__childObject - # Call it as a method. - if parentObj is self.__runner.dom and attribName == 'alert': - # As a special hack, we don't wait for the return - # value from the alert() call, since this is a - # blocking call, and waiting for this could cause - # problems. - needsResponse = False - - if parentObj is self.__runner.dom and attribName == 'eval' and len(args) == 1 and isinstance(args[0], str): - # As another special hack, we make dom.eval() a - # special case, and map it directly into an eval() - # call. If the string begins with 'void ', we further - # assume we're not waiting for a response. - if args[0].startswith('void '): - needsResponse = False - result = self.__runner.scriptRequest('eval', parentObj, value = args[0], needsResponse = needsResponse) - else: - # This is a normal method call. - try: - result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args, needsResponse = needsResponse) - except EnvironmentError: - # Problem on the call. Maybe no such method? - raise AttributeError - - except EnvironmentError: - # Some odd problem on the call. - raise TypeError - - return result - - def __setattr__(self, name, value): - """ setattr will generally fail on method objects. """ - raise AttributeError(name) - - def __delattr__(self, name): - """ delattr will generally fail on method objects. """ - raise AttributeError(name) diff --git a/direct/src/p3d/PackageInfo.py b/direct/src/p3d/PackageInfo.py deleted file mode 100644 index 08a73365b8..0000000000 --- a/direct/src/p3d/PackageInfo.py +++ /dev/null @@ -1,1237 +0,0 @@ -__all__ = ["PackageInfo"] - -from panda3d.core import Filename, DocumentSpec, Multifile, Decompressor, EUOk, EUSuccess, VirtualFileSystem, Thread, getModelPath, ExecutionEnvironment, PStatCollector, TiXmlDocument, TiXmlDeclaration, TiXmlElement -import panda3d.core as core -from direct.p3d.FileSpec import FileSpec -from direct.p3d.ScanDirectoryNode import ScanDirectoryNode -from direct.showbase import VFSImporter -from direct.directnotify.DirectNotifyGlobal import directNotify -from direct.task.TaskManagerGlobal import taskMgr -import os -import sys -import random -import time -import copy - -class PackageInfo: - - """ This class represents a downloadable Panda3D package file that - can be (or has been) installed into the current runtime. It is - the Python equivalent of the P3DPackage class in the core API. """ - - notify = directNotify.newCategory("PackageInfo") - - # Weight factors for computing download progress. This - # attempts to reflect the relative time-per-byte of each of - # these operations. - downloadFactor = 1 - uncompressFactor = 0.01 - unpackFactor = 0.01 - patchFactor = 0.01 - - # These tokens are yielded (not returned) by __downloadFile() and - # other InstallStep functions. - stepComplete = 1 - stepFailed = 2 - restartDownload = 3 - stepContinue = 4 - - UsageBasename = 'usage.xml' - - class InstallStep: - """ This class is one step of the installPlan list; it - represents a single atomic piece of the installation step, and - the relative effort of that piece. When the plan is executed, - it will call the saved function pointer here. """ - def __init__(self, func, bytes, factor, stepType): - self.__funcPtr = func - self.bytesNeeded = bytes - self.bytesDone = 0 - self.bytesFactor = factor - self.stepType = stepType - self.pStatCol = PStatCollector(':App:PackageInstaller:%s' % (stepType)) - - def func(self): - """ self.__funcPtr(self) will return a generator of - tokens. This function defines a new generator that yields - each of those tokens, but wraps each call into the nested - generator within a pair of start/stop collector calls. """ - - self.pStatCol.start() - for token in self.__funcPtr(self): - self.pStatCol.stop() - yield token - self.pStatCol.start() - - # Shouldn't ever get here. - self.pStatCol.stop() - raise StopIteration - - def getEffort(self): - """ Returns the relative amount of effort of this step. """ - return self.bytesNeeded * self.bytesFactor - - def getProgress(self): - """ Returns the progress of this step, in the range - 0..1. """ - if self.bytesNeeded == 0: - return 1 - return min(float(self.bytesDone) / float(self.bytesNeeded), 1) - - def __init__(self, host, packageName, packageVersion, platform = None, - solo = False, asMirror = False, perPlatform = False): - self.host = host - self.packageName = packageName - self.packageVersion = packageVersion - self.platform = platform - self.solo = solo - self.asMirror = asMirror - self.perPlatform = perPlatform - - # This will be active while we are in the middle of a download - # cycle. - self.http = None - - # This will be filled in when the host's contents.xml file is - # read. - self.packageDir = None - - # These will be filled in by HostInfo when the package is read - # from contents.xml. - self.descFile = None - self.importDescFile = None - - # These are filled in when the desc file is successfully read. - self.hasDescFile = False - self.patchVersion = None - self.displayName = None - self.guiApp = False - self.uncompressedArchive = None - self.compressedArchive = None - self.extracts = [] - self.requires = [] - self.installPlans = None - - # This is updated during downloadPackage(). It is in the - # range 0..1. - self.downloadProgress = 0 - - # This is set true when the package file has been fully - # downloaded and unpacked. - self.hasPackage = False - - # This is set true when the package has been "installed", - # meaning it's been added to the paths and all. - self.installed = False - - # This is set true when the package has been updated in this - # session, but not yet written to usage.xml. - self.updated = False - self.diskSpace = None - - def getPackageDir(self): - """ Returns the directory in which this package is installed. - This may not be known until the host's contents.xml file has - been downloaded, which informs us of the host's own install - directory. """ - - if not self.packageDir: - if not self.host.hasContentsFile: - if not self.host.readContentsFile(): - self.host.downloadContentsFile(self.http) - - # Derive the packageDir from the hostDir. - self.packageDir = Filename(self.host.hostDir, self.packageName) - if self.packageVersion: - self.packageDir = Filename(self.packageDir, self.packageVersion) - - if self.host.perPlatform: - # If we're running on a special host that wants us to - # include the platform, we include it. - includePlatform = True - elif self.perPlatform and self.host.appRunner.respectPerPlatform: - # Otherwise, if our package spec wants us to include - # the platform (and our plugin knows about this), then - # we also include it. - includePlatform = True - else: - # Otherwise, we must be running legacy code - # somewhere--either an old package or an old - # plugin--and we therefore shouldn't include the - # platform in the directory hierarchy. - includePlatform = False - - if includePlatform and self.platform: - self.packageDir = Filename(self.packageDir, self.platform) - - return self.packageDir - - def getDownloadEffort(self): - """ Returns the relative amount of effort it will take to - download this package. The units are meaningless, except - relative to other packges.""" - - if not self.installPlans: - return 0 - - # Return the size of plan A, assuming it will work. - plan = self.installPlans[0] - size = sum([step.getEffort() for step in plan]) - - return size - - def getPrevDownloadedEffort(self): - """ Returns a rough estimate of this package's total download - effort, even if it is already downloaded. """ - - effort = 0 - if self.compressedArchive: - effort += self.compressedArchive.size * self.downloadFactor - if self.uncompressedArchive: - effort += self.uncompressedArchive.size * self.uncompressFactor - # Don't bother counting unpacking. - - return effort - - def getFormattedName(self): - """ Returns the name of this package, for output to the user. - This will be the "public" name of the package, as formatted - for user consumption; it will include capital letters and - spaces where appropriate. """ - - if self.displayName: - name = self.displayName - else: - name = self.packageName - if self.packageVersion: - name += ' %s' % (self.packageVersion) - - if self.patchVersion: - name += ' rev %s' % (self.patchVersion) - - return name - - - def setupFilenames(self): - """ This is called by the HostInfo when the package is read - from contents.xml, to set up the internal filenames and such - that rely on some of the information from contents.xml. """ - - dirname, basename = self.descFile.filename.rsplit('/', 1) - self.descFileDirname = dirname - self.descFileBasename = basename - - def checkStatus(self): - """ Checks the current status of the desc file and the package - contents on disk. """ - - if self.hasPackage: - return True - - if not self.hasDescFile: - filename = Filename(self.getPackageDir(), self.descFileBasename) - if self.descFile.quickVerify(self.getPackageDir(), pathname = filename, notify = self.notify): - if self.__readDescFile(): - # Successfully read. We don't need to call - # checkArchiveStatus again, since readDescFile() - # has just done it. - return self.hasPackage - - if self.hasDescFile: - if self.__checkArchiveStatus(): - # It's all good. - self.hasPackage = True - - return self.hasPackage - - def hasCurrentDescFile(self): - """ Returns true if a desc file file has been successfully - read for this package and is still current, false - otherwise. """ - - if not self.host.hasCurrentContentsFile(): - return False - - return self.hasDescFile - - def downloadDescFile(self, http): - """ Downloads the desc file for this particular package, - synchronously, and then reads it. Returns true on success, - false on failure. """ - - for token in self.downloadDescFileGenerator(http): - if token != self.stepContinue: - break - Thread.considerYield() - - return (token == self.stepComplete) - - def downloadDescFileGenerator(self, http): - """ A generator function that implements downloadDescFile() - one piece at a time. It yields one of stepComplete, - stepFailed, or stepContinue. """ - - assert self.descFile - - if self.hasDescFile: - # We've already got one. - yield self.stepComplete; return - - if not self.host.appRunner or self.host.appRunner.verifyContents != self.host.appRunner.P3DVCNever: - # We're allowed to download it. - self.http = http - - func = lambda step, self = self: self.__downloadFile( - None, self.descFile, - urlbase = self.descFile.filename, - filename = self.descFileBasename) - step = self.InstallStep(func, self.descFile.size, self.downloadFactor, 'downloadDesc') - - for token in step.func(): - if token == self.stepContinue: - yield token - else: - break - - while token == self.restartDownload: - # Try again. - func = lambda step, self = self: self.__downloadFile( - None, self.descFile, - urlbase = self.descFile.filename, - filename = self.descFileBasename) - step = self.InstallStep(func, self.descFile.size, self.downloadFactor, 'downloadDesc') - for token in step.func(): - if token == self.stepContinue: - yield token - else: - break - - if token == self.stepFailed: - # Couldn't download the desc file. - yield self.stepFailed; return - - assert token == self.stepComplete - - filename = Filename(self.getPackageDir(), self.descFileBasename) - # Now that we've written the desc file, make it read-only. - os.chmod(filename.toOsSpecific(), 0o444) - - if not self.__readDescFile(): - # Weird, it passed the hash check, but we still can't read - # it. - filename = Filename(self.getPackageDir(), self.descFileBasename) - self.notify.warning("Failure reading %s" % (filename)) - yield self.stepFailed; return - - yield self.stepComplete; return - - def __readDescFile(self): - """ Reads the desc xml file for this particular package, - assuming it's been already downloaded and verified. Returns - true on success, false on failure. """ - - if self.hasDescFile: - # No need to read it again. - return True - - if self.solo: - # If this is a "solo" package, we don't actually "read" - # the desc file; that's the entire contents of the - # package. - self.hasDescFile = True - self.hasPackage = True - return True - - filename = Filename(self.getPackageDir(), self.descFileBasename) - - if not hasattr(core, 'TiXmlDocument'): - return False - doc = core.TiXmlDocument(filename.toOsSpecific()) - if not doc.LoadFile(): - return False - - xpackage = doc.FirstChildElement('package') - if not xpackage: - return False - - try: - self.patchVersion = int(xpackage.Attribute('patch_version') or '') - except ValueError: - self.patchVersion = None - - try: - perPlatform = int(xpackage.Attribute('per_platform') or '') - except ValueError: - perPlatform = False - if perPlatform != self.perPlatform: - self.notify.warning("per_platform disagreement on package %s" % (self.packageName)) - - self.displayName = None - xconfig = xpackage.FirstChildElement('config') - if xconfig: - # The name for display to an English-speaking user. - self.displayName = xconfig.Attribute('display_name') - - # True if any apps that use this package must be GUI apps. - guiApp = xconfig.Attribute('gui_app') - if guiApp: - self.guiApp = int(guiApp) - - # The uncompressed archive, which will be mounted directly, - # and also used for patching. - xuncompressedArchive = xpackage.FirstChildElement('uncompressed_archive') - if xuncompressedArchive: - self.uncompressedArchive = FileSpec() - self.uncompressedArchive.loadXml(xuncompressedArchive) - - # The compressed archive, which is what is downloaded. - xcompressedArchive = xpackage.FirstChildElement('compressed_archive') - if xcompressedArchive: - self.compressedArchive = FileSpec() - self.compressedArchive.loadXml(xcompressedArchive) - - # The list of files that should be extracted to disk. - self.extracts = [] - xextract = xpackage.FirstChildElement('extract') - while xextract: - file = FileSpec() - file.loadXml(xextract) - self.extracts.append(file) - xextract = xextract.NextSiblingElement('extract') - - # The list of additional packages that must be installed for - # this package to function properly. - self.requires = [] - xrequires = xpackage.FirstChildElement('requires') - while xrequires: - packageName = xrequires.Attribute('name') - version = xrequires.Attribute('version') - hostUrl = xrequires.Attribute('host') - if packageName and hostUrl: - host = self.host.appRunner.getHostWithAlt(hostUrl) - self.requires.append((packageName, version, host)) - xrequires = xrequires.NextSiblingElement('requires') - - self.hasDescFile = True - - # Now that we've read the desc file, go ahead and use it to - # verify the download status. - if self.__checkArchiveStatus(): - # It's all fully downloaded, unpacked, and ready. - self.hasPackage = True - return True - - # Still have to download it. - self.__buildInstallPlans() - return True - - def __buildInstallPlans(self): - """ Sets up self.installPlans, a list of one or more "plans" - to download and install the package. """ - - pc = PStatCollector(':App:PackageInstaller:buildInstallPlans') - pc.start() - - self.hasPackage = False - - if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever: - # We're not allowed to download anything. - self.installPlans = [] - pc.stop() - return - - if self.asMirror: - # If we're just downloading a mirror archive, we only need - # to get the compressed archive file. - - # Build a one-item install plan to download the compressed - # archive. - downloadSize = self.compressedArchive.size - func = lambda step, fileSpec = self.compressedArchive: self.__downloadFile(step, fileSpec, allowPartial = True) - - step = self.InstallStep(func, downloadSize, self.downloadFactor, 'download') - installPlan = [step] - self.installPlans = [installPlan] - pc.stop() - return - - # The normal download process. Determine what we will need to - # download, and build a plan (or two) to download it all. - self.installPlans = None - - # We know we will at least need to unpack the archive contents - # at the end. - unpackSize = 0 - for file in self.extracts: - unpackSize += file.size - step = self.InstallStep(self.__unpackArchive, unpackSize, self.unpackFactor, 'unpack') - planA = [step] - - # If the uncompressed archive file is good, that's all we'll - # need to do. - self.uncompressedArchive.actualFile = None - if self.uncompressedArchive.quickVerify(self.getPackageDir(), notify = self.notify): - self.installPlans = [planA] - pc.stop() - return - - # Maybe the compressed archive file is good. - if self.compressedArchive.quickVerify(self.getPackageDir(), notify = self.notify): - uncompressSize = self.uncompressedArchive.size - step = self.InstallStep(self.__uncompressArchive, uncompressSize, self.uncompressFactor, 'uncompress') - planA = [step] + planA - self.installPlans = [planA] - pc.stop() - return - - # Maybe we can download one or more patches. We'll come back - # to that in a minute as plan A. For now, construct plan B, - # which will be to download the whole archive. - planB = planA[:] - - uncompressSize = self.uncompressedArchive.size - step = self.InstallStep(self.__uncompressArchive, uncompressSize, self.uncompressFactor, 'uncompress') - planB = [step] + planB - - downloadSize = self.compressedArchive.size - func = lambda step, fileSpec = self.compressedArchive: self.__downloadFile(step, fileSpec, allowPartial = True) - - step = self.InstallStep(func, downloadSize, self.downloadFactor, 'download') - planB = [step] + planB - - # Now look for patches. Start with the md5 hash from the - # uncompressedArchive file we have on disk, and see if we can - # find a patch chain from this file to our target. - pathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename) - fileSpec = self.uncompressedArchive.actualFile - if fileSpec is None and pathname.exists(): - fileSpec = FileSpec() - fileSpec.fromFile(self.getPackageDir(), self.uncompressedArchive.filename) - plan = None - if fileSpec: - plan = self.__findPatchChain(fileSpec) - if plan: - # We can download patches. Great! That means this is - # plan A, and the full download is plan B (in case - # something goes wrong with the patching). - planA = plan + planA - self.installPlans = [planA, planB] - else: - # There are no patches to download, oh well. Stick with - # plan B as the only plan. - self.installPlans = [planB] - - # In case of unexpected failures on the internet, we will retry - # the full download instead of just giving up. - retries = core.ConfigVariableInt('package-full-dl-retries', 1).getValue() - for retry in range(retries): - self.installPlans.append(planB[:]) - - pc.stop() - - def __scanDirectoryRecursively(self, dirname): - """ Generates a list of Filename objects: all of the files - (not directories) within and below the indicated dirname. """ - - contents = [] - for dirpath, dirnames, filenames in os.walk(dirname.toOsSpecific()): - dirpath = Filename.fromOsSpecific(dirpath) - if dirpath == dirname: - dirpath = Filename('') - else: - dirpath.makeRelativeTo(dirname) - for filename in filenames: - contents.append(Filename(dirpath, filename)) - return contents - - def __removeFileFromList(self, contents, filename): - """ Removes the indicated filename from the given list, if it is - present. """ - try: - contents.remove(Filename(filename)) - except ValueError: - pass - - def __checkArchiveStatus(self): - """ Returns true if the archive and all extractable files are - already correct on disk, false otherwise. """ - - if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever: - # Assume that everything is just fine. - return True - - # Get a list of all of the files in the directory, so we can - # remove files that don't belong. - contents = self.__scanDirectoryRecursively(self.getPackageDir()) - self.__removeFileFromList(contents, self.descFileBasename) - self.__removeFileFromList(contents, self.compressedArchive.filename) - self.__removeFileFromList(contents, self.UsageBasename) - if not self.asMirror: - self.__removeFileFromList(contents, self.uncompressedArchive.filename) - for file in self.extracts: - self.__removeFileFromList(contents, file.filename) - - # Now, any files that are still in the contents list don't - # belong. It's important to remove these files before we - # start verifying the files that we expect to find here, in - # case there is a problem with ambiguous filenames or - # something (e.g. case insensitivity). - for filename in contents: - self.notify.info("Removing %s" % (filename)) - pathname = Filename(self.getPackageDir(), filename) - pathname.unlink() - self.updated = True - - if self.asMirror: - return self.compressedArchive.quickVerify(self.getPackageDir(), notify = self.notify) - - allExtractsOk = True - if not self.uncompressedArchive.quickVerify(self.getPackageDir(), notify = self.notify): - self.notify.debug("File is incorrect: %s" % (self.uncompressedArchive.filename)) - allExtractsOk = False - - if allExtractsOk: - # OK, the uncompressed archive is good; that means there - # shouldn't be a compressed archive file here. - pathname = Filename(self.getPackageDir(), self.compressedArchive.filename) - pathname.unlink() - - for file in self.extracts: - if not file.quickVerify(self.getPackageDir(), notify = self.notify): - self.notify.debug("File is incorrect: %s" % (file.filename)) - allExtractsOk = False - break - - if allExtractsOk: - self.notify.debug("All %s extracts of %s seem good." % ( - len(self.extracts), self.packageName)) - - return allExtractsOk - - def __updateStepProgress(self, step): - """ This callback is made from within the several step - functions as the download step proceeds. It updates - self.downloadProgress with the current progress, so the caller - can asynchronously query this value. """ - - size = self.totalPlanCompleted + self.currentStepEffort * step.getProgress() - self.downloadProgress = min(float(size) / float(self.totalPlanSize), 1) - - def downloadPackage(self, http): - """ Downloads the package file, synchronously, then - uncompresses and unpacks it. Returns true on success, false - on failure. - - This assumes that self.installPlans has already been filled - in, which will have been done by self.__readDescFile(). - """ - - for token in self.downloadPackageGenerator(http): - if token != self.stepContinue: - break - Thread.considerYield() - - return (token == self.stepComplete) - - def downloadPackageGenerator(self, http): - """ A generator function that implements downloadPackage() one - piece at a time. It yields one of stepComplete, stepFailed, - or stepContinue. """ - - assert self.hasDescFile - - if self.hasPackage: - # We've already got one. - yield self.stepComplete; return - - if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever: - # We're not allowed to download anything. Assume it's already downloaded. - yield self.stepComplete; return - - # We should have an install plan by the time we get here. - assert self.installPlans - - self.http = http - for token in self.__followInstallPlans(): - if token == self.stepContinue: - yield token - else: - break - - while token == self.restartDownload: - # Try again. - for token in self.downloadDescFileGenerator(http): - if token == self.stepContinue: - yield token - else: - break - if token == self.stepComplete: - for token in self.__followInstallPlans(): - if token == self.stepContinue: - yield token - else: - break - - if token == self.stepFailed: - yield self.stepFailed; return - - assert token == self.stepComplete - yield self.stepComplete; return - - - def __followInstallPlans(self): - """ Performs all of the steps in self.installPlans. Yields - one of stepComplete, stepFailed, restartDownload, or - stepContinue. """ - - if not self.installPlans: - self.__buildInstallPlans() - - installPlans = self.installPlans - self.installPlans = None - for plan in installPlans: - self.totalPlanSize = sum([step.getEffort() for step in plan]) - self.totalPlanCompleted = 0 - self.downloadProgress = 0 - - planFailed = False - for step in plan: - self.currentStepEffort = step.getEffort() - - for token in step.func(): - if token == self.stepContinue: - yield token - else: - break - - if token == self.restartDownload: - yield token - if token == self.stepFailed: - planFailed = True - break - assert token == self.stepComplete - - self.totalPlanCompleted += self.currentStepEffort - - if not planFailed: - # Successfully downloaded! - yield self.stepComplete; return - - if taskMgr.destroyed: - yield self.stepFailed; return - - # All plans failed. - yield self.stepFailed; return - - def __findPatchChain(self, fileSpec): - """ Finds the chain of patches that leads from the indicated - patch version to the current patch version. If found, - constructs an installPlan that represents the steps of the - patch installation; otherwise, returns None. """ - - from direct.p3d.PatchMaker import PatchMaker - - patchMaker = PatchMaker(self.getPackageDir()) - patchChain = patchMaker.getPatchChainToCurrent(self.descFileBasename, fileSpec) - if patchChain is None: - # No path. - patchMaker.cleanup() - return None - - plan = [] - for patchfile in patchChain: - downloadSize = patchfile.file.size - func = lambda step, fileSpec = patchfile.file: self.__downloadFile(step, fileSpec, allowPartial = True) - step = self.InstallStep(func, downloadSize, self.downloadFactor, 'download') - plan.append(step) - - patchSize = patchfile.targetFile.size - func = lambda step, patchfile = patchfile: self.__applyPatch(step, patchfile) - step = self.InstallStep(func, patchSize, self.patchFactor, 'patch') - plan.append(step) - - patchMaker.cleanup() - return plan - - def __downloadFile(self, step, fileSpec, urlbase = None, filename = None, - allowPartial = False): - """ Downloads the indicated file from the host into - packageDir. Yields one of stepComplete, stepFailed, - restartDownload, or stepContinue. """ - - if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever: - # We're not allowed to download anything. - yield self.stepFailed; return - - self.updated = True - - if not urlbase: - urlbase = self.descFileDirname + '/' + fileSpec.filename - - # Build up a list of URL's to try downloading from. Unlike - # the C++ implementation in P3DPackage.cxx, here we build the - # URL's in forward order. - tryUrls = [] - - if self.host.appRunner and self.host.appRunner.superMirrorUrl: - # We start with the "super mirror", if it's defined. - url = self.host.appRunner.superMirrorUrl + urlbase - tryUrls.append((url, False)) - - if self.host.mirrors: - # Choose two mirrors at random. - mirrors = self.host.mirrors[:] - for i in range(2): - mirror = random.choice(mirrors) - mirrors.remove(mirror) - url = mirror + urlbase - tryUrls.append((url, False)) - if not mirrors: - break - - # After trying two mirrors and failing (or if there are no - # mirrors), go get it from the original host. - url = self.host.downloadUrlPrefix + urlbase - tryUrls.append((url, False)) - - # And finally, if the original host also fails, try again with - # a cache-buster. - tryUrls.append((url, True)) - - for url, cacheBust in tryUrls: - request = DocumentSpec(url) - - if cacheBust: - # On the last attempt to download a particular file, - # we bust through the cache: append a query string to - # do this. - url += '?' + str(int(time.time())) - request = DocumentSpec(url) - request.setCacheControl(DocumentSpec.CCNoCache) - - self.notify.info("%s downloading %s" % (self.packageName, url)) - - if not filename: - filename = fileSpec.filename - targetPathname = Filename(self.getPackageDir(), filename) - targetPathname.setBinary() - - channel = self.http.makeChannel(False) - - # If there's a previous partial download, attempt to resume it. - bytesStarted = 0 - if allowPartial and not cacheBust and targetPathname.exists(): - bytesStarted = targetPathname.getFileSize() - - if bytesStarted < 1024*1024: - # Not enough bytes downloaded to be worth the risk of - # a partial download. - bytesStarted = 0 - elif bytesStarted >= fileSpec.size: - # Couldn't possibly be our file. - bytesStarted = 0 - - if bytesStarted: - self.notify.info("Resuming %s after %s bytes already downloaded" % (url, bytesStarted)) - # Make sure the file is writable. - os.chmod(targetPathname.toOsSpecific(), 0o644) - channel.beginGetSubdocument(request, bytesStarted, 0) - else: - # No partial download possible; get the whole file. - targetPathname.makeDir() - targetPathname.unlink() - channel.beginGetDocument(request) - - channel.downloadToFile(targetPathname) - while channel.run(): - if step: - step.bytesDone = channel.getBytesDownloaded() + channel.getFirstByteDelivered() - if step.bytesDone > step.bytesNeeded: - # Oops, too much data. Might as well abort; - # it's the wrong file. - self.notify.warning("Got more data than expected for download %s" % (url)) - break - - self.__updateStepProgress(step) - - if taskMgr.destroyed: - # If the task manager has been destroyed, we must - # be shutting down. Get out of here. - self.notify.warning("Task Manager destroyed, aborting %s" % (url)) - yield self.stepFailed; return - - yield self.stepContinue - - if step: - step.bytesDone = channel.getBytesDownloaded() + channel.getFirstByteDelivered() - self.__updateStepProgress(step) - - if not channel.isValid(): - self.notify.warning("Failed to download %s" % (url)) - - elif not fileSpec.fullVerify(self.getPackageDir(), pathname = targetPathname, notify = self.notify): - self.notify.warning("After downloading, %s incorrect" % (Filename(fileSpec.filename).getBasename())) - - # This attempt failed. Maybe the original contents.xml - # file is stale. Try re-downloading it now, just to be - # sure. - if self.host.redownloadContentsFile(self.http): - # Yes! Go back and start over from the beginning. - yield self.restartDownload; return - - else: - # Success! - yield self.stepComplete; return - - # Maybe the mirror is bad. Go back and try the next - # mirror. - - # All attempts failed. Maybe the original contents.xml file - # is stale. Try re-downloading it now, just to be sure. - if self.host.redownloadContentsFile(self.http): - # Yes! Go back and start over from the beginning. - yield self.restartDownload; return - - # All mirrors failed; the server (or the internet connection) - # must be just fubar. - yield self.stepFailed; return - - def __applyPatch(self, step, patchfile): - """ Applies the indicated patching in-place to the current - uncompressed archive. The patchfile is removed after the - operation. Yields one of stepComplete, stepFailed, - restartDownload, or stepContinue. """ - - self.updated = True - - origPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename) - patchPathname = Filename(self.getPackageDir(), patchfile.file.filename) - result = Filename.temporary('', 'patch_') - self.notify.info("Patching %s with %s" % (origPathname, patchPathname)) - - p = core.Patchfile() # The C++ class - - ret = p.initiate(patchPathname, origPathname, result) - if ret == EUSuccess: - ret = p.run() - while ret == EUOk: - step.bytesDone = step.bytesNeeded * p.getProgress() - self.__updateStepProgress(step) - if taskMgr.destroyed: - # If the task manager has been destroyed, we must - # be shutting down. Get out of here. - self.notify.warning("Task Manager destroyed, aborting patch %s" % (origPathname)) - yield self.stepFailed; return - - yield self.stepContinue - ret = p.run() - del p - patchPathname.unlink() - - if ret < 0: - self.notify.warning("Patching of %s failed." % (origPathname)) - result.unlink() - yield self.stepFailed; return - - if not result.renameTo(origPathname): - self.notify.warning("Couldn't rename %s to %s" % (result, origPathname)) - yield self.stepFailed; return - - yield self.stepComplete; return - - def __uncompressArchive(self, step): - """ Turns the compressed archive into the uncompressed - archive. Yields one of stepComplete, stepFailed, - restartDownload, or stepContinue. """ - - if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever: - # We're not allowed to! - yield self.stepFailed; return - - self.updated = True - - sourcePathname = Filename(self.getPackageDir(), self.compressedArchive.filename) - targetPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename) - targetPathname.unlink() - self.notify.info("Uncompressing %s to %s" % (sourcePathname, targetPathname)) - decompressor = Decompressor() - decompressor.initiate(sourcePathname, targetPathname) - totalBytes = self.uncompressedArchive.size - result = decompressor.run() - while result == EUOk: - step.bytesDone = int(totalBytes * decompressor.getProgress()) - self.__updateStepProgress(step) - result = decompressor.run() - if taskMgr.destroyed: - # If the task manager has been destroyed, we must - # be shutting down. Get out of here. - self.notify.warning("Task Manager destroyed, aborting decompresss %s" % (sourcePathname)) - yield self.stepFailed; return - - yield self.stepContinue - - if result != EUSuccess: - yield self.stepFailed; return - - step.bytesDone = totalBytes - self.__updateStepProgress(step) - - if not self.uncompressedArchive.quickVerify(self.getPackageDir(), notify= self.notify): - self.notify.warning("after uncompressing, %s still incorrect" % ( - self.uncompressedArchive.filename)) - yield self.stepFailed; return - - # Now that we've verified the archive, make it read-only. - os.chmod(targetPathname.toOsSpecific(), 0o444) - - # Now we can safely remove the compressed archive. - sourcePathname.unlink() - yield self.stepComplete; return - - def __unpackArchive(self, step): - """ Unpacks any files in the archive that want to be unpacked - to disk. Yields one of stepComplete, stepFailed, - restartDownload, or stepContinue. """ - - if not self.extracts: - # Nothing to extract. - self.hasPackage = True - yield self.stepComplete; return - - if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever: - # We're not allowed to! - yield self.stepFailed; return - - self.updated = True - - mfPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename) - self.notify.info("Unpacking %s" % (mfPathname)) - mf = Multifile() - if not mf.openRead(mfPathname): - self.notify.warning("Couldn't open %s" % (mfPathname)) - yield self.stepFailed; return - - allExtractsOk = True - step.bytesDone = 0 - for file in self.extracts: - i = mf.findSubfile(file.filename) - if i == -1: - self.notify.warning("Not in Multifile: %s" % (file.filename)) - allExtractsOk = False - continue - - targetPathname = Filename(self.getPackageDir(), file.filename) - targetPathname.setBinary() - targetPathname.unlink() - if not mf.extractSubfile(i, targetPathname): - self.notify.warning("Couldn't extract: %s" % (file.filename)) - allExtractsOk = False - continue - - if not file.quickVerify(self.getPackageDir(), notify = self.notify): - self.notify.warning("After extracting, still incorrect: %s" % (file.filename)) - allExtractsOk = False - continue - - # Make sure it's executable, and not writable. - os.chmod(targetPathname.toOsSpecific(), 0o555) - - step.bytesDone += file.size - self.__updateStepProgress(step) - if taskMgr.destroyed: - # If the task manager has been destroyed, we must - # be shutting down. Get out of here. - self.notify.warning("Task Manager destroyed, aborting unpacking %s" % (mfPathname)) - yield self.stepFailed; return - - yield self.stepContinue - - if not allExtractsOk: - yield self.stepFailed; return - - self.hasPackage = True - yield self.stepComplete; return - - def installPackage(self, appRunner): - """ Mounts the package and sets up system paths so it becomes - available for use. Returns true on success, false on failure. """ - - assert self.hasPackage - if self.installed: - # Already installed. - return True - assert self not in appRunner.installedPackages - - mfPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename) - mf = Multifile() - if not mf.openRead(mfPathname): - self.notify.warning("Couldn't open %s" % (mfPathname)) - return False - - # We mount it under its actual location on disk. - root = self.getPackageDir() - - vfs = VirtualFileSystem.getGlobalPtr() - vfs.mount(mf, root, vfs.MFReadOnly) - - # Add this to the Python search path, if it's not already - # there. We have to take a bit of care to check if it's - # already there, since there can be some ambiguity in - # os-specific path strings. - osRoot = self.getPackageDir().toOsSpecific() - foundOnPath = False - for p in sys.path: - if osRoot == p: - # Already here, exactly. - foundOnPath = True - break - elif osRoot == Filename.fromOsSpecific(p).toOsSpecific(): - # Already here, with some futzing. - foundOnPath = True - break - - if not foundOnPath: - # Not already here; add it. - sys.path.append(osRoot) - - # Put it on the model-path, too. We do this indiscriminantly, - # because the Panda3D runtime won't be adding things to the - # model-path, so it shouldn't be already there. - getModelPath().appendDirectory(self.getPackageDir()) - - # Set the environment variable to reference the package root. - envvar = '%s_ROOT' % (self.packageName.upper()) - ExecutionEnvironment.setEnvironmentVariable(envvar, osRoot) - - # Add the package root to the system paths. - if sys.platform.startswith('win'): - path = os.environ.get('PATH', '') - os.environ['PATH'] = "%s;%s" % (osRoot, path) - else: - path = os.environ.get('PATH', '') - os.environ['PATH'] = "%s:%s" % (osRoot, path) - path = os.environ.get('LD_LIBRARY_PATH', '') - os.environ['LD_LIBRARY_PATH'] = "%s:%s" % (osRoot, path) - - if sys.platform == "darwin": - path = os.environ.get('DYLD_LIBRARY_PATH', '') - os.environ['DYLD_LIBRARY_PATH'] = "%s:%s" % (osRoot, path) - - # Now that the environment variable is set, read all of the - # prc files in the package. - appRunner.loadMultifilePrcFiles(mf, self.getPackageDir()) - - # Also, find any toplevel Python packages, and add these as - # shared packages. This will allow different packages - # installed in different directories to share Python files as - # if they were all in the same directory. - for filename in mf.getSubfileNames(): - if filename.endswith('/__init__.pyc') or \ - filename.endswith('/__init__.pyo') or \ - filename.endswith('/__init__.py'): - components = filename.split('/')[:-1] - moduleName = '.'.join(components) - VFSImporter.sharedPackages[moduleName] = True - - # Fix up any shared directories so we can load packages from - # disparate locations. - VFSImporter.reloadSharedPackages() - - self.installed = True - appRunner.installedPackages.append(self) - - self.markUsed() - - return True - - def __measureDiskSpace(self): - """ Returns the amount of space used by this package, in - bytes, as determined by examining the actual contents of the - package directory and its subdirectories. """ - - thisDir = ScanDirectoryNode(self.getPackageDir(), ignoreUsageXml = True) - diskSpace = thisDir.getTotalSize() - self.notify.info("Package %s uses %s MB" % ( - self.packageName, (diskSpace + 524288) // 1048576)) - return diskSpace - - def markUsed(self): - """ Marks the package as having been used. This is normally - called automatically by installPackage(). """ - - if not hasattr(core, 'TiXmlDocument'): - return - - if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever: - # Not allowed to write any files to the package directory. - return - - if self.updated: - # If we've just installed a new version of the package, - # re-measure the actual disk space used. - self.diskSpace = self.__measureDiskSpace() - - filename = Filename(self.getPackageDir(), self.UsageBasename) - doc = TiXmlDocument(filename.toOsSpecific()) - if not doc.LoadFile(): - decl = TiXmlDeclaration("1.0", "utf-8", "") - doc.InsertEndChild(decl) - - xusage = doc.FirstChildElement('usage') - if not xusage: - doc.InsertEndChild(TiXmlElement('usage')) - xusage = doc.FirstChildElement('usage') - - now = int(time.time()) - - count = xusage.Attribute('count_app') - try: - count = int(count or '') - except ValueError: - count = 0 - xusage.SetAttribute('first_use', str(now)) - count += 1 - xusage.SetAttribute('count_app', str(count)) - - xusage.SetAttribute('last_use', str(now)) - - if self.updated: - xusage.SetAttribute('last_update', str(now)) - self.updated = False - else: - # Since we haven't changed the disk space, we can just - # read it from the previous xml file. - diskSpace = xusage.Attribute('disk_space') - try: - diskSpace = int(diskSpace or '') - except ValueError: - # Unless it wasn't set already. - self.diskSpace = self.__measureDiskSpace() - - xusage.SetAttribute('disk_space', str(self.diskSpace)) - - # Write the file to a temporary filename, then atomically move - # it to its actual filename, to avoid race conditions when - # updating this file. - tfile = Filename.temporary(str(self.getPackageDir()), '.xml') - if doc.SaveFile(tfile.toOsSpecific()): - tfile.renameTo(filename) - - def getUsage(self): - """ Returns the xusage element that is read from the usage.xml - file, or None if there is no usage.xml file. """ - - if not hasattr(core, 'TiXmlDocument'): - return None - - filename = Filename(self.getPackageDir(), self.UsageBasename) - doc = TiXmlDocument(filename.toOsSpecific()) - if not doc.LoadFile(): - return None - - xusage = doc.FirstChildElement('usage') - if not xusage: - return None - - return copy.copy(xusage) - diff --git a/direct/src/p3d/PackageInstaller.py b/direct/src/p3d/PackageInstaller.py deleted file mode 100644 index cec1e07b11..0000000000 --- a/direct/src/p3d/PackageInstaller.py +++ /dev/null @@ -1,640 +0,0 @@ -__all__ = ["PackageInstaller"] - -from direct.showbase.DirectObject import DirectObject -from direct.stdpy.threading import Lock, RLock -from direct.showbase.MessengerGlobal import messenger -from direct.task.TaskManagerGlobal import taskMgr -from direct.p3d.PackageInfo import PackageInfo -from panda3d.core import TPLow, PStatCollector -from direct.directnotify.DirectNotifyGlobal import directNotify - -class PackageInstaller(DirectObject): - - """ This class is used in a p3d runtime environment to manage the - asynchronous download and installation of packages. If you just - want to install a package synchronously, see - appRunner.installPackage() for a simpler interface. - - To use this class, you should subclass from it and override any of - the six callback methods: downloadStarted(), packageStarted(), - packageProgress(), downloadProgress(), packageFinished(), - downloadFinished(). - - Also see DWBPackageInstaller, which does exactly this, to add a - DirectWaitBar GUI. - - """ - - notify = directNotify.newCategory("PackageInstaller") - - globalLock = Lock() - nextUniqueId = 1 - - # This is a chain of state values progressing forward in time. - S_initial = 0 # addPackage() calls are being made - S_ready = 1 # donePackages() has been called - S_started = 2 # download has started - S_done = 3 # download is over - - class PendingPackage: - """ This class describes a package added to the installer for - download. """ - - notify = directNotify.newCategory("PendingPackage") - - def __init__(self, packageName, version, host): - self.packageName = packageName - self.version = version - self.host = host - - # This will be filled in properly by checkDescFile() or - # getDescFile(); in the meantime, set a placeholder. - self.package = PackageInfo(host, packageName, version) - - # Set true when the package has finished downloading, - # either successfully or unsuccessfully. - self.done = False - - # Set true or false when self.done has been set. - self.success = False - - # Set true when the packageFinished() callback has been - # delivered. - self.notified = False - - # These are used to ensure the callbacks only get - # delivered once for a particular package. - self.calledPackageStarted = False - self.calledPackageFinished = False - - # This is the amount of stuff we have to process to - # install this package, and the amount of stuff we have - # processed so far. "Stuff" includes bytes downloaded, - # bytes uncompressed, and bytes extracted; and each of - # which is weighted differently into one grand total. So, - # the total doesn't really represent bytes; it's a - # unitless number, which means something only as a ratio - # to other packages. Filled in by checkDescFile() or - # getDescFile(). - self.downloadEffort = 0 - - # Similar, but this is the theoretical effort if the - # package were already downloaded. - self.prevDownloadedEffort = 0 - - def __cmp__(self, pp): - """ Python comparision function. This makes all - PendingPackages withe same (packageName, version, host) - combination be deemed equivalent. """ - return cmp((self.packageName, self.version, self.host), - (pp.packageName, pp.version, pp.host)) - - def getProgress(self): - """ Returns the download progress of this package in the - range 0..1. """ - - return self.package.downloadProgress - - def checkDescFile(self): - """ Returns true if the desc file is already downloaded - and good, or false if it needs to be downloaded. """ - - if not self.host.hasCurrentContentsFile(): - # If the contents file isn't ready yet, we can't check - # the desc file yet. - return False - - # All right, get the package info now. - package = self.host.getPackage(self.packageName, self.version) - if not package: - self.notify.warning("Package %s %s not known on %s" % ( - self.packageName, self.version, self.host.hostUrl)) - return False - - self.package = package - self.package.checkStatus() - - if not self.package.hasDescFile: - return False - - self.downloadEffort = self.package.getDownloadEffort() - self.prevDownloadEffort = 0 - if self.downloadEffort == 0: - self.prevDownloadedEffort = self.package.getPrevDownloadedEffort() - - return True - - - def getDescFile(self, http): - """ Synchronously downloads the desc files required for - the package. """ - - if not self.host.downloadContentsFile(http): - return False - - # All right, get the package info now. - package = self.host.getPackage(self.packageName, self.version) - if not package: - self.notify.warning("Package %s %s not known on %s" % ( - self.packageName, self.version, self.host.hostUrl)) - return False - - self.package = package - if not self.package.downloadDescFile(http): - return False - - self.package.checkStatus() - self.downloadEffort = self.package.getDownloadEffort() - self.prevDownloadEffort = 0 - if self.downloadEffort == 0: - self.prevDownloadedEffort = self.package.getPrevDownloadedEffort() - - return True - - def __init__(self, appRunner, taskChain = 'default'): - self.globalLock.acquire() - try: - self.uniqueId = PackageInstaller.nextUniqueId - PackageInstaller.nextUniqueId += 1 - finally: - self.globalLock.release() - - self.appRunner = appRunner - self.taskChain = taskChain - - # If we're to be running on an asynchronous task chain, and - # the task chain hasn't yet been set up already, create the - # default parameters now. - if taskChain != 'default' and not taskMgr.hasTaskChain(self.taskChain): - taskMgr.setupTaskChain(self.taskChain, numThreads = 1, - threadPriority = TPLow) - - self.callbackLock = Lock() - self.calledDownloadStarted = False - self.calledDownloadFinished = False - - # A list of all packages that have been added to the - # installer. - self.packageLock = RLock() - self.packages = [] - self.state = self.S_initial - - # A list of packages that are waiting for their desc files. - self.needsDescFile = [] - self.descFileTask = None - - # A list of packages that are waiting to be downloaded and - # installed. - self.needsDownload = [] - self.downloadTask = None - - # A list of packages that were already done at the time they - # were passed to addPackage(). - self.earlyDone = [] - - # A list of packages that have been successfully installed, or - # packages that have failed. - self.done = [] - self.failed = [] - - # This task is spawned on the default task chain, to update - # the status during the download. - self.progressTask = None - - self.accept('PackageInstaller-%s-allHaveDesc' % self.uniqueId, - self.__allHaveDesc) - self.accept('PackageInstaller-%s-packageStarted' % self.uniqueId, - self.__packageStarted) - self.accept('PackageInstaller-%s-packageDone' % self.uniqueId, - self.__packageDone) - - def destroy(self): - """ Interrupts all pending downloads. No further callbacks - will be made. """ - self.cleanup() - - def cleanup(self): - """ Interrupts all pending downloads. No further callbacks - will be made. """ - - self.packageLock.acquire() - try: - if self.descFileTask: - taskMgr.remove(self.descFileTask) - self.descFileTask = None - if self.downloadTask: - taskMgr.remove(self.downloadTask) - self.downloadTask = None - finally: - self.packageLock.release() - - if self.progressTask: - taskMgr.remove(self.progressTask) - self.progressTask = None - - self.ignoreAll() - - def addPackage(self, packageName, version = None, hostUrl = None): - """ Adds the named package to the list of packages to be - downloaded. Call donePackages() to finish the list. """ - - if self.state != self.S_initial: - raise ValueError('addPackage called after donePackages') - - host = self.appRunner.getHostWithAlt(hostUrl) - pp = self.PendingPackage(packageName, version, host) - - self.packageLock.acquire() - try: - self.__internalAddPackage(pp) - finally: - self.packageLock.release() - - def __internalAddPackage(self, pp): - """ Adds the indicated "pending package" to the appropriate - list(s) for downloading and installing. Assumes packageLock - is already held.""" - - if pp in self.packages: - # Already added. - return - - self.packages.append(pp) - - # We always add the package to needsDescFile, even if we - # already have its desc file; this guarantees that packages - # are downloaded in the order they are added. - self.needsDescFile.append(pp) - if not self.descFileTask: - self.descFileTask = taskMgr.add( - self.__getDescFileTask, 'getDescFile', - taskChain = self.taskChain) - - def donePackages(self): - """ After calling addPackage() for each package to be - installed, call donePackages() to mark the end of the list. - This is necessary to determine what the complete set of - packages is (and therefore how large the total download size - is). None of the low-level callbacks will be made before this - call. """ - - if self.state != self.S_initial: - # We've already been here. - return - - # Throw the messages for packages that were already done - # before we started. - for pp in self.earlyDone: - self.__donePackage(pp, True) - self.earlyDone = [] - - self.packageLock.acquire() - try: - if self.state != self.S_initial: - return - self.state = self.S_ready - if not self.needsDescFile: - # All package desc files are already available; so begin. - self.__prepareToStart() - finally: - self.packageLock.release() - - if not self.packages: - # Trivial no-op. - self.__callDownloadFinished(True) - - def downloadStarted(self): - """ This callback is made at some point after donePackages() - is called; at the time of this callback, the total download - size is known, and we can sensibly report progress through the - whole. """ - - self.notify.info("downloadStarted") - - def packageStarted(self, package): - """ This callback is made for each package between - downloadStarted() and downloadFinished() to indicate the start - of a new package. """ - - self.notify.debug("packageStarted: %s" % (package.packageName)) - - def packageProgress(self, package, progress): - """ This callback is made repeatedly between packageStarted() - and packageFinished() to update the current progress on the - indicated package only. The progress value ranges from 0 - (beginning) to 1 (complete). """ - - self.notify.debug("packageProgress: %s %s" % (package.packageName, progress)) - - def downloadProgress(self, overallProgress): - """ This callback is made repeatedly between downloadStarted() - and downloadFinished() to update the current progress through - all packages. The progress value ranges from 0 (beginning) to - 1 (complete). """ - - self.notify.debug("downloadProgress: %s" % (overallProgress)) - - def packageFinished(self, package, success): - """ This callback is made for each package between - downloadStarted() and downloadFinished() to indicate that a - package has finished downloading. If success is true, there - were no problems and the package is now installed. - - If this package did not require downloading (because it was - already downloaded), this callback will be made immediately, - *without* a corresponding call to packageStarted(), and may - even be made before downloadStarted(). """ - - self.notify.info("packageFinished: %s %s" % (package.packageName, success)) - - def downloadFinished(self, success): - """ This callback is made when all of the packages have been - downloaded and installed (or there has been some failure). If - all packages where successfully installed, success is True. - - If there were no packages that required downloading, this - callback will be made immediately, *without* a corresponding - call to downloadStarted(). """ - - self.notify.info("downloadFinished: %s" % (success)) - - def __prepareToStart(self): - """ This is called internally when transitioning from S_ready - to S_started. It sets up whatever initial values are - needed. Assumes self.packageLock is held. Returns False if - there were no packages to download, and the state was - therefore transitioned immediately to S_done. """ - - if not self.needsDownload: - self.state = self.S_done - return False - - self.state = self.S_started - - assert not self.downloadTask - self.downloadTask = taskMgr.add( - self.__downloadPackageTask, 'downloadPackage', - taskChain = self.taskChain) - - assert not self.progressTask - self.progressTask = taskMgr.add( - self.__progressTask, 'packageProgress') - - return True - - def __allHaveDesc(self): - """ This method is called internally when all of the pending - packages have their desc info. """ - working = True - - self.packageLock.acquire() - try: - if self.state == self.S_ready: - # We've already called donePackages(), so move on now. - working = self.__prepareToStart() - finally: - self.packageLock.release() - - if not working: - self.__callDownloadFinished(True) - - def __packageStarted(self, pp): - """ This method is called when a single package is beginning - to download. """ - - self.__callDownloadStarted() - self.__callPackageStarted(pp) - - def __packageDone(self, pp): - """ This method is called when a single package has been - downloaded and installed, or has failed. """ - - self.__callPackageFinished(pp, pp.success) - pp.notified = True - - # See if there are more packages to go. - success = True - allDone = True - self.packageLock.acquire() - try: - for pp in self.packages: - if pp.notified: - success = success and pp.success - else: - allDone = False - finally: - self.packageLock.release() - - if allDone: - self.__callDownloadFinished(success) - - def __callPackageStarted(self, pp): - """ Calls the packageStarted() callback for a particular - package if it has not already been called, being careful to - avoid race conditions. """ - - self.callbackLock.acquire() - try: - if not pp.calledPackageStarted: - self.packageStarted(pp.package) - self.packageProgress(pp.package, 0) - pp.calledPackageStarted = True - finally: - self.callbackLock.release() - - def __callPackageFinished(self, pp, success): - """ Calls the packageFinished() callback for a paricular - package if it has not already been called, being careful to - avoid race conditions. """ - - self.callbackLock.acquire() - try: - if not pp.calledPackageFinished: - if success: - self.packageProgress(pp.package, 1) - self.packageFinished(pp.package, success) - pp.calledPackageFinished = True - finally: - self.callbackLock.release() - - def __callDownloadStarted(self): - """ Calls the downloadStarted() callback if it has not already - been called, being careful to avoid race conditions. """ - - self.callbackLock.acquire() - try: - if not self.calledDownloadStarted: - self.downloadStarted() - self.downloadProgress(0) - self.calledDownloadStarted = True - finally: - self.callbackLock.release() - - def __callDownloadFinished(self, success): - """ Calls the downloadFinished() callback if it has not - already been called, being careful to avoid race - conditions. """ - - self.callbackLock.acquire() - try: - if not self.calledDownloadFinished: - if success: - self.downloadProgress(1) - self.downloadFinished(success) - self.calledDownloadFinished = True - finally: - self.callbackLock.release() - - def __getDescFileTask(self, task): - - """ This task runs on the aysynchronous task chain; each pass, - it extracts one package from self.needsDescFile and downloads - its desc file. On success, it adds the package to - self.needsDownload. """ - - self.packageLock.acquire() - try: - # If we've finished all of the packages that need desc - # files, stop the task. - if not self.needsDescFile: - self.descFileTask = None - - eventName = 'PackageInstaller-%s-allHaveDesc' % self.uniqueId - messenger.send(eventName, taskChain = 'default') - - return task.done - pp = self.needsDescFile[0] - del self.needsDescFile[0] - finally: - self.packageLock.release() - - # Now serve this one package. - if not pp.checkDescFile(): - if not pp.getDescFile(self.appRunner.http): - self.__donePackage(pp, False) - return task.cont - - # This package is now ready to be downloaded. We always add - # it to needsDownload, even if it's already downloaded, to - # guarantee ordering of packages. - - self.packageLock.acquire() - try: - # Also add any packages required by this one. - for packageName, version, host in pp.package.requires: - pp2 = self.PendingPackage(packageName, version, host) - self.__internalAddPackage(pp2) - self.needsDownload.append(pp) - finally: - self.packageLock.release() - - return task.cont - - def __downloadPackageTask(self, task): - - """ This task runs on the aysynchronous task chain; each pass, - it extracts one package from self.needsDownload and downloads - it. """ - - while True: - self.packageLock.acquire() - try: - # If we're done downloading, stop the task. - if self.state == self.S_done or not self.needsDownload: - self.downloadTask = None - self.packageLock.release() - yield task.done; return - - assert self.state == self.S_started - pp = self.needsDownload[0] - del self.needsDownload[0] - except: - self.packageLock.release() - raise - self.packageLock.release() - - # Now serve this one package. - eventName = 'PackageInstaller-%s-packageStarted' % self.uniqueId - messenger.send(eventName, [pp], taskChain = 'default') - - if not pp.package.hasPackage: - for token in pp.package.downloadPackageGenerator(self.appRunner.http): - if token == pp.package.stepContinue: - yield task.cont - else: - break - - if token != pp.package.stepComplete: - pc = PStatCollector(':App:PackageInstaller:donePackage:%s' % (pp.package.packageName)) - pc.start() - self.__donePackage(pp, False) - pc.stop() - yield task.cont - continue - - # Successfully downloaded and installed. - pc = PStatCollector(':App:PackageInstaller:donePackage:%s' % (pp.package.packageName)) - pc.start() - self.__donePackage(pp, True) - pc.stop() - - # Continue the loop without yielding, so we pick up the - # next package within this same frame. - - def __donePackage(self, pp, success): - """ Marks the indicated package as done, either successfully - or otherwise. """ - assert not pp.done - - if success: - pc = PStatCollector(':App:PackageInstaller:install:%s' % (pp.package.packageName)) - pc.start() - pp.package.installPackage(self.appRunner) - pc.stop() - - self.packageLock.acquire() - try: - pp.done = True - pp.success = success - if success: - self.done.append(pp) - else: - self.failed.append(pp) - finally: - self.packageLock.release() - - eventName = 'PackageInstaller-%s-packageDone' % self.uniqueId - messenger.send(eventName, [pp], taskChain = 'default') - - def __progressTask(self, task): - self.callbackLock.acquire() - try: - if not self.calledDownloadStarted: - # We haven't yet officially started the download. - return task.cont - - if self.calledDownloadFinished: - # We've officially ended the download. - self.progressTask = None - return task.done - - downloadEffort = 0 - currentDownloadSize = 0 - for pp in self.packages: - downloadEffort += pp.downloadEffort + pp.prevDownloadedEffort - packageProgress = pp.getProgress() - currentDownloadSize += pp.downloadEffort * packageProgress + pp.prevDownloadedEffort - if pp.calledPackageStarted and not pp.calledPackageFinished: - self.packageProgress(pp.package, packageProgress) - - if not downloadEffort: - progress = 1 - else: - progress = float(currentDownloadSize) / float(downloadEffort) - self.downloadProgress(progress) - - finally: - self.callbackLock.release() - - return task.cont - diff --git a/direct/src/p3d/PackageMerger.py b/direct/src/p3d/PackageMerger.py deleted file mode 100644 index a22d8f5f2d..0000000000 --- a/direct/src/p3d/PackageMerger.py +++ /dev/null @@ -1,306 +0,0 @@ -__all__ = ["PackageMerger", "PackageMergerError"] - -from direct.p3d.FileSpec import FileSpec -from direct.p3d.SeqValue import SeqValue -from direct.directnotify.DirectNotifyGlobal import * -from panda3d.core import * -import shutil -import os - -class PackageMergerError(Exception): - pass - -class PackageMerger: - """ This class will combine two or more separately-built stage - directories, the output of Packager.py or the ppackage tool, into - a single output directory. It assumes that the clocks on all - hosts are in sync, so that the file across all builds with the - most recent timestamp (indicated in the contents.xml file) is - always the most current version of the file. """ - - notify = directNotify.newCategory("PackageMerger") - - class PackageEntry: - """ This corresponds to a entry in the contents.xml - file. """ - - def __init__(self, xpackage, sourceDir): - self.sourceDir = sourceDir - self.loadXml(xpackage) - - def getKey(self): - """ Returns a tuple used for sorting the PackageEntry - objects uniquely per package. """ - return (self.packageName, self.platform, self.version) - - def isNewer(self, other): - return self.descFile.timestamp > other.descFile.timestamp - - def loadXml(self, xpackage): - self.packageName = xpackage.Attribute('name') - self.platform = xpackage.Attribute('platform') - self.version = xpackage.Attribute('version') - solo = xpackage.Attribute('solo') - self.solo = int(solo or '0') - perPlatform = xpackage.Attribute('per_platform') - self.perPlatform = int(perPlatform or '0') - - self.descFile = FileSpec() - self.descFile.loadXml(xpackage) - - self.validatePackageContents() - - self.descFile.quickVerify(packageDir = self.sourceDir, notify = PackageMerger.notify, correctSelf = True) - - self.packageSeq = SeqValue() - self.packageSeq.loadXml(xpackage, 'seq') - self.packageSetVer = SeqValue() - self.packageSetVer.loadXml(xpackage, 'set_ver') - - self.importDescFile = None - ximport = xpackage.FirstChildElement('import') - if ximport: - self.importDescFile = FileSpec() - self.importDescFile.loadXml(ximport) - self.importDescFile.quickVerify(packageDir = self.sourceDir, notify = PackageMerger.notify, correctSelf = True) - - def makeXml(self): - """ Returns a new TiXmlElement. """ - xpackage = TiXmlElement('package') - xpackage.SetAttribute('name', self.packageName) - if self.platform: - xpackage.SetAttribute('platform', self.platform) - if self.version: - xpackage.SetAttribute('version', self.version) - if self.solo: - xpackage.SetAttribute('solo', '1') - if self.perPlatform: - xpackage.SetAttribute('per_platform', '1') - - self.descFile.storeXml(xpackage) - self.packageSeq.storeXml(xpackage, 'seq') - self.packageSetVer.storeXml(xpackage, 'set_ver') - - if self.importDescFile: - ximport = TiXmlElement('import') - self.importDescFile.storeXml(ximport) - xpackage.InsertEndChild(ximport) - - return xpackage - - def validatePackageContents(self): - """ Validates the contents of the package directory itself - against the expected hashes and timestamps. Updates - hashes and timestamps where needed. """ - - if self.solo: - return - - needsChange = False - packageDescFullpath = Filename(self.sourceDir, self.descFile.filename) - packageDir = Filename(packageDescFullpath.getDirname()) - doc = TiXmlDocument(packageDescFullpath.toOsSpecific()) - if not doc.LoadFile(): - message = "Could not read XML file: %s" % (self.descFile.filename) - raise OSError(message) - - xpackage = doc.FirstChildElement('package') - if not xpackage: - message = "No package definition: %s" % (self.descFile.filename) - raise OSError(message) - - xcompressed = xpackage.FirstChildElement('compressed_archive') - if xcompressed: - spec = FileSpec() - spec.loadXml(xcompressed) - if not spec.quickVerify(packageDir = packageDir, notify = PackageMerger.notify, correctSelf = True): - spec.storeXml(xcompressed) - needsChange = True - - xpatch = xpackage.FirstChildElement('patch') - while xpatch: - spec = FileSpec() - spec.loadXml(xpatch) - if not spec.quickVerify(packageDir = packageDir, notify = PackageMerger.notify, correctSelf = True): - spec.storeXml(xpatch) - needsChange = True - - xpatch = xpatch.NextSiblingElement('patch') - - if needsChange: - PackageMerger.notify.info("Rewriting %s" % (self.descFile.filename)) - doc.SaveFile() - self.descFile.quickVerify(packageDir = self.sourceDir, notify = PackageMerger.notify, correctSelf = True) - - # PackageMerger constructor - def __init__(self, installDir): - self.installDir = installDir - self.xhost = None - self.contents = {} - self.maxAge = None - self.contentsSeq = SeqValue() - - # We allow the first one to fail quietly. - self.__readContentsFile(self.installDir, None) - - def __readContentsFile(self, sourceDir, packageNames): - """ Reads the contents.xml file from the indicated sourceDir, - and updates the internal set of packages appropriately. """ - - assert sourceDir != None, "No source directory was specified!" - contentsFilename = Filename(sourceDir, 'contents.xml') - doc = TiXmlDocument(contentsFilename.toOsSpecific()) - if not doc.LoadFile(): - # Couldn't read file. - return False - - xcontents = doc.FirstChildElement('contents') - if xcontents: - maxAge = xcontents.Attribute('max_age') - if maxAge: - maxAge = int(maxAge) - if self.maxAge is None: - self.maxAge = maxAge - else: - self.maxAge = min(self.maxAge, maxAge) - - contentsSeq = SeqValue() - if contentsSeq.loadXml(xcontents): - self.contentsSeq = max(self.contentsSeq, contentsSeq) - - xhost = xcontents.FirstChildElement('host') - if xhost: - self.xhost = xhost.Clone() - - xpackage = xcontents.FirstChildElement('package') - while xpackage: - pe = self.PackageEntry(xpackage, sourceDir) - - # Filter out any packages not listed in - # packageNames (unless packageNames is None, - # in which case don't filter anything). - if packageNames is None or pe.packageName in packageNames: - other = self.contents.get(pe.getKey(), None) - if not other or pe.isNewer(other): - # Store this package in the resulting output. - self.contents[pe.getKey()] = pe - - xpackage = xpackage.NextSiblingElement('package') - - self.contentsDoc = doc - - return True - - def __writeContentsFile(self): - """ Writes the contents.xml file at the end of processing. """ - - filename = Filename(self.installDir, 'contents.xml') - doc = TiXmlDocument(filename.toOsSpecific()) - decl = TiXmlDeclaration("1.0", "utf-8", "") - doc.InsertEndChild(decl) - - xcontents = TiXmlElement('contents') - if self.xhost: - xcontents.InsertEndChild(self.xhost) - - if self.maxAge is not None: - xcontents.SetAttribute('max_age', str(self.maxAge)) - self.contentsSeq.storeXml(xcontents) - - contents = list(self.contents.items()) - contents.sort() - for key, pe in contents: - xpackage = pe.makeXml() - xcontents.InsertEndChild(xpackage) - - doc.InsertEndChild(xcontents) - doc.SaveFile() - - def __copySubdirectory(self, pe): - """ Copies the subdirectory referenced in the indicated - PackageEntry object into the installDir, replacing the - contents of any similarly-named subdirectory already - there. """ - - dirname = Filename(pe.descFile.filename).getDirname() - self.notify.info("copying %s" % (dirname)) - sourceDirname = Filename(pe.sourceDir, dirname) - targetDirname = Filename(self.installDir, dirname) - - self.__rCopyTree(sourceDirname, targetDirname) - - def __rCopyTree(self, sourceFilename, targetFilename): - """ Recursively copies the contents of sourceDirname onto - targetDirname. This behaves like shutil.copytree, but it does - not remove pre-existing subdirectories. """ - - if targetFilename.exists(): - if not targetFilename.isDirectory(): - # Delete any regular files in the way. - targetFilename.unlink() - - elif not sourceFilename.isDirectory(): - # If the source file is a regular file, but the target - # file is a directory, completely remove the target - # file. - shutil.rmtree(targetFilename.toOsSpecific()) - - else: - # Both the source file and target file are - # directories. - - # We have to clean out the target directory first. - # Instead of using shutil.rmtree(), remove the files in - # this directory one at a time, so we don't inadvertently - # clean out subdirectories too. - files = os.listdir(targetFilename.toOsSpecific()) - for file in files: - f = Filename(targetFilename, file) - if f.isRegularFile(): - f.unlink() - - if sourceFilename.isDirectory(): - # Recursively copying a directory. - Filename(targetFilename, '').makeDir() - files = os.listdir(sourceFilename.toOsSpecific()) - for file in files: - self.__rCopyTree(Filename(sourceFilename, file), - Filename(targetFilename, file)) - else: - # Copying a regular file. - sourceFilename.copyTo(targetFilename) - - # Also try to copy the timestamp, but don't fuss too much - # if it doesn't work. - try: - st = os.stat(sourceFilename.toOsSpecific()) - os.utime(targetFilename.toOsSpecific(), (st.st_atime, st.st_mtime)) - except OSError: - pass - - def merge(self, sourceDir, packageNames = None): - """ Adds the contents of the indicated source directory into - the current pool. If packageNames is not None, it is a list - of package names that we wish to include from the source; - packages not named in this list will be unchanged. """ - - if not self.__readContentsFile(sourceDir, packageNames): - message = "Couldn't read %s" % (sourceDir) - raise PackageMergerError(message) - - def close(self): - """ Finalizes the results of all of the previous calls to - merge(), writes the new contents.xml file, and copies in all - of the new contents. """ - - dirname = Filename(self.installDir, '') - dirname.makeDir() - - for pe in self.contents.values(): - if pe.sourceDir != self.installDir: - # Here's a new subdirectory we have to copy in. - self.__copySubdirectory(pe) - - self.contentsSeq += 1 - self.__writeContentsFile() - diff --git a/direct/src/p3d/Packager.py b/direct/src/p3d/Packager.py deleted file mode 100644 index 4e8a303f7f..0000000000 --- a/direct/src/p3d/Packager.py +++ /dev/null @@ -1,3962 +0,0 @@ -""" This module is used to build a "Package", a collection of files -within a Panda3D Multifile, which can be easily be downloaded and/or -patched onto a client machine, for the purpose of running a large -application. """ - -__all__ = ["Packager", "PackagerError", "OutsideOfPackageError", "ArgumentError"] - -# Important to import panda3d first, to avoid naming conflicts with -# Python's "string" and "Loader" names that are imported later. -from panda3d.core import * -import sys -import os -import glob -import struct -import subprocess -import copy -from direct.p3d.FileSpec import FileSpec -from direct.p3d.SeqValue import SeqValue -from direct.p3d.HostInfo import HostInfo -from direct.showbase import Loader -from direct.showbase import AppRunnerGlobal -from direct.dist import FreezeTool -from direct.directnotify.DirectNotifyGlobal import * - -vfs = VirtualFileSystem.getGlobalPtr() - -class PackagerError(Exception): - pass - -class OutsideOfPackageError(PackagerError): - pass - -class ArgumentError(PackagerError): - pass - -class Packager: - notify = directNotify.newCategory("Packager") - - class PackFile: - def __init__(self, package, filename, - newName = None, deleteTemp = False, - explicit = False, compress = None, extract = None, - text = None, unprocessed = None, - executable = None, dependencyDir = None, - platformSpecific = None, required = False): - assert isinstance(filename, Filename) - self.filename = Filename(filename) - self.newName = newName - self.deleteTemp = deleteTemp - self.explicit = explicit - self.compress = compress - self.extract = extract - self.text = text - self.unprocessed = unprocessed - self.executable = executable - self.dependencyDir = dependencyDir - self.platformSpecific = platformSpecific - self.required = required - - if not self.newName: - self.newName = str(self.filename) - - ext = Filename(self.newName).getExtension() - if ext == 'pz' or ext == 'gz': - # Strip off a .pz extension; we can compress files - # within the Multifile without it. - filename = Filename(self.newName) - filename.setExtension('') - self.newName = str(filename) - ext = Filename(self.newName).getExtension() - if self.compress is None: - self.compress = True - - packager = package.packager - if self.compress is None: - self.compress = (ext not in packager.uncompressibleExtensions and ext not in packager.imageExtensions) - - if self.executable is None: - self.executable = (ext in packager.executableExtensions) - - if self.executable and self.dependencyDir is None: - # By default, install executable dependencies in the - # root directory, which is the one that's added to PATH. - self.dependencyDir = '' - - if self.extract is None: - self.extract = self.executable or (ext in packager.extractExtensions) - if self.platformSpecific is None: - self.platformSpecific = self.executable or (ext in packager.platformSpecificExtensions) - - if self.unprocessed is None: - self.unprocessed = self.executable or (ext in packager.unprocessedExtensions) - - if self.executable: - # Look up the filename along the system PATH, if necessary. - if not packager.resolveLibrary(self.filename): - # If it wasn't found, try looking it up under its - # basename only. Sometimes a Mac user will copy - # the library file out of a framework and put that - # along the PATH, instead of the framework itself. - basename = Filename(self.filename.getBasename()) - if packager.resolveLibrary(basename): - self.filename = basename - - if ext in packager.textExtensions and not self.executable: - self.filename.setText() - else: - self.filename.setBinary() - - # Convert the filename to an unambiguous filename for - # searching. - self.filename.makeTrueCase() - if self.filename.exists() or not self.filename.isLocal(): - self.filename.makeCanonical() - - def isExcluded(self, package): - """ Returns true if this file should be excluded or - skipped, false otherwise. """ - - if self.newName.lower() in package.skipFilenames: - return True - - if not self.explicit: - # Make sure it's not one of our auto-excluded system - # files. (But only make this check if this file was - # not explicitly added.) - - basename = Filename(self.newName).getBasename() - if not package.packager.caseSensitive: - basename = basename.lower() - if basename in package.packager.excludeSystemFiles: - return True - for exclude in package.packager.excludeSystemGlobs: - if exclude.matches(basename): - return True - - # Also check if it was explicitly excluded. As above, - # omit this check for an explicitly-added file: if you - # both include and exclude a file, the file is - # included. - for exclude in package.excludedFilenames: - if exclude.matches(self.filename): - return True - - # A platform-specific file is implicitly excluded from - # not-platform-specific packages. - if self.platformSpecific and package.platformSpecificConfig is False: - return True - - return False - - class ExcludeFilename: - def __init__(self, packager, filename, caseSensitive): - self.packager = packager - self.localOnly = (not filename.getDirname()) - if not self.localOnly: - filename = Filename(filename) - filename.makeCanonical() - self.glob = GlobPattern(str(filename)) - - if self.packager.platform.startswith('win'): - self.glob.setCaseSensitive(False) - elif self.packager.platform.startswith('osx'): - self.glob.setCaseSensitive(False) - - def matches(self, filename): - if self.localOnly: - return self.glob.matches(filename.getBasename()) - else: - return self.glob.matches(str(filename)) - - class PackageEntry: - """ This corresponds to a entry in the contents.xml - file. """ - - def __init__(self): - # The "seq" value increments automatically with each publish. - self.packageSeq = SeqValue() - - # The "set_ver" value is optionally specified in the pdef - # file and does not change unless the user says it does. - self.packageSetVer = SeqValue() - - def getKey(self): - """ Returns a tuple used for sorting the PackageEntry - objects uniquely per package. """ - return (self.packageName, self.platform or "", self.version or "") - - def fromFile(self, packageName, platform, version, solo, perPlatform, - installDir, descFilename, importDescFilename): - self.packageName = packageName - self.platform = platform - self.version = version - self.solo = solo - self.perPlatform = perPlatform - - self.descFile = FileSpec() - self.descFile.fromFile(installDir, descFilename) - - self.importDescFile = None - if importDescFilename: - self.importDescFile = FileSpec() - self.importDescFile.fromFile(installDir, importDescFilename) - - def loadXml(self, xpackage): - self.packageName = xpackage.Attribute('name') - self.platform = xpackage.Attribute('platform') - self.version = xpackage.Attribute('version') - solo = xpackage.Attribute('solo') - self.solo = int(solo or '0') - perPlatform = xpackage.Attribute('per_platform') - self.perPlatform = int(perPlatform or '0') - - self.packageSeq = SeqValue() - self.packageSeq.loadXml(xpackage, 'seq') - - self.packageSetVer = SeqValue() - self.packageSetVer.loadXml(xpackage, 'set_ver') - - self.descFile = FileSpec() - self.descFile.loadXml(xpackage) - - self.importDescFile = None - ximport = xpackage.FirstChildElement('import') - if ximport: - self.importDescFile = FileSpec() - self.importDescFile.loadXml(ximport) - - - def makeXml(self): - """ Returns a new TiXmlElement. """ - xpackage = TiXmlElement('package') - xpackage.SetAttribute('name', self.packageName) - if self.platform: - xpackage.SetAttribute('platform', self.platform) - if self.version: - xpackage.SetAttribute('version', self.version) - if self.solo: - xpackage.SetAttribute('solo', '1') - if self.perPlatform: - xpackage.SetAttribute('per_platform', '1') - - self.packageSeq.storeXml(xpackage, 'seq') - self.packageSetVer.storeXml(xpackage, 'set_ver') - self.descFile.storeXml(xpackage) - - if self.importDescFile: - ximport = TiXmlElement('import') - self.importDescFile.storeXml(ximport) - xpackage.InsertEndChild(ximport) - - return xpackage - - class HostEntry: - def __init__(self, url = None, downloadUrl = None, - descriptiveName = None, hostDir = None, - mirrors = None): - self.url = url - self.downloadUrl = downloadUrl - self.descriptiveName = descriptiveName - self.hostDir = hostDir - self.mirrors = mirrors or [] - self.altHosts = {} - - def loadXml(self, xhost, packager): - self.url = xhost.Attribute('url') - self.downloadUrl = xhost.Attribute('download_url') - self.descriptiveName = xhost.Attribute('descriptive_name') - self.hostDir = xhost.Attribute('host_dir') - self.mirrors = [] - xmirror = xhost.FirstChildElement('mirror') - while xmirror: - url = xmirror.Attribute('url') - self.mirrors.append(url) - xmirror = xmirror.NextSiblingElement('mirror') - - xalthost = xhost.FirstChildElement('alt_host') - while xalthost: - url = xalthost.Attribute('url') - he = packager.addHost(url) - he.loadXml(xalthost, packager) - xalthost = xalthost.NextSiblingElement('alt_host') - - def makeXml(self, packager = None): - """ Returns a new TiXmlElement. """ - xhost = TiXmlElement('host') - xhost.SetAttribute('url', self.url) - if self.downloadUrl and self.downloadUrl != self.url: - xhost.SetAttribute('download_url', self.downloadUrl) - if self.descriptiveName: - xhost.SetAttribute('descriptive_name', self.descriptiveName) - if self.hostDir: - xhost.SetAttribute('host_dir', self.hostDir) - - for mirror in self.mirrors: - xmirror = TiXmlElement('mirror') - xmirror.SetAttribute('url', mirror) - xhost.InsertEndChild(xmirror) - - if packager: - altHosts = sorted(self.altHosts.items()) - for keyword, alt in altHosts: - he = packager.hosts.get(alt, None) - if he: - xalthost = he.makeXml() - xalthost.SetValue('alt_host') - xalthost.SetAttribute('keyword', keyword) - xhost.InsertEndChild(xalthost) - - return xhost - - - class Package: - """ This is the full information on a particular package we - are constructing. Don't confuse it with PackageEntry, above, - which contains only the information found in the toplevel - contents.xml file.""" - - def __init__(self, packageName, packager): - self.packageName = packageName - self.packager = packager - self.notify = packager.notify - - # The platform is initially None until we know the file is - # platform-specific. - self.platform = None - - # This is always true on modern packages. - self.perPlatform = True - - # The arch string, though, is pre-loaded from the system - # arch string, so we can sensibly call otool. - self.arch = self.packager.arch - - self.version = None - self.host = None - self.p3dApplication = False - self.solo = False - self.compressionLevel = 0 - self.importedMapsDir = 'imported_maps' - self.mainModule = None - self.signParams = [] - self.requires = [] - - # This may be set explicitly in the pdef file to a - # particular sequence value. - self.packageSetVer = SeqValue() - - # This is the set of config variables assigned to the - # package. - self.configs = {} - - # This is the set of files and modules, already included - # by required packages, that we can skip. - self.skipFilenames = {} - self.skipModules = {} - - # This is a list of ExcludeFilename objects, representing - # the files that have been explicitly excluded. - self.excludedFilenames = [] - - # This is the list of files we will be adding, and a pair - # of cross references. - self.files = [] - self.sourceFilenames = {} - self.targetFilenames = {} - - # This is the set of files and modules that are - # required and may not be excluded from the package. - self.requiredFilenames = [] - self.requiredModules = [] - - # A list of required packages that were missing. - self.missingPackages = [] - - # This records the current list of modules we have added so - # far. - self.freezer = FreezeTool.Freezer(platform = self.packager.platform) - self.freezer.storePythonSource = self.packager.storePythonSource - - # Map of extensions to files to number (ignored by dir) - self.ignoredDirFiles = {} - - def close(self): - """ Writes out the contents of the current package. Returns True - if the package was constructed successfully, False if one or more - required files or modules are missing. """ - - if not self.p3dApplication and not self.packager.allowPackages: - message = 'Cannot generate packages without an installDir; use -i' - raise PackagerError(message) - - if self.ignoredDirFiles: - exts = sorted(self.ignoredDirFiles.keys()) - total = sum([x for x in self.ignoredDirFiles.values()]) - self.notify.warning("excluded %s files not marked for inclusion: %s" \ - % (total, ", ".join(["'" + ext + "'" for ext in exts]))) - - if not self.host: - self.host = self.packager.host - - # Check the version config variable. - version = self.configs.get('version', None) - if version is not None: - self.version = version - del self.configs['version'] - - # Check the platform_specific config variable. This has - # only three settings: None (unset), True, or False. - self.platformSpecificConfig = self.configs.get('platform_specific', None) - if self.platformSpecificConfig is not None: - # First, convert it to an int, in case it's "0" or "1". - try: - self.platformSpecificConfig = int(self.platformSpecificConfig) - except ValueError: - pass - # Then, make it a bool. - self.platformSpecificConfig = bool(self.platformSpecificConfig) - del self.configs['platform_specific'] - - # A special case when building the "panda3d" package. We - # enforce that the version number matches what we've been - # compiled with. - if self.packageName == 'panda3d': - if self.version is None: - self.version = PandaSystem.getPackageVersionString() - - if self.version != PandaSystem.getPackageVersionString(): - message = 'mismatched Panda3D version: requested %s, but Panda3D is built as %s' % (self.version, PandaSystem.getPackageVersionString()) - raise PackagerError(message) - - if self.host != PandaSystem.getPackageHostUrl(): - message = 'mismatched Panda3D host: requested %s, but Panda3D is built as %s' % (self.host, PandaSystem.getPackageHostUrl()) - raise PackagerError(message) - - if self.p3dApplication: - # Default compression level for an app. - self.compressionLevel = 6 - - # Every p3dapp requires panda3d. - if 'panda3d' not in [p.packageName for p in self.requires]: - assert not self.packager.currentPackage - self.packager.currentPackage = self - self.packager.do_require('panda3d') - self.packager.currentPackage = None - - # If this flag is set, enable allow_python_dev. - if self.packager.allowPythonDev: - self.configs['allow_python_dev'] = True - - if not self.p3dApplication and not self.version: - # If we don't have an implicit version, inherit the - # version from the 'panda3d' package on our require - # list. - for p2 in self.requires: - if p2.packageName == 'panda3d' and p2.version: - self.version = p2.version - break - - if self.solo: - result = self.installSolo() - else: - result = self.installMultifile() - - if self.p3dApplication: - allowPythonDev = self.configs.get('allow_python_dev', 0) - if int(allowPythonDev): - print("\n*** Generating %s.p3d with allow_python_dev enabled ***\n" % (self.packageName)) - - return result - - - def considerPlatform(self): - # Check to see if any of the files are platform-specific, - # making the overall package platform-specific. - - platformSpecific = self.platformSpecificConfig - for file in self.files: - if file.isExcluded(self): - # Skip this file. - continue - if file.platformSpecific: - platformSpecific = True - - if platformSpecific and self.platformSpecificConfig is not False: - if not self.platform: - self.platform = self.packager.platform - - if self.platform and self.platform.startswith('osx_'): - # Get the OSX "arch" specification. - self.arch = self.platform[4:] - - - def installMultifile(self): - """ Installs the package, either as a p3d application, or - as a true package. Either is implemented with a - Multifile. """ - - if self.missingPackages: - missing = ', '.join([name for name, version in self.missingPackages]) - self.notify.warning("Cannot build package %s due to missing dependencies: %s" % (self.packageName, missing)) - self.cleanup() - return False - - self.multifile = Multifile() - - # Write the multifile to a temporary filename until we - # know enough to determine the output filename. - multifileFilename = Filename.temporary('', self.packageName + '.', '.mf') - self.multifile.openReadWrite(multifileFilename) - - if self.p3dApplication: - # p3d files should be tagged to make them executable. - self.multifile.setHeaderPrefix('#! /usr/bin/env panda3d\n') - else: - # Package multifiles might be patched, and therefore - # don't want to record an internal timestamp, which - # would make patching less efficient. - self.multifile.setRecordTimestamp(False) - - # Make sure that all required files are present. - missing = [] - for file in self.requiredFilenames: - if file not in self.files or file.isExcluded(self): - missing.append(file.filename.getBasename()) - if len(missing) > 0: - self.notify.warning("Cannot build package %s, missing required files: %r" % (self.packageName, missing)) - self.cleanup() - return False - - self.extracts = [] - self.components = [] - - # Add the explicit py files that were requested by the - # pdef file. These get turned into Python modules. - for file in self.files: - if file.isExcluded(self): - # Skip this file. - continue - if file.unprocessed: - # Unprocessed files get dealt with below. - continue - - ext = Filename(file.newName).getExtension() - if ext == 'dc': - # Add the modules named implicitly in the dc file. - self.addDcImports(file) - - elif ext == 'py': - self.addPyFile(file) - - # Add the main module, if any. - if not self.mainModule and self.p3dApplication: - message = 'No main_module specified for application %s' % (self.packageName) - raise PackagerError(message) - if self.mainModule: - moduleName, newName = self.mainModule - if newName not in self.freezer.modules: - self.freezer.addModule(moduleName, newName = newName) - - # Now all module files have been added. Exclude modules - # already imported in a required package, and not - # explicitly included by this package. - for moduleName, mdef in self.skipModules.items(): - if moduleName not in self.freezer.modules: - self.freezer.excludeModule( - moduleName, allowChildren = mdef.allowChildren, - forbid = mdef.forbid, fromSource = 'skip') - - # Pick up any unfrozen Python files. - self.freezer.done() - - # But first, make sure that all required modules are present. - missing = [] - moduleDict = dict(self.freezer.getModuleDefs()) - for module in self.requiredModules: - if module not in moduleDict: - missing.append(module) - if len(missing) > 0: - self.notify.warning("Cannot build package %s, missing required modules: %r" % (self.packageName, missing)) - self.cleanup() - return False - - # OK, we can add it. - self.freezer.addToMultifile(self.multifile, self.compressionLevel) - self.addExtensionModules() - - # Add known module names. - self.moduleNames = {} - modules = sorted(self.freezer.modules.items()) - for newName, mdef in modules: - if mdef.guess: - # Not really a module. - continue - - if mdef.fromSource == 'skip': - # This record already appeared in a required - # module; don't repeat it now. - continue - - if mdef.exclude and mdef.implicit: - # Don't bother mentioning implicitly-excluded - # (i.e. missing) modules. - continue - - #if newName == '__main__': - # # Ignore this special case. - # continue - - self.moduleNames[newName] = mdef - - xmodule = TiXmlElement('module') - xmodule.SetAttribute('name', newName) - if mdef.exclude: - xmodule.SetAttribute('exclude', '1') - if mdef.forbid: - xmodule.SetAttribute('forbid', '1') - if mdef.exclude and mdef.allowChildren: - xmodule.SetAttribute('allowChildren', '1') - self.components.append(('m', newName.lower(), xmodule)) - - # Now look for implicit shared-library dependencies. - if self.packager.platform.startswith('win'): - self.__addImplicitDependenciesWindows() - elif self.packager.platform.startswith('osx'): - self.__addImplicitDependenciesOSX() - else: - self.__addImplicitDependenciesPosix() - - # Now add all the real, non-Python files (except model - # files). This will include the extension modules we just - # discovered above. - for file in self.files: - if file.isExcluded(self): - # Skip this file. - continue - ext = Filename(file.newName).getExtension() - if file.unprocessed: - # Add an unprocessed file verbatim. - self.addComponent(file) - elif ext == 'py': - # Already handled, above. - pass - elif file.isExcluded(self): - # Skip this file. - pass - elif ext == 'egg' or ext == 'bam': - # Skip model files this pass. - pass - elif ext == 'dc': - # dc files get a special treatment. - self.addDcFile(file) - elif ext == 'prc': - # So do prc files. - self.addPrcFile(file) - else: - # Any other file. - self.addComponent(file) - - # Now add the model files. It's important to add these - # after we have added all of the texture files, so we can - # determine which textures need to be implicitly pulled - # in. - - # We walk through a copy of the files list, since we might - # be adding more files (textures) to this list as we - # discover them in model files referenced in this list. - for file in self.files[:]: - if file.isExcluded(self): - # Skip this file. - continue - ext = Filename(file.newName).getExtension() - if file.unprocessed: - # Already handled, above. - pass - elif ext == 'py': - # Already handled, above. - pass - elif file.isExcluded(self): - # Skip this file. - pass - elif ext == 'egg': - self.addEggFile(file) - elif ext == 'bam': - self.addBamFile(file) - else: - # Handled above. - pass - - # Check to see if we should be platform-specific. - self.considerPlatform() - - # Now that we've processed all of the component files, - # (and set our platform if necessary), we can generate the - # output filename and write the output files. - - self.packageBasename = self.packageName - packageDir = self.packageName - if self.version: - self.packageBasename += '.' + self.version - packageDir += '/' + self.version - if self.platform: - self.packageBasename += '.' + self.platform - packageDir += '/' + self.platform - - self.packageDesc = self.packageBasename + '.xml' - self.packageImportDesc = self.packageBasename + '.import.xml' - if self.p3dApplication: - self.packageBasename += self.packager.p3dSuffix - self.packageBasename += '.p3d' - packageDir = '' - else: - self.packageBasename += '.mf' - packageDir += '/' - - self.packageDir = packageDir - self.packageFilename = packageDir + self.packageBasename - self.packageDesc = packageDir + self.packageDesc - self.packageImportDesc = packageDir + self.packageImportDesc - - print("Generating %s" % (self.packageFilename)) - - if self.p3dApplication: - self.packageFullpath = Filename(self.packager.p3dInstallDir, self.packageFilename) - self.packageFullpath.makeDir() - self.makeP3dInfo() - else: - self.packageFullpath = Filename(self.packager.installDir, self.packageFilename) - self.packageFullpath.makeDir() - - self.multifile.repack() - - # Also sign the multifile before we close it. - for certificate, chain, pkey, password in self.signParams: - self.multifile.addSignature(certificate, chain or '', pkey or '', password or '') - - self.multifile.close() - - if not multifileFilename.renameTo(self.packageFullpath): - self.notify.error("Cannot move %s to %s" % (multifileFilename, self.packageFullpath)) - - if self.p3dApplication: - # No patches for an application; just move it into place. - # Make the application file executable. - os.chmod(self.packageFullpath.toOsSpecific(), 0o755) - else: - self.readDescFile() - self.packageSeq += 1 - self.perPlatform = True # always true on modern packages. - self.compressMultifile() - self.writeDescFile() - self.writeImportDescFile() - - # Now that we've written out the desc file, we don't - # need to keep around the uncompressed archive - # anymore. - self.packageFullpath.unlink() - - # Replace or add the entry in the contents. - pe = Packager.PackageEntry() - pe.fromFile(self.packageName, self.platform, self.version, - False, self.perPlatform, self.packager.installDir, - self.packageDesc, self.packageImportDesc) - pe.packageSeq = self.packageSeq - pe.packageSetVer = self.packageSetVer - - self.packager.contents[pe.getKey()] = pe - self.packager.contentsChanged = True - - self.cleanup() - return True - - def installSolo(self): - """ Installs the package as a "solo", which means we - simply copy the one file into the install directory. This - is primarily intended for the "coreapi" plugin, which is - just a single dll and a jpg file; but it can support other - kinds of similar "solo" packages as well. """ - - self.considerPlatform() - self.perPlatform = False # Not true on "solo" packages. - - packageDir = self.packageName - if self.platform: - packageDir += '/' + self.platform - if self.version: - packageDir += '/' + self.version - - if not self.packager.allowPackages: - message = 'Cannot generate packages without an installDir; use -i' - raise PackagerError(message) - - installPath = Filename(self.packager.installDir, packageDir) - # Remove any files already in the installPath. - origFiles = vfs.scanDirectory(installPath) - if origFiles: - for origFile in origFiles: - origFile.getFilename().unlink() - - files = [] - for file in self.files: - if file.isExcluded(self): - # Skip this file. - continue - files.append(file) - - if not files: - # No files, never mind. - return - - if len(files) != 1: - raise PackagerError('Multiple files in "solo" package %s' % (self.packageName)) - - Filename(installPath, '').makeDir() - - file = files[0] - targetPath = Filename(installPath, file.newName) - targetPath.setBinary() - file.filename.setBinary() - if not file.filename.copyTo(targetPath): - self.notify.warning("Could not copy %s to %s" % ( - file.filename, targetPath)) - - # Replace or add the entry in the contents. - pe = Packager.PackageEntry() - pe.fromFile(self.packageName, self.platform, self.version, - True, self.perPlatform, self.packager.installDir, - Filename(packageDir, file.newName), None) - peOrig = self.packager.contents.get(pe.getKey(), None) - if peOrig: - pe.packageSeq = peOrig.packageSeq + 1 - pe.packageSetVer = peOrig.packageSetVer - if self.packageSetVer: - pe.packageSetVer = self.packageSetVer - - self.packager.contents[pe.getKey()] = pe - self.packager.contentsChanged = True - - # Hack for coreapi package, to preserve backward compatibility - # with old versions of the runtime, which still called the - # 32-bit Windows platform "win32". - if self.packageName == "coreapi" and self.platform == "win_i386": - pe2 = copy.copy(pe) - pe2.platform = "win32" - self.packager.contents[pe2.getKey()] = pe2 - - self.cleanup() - return True - - def cleanup(self): - # Now that all the files have been packed, we can delete - # the temporary files. - for file in self.files: - if file.deleteTemp: - file.filename.unlink() - - def addFile(self, *args, **kw): - """ Adds the named file to the package. Returns the file - object, or None if it was not added by this call. """ - - file = Packager.PackFile(self, *args, **kw) - if file.filename in self.sourceFilenames: - # Don't bother, it's already here. - return None - - lowerName = file.newName.lower() - if lowerName in self.targetFilenames: - # Another file is already in the same place. - file2 = self.targetFilenames[lowerName] - self.packager.notify.warning( - "%s is shadowing %s" % (file2.filename, file.filename)) - return None - - self.sourceFilenames[file.filename] = file - if file.required: - self.requiredFilenames.append(file) - - if file.text is None and not file.filename.exists(): - if not file.isExcluded(self): - self.packager.notify.warning("No such file: %s" % (file.filename)) - return None - - self.files.append(file) - self.targetFilenames[lowerName] = file - - return file - - def excludeFile(self, filename): - """ Excludes the named file (or glob pattern) from the - package. """ - xfile = Packager.ExcludeFilename(self.packager, filename, self.packager.caseSensitive) - self.excludedFilenames.append(xfile) - - def __addImplicitDependenciesWindows(self): - """ Walks through the list of files, looking for dll's and - exe's that might include implicit dependencies on other - dll's and assembly manifests. Tries to determine those - dependencies, and adds them back into the filelist. """ - - # We walk through the list as we modify it. That's OK, - # because we want to follow the transitive closure of - # dependencies anyway. - for file in self.files: - if not file.executable: - continue - - if file.isExcluded(self): - # Skip this file. - continue - - if file.filename.getExtension().lower() == "manifest": - filenames = self.__parseManifest(file.filename) - if filenames is None: - self.notify.warning("Unable to determine dependent assemblies from %s" % (file.filename)) - continue - - else: - tempFile = Filename.temporary('', 'p3d_', '.txt') - command = 'dumpbin /dependents "%s" >"%s"' % ( - file.filename.toOsSpecific(), - tempFile.toOsSpecific()) - try: - os.system(command) - except: - pass - filenames = None - - if tempFile.exists(): - filenames = self.__parseDependenciesWindows(tempFile) - tempFile.unlink() - if filenames is None: - self.notify.warning("Unable to determine dependencies from %s" % (file.filename)) - filenames = [] - - # Extract the manifest file so we can figure out - # the dependent assemblies. - tempFile = Filename.temporary('', 'p3d_', '.manifest') - resindex = 2 - if file.filename.getExtension().lower() == "exe": - resindex = 1 - command = 'mt -inputresource:"%s";#%d -out:"%s" > nul' % ( - file.filename.toOsSpecific(), - resindex, tempFile.toOsSpecific()) - try: - out = os.system(command) - except: - pass - afilenames = None - - if tempFile.exists(): - afilenames = self.__parseManifest(tempFile) - tempFile.unlink() - - # Also check for an explicit private-assembly - # manifest file on disk. - mfile = file.filename + '.manifest' - if mfile.exists(): - if afilenames is None: - afilenames = [] - afilenames += self.__parseManifest(mfile) - # Since it's an explicit manifest file, it - # means we should include the manifest - # file itself in the package. - newName = Filename(file.dependencyDir, mfile.getBasename()) - self.addFile(mfile, newName = str(newName), - explicit = False, executable = True) - - if afilenames is None and out != 31: - self.notify.warning("Unable to determine dependent assemblies from %s" % (file.filename)) - - if afilenames is not None: - filenames += afilenames - - # Attempt to resolve the dependent filename relative - # to the original filename, before we resolve it along - # the PATH. - path = DSearchPath(Filename(file.filename.getDirname())) - - for filename in filenames: - filename = Filename.fromOsSpecific(filename) - filename.resolveFilename(path) - filename.makeTrueCase() - - newName = Filename(file.dependencyDir, filename.getBasename()) - self.addFile(filename, newName = str(newName), - explicit = False, executable = True) - - def __parseDependenciesWindows(self, tempFile): - """ Reads the indicated temporary file, the output from - dumpbin /dependents, to determine the list of dll's this - executable file depends on. """ - - lines = open(tempFile.toOsSpecific(), 'rU').readlines() - li = 0 - while li < len(lines): - line = lines[li] - li += 1 - if line.find(' has the following dependencies') != -1: - break - - if li < len(lines): - line = lines[li] - if line.strip() == '': - # Skip a blank line. - li += 1 - - # Now we're finding filenames, until the next blank line. - filenames = [] - while li < len(lines): - line = lines[li] - li += 1 - line = line.strip() - if line == '': - # We're done. - return filenames - filenames.append(line) - - # Hmm, we ran out of data. Oh well. - if not filenames: - # Some parse error. - return None - - # At least we got some data. - return filenames - - def __parseManifest(self, tempFile): - """ Reads the indicated application manifest file, to - determine the list of dependent assemblies this - executable file depends on. """ - - doc = TiXmlDocument(tempFile.toOsSpecific()) - if not doc.LoadFile(): - return None - - assembly = doc.FirstChildElement("assembly") - if not assembly: - return None - - # Pick up assemblies that it depends on - filenames = [] - dependency = assembly.FirstChildElement("dependency") - while dependency: - depassembly = dependency.FirstChildElement("dependentAssembly") - if depassembly: - ident = depassembly.FirstChildElement("assemblyIdentity") - if ident: - name = ident.Attribute("name") - if name: - filenames.append(name + ".manifest") - - dependency = dependency.NextSiblingElement("dependency") - - # Pick up direct dll dependencies that it lists - dfile = assembly.FirstChildElement("file") - while dfile: - name = dfile.Attribute("name") - if name: - filenames.append(name) - dfile = dfile.NextSiblingElement("file") - - return filenames - - def __locateFrameworkLibrary(self, library): - """ Locates the given library inside its framework on the - default framework paths, and returns its location as Filename. """ - - # If it's already a full existing path, we - # don't search for it anymore, of course. - if Filename.fromOsSpecific(library).exists(): - return Filename.fromOsSpecific(library) - - # DSearchPath appears not to work for directories. - fpath = [] - fpath.append(Filename("/Library/Frameworks")) - fpath.append(Filename("/System/Library/Frameworks")) - fpath.append(Filename("/Developer/Library/Frameworks")) - fpath.append(Filename(os.path.expanduser("~"), "Library/Frameworks")) - if "HOME" in os.environ: - fpath.append(Filename(os.environ["HOME"], "Library/Frameworks")) - ffilename = Filename(library.split('.framework/', 1)[0].split('/')[-1] + '.framework') - ffilename = Filename(ffilename, library.split('.framework/', 1)[-1]) - - # Look under the system root first, if supplied. - if self.packager.systemRoot: - for i in fpath: - fw = Filename(self.packager.systemRoot, i) - if Filename(fw, ffilename).exists(): - return Filename(fw, ffilename) - - for i in fpath: - if Filename(i, ffilename).exists(): - return Filename(i, ffilename) - - # Not found? Well, let's just return the framework + file - # path, the user will be presented with a warning later. - return ffilename - - def __alterFrameworkDependencies(self, file, framework_deps): - """ Copies the given library file to a temporary directory, - and alters the dependencies so that it doesn't contain absolute - framework dependencies. """ - - if not file.deleteTemp: - # Copy the file to a temporary location because we - # don't want to modify the original (there's a big - # chance that we break it). - - # Copy it every time, because the source file might - # have changed since last time we ran. - assert file.filename.exists(), "File doesn't exist: %s" % file.filename - tmpfile = Filename.temporary('', "p3d_" + file.filename.getBasename()) - tmpfile.setBinary() - file.filename.copyTo(tmpfile) - file.filename = tmpfile - file.deleteTemp = True - - # Alter the dependencies to have a relative path rather than absolute - for filename in framework_deps: - loc = self.__locateFrameworkLibrary(filename) - - if loc == file.filename: - os.system('install_name_tool -id "%s" "%s"' % (os.path.basename(filename), file.filename.toOsSpecific())) - elif "/System/" in loc.toOsSpecific(): - # Let's keep references to system frameworks absolute - os.system('install_name_tool -change "%s" "%s" "%s"' % (filename, loc.toOsSpecific(), file.filename.toOsSpecific())) - else: - os.system('install_name_tool -change "%s" "%s" "%s"' % (filename, os.path.basename(filename), file.filename.toOsSpecific())) - - def __addImplicitDependenciesOSX(self): - """ Walks through the list of files, looking for dylib's - and executables that might include implicit dependencies - on other dylib's. Tries to determine those dependencies, - and adds them back into the filelist. """ - - # We walk through the list as we modify it. That's OK, - # because we want to follow the transitive closure of - # dependencies anyway. - for file in self.files: - if not file.executable: - continue - - if file.isExcluded(self): - # Skip this file. - continue - - origFilename = Filename(file.filename) - - tempFile = Filename.temporary('', 'p3d_', '.txt') - command = '/usr/bin/otool -arch all -L "%s" >"%s"' % ( - origFilename.toOsSpecific(), - tempFile.toOsSpecific()) - if self.arch: - arch = self.arch - if arch == "amd64": - arch = "x86_64" - command = '/usr/bin/otool -arch %s -L "%s" >"%s"' % ( - arch, - origFilename.toOsSpecific(), - tempFile.toOsSpecific()) - exitStatus = os.system(command) - if exitStatus != 0: - self.notify.warning('Command failed: %s' % (command)) - filenames = None - - if tempFile.exists(): - filenames = self.__parseDependenciesOSX(tempFile) - tempFile.unlink() - if filenames is None: - self.notify.warning("Unable to determine dependencies from %s" % (origFilename)) - continue - - # Attempt to resolve the dependent filename relative - # to the original filename, before we resolve it along - # the PATH. - path = DSearchPath(Filename(origFilename.getDirname())) - - # Find the dependencies that are referencing a framework - framework_deps = [] - for filename in filenames: - if '.framework/' in filename: - framework_deps.append(filename) - - if len(framework_deps) > 0: - # Fixes dependencies like @executable_path/../Library/Frameworks/Cg.framework/Cg - self.__alterFrameworkDependencies(file, framework_deps) - - for filename in filenames: - if '@loader_path' in filename: - filename = filename.replace('@loader_path', origFilename.getDirname()) - - if False and '.framework/' in filename: - # It references a framework, and besides the fact - # that those often contain absolute paths, they - # aren't commonly on the library path either. - filename = self.__locateFrameworkLibrary(filename) - filename.setBinary() - else: - # It's just a normal library - find it on the path. - filename = Filename.fromOsSpecific(filename) - filename.setBinary() - - if filename.isLocal(): - filename.resolveFilename(path) - else: - # It's a fully-specified filename; look - # for it under the system root first. - if self.packager.systemRoot: - f2 = Filename(self.packager.systemRoot, filename) - if f2.exists(): - filename = f2 - - # Skip libraries and frameworks in system directory - if "/System/" in filename.toOsSpecific(): - continue - - newName = Filename(file.dependencyDir, filename.getBasename()) - self.addFile(filename, newName = str(newName), - explicit = False, executable = True) - - def __parseDependenciesOSX(self, tempFile): - """ Reads the indicated temporary file, the output from - otool -L, to determine the list of dylibs this - executable file depends on. """ - - lines = open(tempFile.toOsSpecific(), 'rU').readlines() - - filenames = [] - for line in lines: - if not line[0].isspace(): - continue - line = line.strip() - s = line.find(' (compatibility') - if s != -1: - line = line[:s] - else: - s = line.find('.dylib') - if s != -1: - line = line[:s + 6] - else: - continue - filenames.append(line) - - return filenames - - def __readAndStripELF(self, file): - """ Reads the indicated ELF binary, and returns a list with - dependencies. If it contains data that should be stripped, - it writes the stripped library to a temporary file. Returns - None if the file failed to read (e.g. not an ELF file). """ - - # Read the first 16 bytes, which identify the ELF file. - elf = open(file.filename.toOsSpecific(), 'rb') - try: - ident = elf.read(16) - except IOError: - elf.close() - return None - - if not ident.startswith(b"\177ELF"): - # No elf magic! Beware of orcs. - return None - - # Make sure we read in the correct endianness and integer size - byteOrder = "<>"[ord(ident[5:6]) - 1] - elfClass = ord(ident[4:5]) - 1 # 0 = 32-bits, 1 = 64-bits - headerStruct = byteOrder + ("HHIIIIIHHHHHH", "HHIQQQIHHHHHH")[elfClass] - sectionStruct = byteOrder + ("4xI8xIII8xI", "4xI16xQQI12xQ")[elfClass] - dynamicStruct = byteOrder + ("iI", "qQ")[elfClass] - - type, machine, version, entry, phoff, shoff, flags, ehsize, phentsize, phnum, shentsize, shnum, shstrndx \ - = struct.unpack(headerStruct, elf.read(struct.calcsize(headerStruct))) - dynamicSections = [] - stringTables = {} - - # Seek to the section header table and find the .dynamic section. - elf.seek(shoff) - for i in range(shnum): - type, offset, size, link, entsize = struct.unpack_from(sectionStruct, elf.read(shentsize)) - if type == 6 and link != 0: # DYNAMIC type, links to string table - dynamicSections.append((offset, size, link, entsize)) - stringTables[link] = None - - # Read the relevant string tables. - for idx in stringTables.keys(): - elf.seek(shoff + idx * shentsize) - type, offset, size, link, entsize = struct.unpack_from(sectionStruct, elf.read(shentsize)) - if type != 3: continue - elf.seek(offset) - stringTables[idx] = elf.read(size) - - # Loop through the dynamic sections and rewrite it if it has an rpath/runpath. - rewriteSections = [] - filenames = [] - rpath = [] - for offset, size, link, entsize in dynamicSections: - elf.seek(offset) - data = elf.read(entsize) - tag, val = struct.unpack_from(dynamicStruct, data) - newSectionData = b"" - startReplace = None - pad = 0 - - # Read tags until we find a NULL tag. - while tag != 0: - if tag == 1: # A NEEDED entry. Read it from the string table. - filenames.append(stringTables[link][val : stringTables[link].find(b'\0', val)]) - - elif tag == 15 or tag == 29: - rpath += stringTables[link][val : stringTables[link].find(b'\0', val)].split(b':') - # An RPATH or RUNPATH entry. - if not startReplace: - startReplace = elf.tell() - entsize - if startReplace: - pad += entsize - - elif startReplace is not None: - newSectionData += data - - data = elf.read(entsize) - tag, val = struct.unpack_from(dynamicStruct, data) - - if startReplace is not None: - newSectionData += data + (b"\0" * pad) - rewriteSections.append((startReplace, newSectionData)) - elf.close() - - # No rpaths/runpaths found, so nothing to do any more. - if len(rewriteSections) == 0: - return filenames - - # Attempt to resolve any of the directly - # dependent filenames along the RPATH. - for f in range(len(filenames)): - filename = filenames[f] - for rdir in rpath: - if os.path.isfile(os.path.join(rdir, filename)): - filenames[f] = os.path.join(rdir, filename) - break - - if not file.deleteTemp: - # Copy the file to a temporary location because we - # don't want to modify the original (there's a big - # chance that we break it). - - tmpfile = Filename.temporary('', "p3d_" + file.filename.getBasename()) - tmpfile.setBinary() - file.filename.copyTo(tmpfile) - file.filename = tmpfile - file.deleteTemp = True - - # Open the temporary file and rewrite the dynamic sections. - elf = open(file.filename.toOsSpecific(), 'r+b') - for offset, data in rewriteSections: - elf.seek(offset) - elf.write(data) - elf.write(b"\0" * pad) - elf.close() - return filenames - - def __addImplicitDependenciesPosix(self): - """ Walks through the list of files, looking for so's - and executables that might include implicit dependencies - on other so's. Tries to determine those dependencies, - and adds them back into the filelist. """ - - # We walk through the list as we modify it. That's OK, - # because we want to follow the transitive closure of - # dependencies anyway. - for file in self.files: - if not file.executable: - continue - - if file.isExcluded(self): - # Skip this file. - continue - - # Check if this is an ELF binary. - filenames = self.__readAndStripELF(file) - - # If that failed, perhaps ldd will help us. - if filenames is None: - self.notify.warning("Reading ELF library %s failed, using ldd instead" % (file.filename)) - tempFile = Filename.temporary('', 'p3d_', '.txt') - command = 'ldd "%s" >"%s"' % ( - file.filename.toOsSpecific(), - tempFile.toOsSpecific()) - try: - os.system(command) - except: - pass - - if tempFile.exists(): - filenames = self.__parseDependenciesPosix(tempFile) - tempFile.unlink() - - if filenames is None: - self.notify.warning("Unable to determine dependencies from %s" % (file.filename)) - continue - - # Attempt to resolve the dependent filename relative - # to the original filename, before we resolve it along - # the PATH. - path = DSearchPath(Filename(file.filename.getDirname())) - - for filename in filenames: - # These vDSO's provided by Linux aren't - # supposed to be anywhere on the system. - if filename in ["linux-gate.so.1", "linux-vdso.so.1"]: - continue - - filename = Filename.fromOsSpecific(filename) - filename.resolveFilename(path) - filename.setBinary() - - newName = Filename(file.dependencyDir, filename.getBasename()) - self.addFile(filename, newName = str(newName), - explicit = False, executable = True) - - def __parseDependenciesPosix(self, tempFile): - """ Reads the indicated temporary file, the output from - ldd, to determine the list of so's this executable file - depends on. """ - - lines = open(tempFile.toOsSpecific(), 'rU').readlines() - - filenames = [] - for line in lines: - line = line.strip() - s = line.find(' => ') - if s == -1: - continue - - line = line[:s].strip() - filenames.append(line) - - return filenames - - def addExtensionModules(self): - """ Adds the extension modules detected by the freezer to - the current list of files. """ - - freezer = self.freezer - for moduleName, filename in freezer.extras: - filename = Filename.fromOsSpecific(filename) - newName = filename.getBasename() - if '.' in moduleName: - newName = '/'.join(moduleName.split('.')[:-1]) - newName += '/' + filename.getBasename() - # Sometimes the PYTHONPATH has the wrong case in it. - filename.makeTrueCase() - self.addFile(filename, newName = newName, - explicit = False, extract = True, - executable = True, - platformSpecific = True) - freezer.extras = [] - - - def makeP3dInfo(self): - """ Makes the p3d_info.xml file that defines the - application startup parameters and such. """ - - doc = TiXmlDocument() - decl = TiXmlDeclaration("1.0", "utf-8", "") - doc.InsertEndChild(decl) - - xpackage = TiXmlElement('package') - xpackage.SetAttribute('name', self.packageName) - if self.platform: - xpackage.SetAttribute('platform', self.platform) - if self.version: - xpackage.SetAttribute('version', self.version) - - xpackage.SetAttribute('main_module', self.mainModule[1]) - - self.__addConfigs(xpackage) - - requireHosts = {} - for package in self.requires: - xrequires = TiXmlElement('requires') - xrequires.SetAttribute('name', package.packageName) - if package.version: - xrequires.SetAttribute('version', package.version) - xrequires.SetAttribute('host', package.host) - package.packageSeq.storeXml(xrequires, 'seq') - package.packageSetVer.storeXml(xrequires, 'set_ver') - requireHosts[package.host] = True - xpackage.InsertEndChild(xrequires) - - for host in requireHosts.keys(): - he = self.packager.hosts.get(host, None) - if he: - xhost = he.makeXml(packager = self.packager) - xpackage.InsertEndChild(xhost) - - self.extracts.sort() - for name, xextract in self.extracts: - xpackage.InsertEndChild(xextract) - - doc.InsertEndChild(xpackage) - - # Write the xml file to a temporary file on disk, so we - # can add it to the multifile. - filename = Filename.temporary('', 'p3d_', '.xml') - - # This should really be setText() for an xml file, but it - # doesn't really matter that much since tinyxml can read - # it either way; and if we use setBinary() it will remain - # compatible with older versions of the core API that - # didn't understand the SF_text flag. - filename.setBinary() - - doc.SaveFile(filename.toOsSpecific()) - - # It's important not to compress this file: the core API - # runtime can't decode compressed subfiles. - self.multifile.addSubfile('p3d_info.xml', filename, 0) - - self.multifile.flush() - filename.unlink() - - - def compressMultifile(self): - """ Compresses the .mf file into an .mf.pz file. """ - - if self.oldCompressedBasename: - # Remove the previous compressed file first. - compressedPath = Filename(self.packager.installDir, Filename(self.packageDir, self.oldCompressedBasename)) - compressedPath.unlink() - - newCompressedFilename = '%s.pz' % (self.packageFilename) - - # Now build the new version. - compressedPath = Filename(self.packager.installDir, newCompressedFilename) - if not compressFile(self.packageFullpath, compressedPath, 6): - message = 'Unable to write %s' % (compressedPath) - raise PackagerError(message) - - def readDescFile(self): - """ Reads the existing package.xml file before rewriting - it. We need this to preserve the list of patches, and - similar historic data, between sessions. """ - - self.packageSeq = SeqValue() - self.packageSetVer = SeqValue() - self.patchVersion = None - self.patches = [] - - self.oldCompressedBasename = None - - packageDescFullpath = Filename(self.packager.installDir, self.packageDesc) - doc = TiXmlDocument(packageDescFullpath.toOsSpecific()) - if not doc.LoadFile(): - return - - xpackage = doc.FirstChildElement('package') - if not xpackage: - return - - perPlatform = xpackage.Attribute('per_platform') - self.perPlatform = int(perPlatform or '0') - - self.packageSeq.loadXml(xpackage, 'seq') - self.packageSetVer.loadXml(xpackage, 'set_ver') - - xcompressed = xpackage.FirstChildElement('compressed_archive') - if xcompressed: - compressedFilename = xcompressed.Attribute('filename') - if compressedFilename: - self.oldCompressedBasename = compressedFilename - - patchVersion = xpackage.Attribute('patch_version') - if not patchVersion: - patchVersion = xpackage.Attribute('last_patch_version') - if patchVersion: - self.patchVersion = patchVersion - - # Extract the base_version, top_version, and patch - # entries, if any, and preserve these entries verbatim for - # the next version. - xbase = xpackage.FirstChildElement('base_version') - if xbase: - self.patches.append(xbase.Clone()) - xtop = xpackage.FirstChildElement('top_version') - if xtop: - self.patches.append(xtop.Clone()) - - xpatch = xpackage.FirstChildElement('patch') - while xpatch: - self.patches.append(xpatch.Clone()) - xpatch = xpatch.NextSiblingElement('patch') - - def writeDescFile(self): - """ Makes the package.xml file that describes the package - and its contents, for download. """ - - packageDescFullpath = Filename(self.packager.installDir, self.packageDesc) - doc = TiXmlDocument(packageDescFullpath.toOsSpecific()) - decl = TiXmlDeclaration("1.0", "utf-8", "") - doc.InsertEndChild(decl) - - xpackage = TiXmlElement('package') - xpackage.SetAttribute('name', self.packageName) - if self.platform: - xpackage.SetAttribute('platform', self.platform) - if self.version: - xpackage.SetAttribute('version', self.version) - if self.perPlatform: - xpackage.SetAttribute('per_platform', '1') - - if self.patchVersion: - xpackage.SetAttribute('last_patch_version', self.patchVersion) - - self.packageSeq.storeXml(xpackage, 'seq') - self.packageSetVer.storeXml(xpackage, 'set_ver') - - self.__addConfigs(xpackage) - - for package in self.requires: - xrequires = TiXmlElement('requires') - xrequires.SetAttribute('name', package.packageName) - if self.platform and package.platform: - xrequires.SetAttribute('platform', package.platform) - if package.version: - xrequires.SetAttribute('version', package.version) - package.packageSeq.storeXml(xrequires, 'seq') - package.packageSetVer.storeXml(xrequires, 'set_ver') - xrequires.SetAttribute('host', package.host) - xpackage.InsertEndChild(xrequires) - - xuncompressedArchive = self.getFileSpec( - 'uncompressed_archive', self.packageFullpath, - self.packageBasename) - xpackage.InsertEndChild(xuncompressedArchive) - - xcompressedArchive = self.getFileSpec( - 'compressed_archive', self.packageFullpath + '.pz', - self.packageBasename + '.pz') - xpackage.InsertEndChild(xcompressedArchive) - - # Copy in the patch entries read from the previous version - # of the desc file. - for xpatch in self.patches: - xpackage.InsertEndChild(xpatch) - - self.extracts.sort() - for name, xextract in self.extracts: - xpackage.InsertEndChild(xextract) - - doc.InsertEndChild(xpackage) - doc.SaveFile() - - def __addConfigs(self, xpackage): - """ Adds the XML config values defined in self.configs to - the indicated XML element. """ - - if self.configs: - xconfig = TiXmlElement('config') - - for variable, value in self.configs.items(): - if sys.version_info < (3, 0) and isinstance(value, unicode): - xconfig.SetAttribute(variable, value.encode('utf-8')) - elif isinstance(value, bool): - # True or False must be encoded as 1 or 0. - xconfig.SetAttribute(variable, str(int(value))) - else: - xconfig.SetAttribute(variable, str(value)) - - xpackage.InsertEndChild(xconfig) - - def writeImportDescFile(self): - """ Makes the package.import.xml file that describes the - package and its contents, for other packages and - applications that may wish to "require" this one. """ - - packageImportDescFullpath = Filename(self.packager.installDir, self.packageImportDesc) - doc = TiXmlDocument(packageImportDescFullpath.toOsSpecific()) - decl = TiXmlDeclaration("1.0", "utf-8", "") - doc.InsertEndChild(decl) - - xpackage = TiXmlElement('package') - xpackage.SetAttribute('name', self.packageName) - if self.platform: - xpackage.SetAttribute('platform', self.platform) - if self.version: - xpackage.SetAttribute('version', self.version) - xpackage.SetAttribute('host', self.host) - - self.packageSeq.storeXml(xpackage, 'seq') - self.packageSetVer.storeXml(xpackage, 'set_ver') - - requireHosts = {} - requireHosts[self.host] = True - - for package in self.requires: - xrequires = TiXmlElement('requires') - xrequires.SetAttribute('name', package.packageName) - if self.platform and package.platform: - xrequires.SetAttribute('platform', package.platform) - if package.version: - xrequires.SetAttribute('version', package.version) - xrequires.SetAttribute('host', package.host) - package.packageSeq.storeXml(xrequires, 'seq') - package.packageSetVer.storeXml(xrequires, 'set_ver') - requireHosts[package.host] = True - xpackage.InsertEndChild(xrequires) - - # Make sure we also write the full host descriptions for - # any hosts we reference, so we can find these guys later. - for host in requireHosts.keys(): - he = self.packager.hosts.get(host, None) - if he: - xhost = he.makeXml(packager = self.packager) - xpackage.InsertEndChild(xhost) - - self.components.sort() - for type, name, xcomponent in self.components: - xpackage.InsertEndChild(xcomponent) - - doc.InsertEndChild(xpackage) - doc.SaveFile() - - def readImportDescFile(self, filename): - """ Reads the import desc file. Returns True on success, - False on failure. """ - - self.packageSeq = SeqValue() - self.packageSetVer = SeqValue() - - doc = TiXmlDocument(filename.toOsSpecific()) - if not doc.LoadFile(): - return False - xpackage = doc.FirstChildElement('package') - if not xpackage: - return False - - self.packageName = xpackage.Attribute('name') - self.platform = xpackage.Attribute('platform') - self.version = xpackage.Attribute('version') - self.host = xpackage.Attribute('host') - - # Get any new host descriptors. - xhost = xpackage.FirstChildElement('host') - while xhost: - he = self.packager.HostEntry() - he.loadXml(xhost, self) - if he.url not in self.packager.hosts: - self.packager.hosts[he.url] = he - xhost = xhost.NextSiblingElement('host') - - self.packageSeq.loadXml(xpackage, 'seq') - self.packageSetVer.loadXml(xpackage, 'set_ver') - - self.requires = [] - xrequires = xpackage.FirstChildElement('requires') - while xrequires: - packageName = xrequires.Attribute('name') - platform = xrequires.Attribute('platform') - version = xrequires.Attribute('version') - host = xrequires.Attribute('host') - if packageName: - package = self.packager.findPackage( - packageName, platform = platform, version = version, - host = host, requires = self.requires) - if package: - self.requires.append(package) - xrequires = xrequires.NextSiblingElement('requires') - - self.targetFilenames = {} - xcomponent = xpackage.FirstChildElement('component') - while xcomponent: - name = xcomponent.Attribute('filename') - if name: - self.targetFilenames[name.lower()] = True - xcomponent = xcomponent.NextSiblingElement('component') - - self.moduleNames = {} - xmodule = xpackage.FirstChildElement('module') - while xmodule: - moduleName = xmodule.Attribute('name') - exclude = int(xmodule.Attribute('exclude') or 0) - forbid = int(xmodule.Attribute('forbid') or 0) - allowChildren = int(xmodule.Attribute('allowChildren') or 0) - - if moduleName: - mdef = FreezeTool.Freezer.ModuleDef( - moduleName, exclude = exclude, forbid = forbid, - allowChildren = allowChildren) - self.moduleNames[moduleName] = mdef - xmodule = xmodule.NextSiblingElement('module') - - return True - - def getFileSpec(self, element, pathname, newName): - """ Returns an xcomponent or similar element with the file - information for the indicated file. """ - - xspec = TiXmlElement(element) - - size = pathname.getFileSize() - timestamp = pathname.getTimestamp() - - hv = HashVal() - hv.hashFile(pathname) - hash = hv.asHex() - - xspec.SetAttribute('filename', newName) - xspec.SetAttribute('size', str(size)) - xspec.SetAttribute('timestamp', str(timestamp)) - xspec.SetAttribute('hash', hash) - - return xspec - - def addPyFile(self, file): - """ Adds the indicated python file, identified by filename - instead of by module name, to the package. """ - - # Convert the raw filename back to a module name, so we - # can see if we've already loaded this file. We assume - # that all Python files within the package will be rooted - # at the top of the package. - - filename = file.newName.rsplit('.', 1)[0] - moduleName = filename.replace("/", ".") - if moduleName.endswith('.__init__'): - moduleName = moduleName.rsplit('.', 1)[0] - - if moduleName in self.freezer.modules: - # This Python file is already known. We don't have to - # deal with it again. - return - - # Make sure that it is actually in a package. - parentName = moduleName - while '.' in parentName: - parentName = parentName.rsplit('.', 1)[0] - if parentName not in self.freezer.modules: - message = 'Cannot add Python file %s; not in package' % (file.newName) - if file.required or file.explicit: - raise Exception(message) - else: - self.notify.warning(message) - return - - if file.text: - self.freezer.addModule(moduleName, filename = file.filename, text = file.text) - else: - self.freezer.addModule(moduleName, filename = file.filename) - - def addEggFile(self, file): - # Precompile egg files to bam's. - np = self.packager.loader.loadModel(file.filename, self.packager.loaderOptions) - if not np: - raise Exception('Could not read egg file %s' % (file.filename)) - - bamName = Filename(file.newName) - bamName.setExtension('bam') - self.addNode(np.node(), file.filename, str(bamName)) - - def addBamFile(self, file): - # Load the bam file so we can massage its textures. - bamFile = BamFile() - if not bamFile.openRead(file.filename): - raise Exception('Could not read bam file %s' % (file.filename)) - - bamFile.getReader().setLoaderOptions(self.packager.loaderOptions) - - if not bamFile.resolve(): - raise Exception('Could not resolve bam file %s' % (file.filename)) - - node = bamFile.readNode() - if not node: - raise Exception('Not a model file: %s' % (file.filename)) - - self.addNode(node, file.filename, file.newName) - - def addNode(self, node, filename, newName): - """ Converts the indicated node to a bam stream, and adds the - bam file to the multifile under the indicated newName. """ - - # If the Multifile already has a file by this name, don't - # bother adding it again. - if self.multifile.findSubfile(newName) >= 0: - return - - # Be sure to import all of the referenced textures, and tell - # them their new location within the multifile. - - for tex in NodePath(node).findAllTextures(): - if not tex.hasFullpath() and tex.hasRamImage(): - # We need to store this texture as a raw-data image. - # Clear the newName so this will happen - # automatically. - tex.clearFilename() - tex.clearAlphaFilename() - - else: - # We can store this texture as a file reference to its - # image. Copy the file into our multifile, and rename - # its reference in the texture. - if tex.hasFilename(): - tex.setFilename(self.addFoundTexture(tex.getFullpath())) - if tex.hasAlphaFilename(): - tex.setAlphaFilename(self.addFoundTexture(tex.getAlphaFullpath())) - - # Now generate an in-memory bam file. Tell the bam writer to - # keep the textures referenced by their in-multifile path. - bamFile = BamFile() - stream = StringStream() - bamFile.openWrite(stream) - bamFile.getWriter().setFileTextureMode(bamFile.BTMUnchanged) - bamFile.writeObject(node) - bamFile.close() - - # Clean the node out of memory. - node.removeAllChildren() - - # Now we have an in-memory bam file. - stream.seekg(0) - self.multifile.addSubfile(newName, stream, self.compressionLevel) - - # Flush it so the data gets written to disk immediately, so we - # don't have to keep it around in ram. - self.multifile.flush() - - xcomponent = TiXmlElement('component') - xcomponent.SetAttribute('filename', newName) - self.components.append(('c', newName.lower(), xcomponent)) - - def addFoundTexture(self, filename): - """ Adds the newly-discovered texture to the output, if it has - not already been included. Returns the new name within the - package tree. """ - - filename = Filename(filename) - filename.makeCanonical() - - file = self.sourceFilenames.get(filename, None) - if file: - # Never mind, it's already on the list. - return file.newName - - # We have to copy the image into the plugin tree somewhere. - newName = self.importedMapsDir + '/' + filename.getBasename() - uniqueId = 0 - while newName.lower() in self.targetFilenames: - uniqueId += 1 - newName = '%s/%s_%s.%s' % ( - self.importedMapsDir, filename.getBasenameWoExtension(), - uniqueId, filename.getExtension()) - - file = self.addFile( - filename, newName = newName, explicit = False, - compress = False) - - if file: - # If we added the file in this pass, then also - # immediately add it to the multifile (because we - # won't be visiting the files list again). - self.addComponent(file) - - return newName - - def addDcFile(self, file): - """ Adds a dc file to the archive. A dc file gets its - internal comments and parameter names stripped out of the - final result automatically. This is as close as we can - come to "compiling" a dc file, since all of the remaining - symbols are meaningful at runtime. """ - - # First, read in the dc file - from panda3d.direct import DCFile - dcFile = DCFile() - if not dcFile.read(file.filename): - self.notify.error("Unable to parse %s." % (file.filename)) - - # And then write it out without the comments and such. - stream = StringStream() - if not dcFile.write(stream, True): - self.notify.error("Unable to write %s." % (file.filename)) - - file.text = stream.getData() - self.addComponent(file) - - def addDcImports(self, file): - """ Adds the Python modules named by the indicated dc - file. """ - - from panda3d.direct import DCFile - dcFile = DCFile() - if not dcFile.read(file.filename): - self.notify.error("Unable to parse %s." % (file.filename)) - - for n in range(dcFile.getNumImportModules()): - moduleName = dcFile.getImportModule(n) - moduleSuffixes = [] - if '/' in moduleName: - moduleName, suffixes = moduleName.split('/', 1) - moduleSuffixes = suffixes.split('/') - self.freezer.addModule(moduleName) - - for suffix in self.packager.dcClientSuffixes: - if suffix in moduleSuffixes: - self.freezer.addModule(moduleName + suffix) - - for i in range(dcFile.getNumImportSymbols(n)): - symbolName = dcFile.getImportSymbol(n, i) - symbolSuffixes = [] - if '/' in symbolName: - symbolName, suffixes = symbolName.split('/', 1) - symbolSuffixes = suffixes.split('/') - - # "from moduleName import symbolName". - - # Maybe this symbol is itself a module; if that's - # the case, we need to add it to the list also. - self.freezer.addModule('%s.%s' % (moduleName, symbolName), - implicit = True) - for suffix in self.packager.dcClientSuffixes: - if suffix in symbolSuffixes: - self.freezer.addModule('%s.%s%s' % (moduleName, symbolName, suffix), - implicit = True) - - - def addPrcFile(self, file): - """ Adds a prc file to the archive. Like the dc file, - this strips comments and such before adding. It's also - possible to set prcEncryptionKey and/or prcSignCommand to - further manipulate prc files during processing. """ - - # First, read it in. - if file.text: - textLines = file.text.split('\n') - else: - textLines = open(file.filename.toOsSpecific(), 'rU').readlines() - - # Then write it out again, without the comments. - tempFilename = Filename.temporary('', 'p3d_', '.prc') - tempFilename.setBinary() # Binary is more reliable for signing. - temp = open(tempFilename.toOsSpecific(), 'w') - for line in textLines: - line = line.strip() - if line and line[0] != '#': - # Write the line out only if it's not a comment. - temp.write(line + '\n') - temp.close() - - if self.packager.prcSignCommand: - # Now sign the file. - command = '%s -n "%s"' % ( - self.packager.prcSignCommand, tempFilename.toOsSpecific()) - self.notify.info(command) - exitStatus = os.system(command) - if exitStatus != 0: - self.notify.error('Command failed: %s' % (command)) - - if self.packager.prcEncryptionKey: - # And now encrypt it. - if file.newName.endswith('.prc'): - # Change .prc -> .pre - file.newName = file.newName[:-1] + 'e' - - preFilename = Filename.temporary('', 'p3d_', '.pre') - preFilename.setBinary() - tempFilename.setText() - encryptFile(tempFilename, preFilename, self.packager.prcEncryptionKey) - tempFilename.unlink() - tempFilename = preFilename - - if file.deleteTemp: - file.filename.unlink() - - file.filename = tempFilename - file.text = None - file.deleteTemp = True - - self.addComponent(file) - - def addComponent(self, file): - compressionLevel = 0 - if file.compress: - compressionLevel = self.compressionLevel - - if file.text: - stream = StringStream(file.text) - self.multifile.addSubfile(file.newName, stream, compressionLevel) - self.multifile.flush() - - elif file.executable and self.arch: - if not self.__addOsxExecutable(file): - return - - else: - # Copy an ordinary file into the multifile. - self.multifile.addSubfile(file.newName, file.filename, compressionLevel) - if file.extract: - if file.text: - # Better write it to a temporary file, so we can - # get its hash. - tfile = Filename.temporary('', 'p3d_') - open(tfile.toOsSpecific(), 'wb').write(file.text) - xextract = self.getFileSpec('extract', tfile, file.newName) - tfile.unlink() - - else: - # The file data exists on disk already. - xextract = self.getFileSpec('extract', file.filename, file.newName) - self.extracts.append((file.newName.lower(), xextract)) - - xcomponent = TiXmlElement('component') - xcomponent.SetAttribute('filename', file.newName) - self.components.append(('c', file.newName.lower(), xcomponent)) - - def __addOsxExecutable(self, file): - """ Adds an executable or shared library to the multifile, - with respect to OSX's fat-binary features. Returns true - on success, false on failure. """ - - compressionLevel = 0 - if file.compress: - compressionLevel = self.compressionLevel - - # If we're on OSX and adding only files for a - # particular architecture, use lipo to strip out the - # part of the file for that architecture. - - arch = self.arch - if arch == "amd64": - arch = "x86_64" - - # First, we need to verify that it is in fact a - # universal binary. - tfile = Filename.temporary('', 'p3d_') - tfile.setBinary() - command = '/usr/bin/lipo -info "%s" >"%s"' % ( - file.filename.toOsSpecific(), - tfile.toOsSpecific()) - exitStatus = os.system(command) - if exitStatus != 0: - self.notify.warning("Not an executable file: %s" % (file.filename)) - # Just add it anyway. - file.filename.setBinary() - self.multifile.addSubfile(file.newName, file.filename, compressionLevel) - return True - - # The lipo command succeeded, so it really is an - # executable file. Parse the lipo output to figure out - # which architectures the file supports. - arches = [] - lipoData = open(tfile.toOsSpecific(), 'r').read() - tfile.unlink() - if ':' in lipoData: - arches = lipoData.rsplit(':', 1)[1] - arches = arches.split() - - if arches == [arch]: - # The file only contains the one architecture that - # we want anyway. - file.filename.setBinary() - self.multifile.addSubfile(file.newName, file.filename, compressionLevel) - return True - - if arch not in arches: - # The file doesn't support the architecture that we - # want at all. Omit the file. - self.notify.warning("%s doesn't support architecture %s" % ( - file.filename, self.arch)) - return False - - # The file contains multiple architectures. Get - # out just the one we want. - command = '/usr/bin/lipo -thin %s -output "%s" "%s"' % ( - arch, tfile.toOsSpecific(), - file.filename.toOsSpecific()) - exitStatus = os.system(command) - if exitStatus != 0: - self.notify.error('Command failed: %s' % (command)) - self.multifile.addSubfile(file.newName, tfile, compressionLevel) - if file.deleteTemp: - file.filename.unlink() - file.filename = tfile - file.deleteTemp = True - return True - - - def requirePackage(self, package): - """ Indicates a dependency on the given package. This - also implicitly requires all of the package's requirements - as well (though this transitive requirement happens at - runtime, not here at build time). """ - - if package not in self.requires: - self.requires.append(package) - for lowerName in package.targetFilenames: - ext = Filename(lowerName).getExtension() - if ext not in self.packager.nonuniqueExtensions: - self.skipFilenames[lowerName] = True - - for moduleName, mdef in package.moduleNames.items(): - if not mdef.exclude: - self.skipModules[moduleName] = mdef - - # Packager constructor - def __init__(self, platform = None): - - # The following are config settings that the caller may adjust - # before calling any of the command methods. - - # The platform string. - self.setPlatform(platform) - - # This should be set to a Filename. - self.installDir = None - - # If specified, this is a directory to search first for any - # library references, before searching the system. - # Particularly useful on OSX to reference the universal SDK. - self.systemRoot = None - - # Set this true to treat setHost() the same as addHost(), thus - # ignoring any request to specify a particular download host, - # e.g. for testing and development. - self.ignoreSetHost = False - - # Set this to true to verbosely log files ignored by dir(). - self.verbosePrint = False - - # This will be appended to the basename of any .p3d package, - # before the .p3d extension. - self.p3dSuffix = '' - - # The download URL at which these packages will eventually be - # hosted. - self.hosts = {} - self.host = PandaSystem.getPackageHostUrl() - self.addHost(self.host) - - # This will be used when we're not compiling in the packaged - # environment. - self.__hostInfos = {} - self.http = HTTPClient.getGlobalPtr() - - # The maximum amount of time a client should cache the - # contents.xml before re-querying the server, in seconds. - self.maxAge = 0 - - # The contents seq: a tuple of integers, representing the - # current seq value. The contents seq generally increments - # with each modification to the contents.xml file. There is - # also a package seq for each package, which generally - # increments with each modification to the package. - - # The contents seq and package seq are used primarily for - # documentation purposes, to note when a new version is - # released. The package seq value can also be used to verify - # that the contents.xml, desc.xml, and desc.import.xml files - # were all built at the same time. - - # Although the package seqs are used at runtime to verify that - # the latest contents.xml file has been downloaded, they are - # not otherwise used at runtime, and they are not binding on - # the download version. The md5 hash, not the package seq, is - # actually used to differentiate different download versions. - self.contentsSeq = SeqValue() - - # A search list for previously-built local packages. - - # We use a bit of caution to read the Filenames out of the - # config variable. Since cvar.getDirectories() returns a list - # of references to Filename objects stored within the config - # variable itself, we have to make a copy of each Filename - # returned, so they will persist beyond the lifespan of the - # config variable. - cvar = ConfigVariableSearchPath('pdef-path') - self.installSearch = list(map(Filename, cvar.getDirectories())) - - # This is where we cache the location of libraries. - self.libraryCache = {} - - # The system PATH, for searching dll's and exe's. - self.executablePath = DSearchPath() - - # By convention, we include sys.path at the front of - # self.executablePath, mainly to aid makepanda when building - # an rtdist build. - for dirname in sys.path: - self.executablePath.appendDirectory(Filename.fromOsSpecific(dirname)) - - # Now add the actual system search path. - if self.platform.startswith('win'): - self.addWindowsSearchPath(self.executablePath, "PATH") - - else: - if self.platform.startswith('osx'): - self.addPosixSearchPath(self.executablePath, "DYLD_LIBRARY_PATH") - - self.addPosixSearchPath(self.executablePath, "LD_LIBRARY_PATH") - self.addPosixSearchPath(self.executablePath, "PATH") - - if self.platform.startswith('linux'): - # It used to be okay to just add some common paths on Linux. - # But nowadays, each distribution has their own convention for - # where they put their libraries. Instead, we query the ldconfig - # cache, which contains the location of all libraries. - - if not self.loadLdconfigCache(): - # Ugh, failure. All that remains is to guess. This should - # work for the most common Debian configurations. - multiarchDir = "/lib/%s-linux-gnu" % (os.uname()[4]) - if os.path.isdir(multiarchDir): - self.executablePath.appendDirectory(multiarchDir) - if os.path.isdir("/usr/" + multiarchDir): - self.executablePath.appendDirectory("/usr/" + multiarchDir) - - else: - # FreeBSD, or some other system that still makes sense. - self.executablePath.appendDirectory('/lib') - self.executablePath.appendDirectory('/usr/lib') - self.executablePath.appendDirectory('/usr/local/lib') - - if self.platform.startswith('freebsd') and os.uname()[1] == "pcbsd": - self.executablePath.appendDirectory('/usr/PCBSD/local/lib') - - # Set this flag true to automatically add allow_python_dev to - # any applications. - self.allowPythonDev = False - - # Set this flag to store the original Python source files, - # without compiling them to .pyc or .pyo. - self.storePythonSource = False - - # Fill this with a list of (certificate, chain, pkey, - # password) tuples to automatically sign each p3d file - # generated. - self.signParams = [] - - # Optional signing and encrypting features. - self.encryptionKey = None - self.prcEncryptionKey = None - self.prcSignCommand = None - - # This is a list of filename extensions and/or basenames that - # indicate files that should be encrypted within the - # multifile. This provides obfuscation only, not real - # security, since the decryption key must be part of the - # client and is therefore readily available to any hacker. - # Not only is this feature useless, but using it also - # increases the size of your patchfiles, since encrypted files - # can't really be patched. But it's here if you really want - # it. ** Note: Actually, this isn't implemented yet. - #self.encryptExtensions = [] - #self.encryptFiles = [] - - # This is the list of DC import suffixes that should be - # available to the client. Other suffixes, like AI and UD, - # are server-side only and should be ignored by the Scrubber. - self.dcClientSuffixes = ['OV'] - - # Is this file system case-sensitive? - self.caseSensitive = True - if self.platform.startswith('win'): - self.caseSensitive = False - elif self.platform.startswith('osx'): - self.caseSensitive = False - - # Get the list of filename extensions that are recognized as - # image files. - self.imageExtensions = [] - for type in PNMFileTypeRegistry.getGlobalPtr().getTypes(): - self.imageExtensions += type.getExtensions() - - # Other useful extensions. The .pz extension is implicitly - # stripped. - - # Model files. - self.modelExtensions = [ 'egg', 'bam' ] - - # Text files that are copied (and compressed) to the package - # with end-of-line conversion. - self.textExtensions = [ 'prc', 'ptf', 'txt', 'cg', 'sha', 'dc', 'xml' ] - - # Binary files that are copied (and compressed) without - # processing. - self.binaryExtensions = [ 'ttf', 'TTF', 'mid', 'ico', 'cur' ] - - # Files that can have an existence in multiple different - # packages simultaneously without conflict. - self.nonuniqueExtensions = [ 'prc' ] - - # Files that represent an executable or shared library. - if self.platform.startswith('win'): - self.executableExtensions = [ 'dll', 'pyd', 'exe' ] - elif self.platform.startswith('osx'): - self.executableExtensions = [ 'so', 'dylib' ] - else: - self.executableExtensions = [ 'so' ] - - # Files that represent a Windows "manifest" file. These files - # must be explicitly extracted to disk so the OS can find - # them. - if self.platform.startswith('win'): - self.manifestExtensions = [ 'manifest' ] - else: - self.manifestExtensions = [ ] - - # Extensions that are automatically remapped by convention. - self.remapExtensions = {} - if self.platform.startswith('win'): - pass - elif self.platform.startswith('osx'): - self.remapExtensions = { - 'dll' : 'dylib', - 'pyd' : 'so', - 'exe' : '' - } - else: - self.remapExtensions = { - 'dll' : 'so', - 'pyd' : 'so', - 'exe' : '' - } - - # Files that should be extracted to disk. - self.extractExtensions = self.executableExtensions[:] + self.manifestExtensions[:] + [ 'ico', 'cur' ] - - # Files that indicate a platform dependency. - self.platformSpecificExtensions = self.executableExtensions[:] - - # Binary files that are considered uncompressible, and are - # copied without compression. - self.uncompressibleExtensions = [ 'mp3', 'ogg', 'ogv', 'wav', 'rml', 'rcss', 'otf' ] - # wav files are compressible, but p3openal_audio won't load - # them compressed. - # rml, rcss and otf files must be added here because - # libRocket wants to be able to seek in these files. - - # Files which are not to be processed further, but which - # should be added exactly byte-for-byte as they are. - self.unprocessedExtensions = [] - - # Files for which warnings should be suppressed when they are - # not handled by dir() - self.suppressWarningForExtensions = ['', 'pyc', 'pyo', - 'p3d', 'pdef', - 'c', 'C', 'cxx', 'cpp', 'h', 'H', - 'hpp', 'pp', 'I', 'pem', 'p12', 'crt', - 'o', 'obj', 'a', 'lib', 'bc', 'll'] - - # System files that should never be packaged. For - # case-insensitive filesystems (like Windows and OSX), put the - # lowercase filename here. Case-sensitive filesystems should - # use the correct case. - self.excludeSystemFiles = [ - 'kernel32.dll', 'user32.dll', 'wsock32.dll', 'ws2_32.dll', - 'advapi32.dll', 'opengl32.dll', 'glu32.dll', 'gdi32.dll', - 'shell32.dll', 'ntdll.dll', 'ws2help.dll', 'rpcrt4.dll', - 'imm32.dll', 'ddraw.dll', 'shlwapi.dll', 'secur32.dll', - 'dciman32.dll', 'comdlg32.dll', 'comctl32.dll', 'ole32.dll', - 'oleaut32.dll', 'gdiplus.dll', 'winmm.dll', 'iphlpapi.dll', - 'msvcrt.dll', 'kernelbase.dll', 'msimg32.dll', 'msacm32.dll', - - 'libsystem.b.dylib', 'libmathcommon.a.dylib', 'libmx.a.dylib', - 'libstdc++.6.dylib', 'libobjc.a.dylib', 'libauto.dylib', - ] - - # As above, but with filename globbing to catch a range of - # filenames. - self.excludeSystemGlobs = [ - GlobPattern('d3dx9_*.dll'), - GlobPattern('api-ms-win-*.dll'), - - GlobPattern('libGL.so*'), - GlobPattern('libGLU.so*'), - GlobPattern('libGLcore.so*'), - GlobPattern('libGLES*.so*'), - GlobPattern('libEGL.so*'), - GlobPattern('libX11.so*'), - GlobPattern('libXau.so*'), - GlobPattern('libXdmcp.so*'), - GlobPattern('libxcb*.so*'), - GlobPattern('libc.so*'), - GlobPattern('libgcc_s.so*'), - GlobPattern('libdl.so*'), - GlobPattern('libm.so*'), - GlobPattern('libnvidia*.so*'), - GlobPattern('libpthread.so*'), - GlobPattern('libthr.so*'), - GlobPattern('ld-linux.so*'), - GlobPattern('ld-linux-*.so*'), - GlobPattern('librt.so*'), - ] - - # A Loader for loading models. - self.loader = Loader.Loader(self) - self.sfxManagerList = None - self.musicManager = None - - # These options will be used when loading models and textures. By - # default we don't load textures beyond the header and don't store - # models in the RAM cache in order to conserve on memory usage. - opts = LoaderOptions() - opts.setFlags(opts.getFlags() | LoaderOptions.LFNoRamCache) - opts.setTextureFlags(opts.getTextureFlags() & ~LoaderOptions.TFPreload) - self.loaderOptions = opts - - # This is filled in during readPackageDef(). - self.packageList = [] - - # A table of all known packages by name. - self.packages = {} - - # A list of PackageEntry objects read from the contents.xml - # file. - self.contents = {} - - def loadLdconfigCache(self): - """ On GNU/Linux, runs ldconfig -p to find out where all the - libraries on the system are located. Assumes that the platform - has already been set. """ - - if not os.path.isfile('/sbin/ldconfig'): - return False - - handle = subprocess.Popen(['/sbin/ldconfig', '-p'], stdout=subprocess.PIPE, universal_newlines=True) - out, err = handle.communicate() - - if handle.returncode != 0: - self.notify.warning("/sbin/ldconfig -p returned code %d" %(handle.returncode)) - return False - - for line in out.splitlines(): - if '=>' not in line: - continue - - prefix, location = line.rsplit('=>', 1) - prefix = prefix.strip() - location = location.strip() - - if not location or not prefix or ' ' not in prefix: - self.notify.warning("Ignoring malformed ldconfig -p line: " + line) - continue - - lib, opts = prefix.split(' ', 1) - if ('x86-64' in opts) != self.platform.endswith('_amd64'): - # This entry isn't meant for our architecture. I think - # x86-64 is the only platform where ldconfig supplies - # this extra arch string. - continue - - self.libraryCache[lib] = Filename.fromOsSpecific(location) - - return True - - def resolveLibrary(self, filename): - """ Resolves the given shared library filename along the executable path, - or by cross-referencing it with the library cache. """ - - path = str(filename) - - if path in self.libraryCache: - filename.setFullpath(self.libraryCache[path].getFullpath()) - return True - - if filename.resolveFilename(self.executablePath): - self.libraryCache[path] = Filename(filename) - return True - - return False - - def setPlatform(self, platform = None): - """ Sets the platform that this Packager will compute for. On - OSX, this can be used to specify the particular architecture - we are building; on other platforms, it is probably a mistake - to set this. - - You should call this before doing anything else with the - Packager. It's even better to pass the platform string to the - constructor. """ - - self.platform = platform or PandaSystem.getPlatform() - - # OSX uses this "arch" string for the otool and lipo commands. - self.arch = None - if self.platform.startswith('osx_'): - self.arch = self.platform[4:] - - - def setHost(self, host, downloadUrl = None, - descriptiveName = None, hostDir = None, - mirrors = None): - """ Specifies the URL that will ultimately host these - contents. """ - - if not self.ignoreSetHost: - self.host = host - - self.addHost(host, downloadUrl = downloadUrl, - descriptiveName = descriptiveName, hostDir = hostDir, - mirrors = mirrors) - - def addHost(self, host, downloadUrl = None, descriptiveName = None, - hostDir = None, mirrors = None): - """ Adds a host to the list of known download hosts. This - information will be written into any p3d files that reference - this host; this can be used to pre-define the possible mirrors - for a given host, for instance. Returns the newly-created - HostEntry object.""" - - scheme = URLSpec(host).getScheme() - if scheme == 'https' and downloadUrl is None: - # If we specified an SSL-protected host URL, but no - # explicit download URL, then assume the download URL is - # the same, over cleartext. - url = URLSpec(host) - url.setScheme('http') - downloadUrl = url.getUrl() - - he = self.hosts.get(host, None) - if he is None: - # Define a new host entry - he = self.HostEntry(host, downloadUrl = downloadUrl, - descriptiveName = descriptiveName, - hostDir = hostDir, mirrors = mirrors) - self.hosts[host] = he - else: - # Update an existing host entry - if downloadUrl is not None: - he.downloadUrl = downloadUrl - if descriptiveName is not None: - he.descriptiveName = descriptiveName - if hostDir is not None: - he.hostDir = hostDir - if mirrors is not None: - he.mirrors = mirrors - - return he - - def addAltHost(self, keyword, altHost, origHost = None, - downloadUrl = None, descriptiveName = None, - hostDir = None, mirrors = None): - """ Adds an alternate host to any already-known host. This - defines an alternate server that may be contacted, if - specified on the HTML page, which hosts a different version of - the server's contents. (This is different from a mirror, - which hosts an identical version of the server's contents.) - """ - - if not origHost: - origHost = self.host - - self.addHost(altHost, downloadUrl = downloadUrl, - descriptiveName = descriptiveName, hostDir = hostDir, - mirrors = mirrors) - he = self.addHost(origHost) - he.altHosts[keyword] = altHost - - def addWindowsSearchPath(self, searchPath, varname): - """ Expands $varname, interpreting as a Windows-style search - path, and adds its contents to the indicated DSearchPath. """ - - path = ExecutionEnvironment.getEnvironmentVariable(varname) - if len(path) == 0: - if varname not in os.environ: - return - path = os.environ[varname] - for dirname in path.split(';'): - dirname = Filename.fromOsSpecific(dirname) - if dirname.makeTrueCase(): - searchPath.appendDirectory(dirname) - - def addPosixSearchPath(self, searchPath, varname): - """ Expands $varname, interpreting as a Posix-style search - path, and adds its contents to the indicated DSearchPath. """ - - path = ExecutionEnvironment.getEnvironmentVariable(varname) - if len(path) == 0: - if varname not in os.environ: - return - path = os.environ[varname] - for dirname in path.split(':'): - dirname = Filename.fromOsSpecific(dirname) - if dirname.makeTrueCase(): - searchPath.appendDirectory(dirname) - - def _ensureExtensions(self): - self.knownExtensions = \ - self.imageExtensions + \ - self.modelExtensions + \ - self.textExtensions + \ - self.binaryExtensions + \ - self.uncompressibleExtensions + \ - self.unprocessedExtensions - - def setup(self): - """ Call this method to initialize the class after filling in - some of the values in the constructor. """ - - self._ensureExtensions() - - self.currentPackage = None - - if self.installDir: - # If we were given an install directory, we can build - # packages as well as plain p3d files, and it all goes - # into the specified directory. - self.p3dInstallDir = self.installDir - self.allowPackages = True - else: - # If we don't have an actual install directory, we can - # only build p3d files, and we drop them into the current - # directory. - self.p3dInstallDir = '.' - self.allowPackages = False - - if not PandaSystem.getPackageVersionString() or not PandaSystem.getPackageHostUrl(): - raise PackagerError('This script must be run using a version of Panda3D that has been built\nfor distribution. Try using ppackage.p3d or packp3d.p3d instead.\nIf you are running this script for development purposes, you may also\nset the Config variable panda-package-host-url to the URL you expect\nto download these contents from (for instance, a file:// URL).') - - self.readContentsFile() - - def close(self): - """ Called after reading all of the package def files, this - performs any final cleanup appropriate. """ - - self.writeContentsFile() - - def buildPatches(self, packages): - """ Call this after calling close(), to build patches for the - indicated packages. """ - - # We quietly ignore any p3d applications or solo packages - # passed in the packages list; we only build patches for - # actual Multifile-based packages. - packageNames = [] - for package in packages: - if not package.p3dApplication and not package.solo: - packageNames.append(package.packageName) - - if packageNames: - from .PatchMaker import PatchMaker - pm = PatchMaker(self.installDir) - pm.buildPatches(packageNames = packageNames) - - def readPackageDef(self, packageDef, packageNames = None): - """ Reads the named .pdef file and constructs the named - packages, or all packages if packageNames is None. Raises an - exception if the pdef file is invalid. Returns the list of - packages constructed. """ - - self.notify.info('Reading %s' % (packageDef)) - - # We use exec to "read" the .pdef file. This has the nice - # side-effect that the user can put arbitrary Python code in - # there to control conditional execution, and such. - - # Set up the namespace dictionary for exec. - globals = {} - globals['__name__'] = packageDef.getBasenameWoExtension() - globals['__dir__'] = Filename(packageDef.getDirname()).toOsSpecific() - globals['__file__'] = packageDef.toOsSpecific() - globals['packageDef'] = packageDef - - globals['platform'] = self.platform - globals['packager'] = self - - # We'll stuff all of the predefined functions, and the - # predefined classes, in the global dictionary, so the pdef - # file can reference them. - - # By convention, the existence of a method of this class named - # do_foo(self) is sufficient to define a pdef method call - # foo(). - for methodName in list(self.__class__.__dict__.keys()): - if methodName.startswith('do_'): - name = methodName[3:] - c = func_closure(name) - globals[name] = c.generic_func - - globals['p3d'] = class_p3d - globals['package'] = class_package - globals['solo'] = class_solo - - # Now exec the pdef file. Assuming there are no syntax - # errors, and that the pdef file doesn't contain any really - # crazy Python code, all this will do is fill in the - # '__statements' list in the module scope. - fn = packageDef.toOsSpecific() - f = open(fn) - code = compile(f.read(), fn, 'exec') - f.close() - - # It appears that having a separate globals and locals - # dictionary causes problems with resolving symbols within a - # class scope. So, we just use one dictionary, the globals. - exec(code, globals) - - packages = [] - - # Now iterate through the statements and operate on them. - statements = globals.get('__statements', []) - if not statements: - self.notify.info("No packages defined.") - - try: - for (lineno, stype, name, args, kw) in statements: - if stype == 'class': - if packageNames is None or name in packageNames: - classDef = globals[name] - p3dApplication = (class_p3d in classDef.__bases__) - solo = (class_solo in classDef.__bases__) - self.beginPackage(name, p3dApplication = p3dApplication, - solo = solo) - statements = classDef.__dict__.get('__statements', []) - if not statements: - self.notify.info("No files added to %s" % (name)) - for (lineno, stype, sname, args, kw) in statements: - if stype == 'class': - raise PackagerError('Nested classes not allowed') - self.__evalFunc(sname, args, kw) - package = self.endPackage() - if package is not None: - packages.append(package) - elif packageNames is not None: - # If the name is explicitly specified, this means - # we should abort if the package faild to construct. - raise PackagerError('Failed to construct %s' % name) - else: - self.__evalFunc(name, args, kw) - except PackagerError: - # Append the line number and file name to the exception - # error message. - inst = sys.exc_info()[1] - if not inst.args: - inst.args = ('Error',) - - inst.args = (inst.args[0] + ' on line %s of %s' % (lineno, packageDef),) - raise - - return packages - - def __evalFunc(self, name, args, kw): - """ This is called from readPackageDef(), above, to call the - function do_name(*args, **kw), as extracted from the pdef - file. """ - - funcname = 'do_%s' % (name) - func = getattr(self, funcname) - try: - func(*args, **kw) - except OutsideOfPackageError: - message = '%s encountered outside of package definition' % (name) - raise OutsideOfPackageError(message) - - def __expandTabs(self, line, tabWidth = 8): - """ Expands tab characters in the line to 8 spaces. """ - p = 0 - while p < len(line): - if line[p] == '\t': - # Expand a tab. - nextStop = ((p + tabWidth) / tabWidth) * tabWidth - numSpaces = nextStop - p - line = line[:p] + ' ' * numSpaces + line[p + 1:] - p = nextStop - else: - p += 1 - - return line - - def __countLeadingWhitespace(self, line): - """ Returns the number of leading whitespace characters in the - line. """ - - line = self.__expandTabs(line) - return len(line) - len(line.lstrip()) - - def __stripLeadingWhitespace(self, line, whitespaceCount): - """ Removes the indicated number of whitespace characters, but - no more. """ - - line = self.__expandTabs(line) - line = line[:whitespaceCount].lstrip() + line[whitespaceCount:] - return line - - def __parseArgs(self, words, argList): - args = {} - - while len(words) > 1: - arg = words[-1] - if '=' not in arg: - return args - - parameter, value = arg.split('=', 1) - parameter = parameter.strip() - value = value.strip() - if parameter not in argList: - message = 'Unknown parameter %s' % (parameter) - raise PackagerError(message) - if parameter in args: - message = 'Duplicate parameter %s' % (parameter) - raise PackagerError(message) - - args[parameter] = value - - del words[-1] - - - def beginPackage(self, packageName, p3dApplication = False, - solo = False): - """ Begins a new package specification. packageName is the - basename of the package. Follow this with a number of calls - to file() etc., and close the package with endPackage(). """ - - if self.currentPackage: - raise PackagerError('unclosed endPackage %s' % (self.currentPackage.packageName)) - - package = self.Package(packageName, self) - self.currentPackage = package - - package.p3dApplication = p3dApplication - package.solo = solo - - if not package.p3dApplication and not self.allowPackages: - message = 'Cannot generate packages without an installDir; use -i' - raise PackagerError(message) - - - def endPackage(self): - """ Closes the current package specification. This actually - generates the package file. Returns the finished package, - or None if the package failed to close (e.g. missing files). """ - - if not self.currentPackage: - raise PackagerError('unmatched endPackage') - - package = self.currentPackage - package.signParams += self.signParams[:] - - self.currentPackage = None - if not package.close(): - return None - - self.packageList.append(package) - self.packages[(package.packageName, package.platform, package.version)] = package - self.currentPackage = None - - return package - - def findPackage(self, packageName, platform = None, version = None, - host = None, requires = None): - """ Searches for the named package from a previous publish - operation along the install search path. - - If requires is not None, it is a list of Package objects that - are already required. The new Package object must be - compatible with the existing Packages, or an error is - returned. This is also useful for determining the appropriate - package version to choose when a version is not specified. - - Returns the Package object, or None if the package cannot be - located. """ - - # Is it a package we already have resident? - package = self.packages.get((packageName, platform or self.platform, version, host), None) - if package: - return package - - # Look on the searchlist. - for dirname in self.installSearch: - package = self.__scanPackageDir(dirname, packageName, platform or self.platform, version, host, requires = requires) - if not package: - package = self.__scanPackageDir(dirname, packageName, platform, version, host, requires = requires) - - if package and host and package.host != host: - # Wrong host. - package = None - - if package: - break - - if not package: - # Query the indicated host. - package = self.__findPackageOnHost(packageName, platform or self.platform, version or None, host, requires = requires) - if not package: - package = self.__findPackageOnHost(packageName, platform, version, host, requires = requires) - - if package: - package = self.packages.setdefault((package.packageName, package.platform, package.version, package.host), package) - self.packages[(packageName, platform or self.platform, version, host)] = package - return package - - return None - - def __scanPackageDir(self, rootDir, packageName, platform, version, - host, requires = None): - """ Scans a directory on disk, looking for *.import.xml files - that match the indicated packageName and optional version. If a - suitable xml file is found, reads it and returns the assocated - Package definition. - - If a version is not specified, and multiple versions are - available, the highest-numbered version that matches will be - selected. - """ - - packages = [] - - if version: - # A specific version package. - versionList = [version] - else: - # An unversioned package, or any old version. - versionList = [None, '*'] - - for version in versionList: - packageDir = Filename(rootDir, packageName) - basename = packageName - - if version: - # A specific or nonspecific version package. - packageDir = Filename(packageDir, version) - basename += '.%s' % (version) - - if platform: - packageDir = Filename(packageDir, platform) - basename += '.%s' % (platform) - - # Actually, the host means little for this search, since we're - # only looking in a local directory at this point. - - basename += '.import.xml' - filename = Filename(packageDir, basename) - filelist = glob.glob(filename.toOsSpecific()) - if not filelist: - # It doesn't exist in the nested directory; try the root - # directory. - filename = Filename(rootDir, basename) - filelist = glob.glob(filename.toOsSpecific()) - - for file in filelist: - package = self.__readPackageImportDescFile(Filename.fromOsSpecific(file)) - packages.append(package) - - self.__sortImportPackages(packages) - for package in packages: - if package and self.__packageIsValid(package, requires, platform): - return package - - return None - - def __findPackageOnHost(self, packageName, platform, version, hostUrl, requires = None): - appRunner = AppRunnerGlobal.appRunner - - # Make sure we have a fresh version of the contents file. - host = self.__getHostInfo(hostUrl) - if not host.downloadContentsFile(self.http): - return None - - packageInfos = [] - packageInfo = host.getPackage(packageName, version, platform = platform) - if not packageInfo and not version: - # No explicit version is specified, first fallback: look - # for the compiled-in version. - packageInfo = host.getPackage(packageName, PandaSystem.getPackageVersionString(), platform = platform) - - if not packageInfo and not version: - # No explicit version is specified, second fallback: get - # the highest-numbered version available. - packageInfos = host.getPackages(packageName, platform = platform) - self.__sortPackageInfos(packageInfos) - - if packageInfo and not packageInfos: - packageInfos = [packageInfo] - - for packageInfo in packageInfos: - if not packageInfo or not packageInfo.importDescFile: - continue - - # Now we've retrieved a PackageInfo. Get the import desc file - # from it. - if host.hostDir: - filename = Filename(host.hostDir, 'imports/' + packageInfo.importDescFile.basename) - else: - # We're not running in the packaged environment, so download - # to a temporary file instead of the host directory. - filename = Filename.temporary('', 'import_' + packageInfo.importDescFile.basename, '.xml') - - if not host.freshenFile(self.http, packageInfo.importDescFile, filename): - self.notify.error("Couldn't download import file.") - continue - - # Now that we have the import desc file, use it to load one of - # our Package objects. - package = self.Package('', self) - success = package.readImportDescFile(filename) - - if not host.hostDir: - # Don't forget to delete the temporary file we created. - filename.unlink() - - if success and self.__packageIsValid(package, requires, platform): - return package - - # Couldn't find a suitable package. - return None - - def __getHostInfo(self, hostUrl = None): - """ This shadows appRunner.getHost(), for the purpose of running - outside the packaged environment. """ - - if not hostUrl: - hostUrl = PandaSystem.getPackageHostUrl() - - if AppRunnerGlobal.appRunner: - return AppRunnerGlobal.appRunner.getHost(hostUrl) - - host = self.__hostInfos.get(hostUrl, None) - if not host: - host = HostInfo(hostUrl) - self.__hostInfos[hostUrl] = host - return host - - def __sortImportPackages(self, packages): - """ Given a list of Packages read from *.import.xml filenames, - sorts them in reverse order by version, so that the - highest-numbered versions appear first in the list. """ - - tuples = [] - for package in packages: - version = self.__makeVersionTuple(package.version) - tuples.append((version, package)) - tuples.sort(reverse = True) - - return [t[1] for t in tuples] - - def __sortPackageInfos(self, packages): - """ Given a list of PackageInfos retrieved from a Host, sorts - them in reverse order by version, so that the highest-numbered - versions appear first in the list. """ - - tuples = [] - for package in packages: - version = self.__makeVersionTuple(package.packageVersion) - tuples.append((version, package)) - tuples.sort(reverse = True) - - return [t[1] for t in tuples] - - def __makeVersionTuple(self, version): - """ Converts a version string into a tuple for sorting, by - separating out numbers into separate numeric fields, so that - version numbers sort numerically where appropriate. """ - - if not version: - return ('',) - - words = [] - p = 0 - while p < len(version): - # Scan to the first digit. - w = '' - while p < len(version) and not version[p].isdigit(): - w += version[p] - p += 1 - words.append(w) - - # Scan to the end of the string of digits. - w = '' - while p < len(version) and version[p].isdigit(): - w += version[p] - p += 1 - if w: - words.append(int(w)) - - return tuple(words) - - def __packageIsValid(self, package, requires, platform): - """ Returns true if the package is valid, meaning it can be - imported without conflicts with existing packages already - required (such as different versions of panda3d). """ - - if package.platform and package.platform != platform: - # Incorrect platform. - return False - - if not requires: - # No other restrictions. - return True - - # Really, we only check the panda3d package. The other - # packages will list this as a dependency, and this is all - # that matters. - - panda1 = self.__findPackageInRequires('panda3d', [package] + package.requires) - panda2 = self.__findPackageInRequires('panda3d', requires) - - if not panda1 or not panda2: - return True - - if panda1.version == panda2.version: - return True - - print('Rejecting package %s, version "%s": depends on %s, version "%s" instead of version "%s"' % ( - package.packageName, package.version, - panda1.packageName, panda1.version, panda2.version)) - return False - - def __findPackageInRequires(self, packageName, list): - """ Returns the first package with the indicated name in the - list of packages, or in the list of packages required by the - packages in the list. """ - - for package in list: - if package.packageName == packageName: - return package - p2 = self.__findPackageInRequires(packageName, package.requires) - if p2: - return p2 - - return None - - def __readPackageImportDescFile(self, filename): - """ Reads the named xml file as a Package, and returns it if - valid, or None otherwise. """ - - package = self.Package('', self) - if package.readImportDescFile(filename): - return package - - return None - - def do_setVer(self, value): - """ Sets an explicit set_ver number for the package, as a tuple - of integers, or as a string of dot-separated integers. """ - - self.currentPackage.packageSetVer = SeqValue(value) - - def do_config(self, **kw): - """ Called with any number of keyword parameters. For each - keyword parameter, sets the corresponding p3d config variable - to the given value. This will be written into the - p3d_info.xml file at the top of the application, or to the - package desc file for a package file. """ - - if not self.currentPackage: - raise OutsideOfPackageError - - for keyword, value in list(kw.items()): - self.currentPackage.configs[keyword] = value - - def do_require(self, *args, **kw): - """ Indicates a dependency on the named package(s), supplied - as a name. - - Attempts to install this package will implicitly install the - named package also. Files already included in the named - package will be omitted from this one when building it. """ - - self.requirePackagesNamed(args, **kw) - - def requirePackagesNamed(self, names, version = None, host = None): - """ Indicates a dependency on the named package(s), supplied - as a name. - - Attempts to install this package will implicitly install the - named package also. Files already included in the named - package will be omitted from this one when building it. """ - - if not self.currentPackage: - raise OutsideOfPackageError - - for packageName in names: - # A special case when requiring the "panda3d" package. We - # supply the version number which we've been compiled with - # as a default. - pversion = version - phost = host - if packageName == 'panda3d': - if not pversion: - pversion = PandaSystem.getPackageVersionString() - if not phost: - phost = PandaSystem.getPackageHostUrl() - - package = self.findPackage(packageName, version = pversion, host = phost, - requires = self.currentPackage.requires) - if not package: - message = 'Unknown package %s, version "%s"' % (packageName, version) - self.notify.warning(message) - self.currentPackage.missingPackages.append((packageName, pversion)) - continue - - self.requirePackage(package) - - def requirePackage(self, package): - """ Indicates a dependency on the indicated package, supplied - as a Package object. - - Attempts to install this package will implicitly install the - named package also. Files already included in the named - package will be omitted from this one. """ - - if not self.currentPackage: - raise OutsideOfPackageError - - # A special case when requiring the "panda3d" package. We - # complain if the version number doesn't match what we've been - # compiled with. - if package.packageName == 'panda3d': - if package.version != PandaSystem.getPackageVersionString(): - self.notify.warning("Requiring panda3d version %s, which does not match the current build of Panda, which is version %s." % (package.version, PandaSystem.getPackageVersionString())) - elif package.host != PandaSystem.getPackageHostUrl(): - self.notify.warning("Requiring panda3d host %s, which does not match the current build of Panda, which is host %s." % (package.host, PandaSystem.getPackageHostUrl())) - - self.currentPackage.requirePackage(package) - - def do_module(self, *args, **kw): - """ Adds the indicated Python module(s) to the current package. """ - self.addModule(args, **kw) - - def addModule(self, moduleNames, newName = None, filename = None, required = False): - if not self.currentPackage: - raise OutsideOfPackageError - - if (newName or filename) and len(moduleNames) != 1: - raise PackagerError('Cannot specify newName with multiple modules') - - if required: - self.currentPackage.requiredModules += moduleNames - - for moduleName in moduleNames: - self.currentPackage.freezer.addModule(moduleName, newName = newName, filename = filename) - - def do_excludeModule(self, *args): - """ Marks the indicated Python module as not to be included. """ - - if not self.currentPackage: - raise OutsideOfPackageError - - for moduleName in args: - self.currentPackage.freezer.excludeModule(moduleName) - - def do_main(self, filename): - """ Includes the indicated file as __main__ module of the application. - Also updates mainModule to point to this module. """ - - self.addModule(['__main__'], '__main__', filename, required = True) - self.currentPackage.mainModule = ('__main__', '__main__') - - def do_mainModule(self, moduleName, newName = None, filename = None): - """ Names the indicated module as the "main" module of the - application or exe. In most cases, you will want to use main() - instead. """ - - if not self.currentPackage: - raise OutsideOfPackageError - - if self.currentPackage.mainModule and self.currentPackage.mainModule[0] != moduleName: - self.notify.warning("Replacing mainModule %s with %s" % ( - self.currentPackage.mainModule[0], moduleName)) - - if not newName: - newName = moduleName - - if filename: - filename = Filename(filename) - newFilename = Filename('/'.join(moduleName.split('.'))) - newFilename.setExtension(filename.getExtension()) - self.currentPackage.addFile( - filename, newName = str(newFilename), - explicit = True, extract = True, required = True) - - self.currentPackage.mainModule = (moduleName, newName) - - def do_sign(self, certificate, chain = None, pkey = None, password = None): - """ Signs the resulting p3d file (or package multifile) with - the indicated certificate. If needed, the chain file should - contain the list of additional certificate authorities needed - to validate the signing certificate. The pkey file should - contain the private key. - - It is also legal for the certificate file to contain the chain - and private key embedded within it. - - If the private key is encrypted, the password should be - supplied. """ - - self.currentPackage.signParams.append((certificate, chain, pkey, password)) - - def do_setupPanda3D(self, p3dpythonName=None, p3dpythonwName=None): - """ A special convenience command that adds the minimum - startup modules for a panda3d package, intended for developers - producing their own custom panda3d for download. Should be - called before any other Python modules are named. """ - - # This module and all its dependencies come frozen into p3dpython. - # We should mark them as having already been added so that we don't - # add them again to the Multifile. - self.do_module('direct.showbase.VFSImporter') - self.currentPackage.freezer.done(addStartupModules=True) - self.currentPackage.freezer.writeCode(None) - self.currentPackage.addExtensionModules() - self.currentPackage.freezer.reset() - - self.do_file('panda3d/core.pyd', newDir='panda3d') - - # This is the key Python module that is imported at runtime to - # start an application running. - self.do_module('direct.p3d.AppRunner') - - # This is the main program that drives the runtime Python. It - # is responsible for importing direct.p3d.AppRunner to start an - # application running. The program comes in two parts: an - # executable, and an associated dynamic library. Note that the - # .exe and .dll extensions are automatically replaced with the - # appropriate platform-specific extensions. - - if self.platform.startswith('osx'): - # On Mac, we package up a P3DPython.app bundle. This - # includes specifications in the plist file to avoid - # creating a dock icon and stuff. - - resources = [] - - # Find p3dpython.plist in the direct source tree. - import direct - plist = Filename(direct.__path__[0], 'plugin/p3dpython.plist') - -## # Find panda3d.icns in the models tree. -## filename = Filename('plugin_images/panda3d.icns') -## found = filename.resolveFilename(getModelPath().getValue()) -## if not found: -## found = filename.resolveFilename("models") -## if found: -## resources.append(filename) - - self.do_makeBundle('P3DPython.app', plist, executable = 'p3dpython', - resources = resources, dependencyDir = '') - - else: - # Anywhere else, we just ship the executable file p3dpython.exe. - if p3dpythonName is None: - p3dpythonName = 'p3dpython' - else: - self.do_config(p3dpython_name=p3dpythonName) - - if self.platform.startswith('win'): - self.do_file('p3dpython.exe', newName=p3dpythonName+'.exe') - else: - self.do_file('p3dpython.exe', newName=p3dpythonName) - - # The "Windows" executable appends a 'w' to whatever name is used - # above, unless an override name is explicitly specified. - if self.platform.startswith('win'): - if p3dpythonwName is None: - p3dpythonwName = p3dpythonName+'w' - else: - self.do_config(p3dpythonw_name=p3dpythonwName) - - if self.platform.startswith('win'): - self.do_file('p3dpythonw.exe', newName=p3dpythonwName+'.exe') - else: - self.do_file('p3dpythonw.exe', newName=p3dpythonwName) - - self.do_file('libp3dpython.dll') - - def do_freeze(self, filename, compileToExe = False): - """ Freezes all of the current Python code into either an - executable (if compileToExe is true) or a dynamic library (if - it is false). The resulting compiled binary is added to the - current package under the indicated filename. The filename - should not include an extension; that will be added. """ - - if not self.currentPackage: - raise OutsideOfPackageError - - package = self.currentPackage - freezer = package.freezer - - if package.mainModule and not compileToExe: - self.notify.warning("Ignoring main_module for dll %s" % (filename)) - package.mainModule = None - if not package.mainModule and compileToExe: - message = "No main_module specified for exe %s" % (filename) - raise PackagerError(message) - - if package.mainModule: - moduleName, newName = package.mainModule - if compileToExe: - # If we're producing an exe, the main module must - # be called "__main__". - newName = '__main__' - package.mainModule = (moduleName, newName) - - if newName not in freezer.modules: - freezer.addModule(moduleName, newName = newName) - else: - freezer.modules[newName] = freezer.modules[moduleName] - freezer.done(addStartupModules = compileToExe) - - dirname = '' - basename = filename - if '/' in basename: - dirname, basename = filename.rsplit('/', 1) - dirname += '/' - - basename = freezer.generateCode(basename, compileToExe = compileToExe) - - package.addFile(Filename(basename), newName = dirname + basename, - deleteTemp = True, explicit = True, extract = True) - package.addExtensionModules() - if not package.platform: - package.platform = self.platform - - # Reset the freezer for more Python files. - freezer.reset() - package.mainModule = None - - def do_makeBundle(self, bundleName, plist, executable = None, - resources = None, dependencyDir = None): - """ Constructs a minimal OSX "bundle" consisting of an - executable and a plist file, with optional resource files - (such as icons), and adds it to the package under the given - name. """ - - contents = bundleName + '/Contents' - - self.addFiles([plist], newName = contents + '/Info.plist', - extract = True) - if executable: - basename = Filename(executable).getBasename() - self.addFiles([executable], newName = contents + '/MacOS/' + basename, - extract = True, executable = True, dependencyDir = dependencyDir) - if resources: - self.addFiles(resources, newDir = contents + '/Resources', - extract = True, dependencyDir = dependencyDir) - - - - def do_file(self, *args, **kw): - """ Adds the indicated file or files to the current package. - See addFiles(). """ - - self.addFiles(args, **kw) - - def addFiles(self, filenames, text = None, newName = None, - newDir = None, extract = None, executable = None, - deleteTemp = False, literal = False, - dependencyDir = None, required = False): - - """ Adds the indicated arbitrary files to the current package. - - filenames is a list of Filename or string objects, and each - may include shell globbing characters. - - Each file is placed in the named directory, or the toplevel - directory if no directory is specified. - - Certain special behavior is invoked based on the filename - extension. For instance, .py files may be automatically - compiled and stored as Python modules. - - If newDir is not None, it specifies the directory in which the - file should be placed. In this case, all files matched by the - filename expression are placed in the named directory. - - If newName is not None, it specifies a new filename. In this - case, newDir is ignored, and the filename expression must - match only one file. - - If newName and newDir are both None, the file is placed in the - toplevel directory, regardless of its source directory. - - If text is nonempty, it contains the text of the file. In - this case, the filename is not read, but the supplied text is - used instead. - - If extract is true, the file is explicitly extracted at - runtime. - - If executable is true, the file is marked as an executable - filename, for special treatment. - - If deleteTemp is true, the file is a temporary file and will - be deleted after its contents are copied to the package. - - If literal is true, then the file extension will be respected - exactly as it appears, and glob characters will not be - expanded. If this is false, then .dll or .exe files will be - renamed to .dylib and no extension on OSX (or .so on Linux); - and glob characters will be expanded. - - If required is true, then the file is marked a vital part of - the package. The package will not be built if this file - somehow cannot be added to the package. - - """ - - if not self.currentPackage: - raise OutsideOfPackageError - - files = [] - explicit = True - - for filename in filenames: - filename = Filename(filename) - - if literal: - thisFiles = [filename.toOsSpecific()] - - else: - ext = filename.getExtension() - - # A special case, since OSX and Linux don't have a - # standard extension for program files. - if executable is None and ext == 'exe': - executable = True - - newExt = self.remapExtensions.get(ext, None) - if newExt is not None: - filename.setExtension(newExt) - - thisFiles = glob.glob(filename.toOsSpecific()) - if not thisFiles: - thisFiles = [filename.toOsSpecific()] - - if newExt == 'dll' or (ext == 'dll' and newExt is None): - # Go through the dsoFilename interface on Windows, - # to insert a _d if we are running on a debug - # build. - dllFilename = Filename(filename) - dllFilename.setExtension('so') - dllFilename = Filename.dsoFilename(str(dllFilename)) - if dllFilename != filename: - thisFiles = glob.glob(filename.toOsSpecific()) - if not thisFiles: - # We have to resolve this filename to - # determine if it's a _d or not. - if self.resolveLibrary(dllFilename): - thisFiles = [dllFilename.toOsSpecific()] - else: - thisFiles = [filename.toOsSpecific()] - - if len(thisFiles) > 1: - explicit = False - files += thisFiles - - prefix = '' - if newDir is not None: - prefix = str(Filename(newDir)) - if prefix and prefix[-1] != '/': - prefix += '/' - - if newName: - if len(files) != 1: - message = 'Cannot install multiple files on target filename %s' % (newName) - raise PackagerError(message) - - if text: - if len(files) != 1: - message = 'Cannot install text to multiple files' - raise PackagerError(message) - if not newName: - newName = str(filenames[0]) - - for filename in files: - filename = Filename.fromOsSpecific(filename) - basename = filename.getBasename() - name = newName - if not name: - name = prefix + basename - - self.currentPackage.addFile( - filename, newName = name, extract = extract, - explicit = explicit, executable = executable, - text = text, deleteTemp = deleteTemp, - dependencyDir = dependencyDir, required = required) - - def do_exclude(self, filename): - """ Marks the indicated filename as not to be included. The - filename may include shell globbing characters, and may or may - not include a dirname. (If it does not include a dirname, it - refers to any file with the given basename from any - directory.)""" - - if not self.currentPackage: - raise OutsideOfPackageError - - filename = Filename(filename) - self.currentPackage.excludeFile(filename) - - - def do_includeExtensions(self, executableExtensions = None, extractExtensions = None, - imageExtensions = None, textExtensions = None, - uncompressibleExtensions = None, unprocessedExtensions = None, - suppressWarningForExtensions = None): - """ Ensure that dir() will include files with the given extensions. - The extensions should not have '.' prefixes. - - All except 'suppressWarningForExtensions' allow the given kinds of files - to be packaged with their respective semantics (read the source). - - 'suppressWarningForExtensions' lists extensions *expected* to be ignored, - so no warnings will be emitted for them. - """ - if executableExtensions: - self.executableExtensions += executableExtensions - - if extractExtensions: - self.extractExtensions += extractExtensions - - if imageExtensions: - self.imageExtensions += imageExtensions - - if textExtensions: - self.textExtensions += textExtensions - - if uncompressibleExtensions: - self.uncompressibleExtensions += uncompressibleExtensions - - if unprocessedExtensions: - self.unprocessedExtensions += unprocessedExtensions - - if suppressWarningForExtensions: - self.suppressWarningForExtensions += suppressWarningForExtensions - - self._ensureExtensions() - - def do_dir(self, dirname, newDir = None, unprocessed = None): - - """ Adds the indicated directory hierarchy to the current - package. The directory hierarchy is walked recursively, and - all files that match a known extension are added to the package. - - newDir specifies the directory name within the package which - the contents of the named directory should be installed to. - If it is omitted, the contents of the named directory are - installed to the root of the package. - - If unprocessed is false (the default), bam files are loaded and - scanned for textures, and these texture paths within the bam - files are manipulated to point to the new paths within the - package. If unprocessed is true, this operation is bypassed, - and bam files are packed exactly as they are. - """ - - if not self.currentPackage: - raise OutsideOfPackageError - - dirname = Filename(dirname) - if not newDir: - newDir = '' - - # Adding the directory to sys.path is a cheesy way to help the - # modulefinder find it. - sys.path.append(dirname.toOsSpecific()) - self.__recurseDir(dirname, newDir, unprocessed = unprocessed) - - def __recurseDir(self, filename, newName, unprocessed = None, packageTree = None): - if filename.isDirectory(): - # It's a directory name. Recurse. - prefix = newName - if prefix and prefix[-1] != '/': - prefix += '/' - - # First check if this is a Python package tree. If so, add it - # implicitly as a module. - dirList = vfs.scanDirectory(filename) - for subfile in dirList: - filename = subfile.getFilename() - if filename.getBasename() == '__init__.py': - moduleName = newName.replace("/", ".") - self.addModule([moduleName], filename=filename) - - for subfile in dirList: - filename = subfile.getFilename() - self.__recurseDir(filename, prefix + filename.getBasename(), - unprocessed = unprocessed) - return - elif not filename.exists(): - # It doesn't exist. Perhaps it's a virtual file. Ignore it. - return - - # It's a file name. Add it. - ext = filename.getExtension() - if ext == 'py': - self.currentPackage.addFile(filename, newName = newName, - explicit = False, unprocessed = unprocessed) - else: - if ext == 'pz' or ext == 'gz': - # Strip off an implicit .pz extension. - newFilename = Filename(filename) - newFilename.setExtension('') - newFilename = Filename(str(newFilename)) - ext = newFilename.getExtension() - - if ext in self.knownExtensions: - if ext in self.textExtensions: - filename.setText() - else: - filename.setBinary() - self.currentPackage.addFile(filename, newName = newName, - explicit = False, unprocessed = unprocessed) - elif not ext in self.suppressWarningForExtensions: - newCount = self.currentPackage.ignoredDirFiles.get(ext, 0) + 1 - self.currentPackage.ignoredDirFiles[ext] = newCount - - if self.verbosePrint: - self.notify.warning("ignoring file %s" % filename) - - def readContentsFile(self): - """ Reads the contents.xml file at the beginning of - processing. """ - - self.hosts = {} - # Since we've blown away the self.hosts map, we have to make - # sure that our own host at least is added to the map. - self.addHost(self.host) - - self.maxAge = 0 - self.contentsSeq = SeqValue() - self.contents = {} - self.contentsChanged = False - - if not self.allowPackages: - # Don't bother. - return - - contentsFilename = Filename(self.installDir, 'contents.xml') - doc = TiXmlDocument(contentsFilename.toOsSpecific()) - if not doc.LoadFile(): - # Couldn't read file. - return - - xcontents = doc.FirstChildElement('contents') - if xcontents: - maxAge = xcontents.Attribute('max_age') - if maxAge: - self.maxAge = int(maxAge) - - self.contentsSeq.loadXml(xcontents) - - xhost = xcontents.FirstChildElement('host') - if xhost: - he = self.HostEntry() - he.loadXml(xhost, self) - self.hosts[he.url] = he - self.host = he.url - - xpackage = xcontents.FirstChildElement('package') - while xpackage: - pe = self.PackageEntry() - pe.loadXml(xpackage) - self.contents[pe.getKey()] = pe - xpackage = xpackage.NextSiblingElement('package') - - def writeContentsFile(self): - """ Rewrites the contents.xml file at the end of - processing. """ - - if not self.contentsChanged: - # No need to rewrite. - return - - contentsFilename = Filename(self.installDir, 'contents.xml') - doc = TiXmlDocument(contentsFilename.toOsSpecific()) - decl = TiXmlDeclaration("1.0", "utf-8", "") - doc.InsertEndChild(decl) - - xcontents = TiXmlElement('contents') - if self.maxAge: - xcontents.SetAttribute('max_age', str(self.maxAge)) - - self.contentsSeq += 1 - self.contentsSeq.storeXml(xcontents) - - if self.host: - he = self.hosts.get(self.host, None) - if he: - xhost = he.makeXml(packager = self) - xcontents.InsertEndChild(xhost) - - contents = sorted(self.contents.items()) - for key, pe in contents: - xpackage = pe.makeXml() - xcontents.InsertEndChild(xpackage) - - doc.InsertEndChild(xcontents) - doc.SaveFile() - - -# The following class and function definitions represent a few sneaky -# Python tricks to allow the pdef syntax to contain the pseudo-Python -# code they do. These tricks bind the function and class definitions -# into a bit table as they are parsed from the pdef file, so we can -# walk through that table later and perform the operations requested -# in order. - -class metaclass_def(type): - """ A metaclass is invoked by Python when the class definition is - read, for instance to define a child class. By defining a - metaclass for class_p3d and class_package, we can get a callback - when we encounter "class foo(p3d)" in the pdef file. The callback - actually happens after all of the code within the class scope has - been parsed first. """ - - def __new__(self, name, bases, dict): - - # At the point of the callback, now, "name" is the name of the - # class we are instantiating, "bases" is the list of parent - # classes, and "dict" is the class dictionary we have just - # parsed. - - # If "dict" contains __metaclass__, then we must be parsing - # class_p3d or class_ppackage, below--skip it. But if it - # doesn't contain __metaclass__, then we must be parsing - # "class foo(p3d)" (or whatever) from the pdef file. - - if '__metaclass__' not in dict: - # Get the context in which this class was created - # (presumably, the module scope) out of the stack frame. - frame = sys._getframe(1) - mdict = frame.f_locals - lineno = frame.f_lineno - - # Store the class name on a statements list in that - # context, so we can later resolve the class names in - # the order they appeared in the file. - mdict.setdefault('__statements', []).append((lineno, 'class', name, None, None)) - - return type.__new__(self, name, bases, dict) - -# Define these dynamically to stay compatible with Python 2 and 3. -class_p3d = metaclass_def(str('class_p3d'), (), {}) -class_package = metaclass_def(str('class_package'), (), {}) -class_solo = metaclass_def(str('class_solo'), (), {}) - -class func_closure: - - """ This class is used to create a closure on the function name, - and also allows the *args, **kw syntax. In Python, the lambda - syntax, used with default parameters, is used more often to create - a closure (that is, a binding of one or more variables into a - callable object), but that syntax doesn't work with **kw. - Fortunately, a class method is also a form of a closure, because - it binds self; and this doesn't have any syntax problems with - **kw. """ - - def __init__(self, name): - self.name = name - - def generic_func(self, *args, **kw): - """ This method is bound to all the functions that might be - called from the pdef file. It's a special function; when it is - called, it does nothing but store its name and arguments in the - caller's local scope, where they can be pulled out later. """ - - # Get the context in which this function was called (presumably, - # the class dictionary) out of the stack frame. - frame = sys._getframe(1) - cldict = frame.f_locals - lineno = frame.f_lineno - - # Store the function on a statements list in that context, so we - # can later walk through the function calls for each class. - cldict.setdefault('__statements', []).append((lineno, 'func', self.name, args, kw)) diff --git a/direct/src/p3d/PatchMaker.py b/direct/src/p3d/PatchMaker.py deleted file mode 100644 index c1624b3cc1..0000000000 --- a/direct/src/p3d/PatchMaker.py +++ /dev/null @@ -1,814 +0,0 @@ -__all__ = ["PatchMaker"] - -from direct.p3d.FileSpec import FileSpec -from direct.p3d.SeqValue import SeqValue -from panda3d.core import * -import copy - -class PatchMaker: - """ This class will operate on an existing package install - directory, as generated by the Packager, and create patchfiles - between versions as needed. It is also used at runtime, to apply - the downloaded patches. """ - - class PackageVersion: - """ A specific patch version of a package. This is not just - the package's "version" string; it also corresponds to the - particular patch version, which increments independently of - the "version". """ - - def __init__(self, packageName, platform, version, hostUrl, file): - self.packageName = packageName - self.platform = platform - self.version = version - self.hostUrl = hostUrl - self.file = file - self.printName = None - - # The Package object that produces this version, if this - # is the current, base, or top form, respectively. - self.packageCurrent = None - self.packageBase = None - self.packageTop = None - - # A list of patchfiles that can produce this version. - self.fromPatches = [] - - # A list of patchfiles that can start from this version. - self.toPatches = [] - - # A temporary file for re-creating the archive file for - # this version. - self.tempFile = None - - def cleanup(self): - if self.tempFile: - self.tempFile.unlink() - - def getPatchChain(self, startPv, alreadyVisited = []): - """ Returns a list of patches that, when applied in - sequence to the indicated PackageVersion object, will - produce this PackageVersion object. Returns None if no - chain can be found. """ - - if self is startPv: - # We're already here. A zero-length patch chain is - # therefore the answer. - return [] - if self in alreadyVisited: - # We've already been here; this is a loop. Avoid - # infinite recursion. - return None - - alreadyVisited = alreadyVisited[:] - alreadyVisited.append(self) - - bestPatchChain = None - for patchfile in self.fromPatches: - fromPv = patchfile.fromPv - patchChain = fromPv.getPatchChain(startPv, alreadyVisited) - if patchChain is not None: - # There's a path through this patchfile. - patchChain = patchChain + [patchfile] - if bestPatchChain is None or len(patchChain) < len(bestPatchChain): - bestPatchChain = patchChain - - # Return the shortest path found, or None if there were no - # paths found. - return bestPatchChain - - def getRecreateFilePlan(self, alreadyVisited = []): - """ Returns the tuple (startFile, startPv, plan), - describing how to recreate the archive file for this - version. startFile and startPv is the Filename and - packageVersion of the file to start with, and plan is a - list of tuples (patchfile, pv), listing the patches to - apply in sequence, and the packageVersion object - associated with each patch. Returns (None, None, None) if - there is no way to recreate this archive file. """ - - if self.tempFile: - return (self.tempFile, self, []) - - if self in alreadyVisited: - # We've already been here; this is a loop. Avoid - # infinite recursion. - return (None, None, None) - - alreadyVisited = alreadyVisited[:] - alreadyVisited.append(self) - - if self.packageCurrent: - # This PackageVersion instance represents the current - # version of some package. - package = self.packageCurrent - return (Filename(package.packageDir, package.compressedFilename), self, []) - - if self.packageBase: - # This PackageVersion instance represents the base - # (oldest) version of some package. - package = self.packageBase - return (Filename(package.packageDir, package.baseFile.filename + '.pz'), self, []) - - # We'll need to re-create the file. - bestPlan = None - bestStartFile = None - bestStartPv = None - for patchfile in self.fromPatches: - fromPv = patchfile.fromPv - startFile, startPv, plan = fromPv.getRecreateFilePlan(alreadyVisited) - if plan is not None: - # There's a path through this patchfile. - plan = plan + [(patchfile, self)] - if bestPlan is None or len(plan) < len(bestPlan): - bestPlan = plan - bestStartFile = startFile - bestStartPv = startPv - - # Return the shortest path found, or None if there were no - # paths found. - return (bestStartFile, bestStartPv, bestPlan) - - def getFile(self): - """ Returns the Filename of the archive file associated - with this version. If the file doesn't actually exist on - disk, a temporary file will be created. Returns None if - the file can't be recreated. """ - - startFile, startPv, plan = self.getRecreateFilePlan() - - if startFile.getExtension() in ('pz', 'gz'): - # If the starting file is compressed, we have to - # decompress it first. - assert startPv.tempFile is None - startPv.tempFile = Filename.temporary('', 'patch_') - if not decompressFile(startFile, startPv.tempFile): - # Failure trying to decompress the file. - return None - startFile = startPv.tempFile - - if not plan: - # If plan is a zero-length list, we're already - # here--return startFile. If plan is None, there's no - # solution, and startFile is None. In either case, we - # can return startFile. - return startFile - - # If plan is a non-empty list, we have to walk the list to - # apply the patch plan. - prevFile = startFile - for patchfile, pv in plan: - fromPv = patchfile.fromPv - patchFilename = Filename(patchfile.package.packageDir, patchfile.file.filename) - result = self.applyPatch(prevFile, patchFilename) - if not result: - # Failure trying to re-create the file. - return None - - pv.tempFile = result - prevFile = result - - # Successfully patched. - assert pv is self and prevFile is self.tempFile - return prevFile - - def applyPatch(self, origFile, patchFilename): - """ Applies the named patch to the indicated original - file, storing the results in a temporary file, and returns - that temporary Filename. Returns None on failure. """ - - result = Filename.temporary('', 'patch_') - p = Patchfile() - if not p.apply(patchFilename, origFile, result): - print("Internal patching failed: %s" % (patchFilename)) - return None - - return result - - def getNext(self, package): - """ Gets the next patch in the chain towards this - package. """ - for patch in self.toPatches: - if patch.packageName == package.packageName and \ - patch.platform == package.platform and \ - patch.version == package.version and \ - patch.hostUrl == package.hostUrl: - return patch.toPv - - return None - - class Patchfile: - """ A single patchfile for a package. """ - - def __init__(self, package): - self.package = package - self.packageName = package.packageName - self.platform = package.platform - self.version = package.version - self.hostUrl = None - - # FileSpec for the patchfile itself - self.file = None - - # FileSpec for the package file that the patch is applied to - self.sourceFile = None - - # FileSpec for the package file that the patch generates - self.targetFile = None - - # The PackageVersion corresponding to our sourceFile - self.fromPv = None - - # The PackageVersion corresponding to our targetFile - self.toPv = None - - def getSourceKey(self): - """ Returns the key for locating the package that this - patchfile can be applied to. """ - return (self.packageName, self.platform, self.version, self.hostUrl, self.sourceFile) - - def getTargetKey(self): - """ Returns the key for locating the package that this - patchfile will generate. """ - return (self.packageName, self.platform, self.version, self.hostUrl, self.targetFile) - - def fromFile(self, packageDir, patchFilename, sourceFile, targetFile): - """ Creates the data structures from an existing patchfile - on disk. """ - - self.file = FileSpec() - self.file.fromFile(packageDir, patchFilename) - self.sourceFile = sourceFile - self.targetFile = targetFile - - def loadXml(self, xpatch): - """ Reads the data structures from an xml file. """ - - self.packageName = xpatch.Attribute('name') or self.packageName - self.platform = xpatch.Attribute('platform') or self.platform - self.version = xpatch.Attribute('version') or self.version - self.hostUrl = xpatch.Attribute('host') or self.hostUrl - - self.file = FileSpec() - self.file.loadXml(xpatch) - - xsource = xpatch.FirstChildElement('source') - if xsource: - self.sourceFile = FileSpec() - self.sourceFile.loadXml(xsource) - - xtarget = xpatch.FirstChildElement('target') - if xtarget: - self.targetFile = FileSpec() - self.targetFile.loadXml(xtarget) - - def makeXml(self, package): - xpatch = TiXmlElement('patch') - - if self.packageName != package.packageName: - xpatch.SetAttribute('name', self.packageName) - if self.platform != package.platform: - xpatch.SetAttribute('platform', self.platform) - if self.version != package.version: - xpatch.SetAttribute('version', self.version) - if self.hostUrl != package.hostUrl: - xpatch.SetAttribute('host', self.hostUrl) - - self.file.storeXml(xpatch) - - xsource = TiXmlElement('source') - self.sourceFile.storeMiniXml(xsource) - xpatch.InsertEndChild(xsource) - - xtarget = TiXmlElement('target') - self.targetFile.storeMiniXml(xtarget) - xpatch.InsertEndChild(xtarget) - - return xpatch - - class Package: - """ This is a particular package. This contains all of the - information needed to reconstruct the package's desc file. """ - - def __init__(self, packageDesc, patchMaker, xpackage = None): - self.packageDir = Filename(patchMaker.installDir, packageDesc.getDirname()) - self.packageDesc = packageDesc - self.patchMaker = patchMaker - self.contentsDocPackage = xpackage - self.patchVersion = 1 - self.currentPv = None - self.basePv = None - self.topPv = None - - self.packageName = None - self.platform = None - self.version = None - self.hostUrl = None - self.currentFile = None - self.baseFile = None - - self.doc = None - self.anyChanges = False - self.patches = [] - - def getCurrentKey(self): - """ Returns the key to locate the current version of this - package. """ - - return (self.packageName, self.platform, self.version, self.hostUrl, self.currentFile) - - def getBaseKey(self): - """ Returns the key to locate the "base" or oldest version - of this package. """ - - return (self.packageName, self.platform, self.version, self.hostUrl, self.baseFile) - - def getTopKey(self): - """ Returns the key to locate the "top" or newest version - of this package. """ - - return (self.packageName, self.platform, self.version, self.hostUrl, self.topFile) - - def getGenericKey(self, fileSpec): - """ Returns the key that has the indicated hash. """ - return (self.packageName, self.platform, self.version, self.hostUrl, fileSpec) - - def readDescFile(self, doProcessing = False): - """ Reads the existing package.xml file and stores it in - this class for later rewriting. if doProcessing is true, - it may massage the file and the directory contents in - preparation for building patches. Returns true on - success, false on failure. """ - - self.anyChanges = False - - packageDescFullpath = Filename(self.patchMaker.installDir, self.packageDesc) - self.doc = TiXmlDocument(packageDescFullpath.toOsSpecific()) - if not self.doc.LoadFile(): - print("Couldn't read %s" % (packageDescFullpath)) - return False - - xpackage = self.doc.FirstChildElement('package') - if not xpackage: - return False - self.packageName = xpackage.Attribute('name') - self.platform = xpackage.Attribute('platform') - self.version = xpackage.Attribute('version') - - # All packages we defined in-line are assigned to the - # "none" host. TODO: support patching from packages on - # other hosts, which means we'll need to fill in a value - # here for those hosts. - self.hostUrl = None - - self.currentFile = None - self.baseFile = None - self.topFile = None - self.compressedFilename = None - compressedFile = None - - # Assume there are changes for this version, until we - # discover that there aren't. - isNewVersion = True - - # Get the actual current version. - xarchive = xpackage.FirstChildElement('uncompressed_archive') - if xarchive: - self.currentFile = FileSpec() - self.currentFile.loadXml(xarchive) - - # Get the top_version--the top (newest) of the patch - # chain. - xarchive = xpackage.FirstChildElement('top_version') - if xarchive: - self.topFile = FileSpec() - self.topFile.loadXml(xarchive) - - if self.topFile.hash == self.currentFile.hash: - # No new version this pass. - isNewVersion = False - else: - # There's a new version this pass. Update it. - self.anyChanges = True - - else: - # If there isn't a top_version yet, we have to make - # one, by duplicating the currentFile. - self.topFile = copy.copy(self.currentFile) - self.anyChanges = True - - # Get the current patch version. If we have a - # patch_version attribute, it refers to this particular - # instance of the file, and that is the current patch - # version number. If we only have a last_patch_version - # attribute, it means a patch has not yet been built for - # this particular instance, and that number is the - # previous version's patch version number. - patchVersion = xpackage.Attribute('patch_version') - if patchVersion: - self.patchVersion = int(patchVersion) - else: - patchVersion = xpackage.Attribute('last_patch_version') - if patchVersion: - self.patchVersion = int(patchVersion) - if isNewVersion: - self.patchVersion += 1 - self.anyChanges = True - - # Put the patchVersion in the compressed filename, for - # cache-busting. This means when the version changes, its - # URL will also change, guaranteeing that users will - # download the latest version, and not some stale cache - # file. - xcompressed = xpackage.FirstChildElement('compressed_archive') - if xcompressed: - compressedFile = FileSpec() - compressedFile.loadXml(xcompressed) - - oldCompressedFilename = compressedFile.filename - self.compressedFilename = oldCompressedFilename - - if doProcessing: - newCompressedFilename = '%s.%s.pz' % (self.currentFile.filename, self.patchVersion) - if newCompressedFilename != oldCompressedFilename: - oldCompressedPathname = Filename(self.packageDir, oldCompressedFilename) - newCompressedPathname = Filename(self.packageDir, newCompressedFilename) - if oldCompressedPathname.renameTo(newCompressedPathname): - compressedFile.fromFile(self.packageDir, newCompressedFilename) - compressedFile.storeXml(xcompressed) - - self.compressedFilename = newCompressedFilename - self.anyChanges = True - - # Get the base_version--the bottom (oldest) of the patch - # chain. - xarchive = xpackage.FirstChildElement('base_version') - if xarchive: - self.baseFile = FileSpec() - self.baseFile.loadXml(xarchive) - else: - # If there isn't a base_version yet, we have to make - # one, by duplicating the currentFile. - self.baseFile = copy.copy(self.currentFile) - - # Note that the we only store the compressed version - # of base_filename on disk, but we store the md5 of - # the uncompressed version in the xml file. To - # emphasize this, we name it without the .pz extension - # in the xml file, even though the compressed file on - # disk actually has a .pz extension. - self.baseFile.filename += '.base' - - # Also duplicate the (compressed) file itself. - if doProcessing and self.compressedFilename: - fromPathname = Filename(self.packageDir, self.compressedFilename) - toPathname = Filename(self.packageDir, self.baseFile.filename + '.pz') - fromPathname.copyTo(toPathname) - self.anyChanges = True - - self.patches = [] - xpatch = xpackage.FirstChildElement('patch') - while xpatch: - patchfile = PatchMaker.Patchfile(self) - patchfile.loadXml(xpatch) - self.patches.append(patchfile) - xpatch = xpatch.NextSiblingElement('patch') - - return True - - def writeDescFile(self): - """ Rewrites the desc file with the new patch - information. """ - - if not self.anyChanges: - # No need to rewrite. - return - - xpackage = self.doc.FirstChildElement('package') - if not xpackage: - return - - packageSeq = SeqValue() - packageSeq.loadXml(xpackage, 'seq') - packageSeq += 1 - packageSeq.storeXml(xpackage, 'seq') - - # Remove all of the old patch entries from the desc file - # we read earlier. - xremove = [] - for value in ['base_version', 'top_version', 'patch']: - xpatch = xpackage.FirstChildElement(value) - while xpatch: - xremove.append(xpatch) - xpatch = xpatch.NextSiblingElement(value) - - for xelement in xremove: - xpackage.RemoveChild(xelement) - - xpackage.RemoveAttribute('last_patch_version') - - # Now replace them with the current patch information. - xpackage.SetAttribute('patch_version', str(self.patchVersion)) - - xarchive = TiXmlElement('base_version') - self.baseFile.storeXml(xarchive) - xpackage.InsertEndChild(xarchive) - - # The current version is now the top version. - xarchive = TiXmlElement('top_version') - self.currentFile.storeXml(xarchive) - xpackage.InsertEndChild(xarchive) - - for patchfile in self.patches: - xpatch = patchfile.makeXml(self) - xpackage.InsertEndChild(xpatch) - - self.doc.SaveFile() - - # Also copy the seq to the import desc file, for - # documentation purposes. - - importDescFilename = str(self.packageDesc)[:-3] + 'import.xml' - importDescFullpath = Filename(self.patchMaker.installDir, importDescFilename) - doc = TiXmlDocument(importDescFullpath.toOsSpecific()) - if doc.LoadFile(): - xpackage = doc.FirstChildElement('package') - if xpackage: - packageSeq.storeXml(xpackage, 'seq') - doc.SaveFile() - else: - print("Couldn't read %s" % (importDescFullpath)) - - if self.contentsDocPackage: - # Now that we've rewritten the xml file, we have to - # change the contents.xml file that references it to - # indicate the new file hash. - fileSpec = FileSpec() - fileSpec.fromFile(self.patchMaker.installDir, self.packageDesc) - fileSpec.storeXml(self.contentsDocPackage) - - # Also important to update the import.xml hash. - ximport = self.contentsDocPackage.FirstChildElement('import') - if ximport: - fileSpec = FileSpec() - fileSpec.fromFile(self.patchMaker.installDir, importDescFilename) - fileSpec.storeXml(ximport) - - # Also copy the package seq value into the - # contents.xml file, mainly for documentation purposes - # (the authoritative seq value is within the desc - # file). - packageSeq.storeXml(self.contentsDocPackage, 'seq') - - - # PatchMaker constructor. - def __init__(self, installDir): - self.installDir = installDir - self.packageVersions = {} - self.packages = [] - - def buildPatches(self, packageNames = None): - """ Makes the patches required in a particular directory - structure on disk. If packageNames is None, this makes - patches for all packages; otherwise, it should be a list of - package name strings, limiting the set of packages that are - processed. """ - - if not self.readContentsFile(): - return False - self.buildPatchChains() - if packageNames is None: - self.processAllPackages() - else: - self.processSomePackages(packageNames) - - self.writeContentsFile() - self.cleanup() - return True - - def cleanup(self): - """ Should be called on exit to remove temporary files and - such created during processing. """ - - for pv in self.packageVersions.values(): - pv.cleanup() - - def getPatchChainToCurrent(self, descFilename, fileSpec): - """ Reads the package defined in the indicated desc file, and - constructs a patch chain from the version represented by - fileSpec to the current version of this package, if possible. - Returns the patch chain if successful, or None otherwise. """ - - package = self.readPackageDescFile(descFilename) - if not package: - return None - - self.buildPatchChains() - fromPv = self.getPackageVersion(package.getGenericKey(fileSpec)) - toPv = package.currentPv - - patchChain = None - if toPv and fromPv: - patchChain = toPv.getPatchChain(fromPv) - - return patchChain - - def readPackageDescFile(self, descFilename): - """ Reads a desc file associated with a particular package, - and adds the package to self.packages. Returns the Package - object, or None on failure. """ - - package = self.Package(Filename(descFilename), self) - if not package.readDescFile(doProcessing = False): - return None - - self.packages.append(package) - return package - - def readContentsFile(self): - """ Reads the contents.xml file at the beginning of - processing. """ - - contentsFilename = Filename(self.installDir, 'contents.xml') - doc = TiXmlDocument(contentsFilename.toOsSpecific()) - if not doc.LoadFile(): - # Couldn't read file. - print("couldn't read %s" % (contentsFilename)) - return False - - xcontents = doc.FirstChildElement('contents') - if xcontents: - contentsSeq = SeqValue() - contentsSeq.loadXml(xcontents) - contentsSeq += 1 - contentsSeq.storeXml(xcontents) - - xpackage = xcontents.FirstChildElement('package') - while xpackage: - solo = xpackage.Attribute('solo') - solo = int(solo or '0') - filename = xpackage.Attribute('filename') - if filename and not solo: - filename = Filename(filename) - package = self.Package(filename, self, xpackage) - package.readDescFile(doProcessing = True) - self.packages.append(package) - - xpackage = xpackage.NextSiblingElement('package') - - self.contentsDoc = doc - - return True - - def writeContentsFile(self): - """ Writes the contents.xml file at the end of processing. """ - - # We also have to write the desc file for all packages that - # might need it, because we might have changed some of them on - # read. - for package in self.packages: - package.writeDescFile() - - # The above writeDescFile() call should also update each - # package's element within the contents.xml document, so all - # we have to do now is write out the document. - self.contentsDoc.SaveFile() - - def getPackageVersion(self, key): - """ Returns a shared PackageVersion object for the indicated - key. """ - - packageName, platform, version, hostUrl, file = key - - # We actually key on the hash, not the FileSpec itself. - k = (packageName, platform, version, hostUrl, file.hash) - pv = self.packageVersions.get(k, None) - if not pv: - pv = self.PackageVersion(*key) - self.packageVersions[k] = pv - return pv - - def buildPatchChains(self): - """ Builds up the chains of PackageVersions and the patchfiles - that connect them. """ - - self.patchFilenames = {} - - for package in self.packages: - if not package.baseFile: - # This package doesn't have any versions yet. - continue - - currentPv = self.getPackageVersion(package.getCurrentKey()) - package.currentPv = currentPv - currentPv.packageCurrent = package - currentPv.printName = package.currentFile.filename - - basePv = self.getPackageVersion(package.getBaseKey()) - package.basePv = basePv - basePv.packageBase = package - basePv.printName = package.baseFile.filename - - topPv = self.getPackageVersion(package.getTopKey()) - package.topPv = topPv - topPv.packageTop = package - - for patchfile in package.patches: - self.recordPatchfile(patchfile) - - def recordPatchfile(self, patchfile): - """ Adds the indicated patchfile to the patch chains. """ - self.patchFilenames[patchfile.file.filename] = patchfile - - fromPv = self.getPackageVersion(patchfile.getSourceKey()) - patchfile.fromPv = fromPv - fromPv.toPatches.append(patchfile) - - toPv = self.getPackageVersion(patchfile.getTargetKey()) - patchfile.toPv = toPv - toPv.fromPatches.append(patchfile) - toPv.printName = patchfile.file.filename - - def processSomePackages(self, packageNames): - """ Builds missing patches only for the named packages. """ - - remainingNames = packageNames[:] - for package in self.packages: - if package.packageName in packageNames: - self.processPackage(package) - if package.packageName in remainingNames: - remainingNames.remove(package.packageName) - - if remainingNames: - print("Unknown packages: %s" % (remainingNames,)) - - def processAllPackages(self): - """ Walks through the list of packages, and builds missing - patches for each one. """ - - for package in self.packages: - self.processPackage(package) - - def processPackage(self, package): - """ Builds missing patches for the indicated package. """ - - if not package.baseFile: - # No versions. - return - - # What's the current version on the top of the tree? - topPv = package.topPv - currentPv = package.currentPv - - if topPv != currentPv: - # They're different, so build a new patch. - filename = Filename(package.currentFile.filename + '.%s.patch' % (package.patchVersion)) - assert filename not in self.patchFilenames - if not self.buildPatch(topPv, currentPv, package, filename): - raise Exception("Couldn't build patch.") - - def buildPatch(self, v1, v2, package, patchFilename): - """ Builds a patch from PackageVersion v1 to PackageVersion - v2, and stores it in patchFilename.pz. Returns true on - success, false on failure.""" - - pathname = Filename(package.packageDir, patchFilename) - if not self.buildPatchFile(v1.getFile(), v2.getFile(), pathname, - v1.printName, v2.printName): - return False - - compressedPathname = Filename(pathname + '.pz') - compressedPathname.unlink() - if not compressFile(pathname, compressedPathname, 9): - raise Exception("Couldn't compress patch.") - pathname.unlink() - - patchfile = self.Patchfile(package) - patchfile.fromFile(package.packageDir, patchFilename + '.pz', - v1.file, v2.file) - package.patches.append(patchfile) - package.anyChanges = True - - self.recordPatchfile(patchfile) - - return True - - def buildPatchFile(self, origFilename, newFilename, patchFilename, - printOrigName, printNewName): - """ Creates a patch file from origFilename to newFilename, - storing the result in patchFilename. Returns true on success, - false on failure. """ - - if not origFilename.exists(): - # No original version to patch from. - return False - - print("Building patch from %s to %s" % (printOrigName, printNewName)) - patchFilename.unlink() - p = Patchfile() # The C++ class - if p.build(origFilename, newFilename, patchFilename): - return True - - # Unable to build a patch for some reason. - patchFilename.unlink() - return False diff --git a/direct/src/p3d/ScanDirectoryNode.py b/direct/src/p3d/ScanDirectoryNode.py deleted file mode 100644 index 429b8f0fd2..0000000000 --- a/direct/src/p3d/ScanDirectoryNode.py +++ /dev/null @@ -1,95 +0,0 @@ -__all__ = ["ScanDirectoryNode"] - -from panda3d.core import VirtualFileSystem, VirtualFileMountSystem, Filename, TiXmlDocument -vfs = VirtualFileSystem.getGlobalPtr() - -class ScanDirectoryNode: - """ This class is used to scan a list of files on disk. """ - - def __init__(self, pathname, ignoreUsageXml = False): - self.pathname = pathname - self.filenames = [] - self.fileSize = 0 - self.nested = [] - self.nestedSize = 0 - - xusage = None - if not ignoreUsageXml: - # Look for a usage.xml file in this directory. If we find - # one, we read it for the file size and then stop here, as - # an optimization. - usageFilename = Filename(pathname, 'usage.xml') - doc = TiXmlDocument(usageFilename.toOsSpecific()) - if doc.LoadFile(): - xusage = doc.FirstChildElement('usage') - if xusage: - diskSpace = xusage.Attribute('disk_space') - try: - diskSpace = int(diskSpace or '') - except ValueError: - diskSpace = None - if diskSpace is not None: - self.fileSize = diskSpace - return - - files = vfs.scanDirectory(self.pathname) - if files is None: - files = [] - for vfile in files: - if hasattr(vfile, 'getMount'): - if not isinstance(vfile.getMount(), VirtualFileMountSystem): - # Not a real file; ignore it. - continue - - if vfile.isDirectory(): - # A nested directory. - subdir = ScanDirectoryNode(vfile.getFilename(), ignoreUsageXml = ignoreUsageXml) - self.nested.append(subdir) - self.nestedSize += subdir.getTotalSize() - - elif vfile.isRegularFile(): - # A nested file. - self.filenames.append(vfile.getFilename()) - self.fileSize += vfile.getFileSize() - - else: - # Some other wacky file thing. - self.filenames.append(vfile.getFilename()) - - if xusage: - # Now update the usage.xml file with the newly-determined - # disk space. - xusage.SetAttribute('disk_space', str(self.getTotalSize())) - tfile = Filename.temporary(str(pathname), '.xml') - if doc.SaveFile(tfile.toOsSpecific()): - tfile.renameTo(usageFilename) - - def getTotalSize(self): - return self.nestedSize + self.fileSize - - def extractSubdir(self, pathname): - """ Finds the ScanDirectoryNode within this node that - corresponds to the indicated full pathname. If it is found, - removes it from its parent, and returns it. If it is not - found, returns None. """ - - # We could be a little smarter here, but why bother. Just - # recursively search all children. - for subdir in self.nested: - if subdir.pathname == pathname: - self.nested.remove(subdir) - self.nestedSize -= subdir.getTotalSize() - return subdir - - result = subdir.extractSubdir(pathname) - if result: - self.nestedSize -= result.getTotalSize() - if subdir.getTotalSize() == 0: - # No other files in the subdirectory that contains - # this package; remove it too. - self.nested.remove(subdir) - return result - - return None - - diff --git a/direct/src/p3d/SeqValue.py b/direct/src/p3d/SeqValue.py deleted file mode 100644 index 958132f74b..0000000000 --- a/direct/src/p3d/SeqValue.py +++ /dev/null @@ -1,90 +0,0 @@ -__all__ = ["SeqValue"] - -class SeqValue: - - """ This represents a sequence value read from a contents.xml - file, either from the or the section. It's - represented as series of dotted integers in the xml file, and - stored internally as a tuple of integers. - - It may be incremented, which increments only the last integer in - the series; or it may be compared with another SeqValue, which - compares all of the integers componentwise. """ - - def __init__(self, value = None): - self.value = () - if value is not None: - self.set(value) - - def set(self, value): - """ Sets the seq from the indicated value of unspecified - type. """ - if isinstance(value, tuple): - self.setFromTuple(value) - elif isinstance(value, str): - self.setFromString(value) - else: - raise TypeError('Invalid sequence type: %s' % (value,)) - - def setFromTuple(self, value): - """ Sets the seq from the indicated tuple of integers. """ - assert isinstance(value, tuple) - self.value = value - - def setFromString(self, value): - """ Sets the seq from the indicated string of dot-separated - integers. Raises ValueError on error. """ - assert isinstance(value, str) - - self.value = () - if value: - value = value.split('.') - value = map(int, value) - self.value = tuple(value) - - def loadXml(self, xelement, attribute = 'seq'): - """ Reads the seq from the indicated XML element. Returns - true if loaded, false if not given or if there was an - error. """ - - self.value = () - value = xelement.Attribute(attribute) - if value: - try: - self.setFromString(value) - except ValueError: - return False - return True - - return False - - - def storeXml(self, xelement, attribute = 'seq'): - """ Adds the seq to the indicated XML element. """ - if self.value: - value = '.'.join(map(str, self.value)) - xelement.SetAttribute(attribute, value) - - def __add__(self, inc): - """ Increments the seq value, returning the new value. """ - if not self.value: - value = (1,) - else: - value = self.value[:-1] + (self.value[-1] + inc,) - return SeqValue(value) - - def __cmp__(self, other): - """ Compares to another seq value. """ - return cmp(self.value, other.value) - - def __lt__(self, other): - return self.value < other.value - - def __gt__(self, other): - return self.value > other.value - - def __bool__(self): - return bool(self.value) - - def __str__(self): - return 'SeqValue%s' % (repr(self.value)) diff --git a/direct/src/p3d/__init__.py b/direct/src/p3d/__init__.py deleted file mode 100644 index e6ab93b09d..0000000000 --- a/direct/src/p3d/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -This package provides the Python interface to functionality relating to -the Panda3D Runtime environment. -""" diff --git a/direct/src/p3d/coreapi.pdef b/direct/src/p3d/coreapi.pdef deleted file mode 100644 index 03612be605..0000000000 --- a/direct/src/p3d/coreapi.pdef +++ /dev/null @@ -1,124 +0,0 @@ -from panda3d.core import getModelPath, Filename, ConfigVariableFilename, DSearchPath, ExecutionEnvironment, PandaSystem - -# This file defines a number of standard "packages" that correspond to -# a Panda3D distribution. These packages are built by passing this -# file to the ppackage utility, either as a packaged application, or -# as the module direct.p3d.ppackage. - -# The packages in this file define the "Core API". This is the second -# installed piece of the three-part plugin system (the plugin, the -# core API, Panda3D). - -# These packages are downloaded directly by the plugin, from the host -# specified by the value of PANDA_PACKAGE_HOST_URL compiled into the -# plugin. Thus, these packages are inextricably tied to the -# particular plugin they have been built with. They do not have to be -# present on a server that hosts a version of Panda3D for download, -# just on the server that hosts the plugin itself. These packages do -# not need to be updated with each new version of Panda3D. - -# Also see panda3d.pdef. - -class coreapi(solo): - # The special "coreapi" package. As a "solo", this is just a - # single .dll (or dylib, or whatever). - setVer(PandaSystem.getP3dCoreapiVersionString()) - file('p3d_plugin.dll') - -class images(package): - # The default startup images are stored as their own package. - names = ['download', 'failed', 'play_click', 'play_ready', 'play_rollover', - 'auth_click', 'auth_ready', 'auth_rollover'] - configDict = {} - - # Construct a search path to look for the images. - search = DSearchPath() - - # First on the path: an explicit $PLUGIN_IMAGES env var. - if ExecutionEnvironment.hasEnvironmentVariable('PLUGIN_IMAGES'): - search.appendDirectory(Filename.expandFrom('$PLUGIN_IMAGES')) - - # Next on the path: the models/plugin_images directory within the - # current directory. - search.appendDirectory('models/plugin_images') - - # Finally on the path: models/plugin_images within the model - # search path. - for dir in getModelPath().getDirectories(): - search.appendDirectory(Filename(dir, 'plugin_images')) - - for name in names: - # Look for a png image first. - basename = '%s.png' % (name) - filename = Filename(basename) - found = filename.resolveFilename(search) - if not found: - # Then try a jpeg image. - basename = '%s.jpg' % (name) - filename = Filename(basename) - found = filename.resolveFilename(search) - - if found: - # Add the image file to the package - file(filename, newName = basename, extract = True) - - # And set the config variable to reference it. - token = '%s_img' % (name) - configDict[token] = basename - else: - print("Could not locate %s" % (filename)) - - # Also make a few special cases. We use the same default image - # for many of the states. - download = configDict.get('download_img', None) - if download: - configDict['ready_img'] = download - configDict['unauth_img'] = download - configDict['launch_img'] = download - configDict['active_img'] = download - - config(**configDict) - -class certlist(package): - # This package holds any certificates that should be considered - # pre-approved by the plugin vendor. If you build and host your - # own version of the Panda3D plugin, you can add your own - # certificates to this list. The certificates in this package - # will be assumed to have been approved by the user when he/she - # installed the plugin. - - # They should be PEM-encoded and their filenames must end in the - # ".pem" or ".crt" extension, and they should be added with the - # extract = True flag so they will be extracted to disk. - pass - - -class p3dcert(package): - # This special application, used to pop up a dialog to prompt the - # user to accept or deny unknown applications, is its own package. - config(display_name = "Authorization Dialog") - - if platform.startswith('osx'): - # On Mac, we package up a P3DCert.app bundle. This includes - # specifications in the plist file to avoid creating a dock - # icon and stuff. - - # Find p3dcert.plist in the direct source tree. - import direct - plist = Filename(direct.__path__[0], 'plugin/p3dcert.plist') - makeBundle('P3DCert.app', plist, executable = 'p3dcert') - - else: - # Anywhere else, we just ship the executable file p3dcert.exe. - file('p3dcert.exe', required = True) - - -class p3dembed(package): - # This contains the p3dembed executable, that is used by - # pdeploy to generate a self-extracting .p3d executable. - - config(platform_specific = True) - file('p3dembed.exe', required = True) - - if platform.startswith('win'): - file('p3dembedw.exe', required = True) diff --git a/direct/src/p3d/p3dWrapper.c b/direct/src/p3d/p3dWrapper.c deleted file mode 100644 index ef96e036f9..0000000000 --- a/direct/src/p3d/p3dWrapper.c +++ /dev/null @@ -1,127 +0,0 @@ -/* Filename: p3dWrapper.c - * Created by: rdb (16Jan10) - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/* p3dWrapper is a small wrapper executable that locates a .p3d file - in the same directory as this executable file, with the same name - (except .p3d instead of .exe of course). It is only meant to be - used on Windows system, it is not needed on Unix-like systems. */ - -#include -#include -#include -#include -#include -#include -#include - -#define BUFFER_SIZE 1024 - -/* It makes sense to use "App Paths\panda3d.exe". However, Microsoft - decided in their infinite wisdom to disable Redirection for that - key from Windows 7 onward, so we can't rely on it producing a - result appropriate to the right architecture when both the 32-bit - and 64-bit versions of the runtime are installed. Beh. */ - -#define UNINST_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Panda3D Game Engine" - -int main (int argc, char* argv[]) { - int i; - char buffer[BUFFER_SIZE]; - char* p3dfile; - char* runtime = NULL; - DWORD size; - STARTUPINFO si; - PROCESS_INFORMATION pi; - char *cmd; - char *newcmd; - HKEY hKey = 0; - char buf[1024] = {0}; - DWORD dwType = REG_SZ; - DWORD dwBufSize = sizeof(buf); - size = GetModuleFileName(NULL, buffer, BUFFER_SIZE); - assert (size > 0); - - /* Chop off the .exe and replace it by .p3d. */ - p3dfile = (char*) _alloca(size + 1); - memcpy(p3dfile, buffer, size); - p3dfile[size] = 0; - memcpy(p3dfile + size - 3, "p3d", 3); - - /* Find the location of panda3d.exe using the registry path. */ -#ifdef _WIN64 - /* If we're on 64-bit Windows, try the 64-bit registry first. */ - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, UNINST_KEY, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &hKey) == ERROR_SUCCESS) { - if (RegQueryValueEx(hKey, "DisplayIcon", 0, &dwType, (BYTE*) buf, &dwBufSize) == ERROR_SUCCESS) { - char *slash = strrchr(buf, '\\'); - if (slash != NULL) { - strcpy(slash, "\\panda3d.exe"); - runtime = buf; - } - } - RegCloseKey(hKey); - } -#endif - - /* On 32-bit Windows, or no 64-bit Runtime installed. Try 32-bit registry. */ - if (runtime == NULL) { - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, UNINST_KEY, 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS) { - if (RegQueryValueEx(hKey, "DisplayIcon", 0, &dwType, (BYTE*) buf, &dwBufSize) == ERROR_SUCCESS) { - char *slash = strrchr(buf, '\\'); - if (slash != NULL) { - strcpy(slash, "\\panda3d.exe"); - runtime = buf; - } - } - RegCloseKey(hKey); - } - } - - /* Backward compatibility: Runtime 1.0.4 and below looked for the below key, even though the - above keys should work fine, but let's just be certain. - Find the Panda3D applet\DefaultIcon key and extract the path to the runtime from there. */ - if (runtime == NULL) { - if (RegOpenKey(HKEY_CLASSES_ROOT, "Panda3D applet\\DefaultIcon", &hKey) == ERROR_SUCCESS) { - if (RegQueryValueEx(hKey, 0, 0, &dwType, (BYTE*) buf, &dwBufSize) == ERROR_SUCCESS) { - char *slash = strrchr(buf, '\\'); - if (slash != NULL) { - strcpy(slash, "\\panda3d.exe"); - runtime = buf; - } - } else { - fprintf(stderr, "Failed to read registry key. Try reinstalling the Panda3D Runtime.\n"); - return 1; - } - RegCloseKey(hKey); - } else { - fprintf(stderr, "The Panda3D Runtime does not appear to be installed!\n"); - return 1; - } - } - - if (runtime == NULL) { - fprintf(stderr, "Failed to find panda3d.exe in registry. Try reinstalling the Panda3D Runtime.\n"); - return 1; - } - - /* Build the command-line and run panda3d.exe. */ - cmd = GetCommandLine(); - newcmd = (char*) _alloca(strlen(runtime) + strlen(p3dfile) + strlen(cmd) - strlen (argv[0]) + 7); - sprintf(newcmd, "\"%s\" \"%s\" %s", runtime, p3dfile, cmd + strlen(argv[0])); - memset(&si, 0, sizeof(si)); - si.cb = sizeof(STARTUPINFO); - if (CreateProcess(runtime, newcmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { - WaitForSingleObject(pi.hProcess, INFINITE); - } - return 0; -} diff --git a/direct/src/p3d/packp3d.py b/direct/src/p3d/packp3d.py deleted file mode 100755 index dee4fa2656..0000000000 --- a/direct/src/p3d/packp3d.py +++ /dev/null @@ -1,241 +0,0 @@ -#! /usr/bin/env python - -usageText = """ - -This command will pack a Panda application, consisting of a directory -tree of .py files and models, into a p3d file for convenient -distribution. The resulting p3d file can be run by the Panda3D -runtime executable, or by the Panda3D web browser plugin. - -This command will build p3d files that reference Panda3D %s, -from host %s . - -Also see ppackage, a more powerful (but more complex) tool that can -also be used to build p3d applications, using a pdef description file. - -Usage: - - %s [opts] -o app.p3d - -Options: - - -o app.p3d - Specify the name of the p3d file to generate. This is required. - - -d application_root - Specify the root directory of the application source; this is a - directory tree that contains all of your .py files and models. - If this is omitted, the default is the current directory. - - -m main.py - Names the Python file that begins the application. This should - be a file within the root directory. If this is omitted, the - default is a file named "main.py", or if there is only one Python - file present, it is used. If this file contains a function - called main(), that function will be called after importing it - (this is preferable to having the module start itself immediately - upon importing). - - -S file.crt[,chain.crt[,file.key[,\"password\"]]] - Signs the resulting p3d with the indicated certificate. You may - specify the signing certificate, the optional authorization - chain, and the private key in three different files, or they may - all be combined in the first file. If the private key is - encrypted, the password will be required to decrypt it. - - -e ext - Adds a new extension to be processed as a generic, compressible - file type. Do not include the leading dot. Files matching this - extension found within the root directory will be automatically - added to the p3d file, in compressed form. This option may be - repeated as necessary. - - -n ext - Adds a new extension to be processed as a noncompressible file - type. Files matching this extension will be added to the p3d - file, in their original, uncompressed form. You should use this - instead of -e for files that are uncompressible by their nature - (e.g. mpg files). This option may be repeated as necessary. - - -x ext - Marks files with the given extensions of needing to be physically - extracted to disk before they can be loaded. This is used for - file types that cannot be loaded via the virtual file system, - such as .ico files on Windows. - This option is currently only implemented when deploying the - application with pdeploy. - - -p python_lib_dir - Adds a directory to search for additional Python modules. You - can use this to add your system's Python path, to allow packp3d - to find any system modules not included in the standard Panda3D - release, but your version of Python must match this one (%s). - This option may be repeated to add multiple directories. - - -c config=value - Sets the indicated config flag in the application. This option - may be repeated as necessary. - - -r package[,version[,hostURL]] - Names an additional package that this application requires at - startup time. The default package is 'panda3d'; you may repeat - this option to indicate dependencies on additional packages. - - -s search_dir - Additional directories to search for previously-built packages. - This option may be repeated as necessary. These directories may - also be specified with the pdef-path Config.prc variable. - - -D - Sets the allow_python_dev flag in the application. This enables - additional runtime debug operations, particularly the -i option - to the panda3d command, which enables a live Python prompt within - the application's environment. Setting this flag may be useful - to develop an application initially, but should not be set on an - application intended for deployment. - -""" - -import sys -import os -import getopt -import glob -from direct.p3d import Packager -from panda3d.core import * - -# Temp hack for debugging. -#from direct.p3d.AppRunner import dummyAppRunner; dummyAppRunner() - -class ArgumentError(Exception): - pass - -def makePackedApp(args): - opts, args = getopt.getopt(args, 'o:d:m:S:e:n:x:p:c:r:s:Dh') - - packager = Packager.Packager() - - appFilename = None - root = Filename('.') - main = None - configFlags = [] - requires = [] - allowPythonDev = False - - for option, value in opts: - if option == '-o': - appFilename = Filename.fromOsSpecific(value) - elif option == '-d': - root = Filename.fromOsSpecific(value) - elif option == '-m': - main = value - elif option == '-S': - tokens = value.split(',') - while len(tokens) < 4: - tokens.append('') - certificate, chain, pkey, password = tokens[:4] - packager.signParams.append((Filename.fromOsSpecific(certificate), - Filename.fromOsSpecific(chain), - Filename.fromOsSpecific(pkey), - Filename.fromOsSpecific(password))) - elif option == '-e': - packager.binaryExtensions.append(value) - elif option == '-n': - packager.uncompressibleExtensions.append(value) - elif option == '-x': - packager.extractExtensions.append(value) - elif option == '-p': - sys.path.append(value) - elif option == '-c': - configFlags.append(value.split('=', 1)) - elif option == '-r': - tokens = value.split(',') - while len(tokens) < 3: - tokens.append('') - name, version, host = tokens[:3] - requires.append((name, version, host)) - elif option == '-s': - packager.installSearch.append(Filename.fromOsSpecific(value)) - elif option == '-D': - allowPythonDev = True - elif option == '-h': - print(usageText % ( - PandaSystem.getPackageVersionString(), - PandaSystem.getPackageHostUrl(), - os.path.split(sys.argv[0])[1], - '%s.%s' % (sys.version_info[0], sys.version_info[1]))) - sys.exit(0) - - if not appFilename: - raise ArgumentError("No target app specified. Use:\n %s -o app.p3d\nUse -h to get more usage information." % (os.path.split(sys.argv[0])[1])) - - if args: - raise ArgumentError("Extra arguments on command line.") - - if appFilename.getExtension() != 'p3d': - raise ArgumentError('Application filename must end in ".p3d".') - - appDir = Filename(appFilename.getDirname()) - if not appDir: - appDir = Filename('.') - appBase = appFilename.getBasenameWoExtension() - - if main: - main = Filename.fromOsSpecific(main) - main.makeAbsolute(root) - else: - main = Filename(root, 'main.py') - if not main.exists(): - main = glob.glob(os.path.join(root.toOsSpecific(), '*.py')) - if len(main) == 0: - raise ArgumentError('No Python files in root directory.') - elif len(main) > 1: - raise ArgumentError('Multiple Python files in root directory; specify the main application with -m "main".') - - main = Filename.fromOsSpecific(os.path.split(main[0])[1]) - main.makeAbsolute(root) - - packager.installDir = appDir - packager.allowPythonDev = allowPythonDev - - # Put the root directory on the front of the model-path, so that - # any texture references in egg or bam files that reference - # textures from the top of the root directory will be properly - # resolved. - getModelPath().prependDirectory(root) - - try: - packager.setup() - packager.beginPackage(appBase, p3dApplication = True) - - # Pre-require panda3d, to give a less-confusing error message - # if one of our requirements pulls in a wrong version of - # panda3d. - if 'panda3d' not in [t[0] for t in requires]: - packager.do_require('panda3d') - - for name, version, host in requires: - packager.do_require(name, version = version, host = host) - - if configFlags: - packager.do_config(**dict(configFlags)) - - packager.do_dir(root) - packager.do_main(main) - packager.endPackage() - packager.close() - - except Packager.PackagerError: - # Just print the error message and exit gracefully. - inst = sys.exc_info()[1] - print(inst.args[0]) - sys.exit(1) - -try: - makePackedApp(sys.argv[1:]) -except ArgumentError as e: - print(e.args[0]) - sys.exit(1) - -# An explicit call to exit() is required to exit the program, when -# this module is packaged in a p3d file. -sys.exit(0) diff --git a/direct/src/p3d/panda3d.pdef b/direct/src/p3d/panda3d.pdef deleted file mode 100644 index 78e7af2cf0..0000000000 --- a/direct/src/p3d/panda3d.pdef +++ /dev/null @@ -1,518 +0,0 @@ -import sys -from panda3d.core import Filename, PandaSystem, getModelPath - -# This file defines a number of standard "packages" that correspond to -# a Panda3D distribution. These packages are built by passing this -# file to the ppackage utility, either as a packaged application, or -# as the module direct.p3d.ppackage. - -# The packages in this file define Panda3D itself. This is the third -# installed piece of the three-part plugin system (the plugin, the -# core API, Panda3D). - -# When needed, these packages are downloaded by the core API, from the -# host URL specified in a given p3d file, and not from any hardcoded -# URL. Thus, any custom version of Panda3D may be hosted on any -# server in the world, and any version of the plugin can download it. - -# Also see coreapi.pdef. - -class panda3d(package): - # The main Panda3D package. Contains Python and most of the graphics - # code in Panda3D. - - config(display_name = "Panda3D") - - # First, add the minimum startup files for a Panda3D package. - # These are files that the Panda3D runtime will explicitly look - # for by name in order to get itself bootstrapped. - setupPanda3D() - - # These are Python modules that are needed by most Panda3D - # applications. It doesn't matter too much if we miss one or two - # here, since any module imported by any of this code will - # automatically be included as well, and we end up with a pretty - # complete list that way. - module('direct.directbase.DirectStart', - # Don't want to include all files in direct.p3d, because - # that picks up the runtime scripts too, which are their - # own p3d files below. - 'direct.p3d.AppRunner', - 'direct.p3d.DWBPackageInstaller', - 'direct.actor.*', - 'direct.controls.*', - 'direct.directdevices.*', - 'direct.directnotify.*', - 'direct.directtools.*', - 'direct.directutil.*', - 'direct.distributed.*', - 'direct.filter.*', - 'direct.fsm.*', - 'direct.gui.*', - 'direct.interval.*', - 'direct.particles.*', - 'direct.showbase.*', - 'direct.showutil.*', - 'direct.stdpy.*', - 'direct.task.*') - - # Keep these modules for backward compatibility. - module('pandac.PandaModules', 'pandac.extension_native_helpers') - - module('panda3d.core', - 'panda3d.direct', - 'panda3d.fx', - 'panda3d.physics') - - # Include various standard Python encodings. The rest is in morepy. - module('encodings', 'encodings.aliases', 'encodings.undefined', - 'encodings.utf_8', 'encodings.ascii', 'encodings.mbcs', - 'encodings.latin_1', 'io') - if sys.version_info < (3, 0): - module('encodings.string_escape') - - # pandac.PandaModules pulls in other Panda3D libs automatically. - # Exclude these Panda3D libs; they are big and many applications don't - # use them. We define them as separate, optional packages, below, - # except for skel - this is useless in a shipped game anyways. - excludeModule('panda3d.egg') - excludeModule('panda3d.ode') - excludeModule('panda3d.bullet') - excludeModule('panda3d.vision') - excludeModule('panda3d.skel') - excludeModule('panda3d.physx') - excludeModule('panda3d.ai') - excludeModule('panda3d.vrpn') - - excludeModule('libpandaegg') - excludeModule('libpandaode') - excludeModule('libpandabullet') - excludeModule('libp3vision') - excludeModule('libpandaskel') - excludeModule('libpandaphysx') - excludeModule('libpandaai') - excludeModule('libp3vrpn') - - # Exclude these GUI toolkits; they're big, and many applications don't - # use them. We define them as separate, optional packages, below. - excludeModule('wx', - 'direct.showbase.WxGlobal') - - excludeModule('Tkinter', 'tkinter', 'Pmw', 'tkinter.simpledialog', - 'direct.showbase.TkGlobal', - 'direct.tkpanels', 'direct.tkwidgets', - 'tkCommonDialog', 'tkMessageBox', 'tkSimpleDialog') - - excludeModule('MySQLdb', 'MySQLdb.connections', 'MySQLdb.constants', - 'MySQLdb.converters', '_mysql') - - # Some code in distributed conditionally imports these. - excludeModule('otp.ai', 'otp.ai.AIZoneData') - - # Most of the core Panda3D DLL's will be included implicitly due to - # being referenced by the above Python code. Here we name a few more - # that are also needed, but aren't referenced by any code. Again, - # note that the .dll extension is automatically replaced with the - # platform-specific extension for an executable. - file('libpandagl.dll', 'libp3tinydisplay.dll') - if platform.startswith('win'): - file('libpandadx9.dll') - - # A basic config file is needed to lay some some fundamental runtime - # variables. - if platform.startswith('win'): - auxDisplays = """ -aux-display pandagl -aux-display pandadx9 -aux-display p3tinydisplay -""" - else: - auxDisplays = """ -aux-display pandagl -aux-display p3tinydisplay -""" - - file('Config.prc', extract = True, text = """ -plugin-path $PANDA3D_ROOT -default-model-extension .bam -cull-bin gui-popup 60 unsorted -""" + auxDisplays) - -class morepy(package): - # Additional Python modules provided by the standard Python - # library. Include this package to get the full suite of standard - # Python functionality. - - config(display_name = "Python standard library") - require('panda3d') - - module('string', 're', 'struct', 'difflib', - 'textwrap', 'codecs', 'unicodedata', 'stringprep', 'datetime', - 'calendar', 'collections', 'heapq', 'bisect', 'array', - 'sched', 'queue', 'weakref', 'types', 'copy', 'pprint', - 'numbers', 'math', 'cmath', 'decimal', 'fractions', - 'random', 'itertools', 'functools', 'operator', 'os.path', - 'fileinput', 'filecmp', 'tempfile', 'glob', 'fnmatch', 'linecache', - 'shutil', 'macpath', 'pickle', 'shelve', 'marshal', 'zlib', - 'gzip', 'bz2', 'zipfile', 'tarfile', 'csv', 'netrc', - 'xdrlib', 'plistlib', 'hashlib', 'hmac', 'os', - 'time', 'optparse', 'getopt', 'logging', 'logging.*') - module('getpass', 'curses', 'curses.textpad', 'curses.wrapper', - 'curses.ascii', 'curses.panel', 'platform', 'errno', - 'ctypes', 'select', 'threading', 'dummy_threading', 'dummy_thread', - 'multiprocessing', 'mmap', 'readline', 'rlcompleter', 'subprocess', - 'socket', 'ssl', 'signal', 'asyncore', 'asynchat', 'email', 'json', - 'mailcap', 'mailbox', 'mimetypes', 'base64', 'binhex', 'binascii', - 'quopri', 'uu', 'xml.parsers.expat', 'xml.dom', 'xml.dom.minidom', - 'xml.dom.pulldom', 'xml.sax', 'xml.sax.handler', - 'xml.sax.saxutils', 'xml.sax.xmlreader', - 'xml.etree.ElementTree', 'webbrowser', 'cgi', 'cgitb') - module('wsgiref', 'ftplib', 'poplib', 'imaplib', 'nntplib', 'smtplib', - 'smtpd', 'telnetlib', 'uuid', 'audioop', 'aifc', 'sunau', - 'wave', 'chunk', 'colorsys', 'imghdr', 'sndhdr', - 'ossaudiodev', 'gettext', 'locale', 'cmd', 'shlex', - 'pydoc', 'doctest', 'unittest', 'test', - 'bdb', 'pdb', 'timeit', 'trace', 'sys') - module('warnings', 'contextlib', 'abc', - 'atexit', 'traceback', '__future__', 'gc', 'inspect', - 'site', 'fpectl', 'code', 'codeop', 'zipimport', 'pkgutil', - 'modulefinder', 'runpy', 'parser', 'symtable', 'symbol', - 'token', 'keyword', 'tokenize', 'tabnanny', 'pyclbr', - 'py_compile', 'compileall', 'dis', 'pickletools', - 'distutils', 'msilib', 'msvcrt', 'winsound', 'posix', 'pwd', 'spwd', - 'grp', 'crypt', 'termios', 'tty', 'pty', 'fcntl', 'pipes', - 'resource', 'nis', 'syslog', 'ast') - - # Handle module name differences between Python 2 and Python 3 (see PEP3108) - if sys.version_info[0] < 3: - # Deprecated modules that were removed in Python 3 - module('posixfile', 'rfc822', 'mimetools','MimeWriter', 'mimify', - 'multifile', 'sets', 'md5', 'sha', 'imp', 'formatter') - # Mac-specific modules that were removed - module('autoGIL', 'ColorPicker', 'EasyDialogs', 'findertools', - 'FrameWork', 'ic', 'MacOS', 'macostools') - # Removed because they were hardly used - module('imputil', 'mutex', 'user', 'new') - # Removed because they were obsolete - module('Bastion', 'rexec', 'commands', 'dircache', 'dl', 'fpformat', - 'htmllib', 'imageop', 'mhlib', 'popen2', 'sgmllib', 'stat', - 'statvfs', 'thread', 'UserDict', 'UserList', 'UserString', - 'future_builtins', 'hotshot', 'bsddb') - # Renamed to fix PEP8 violations - module('_winreg', 'ConfigParser', 'copy_reg', 'SocketServer') - # C and Python implementations of the same interface were merged - module('cPickle', 'cStringIO', 'StringIO') - # Renamed because of poorly chosen names - module('repr', 'test.test_support', '__builtins__') - # Modules that got grouped under packages - module('anydbm', 'whichdb', 'dbm', 'dumbdbm', 'gdbm', 'dbhash') - module('HTMLParser', 'htmlentitydefs') - module('BaseHTTPServer', 'SimpleHTTPServer', 'cookielib', 'Cookie', - 'CGIHTTPServer', 'httplib') - module('urllib', 'urllib2', 'urlparse', 'robotparser') - module('xmlrpclib', 'SimpleXMLRPCServer', 'DocXMLRPCServer') - else: - # Renamed to fix PEP8 violations - module('winreg', 'configparser', 'copyreg', 'socketserver') - # Renamed because of poorly chosen names - module('reprlib', 'test.support', 'builtins') - # Modules that got grouped under packages - module('dbm') - module('html') - module('http') - module('urllib') - module('xmlrpc') - - # To add the multitude of standard Python string encodings. - module('encodings', 'encodings.*') - -class models(package): - # The standard models package. This is the contents of the - # "models" directory that ships with Panda; it includes a few - # default fonts, and some silly little sample models like smiley - # and teapot. - config(display_name = "Standard models") - - # We assign it the same version as the panda3d package. This - # would be assigned by default if we had a requirement on panda3d, - # but there's no real reason to declare that requirement. - config(version = PandaSystem.getPackageVersionString()) - - # Look for cmss12.egg on the model-path. Wherever this is found, - # we assume this is the models directory. - pathname = getModelPath().findFile('cmss12.egg') - if pathname: - dir(pathname.getDirname(), newDir = 'models') - - # Some people are used to loading the models without models/ prefix. - file('models.prc', extract = True, text = "model-path $MODELS_ROOT/models") - - -class fmod(package): - # This package includes the FMod audio library. This is - # full-featured and robust, but it is closed-source and the - # licensing is cost-free only for non-commercial products. - - config(display_name = "FMod audio library") - require('panda3d') - - file('libp3fmod_audio.dll', required = True) - - file('fmod.prc', extract = True, text = """ -plugin-path $FMOD_ROOT -audio-library-name p3fmod_audio -""") - -class openal(package): - # This package includes the OpenAL audio libraries. This is free - # in both senses, but there are some technical issues, especially - # on certain platforms. - - config(display_name = "OpenAL audio library") - require('panda3d') - - file('libp3openal_audio.dll', required = True) - file('OpenAL32.dll') - file('soft_oal.dll') - file('wrap_oal.dll') - - file('openal.prc', extract = True, text = """ -plugin-path $OPENAL_ROOT -audio-library-name p3openal_audio -""") - -class audio(package): - # This package includes the best audio library for the given - # platform, assuming a non-commercial application. - require('panda3d') - - # OpenAL seems to work pretty well universally now. - require('openal') - -class ffmpeg(package): - # This package includes the ffmpeg libraries and integration. - # It's a bit big, and is restricted by patents and the restrictive - # GPLv3 license, which is why it is included as a separate package. - - config(display_name = "FFMpeg multimedia decoder") - require('panda3d') - - file('libp3ffmpeg.dll', required = True) - - file('ffmpeg.prc', extract = True, text = """ -plugin-path $FFMPEG_ROOT -load-audio-type * p3ffmpeg -load-video-type * p3ffmpeg -""") - -class egg(package): - # This package contains the code for reading and operating on egg - # files and other model files. Since the Packager automatically - # converts egg files to bam files, this is not needed for most - # Panda3D applications. - - config(display_name = "Panda3D egg loader") - require('panda3d') - - module('panda3d.egg', required = True) - file('libpandaegg.dll', required = True) - file('libp3ptloader.dll', required = True) - - file('egg.prc', extract = True, text = """ -plugin-path $EGG_ROOT -load-file-type egg pandaegg -load-file-type p3ptloader - -# These are excerpted from the default Confauto.prc file. -egg-object-type-portal portal { 1 } -egg-object-type-polylight polylight { 1 } -egg-object-type-seq24 { 1 } fps { 24 } -egg-object-type-seq12 { 1 } fps { 12 } -egg-object-type-indexed indexed { 1 } -egg-object-type-seq10 { 1 } fps { 10 } -egg-object-type-seq8 { 1 } fps { 8 } -egg-object-type-seq6 { 1 } fps { 6 } -egg-object-type-seq4 { 1 } fps { 4 } -egg-object-type-seq2 { 1 } fps { 2 } - -egg-object-type-binary alpha { binary } -egg-object-type-dual alpha { dual } -egg-object-type-glass alpha { blend_no_occlude } - -egg-object-type-model { 1 } -egg-object-type-dcs { 1 } -egg-object-type-notouch { no_touch } - -egg-object-type-barrier { Polyset descend } -egg-object-type-sphere { Sphere descend } -egg-object-type-invsphere { InvSphere descend } -egg-object-type-tube { Tube descend } -egg-object-type-trigger { Polyset descend intangible } -egg-object-type-trigger-sphere { Sphere descend intangible } -egg-object-type-floor { Polyset descend level } -egg-object-type-dupefloor { Polyset keep descend level } -egg-object-type-bubble { Sphere keep descend } -egg-object-type-ghost collide-mask { 0 } -egg-object-type-glow blend { add } -egg-object-type-direct-widget collide-mask { 0x80000000 } { Polyset descend } -""") - -class ode(package): - # This package contains the code for the ODE integration. - # As not every application uses the ODE layers, and to cut down - # the download size, it is provided as separate package. - - config(display_name = "Panda3D Open Dynamics Engine integration") - require('panda3d') - - module('panda3d.ode', required = True) - file('libpandaode.dll', required = True) - -class bullet(package): - # This package contains the code for the Bullet integration. - # As not every application uses the Bullet layers, and to cut down - # the download size, it is provided as separate package. - - config(display_name = "Panda3D Bullet integration") - require('panda3d') - - module('panda3d.bullet', required = True) - file('libpandabullet.dll', required = True) - -class physx(package): - # This package contains the code for the NVIDIA PhysX integration. - # As not every application uses the NVIDIA PhysX layers, and to cut down - # the download size, it is provided as separate package. - - config(display_name = "Panda3D PhysX integration") - require('panda3d') - - module('panda3d.physx', required = True) - file('libpandaphysx.dll', required = True) - file('physxcudart_20.dll') - file('PhysXDevice.dll') - -class ai(package): - # This package contains the PandAI library for pathfinding - # and seek/flee behaviors. As some games may not need this - # functionality, it is provided as separate package to save - # on package download size. - - config(display_name = "Panda3D AI modules") - require('panda3d') - - module('panda3d.ai', required = True) - file('libpandaai.dll', required = True) - -class vision(package): - # This package contains the code for webcam support, augmented - # reality and computer vision. As many games will not need this - # functionality, it is provided as separate package to save - # on package download size. - - config(display_name = "Panda3D vision modules") - require('panda3d') - - module('panda3d.vision', required = True) - file('libp3vision.dll', required = True) - -class rocket(package): - # This package contains the code for libRocket integration. - # As not every application uses libRocket GUI, and to cut down - # the download size, it is provided as separate package. - - config(display_name = "Panda3D libRocket support") - require('panda3d') - - file('rocket.py', extract = True, text = """ -from _rocketcore import * -try: - from _rocketcontrols import * -except ImportError: - pass -""") - - module('panda3d.rocket', '_rocketcore', required = True) - module('_rocketcontrols') - file('libp3rocket.dll', required = True) - -class vrpn(package): - # This package contains the code for VRPN integration. - # Most applications don't use this, which is why it's separate. - - config(display_name = "Panda3D VRPN support") - require('panda3d') - - module('panda3d.vrpn', required = True) - file('libp3vrpn.dll', required = True) - - -class packp3d(p3d): - # This application is a command-line convenience for building a p3d - # application out of a directory hierarchy on disk. We build it here - # into its own p3d application, to allow end-users to easily build p3d - # applications using the appropriate version of Python and Panda for - # the targeted runtime. - - config(display_name = "Panda3D Application Packer", - hidden = True, platform_specific = False, - keep_user_env = True) - require('panda3d', 'egg') - - mainModule('direct.p3d.packp3d') - - file('packp3d.prc', extract = True, text = "preload-textures false") - - -class ppackage(p3d): - # As above, a packaging utility. This is the fully-general ppackage - # utility, which reads pdef files (like this one!) and creates one or - # more packages or p3d applications. - - config(display_name = "Panda3D General Package Utility", - hidden = True, platform_specific = False, - keep_user_env = True) - require('panda3d', 'egg') - - mainModule('direct.p3d.ppackage') - - file('ppackage.prc', extract = True, text = "preload-textures false") - - -class ppatcher(p3d): - # A handy utility to go along with ppackage. This builds - # patchfiles as needed in the directory structure created by - # ppackage. - - config(display_name = "Panda3D Patch Maker", - hidden = True, platform_specific = False, - keep_user_env = True) - require('panda3d') - - mainModule('direct.p3d.ppatcher') - - -class pmerge(p3d): - # Another handy utility to go along with ppackage. This - # merges multiple directory structures as created by - # ppackage into a single one. - - config(display_name = "Panda3D Package Merger", - hidden = True, platform_specific = False, - keep_user_env = True) - require('panda3d') - - mainModule('direct.p3d.pmerge') - - -class pdeploy(p3d): - # This utility can distribute a game in the form of - # a standalone executable or a graphical installer. - - config(display_name = "Panda3D Deployment Tool", - hidden = True, platform_specific = False, - keep_user_env = True) - require('panda3d', 'morepy') - - mainModule('direct.p3d.pdeploy') - diff --git a/direct/src/p3d/pdeploy.py b/direct/src/p3d/pdeploy.py deleted file mode 100644 index f98d10b65d..0000000000 --- a/direct/src/p3d/pdeploy.py +++ /dev/null @@ -1,359 +0,0 @@ -#! /usr/bin/env python - -usageText = """ - -This command will help you to distribute your Panda application, -consisting of a .p3d package, into a standalone executable, graphical -installer or an HTML webpage. It will attempt to create packages -for every platform, if possible. -Note that pdeploy requires an internet connection to run. - -When used with the 'installer' option, it is strongly advisable -to specify most if not all of the descriptive information that can -be passed on the command-line. - -Usage: - - %(prog)s [opts] app.p3d standalone|installer|html - -Modes: - - standalone - A standalone executable will be created that embeds the given - p3d file. The resulting executable will require an - internet connection in order to run properly. - - installer - In this mode, installable packages will be created for as many - platforms as possible. To create Windows installers on - non-Windows platforms, you need to have the "makensis" utility - on your system PATH environment variable. - - html - An HTML webpage will be generated that can be used to view - the provided p3d file in a browser. - -Options: - - -n your_app - Short, lowercase name of the application or game. May only - contain alphanumeric characters, underscore or dash. This - name will also define the output file(s) of the process. - If omitted, the basename of the p3d file is used. - - -N "Your Application" - Full name of the application or game. This is the - name that will be displayed to the end-user. - The 'display_name' config is used by default. If it - is missing, the short name is used. - - -v version_number - This should define the version number of your application - or game. In some deploy modes, this argument is required. - This should only contain alphanumeric characters, dots and - dashes, as otherwise the result of the deployment may be - invalid on some platforms. - - -o output_dir - Indicates the directory where the output will be stored. - Within this directory, subdirectories will be created - for every platform, unless -t is provided. - If omitted, the current working directory is assumed. - - -t token=value - Defines a web token or parameter to pass to the application. - Use this to configure how the application will be run. - You can pass as many -t options as you need. Some examples of - useful token names are width, height, log_basename, auto_start, - hidden and console_environment. - - -P platform - If this option is provided, it should specify a comma- - separated list of platforms that the p3d package will be - deployed for. If omitted, it will be built for all platforms. - This option may be specified multiple times. - Examples of valid platforms are win_i386, linux_amd64 and osx_ppc. - - -c - If this option is provided, any -P options are ignored and - the p3d package is only deployed for the current platform. - Furthermore, no per-platform subdirectories will be created - inside the output dirctory. - - -s - This option only has effect in 'installer' mode. If it is - provided, the resulting installers will be fully self-contained, - will not require an internet connection to run, and start up - much faster. Note that pdeploy will also take a very long time - to finish when -s is provided. - If it is omitted, pdeploy will finish much quicker, and the - resulting installers will be smaller, but they will require - an internet connection for the first run, and the load time - will be considerably longer. - - -l "License Name" - Specifies the name of the software license that the game - or application is licensed under. - Only relevant when generating a graphical installer. - - -L licensefile.txt - This should point to a file that contains the full text - describing the software license that the game or application - is licensed under. - Only relevant when generating a graphical installer. - - -O - Specify this option when generating a graphical installer to omit - the default checkboxes for "run this program" and "install a - desktop shortcut" on completion. - - -a com.your_company - Short identifier of the author of the application. The default - is "org.panda3d", but you will most likely want to change - it to your own name or that of your organization or company. - Only relevant when generating a graphical installer. - - -A "Your Company" - Full name of the author of the application. The default is - determined from the GECOS information of the current user if - available; if not, your username is used. - Only relevant when generating a graphical installer. - - -e "you@your_company.com" - E-mail address of the maintainer of the application. The default - is username@hostname. - Only relevant when generating a graphical installer. - - -i "path/icon32.png" -i "path/icon48.png" -i "path/icon128.png" - This option should be repeated several times with different square - image sizes. These images will then be combined to form an icon - file that will be used to represent the installed application. - To support all platforms, it is recommended to supply images of - the sizes 16x16, 32x32, 48x48, 128x128, 256x256, and 512x512. - The larger icon sizes can safely be omitted if you cannot - provide images in that resolution. - It is recommended to use .png images for correct transparency. - If no images are provided, no icon will be generated. - Only relevant when generating a graphical installer. - - -h - Display this help - -""" - -DEPLOY_MODES = ["standalone", "installer", "html"] - -import sys -import os -import getopt -from direct.p3d.DeploymentTools import Standalone, Installer, Icon -from panda3d.core import Filename, PandaSystem - -def usage(code, msg = ''): - if not msg: - sys.stderr.write(usageText % {'prog' : os.path.split(sys.argv[0])[1]}) - sys.stderr.write(msg + '\n') - sys.exit(code) - -shortname = "" -fullname = "" -version = "" -outputDir = Filename("./") -tokens = {} -platforms = [] -currentPlatform = False -licensename = "" -licensefile = Filename() -authorid = "" -authorname = "" -authoremail = "" -iconFiles = [] -includeRequires = False -omitDefaultCheckboxes = False - -try: - opts, args = getopt.getopt(sys.argv[1:], 'n:N:v:o:t:P:csOl:L:a:A:e:i:h') -except getopt.error as msg: - usage(1, msg or 'Invalid option') - -for opt, arg in opts: - if opt == '-n': - shortname = arg.strip() - elif opt == '-N': - fullname = arg.strip() - elif opt == '-v': - version = arg.strip() - elif opt == '-o': - outputDir = Filename.fromOsSpecific(arg) - elif opt == '-t': - token = arg.strip().split("=", 1) - tokens[token[0]] = token[1] - elif opt == '-P': - platforms.append(arg) - elif opt == '-c': - currentPlatform = True - elif opt == '-s': - includeRequires = True - elif opt == '-O': - omitDefaultCheckboxes = True - elif opt == '-l': - licensename = arg.strip() - elif opt == '-L': - licensefile = Filename.fromOsSpecific(arg) - elif opt == '-a': - authorid = arg.strip() - elif opt == '-A': - authorname = arg.strip() - elif opt == '-e': - authoremail = arg.strip() - elif opt == '-i': - iconFiles.append(Filename.fromOsSpecific(arg)) - - elif opt == '-h': - usage(0) - else: - msg = 'illegal option: ' + flag - print(msg) - sys.exit(1, msg) - -if not args or len(args) != 2: - msg = 'Wrong number of arguments, received %s, expected 2' % ( - len(args or [])) - usage(1, msg) - -appFilename = Filename.fromOsSpecific(args[0]) -if appFilename.getExtension().lower() != 'p3d': - print('Application filename must end in ".p3d".') - sys.exit(1) -deploy_mode = args[1].lower() - -if not appFilename.exists(): - print('Application filename does not exist!') - sys.exit(1) - -if shortname == '': - shortname = appFilename.getBasenameWoExtension() - -if shortname.lower() != shortname or ' ' in shortname: - print('\nProvided short name should be lowercase, and may not contain spaces!\n') - -if version == '' and deploy_mode == 'installer': - print('\nA version number is required in "installer" mode.\n') - sys.exit(1) - -if not outputDir: - print('\nYou must name the output directory with the -o parameter.\n') - sys.exit(1) -if not outputDir.exists(): - print('\nThe specified output directory does not exist!\n') - sys.exit(1) -elif not outputDir.isDirectory(): - print('\nThe specified output directory is a file!\n') - sys.exit(1) - -if deploy_mode == 'standalone': - s = Standalone(appFilename, tokens) - s.basename = shortname - - if currentPlatform: - platform = PandaSystem.getPlatform() - if platform.startswith("win"): - s.build(Filename(outputDir, shortname + ".exe"), platform) - else: - s.build(Filename(outputDir, shortname), platform) - elif len(platforms) == 0: - s.buildAll(outputDir) - else: - for platform in platforms: - if platform.startswith("win"): - s.build(Filename(outputDir, platform + "/" + shortname + ".exe"), platform) - else: - s.build(Filename(outputDir, platform + "/" + shortname), platform) - -elif deploy_mode == 'installer': - if includeRequires: - tokens["verify_contents"] = "never" - i = Installer(appFilename, shortname, fullname, version, tokens = tokens) - i.includeRequires = includeRequires - if omitDefaultCheckboxes: - i.offerRun = False - i.offerDesktopShortcut = False - i.licensename = licensename - i.licensefile = licensefile - if authorid: - i.authorid = authorid - if authorname: - i.authorname = authorname - if authoremail: - i.authoremail = authoremail - if not authorname or not authoremail or not authorid: - print("Using author \"%s\" <%s> with ID %s" % \ - (i.authorname, i.authoremail, i.authorid)) - - # Add the supplied icon images - if len(iconFiles) > 0: - failed = False - i.icon = Icon() - for iconFile in iconFiles: - if not i.icon.addImage(iconFile): - print('\nFailed to add icon image "%s"!\n' % iconFile) - failed = True - if failed: - sys.exit(1) - - # Now build for the requested platforms. - if currentPlatform: - platform = PandaSystem.getPlatform() - if platform.startswith("win"): - i.build(outputDir, platform) - else: - i.build(outputDir, platform) - elif len(platforms) == 0: - i.buildAll(outputDir) - else: - for platform in platforms: - output = Filename(outputDir, platform + "/") - output.makeDir() - i.build(output, platform) - - del i - -elif deploy_mode == 'html': - w, h = tokens.get("width", 800), tokens.get("height", 600) - if "data" not in tokens: - tokens["data"] = appFilename.getBasename() - - print("Creating %s.html..." % shortname) - html = open(Filename(outputDir, shortname + ".html").toOsSpecific(), "w") - html.write("\n") - html.write("\n") - html.write(" \n") - html.write(" %s\n" % fullname) - html.write(" \n") - if authorname: - html.write(" \n" % authorname.replace('"', '\\"')) - html.write(" \n") - html.write(" \n") - html.write(" ") - html.write(" \n" % (w, h)) - for key, value in tokens.items(): - html.write(" \n" % (key, value.replace('"', '\\"'))) - html.write(" \n") - html.write(" \n") - html.write(" \n") - html.write("\n") - html.close() -else: - usage(1, 'Invalid deployment mode!') - -# An explicit call to exit() is required to exit the program, when -# this module is packaged in a p3d file. -sys.exit(0) - diff --git a/direct/src/p3d/pmerge.py b/direct/src/p3d/pmerge.py deleted file mode 100755 index 99492df35b..0000000000 --- a/direct/src/p3d/pmerge.py +++ /dev/null @@ -1,106 +0,0 @@ -#! /usr/bin/env python - -usageText = """ - -This script can be used to merge together the contents of two or more -separately-built stage directories, built independently via ppackage, -or via Packager.py. This script also verifies the hash, file size, -and timestamp values in the stage directory as it runs, so it can be -run on a single standalone directory just to perform this validation. - -This script is actually a wrapper around Panda's PackageMerger.py. - -Usage: - - %(prog)s [opts] [inputdir1 .. inputdirN] - -Parameters: - - inputdir1 .. inputdirN - Specify the full path to the input directories you wish to merge. - These are the directories specified by -i on the previous - invocations of ppackage. The order is mostly unimportant. - -Options: - - -i install_dir - The full path to the final install directory. This may also - contain some pre-existing contents; if so, it is merged with all - of the input directories as well. The contents of this directory - are checked for self-consistency with regards to hashes and - timestamps. - - -p packageName[,packageName...] - Specifies one or more particular packages by name that are to be - included from the input directories. Any packages not in this - list are left unchanged in the install directory, even if there - is a newer version in one of the input directories. If no - packages are named, all packages are involved. This option may - be repeated. - - -h - Display this help - -""" - -import sys -import getopt -import os - -from direct.p3d import PackageMerger -from panda3d.core import Filename - -def usage(code, msg = ''): - sys.stderr.write(usageText % {'prog' : os.path.split(sys.argv[0])[1]}) - sys.stderr.write(msg + '\n') - sys.exit(code) - -try: - opts, args = getopt.getopt(sys.argv[1:], 'i:p:h') -except getopt.error as msg: - usage(1, msg) - -installDir = None -packageNames = [] -for opt, arg in opts: - if opt == '-i': - installDir = Filename.fromOsSpecific(arg) - elif opt == '-p': - packageNames += arg.split(',') - - elif opt == '-h': - usage(0) - else: - print('illegal option: ' + arg) - sys.exit(1) - -if not packageNames: - # No package names means allow all packages. - packageNames = None - -inputDirs = [] -for arg in args: - inputDirs.append(Filename.fromOsSpecific(arg)) - -# It's now legal to have no input files if you only want to verify -# timestamps and hashes. -## if not inputDirs: -## print "no input directories specified." -## sys.exit(1) - -try: - pm = PackageMerger.PackageMerger(installDir) - for dir in inputDirs: - pm.merge(dir, packageNames = packageNames) - pm.close() - -except PackageMerger.PackageMergerError: - # Just print the error message and exit gracefully. - inst = sys.exc_info()[1] - print(inst.args[0]) - sys.exit(1) - - -# An explicit call to exit() is required to exit the program, when -# this module is packaged in a p3d file. -sys.exit(0) diff --git a/direct/src/p3d/ppackage.py b/direct/src/p3d/ppackage.py deleted file mode 100755 index cf42ea8403..0000000000 --- a/direct/src/p3d/ppackage.py +++ /dev/null @@ -1,267 +0,0 @@ -#! /usr/bin/env python - -usageText = """ -This script can be used to produce a Panda3D downloadable "package", -which may contain arbitrary files--for instance, Python code, bam -files, and/or compiled DLL's--and which may be downloaded by -application code to extend an application at runtime. - -In addition to packages, this script can also be used to build -standalone p3d applications, that is, packaged Python code in a p3d -file, for execution by the Panda3D plugin or runtime. (But also see -packp3d, which is designed to be a simpler interface for building -applications.) - -This command will build p3d files that reference Panda3D %(version)s, -from host %(host)s . - -This script is actually a wrapper around Panda's Packager.py, which -can be invoked directly by Python code that requires a programmatic -interface to building packages. - -Usage: - - %(prog)s [opts] package.pdef [packageName1 .. packageNameN] - -Parameters: - - package.pdef - The config file that describes the contents of the package file(s) - to be built, in excruciating detail. See the Panda3D manual for - the syntax of this file. - - packageName1 .. packageNameN - Specify the names of the package(s) you wish to build out of the - package.pdef file. This allows you to build only a subset of the - packages defined in this file. If you omit these parameters, all - packages are built, and packages that cannot be built are silently - ignored. - -Options: - - -i install_dir - The full path to a local directory to copy the - ready-to-be-published files into. This directory structure may - contain multiple different packages from multiple different - invocations of this script. It is the user's responsibility to - copy this directory structure to a server, which will have the - URL specified by the packager.setHost() call appearing within the - pdef file. - - -p - Automatically build patches against previous versions after - generating the results. Patches are difference files that users - can download when updating a package, in lieu of redownloading - the whole package; this happens automatically if patches are - present. You should generally build patches when you are - committing to a final, public-facing release. Patches are - usually a good idea, but generating a patch for each internal - test build may needlessly generate a lot of small, inefficient - patch files instead of a few larger ones. You can also generate - patches after the fact, by running ppatcher on the install - directory. - - -s search_dir - Additional directories to search for previously-built packages. - This option may be repeated as necessary. These directories may - also be specified with the pdef-path Config.prc variable. - - -S file.crt[,chain.crt[,file.key[,\"password\"]]] - Signs any resulting p3d file(s) with the indicated certificate. - You may specify the signing certificate, the optional - authorization chain, and the private key in three different - files, or they may all be combined in the first file. If the - private key is encrypted, the password will be required to - decrypt it. - - -D - Sets the allow_python_dev flag in any applications built with - this command. This enables additional runtime debug operations, - particularly the -i option to the panda3d command, which enables - a live Python prompt within the application's environment. - Setting this flag may be useful to develop an application - initially, but should not be set on an application intended for - deployment. - - -N - If this option is set, Packager will not try to compile any Python - files to .pyc or .pyo, instead storing the original source files. - - -u - On the Mac OSX platform, this means that Panda was built with - universal binaries, and the package should be built that way as - well (that is, a version of the the package should be created for - each supported architecture). On other platforms, this option - does nothing. This is therefore safe to apply in all cases, if - you wish to take advantage of universal binaries. This is - equivalent to "-P osx_i386 -P osx_amd64" on Mac platforms. - - -P platform - Specify the platform to masquerade as. The default is whatever - platform Panda has been built for. You can use this on Mac OSX - in order to build packages for an alternate architecture if you - have built Panda with universal binaries; you may repeat this - option for each architecture you wish to support. For other - platforms, it is probably a mistake to set this. However, see - the option -u. - - -R sysroot - Specify the sysroot that these files were compiled against. This - will shadow the system shared libraries, so that alternate - versions are used instead of the system versions. If any program - references a library, say /usr/lib/libfoo.so, and - /sysroot/usr/lib/libfoo.so exists instead, that file will be used - instead of the system library. This is particularly useful for - cross-compilation. At the moment, this is supported only on OSX. - - -H - Treats a packager.setHost() call in the pdef file as if it were - merely a call to packager.addHost(). This allows producing a - package for an alternate host than its normally configured host, - which is sometimes useful in development. - - -a suffix - Appends the given suffix to the p3d filename, before the extension. - This is useful when the same packages are compiled several times - but using different settings, and you want to mark those packages - as such. This only applies to .p3d packages, not to other types - of packages! - - -v - Emit a warning for any file not recognized by the dir() command - (indicating there may be a need for addExtensions(...)). - - -h - Display this help -""" - -import sys -import getopt -import os - -from direct.p3d import Packager -from panda3d.core import * - -def usage(code, msg = ''): - sys.stderr.write(usageText % { - 'version' : PandaSystem.getPackageVersionString(), - 'host' : PandaSystem.getPackageHostUrl(), - 'prog' : os.path.split(sys.argv[0])[1] - }) - sys.stderr.write(msg + '\n') - sys.exit(code) - -installDir = None -buildPatches = False -installSearch = [] -signParams = [] -allowPythonDev = False -storePythonSource = False -universalBinaries = False -systemRoot = None -ignoreSetHost = False -verbosePrint = False -p3dSuffix = '' -platforms = [] - -try: - opts, args = getopt.getopt(sys.argv[1:], 'i:ps:S:DNuP:R:Ha:hv') -except getopt.error as msg: - usage(1, msg) - -for opt, arg in opts: - if opt == '-i': - installDir = Filename.fromOsSpecific(arg) - elif opt == '-p': - buildPatches = True - elif opt == '-s': - installSearch.append(Filename.fromOsSpecific(arg)) - elif opt == '-S': - tokens = arg.split(',') - while len(tokens) < 4: - tokens.append('') - certificate, chain, pkey, password = tokens[:4] - signParams.append((Filename.fromOsSpecific(certificate), - Filename.fromOsSpecific(chain), - Filename.fromOsSpecific(pkey), - Filename.fromOsSpecific(password))) - elif opt == '-D': - allowPythonDev = True - elif opt == '-N': - storePythonSource = True - elif opt == '-u': - universalBinaries = True - elif opt == '-P': - platforms.append(arg) - elif opt == '-R': - systemRoot = arg - elif opt == '-H': - ignoreSetHost = True - elif opt == '-a': - p3dSuffix = arg - - elif opt == '-v': - verbosePrint = True - - elif opt == '-h': - usage(0) - else: - print('illegal option: ' + arg) - sys.exit(1) - -if not args: - usage(0) - -packageDef = Filename.fromOsSpecific(args[0]) -packageNames = None -if len(args) > 1: - packageNames = args[1:] - -# Add the directory containing the pdef file itself to sys.path, to -# help the Packager locate modules where a pathname isn't specified. -dirname = packageDef.getDirname() -if dirname: - sys.path.append(Filename(dirname).toOsSpecific()) -else: - sys.path.append('.') - -if universalBinaries: - if platforms: - print('\nYou may not specify both -u and -P.\n') - sys.exit(1) - if PandaSystem.getPlatform().startswith('osx_'): - platforms = ['osx_i386', 'osx_amd64'] - -if not platforms: - platforms = [PandaSystem.getPlatform()] - -for platform in platforms: - packager = Packager.Packager(platform = platform) - packager.installDir = installDir - packager.installSearch = installSearch + packager.installSearch - if installDir is not None: - packager.installSearch = [installDir] + packager.installSearch - packager.signParams = signParams - packager.allowPythonDev = allowPythonDev - packager.storePythonSource = storePythonSource - packager.systemRoot = systemRoot - packager.ignoreSetHost = ignoreSetHost - packager.verbosePrint = verbosePrint - packager.p3dSuffix = p3dSuffix - - try: - packager.setup() - packages = packager.readPackageDef(packageDef, packageNames = packageNames) - packager.close() - if buildPatches: - packager.buildPatches(packages) - - except Packager.PackagerError: - # Just print the error message and exit gracefully. - inst = sys.exc_info()[1] - print(inst.args[0]) - sys.exit(1) - -# An explicit call to exit() is required to exit the program, when -# this module is packaged in a p3d file. -sys.exit(0) diff --git a/direct/src/p3d/ppatcher.py b/direct/src/p3d/ppatcher.py deleted file mode 100755 index 69e9689622..0000000000 --- a/direct/src/p3d/ppatcher.py +++ /dev/null @@ -1,101 +0,0 @@ -#! /usr/bin/env python - -usageText = """ - -This script generates the patches required to support incremental -download of Panda3D packages. It can be run as a post-process on a -directory hierarchy created by ppackage; it will examine the directory -hierarchy, and create any patches that appear to be missing. - -You may run ppackage on the same directory hierarchy as many times as -you like, without creating patches. You may then download and test -the resulting files--users connecting to the tree without fresh -patches will be forced to download the entire file, instead of making -an incremental download, but the entire process will work otherwise. -When you are satisfied that all of the files are ready to be released, -you may run ppackage on the directory hierarchy to generate the -required patches. - -Generating the patches just before final release is a good idea to -limit the number of trivially small patches that are created. Each -time this script is run, a patch is created from the previous version, -and these patches daisy-chain together to define a complete update -sequence. If you run this script on internal releases, you will -generate a long chain of small patches that your users must download; -this is pointless if there is no possibility of anyone having -downloaded one of the intervening versions. - -You can also generate patches with the -p option to ppackage, but that -only generates patches for the specific packages built by that -invocation of ppackage. If you use the ppatcher script instead, it -will generate patches for all packages (or the set of packages that -you name specifically). - -This script is actually a wrapper around Panda's PatchMaker.py. - -Usage: - - %(prog)s [opts] [packageName1 .. packageNameN] - -Parameters: - - packageName1 .. packageNameN - Specify the names of the package(s) you wish to generate patches - for. This allows you to build patches for only a subset of the - packages found in the tree. If you omit these parameters, patches - are built for all packages that require them. - -Options: - - -i install_dir - The full path to the install directory. This should be the same - directory named by the -i parameter to ppackage. - - -h - Display this help - -""" - -import sys -import getopt -import os - -from direct.p3d.PatchMaker import PatchMaker -from panda3d.core import Filename - -def usage(code, msg = ''): - sys.stderr.write(usageText % {'prog' : os.path.split(sys.argv[0])[1]}) - sys.stderr.write(msg + '\n') - sys.exit(code) - -try: - opts, args = getopt.getopt(sys.argv[1:], 'i:h') -except getopt.error as msg: - usage(1, msg) - -installDir = None -for opt, arg in opts: - if opt == '-i': - installDir = Filename.fromOsSpecific(arg) - - elif opt == '-h': - usage(0) - else: - print('illegal option: ' + arg) - sys.exit(1) - -packageNames = args - -if not installDir: - installDir = Filename('install') - -if not packageNames: - # "None" means all packages. - packageNames = None - -pm = PatchMaker(installDir) -pm.buildPatches(packageNames = packageNames) - -# An explicit call to exit() is required to exit the program, when -# this module is packaged in a p3d file. -sys.exit(0) diff --git a/direct/src/p3d/runp3d.py b/direct/src/p3d/runp3d.py deleted file mode 100644 index 2ab457bc3c..0000000000 --- a/direct/src/p3d/runp3d.py +++ /dev/null @@ -1,79 +0,0 @@ -#! /usr/bin/env python - -""" - -This tool will invoke the AppRunner to execute a packaged p3d -application. It requires that that the current Panda3D and Python -versions match the version expected by the application. - -Normally, you do not need to use this tool; instead, use the provided -standalone panda3d executable to invoke any p3d application. Using -panda3d will guarantee that the correct versions of Panda3D and Python -are used to run the application. However, there may be occasions when -it is useful to use this tool to run the application with the current -build instead of with its advertised version requirements. - -Usage: - - runp3d.py app.p3d [args] - -The command-line arguments following the application name are passed -into the application unchanged. - -See pack3d.p3d for an application that generates these p3d files. - -""" - -import sys -import getopt -from .AppRunner import AppRunner, ArgumentError -from direct.task.TaskManagerGlobal import taskMgr -from panda3d.core import Filename - -def parseSysArgs(): - """ Handles sys.argv, if there are any local arguments, and - returns a new argv suitable for passing into the - application. """ - - # We prefix a "+" sign, following the GNU convention, to tell - # getopt not to parse options following the first non-option - # parameter. - opts, args = getopt.getopt(sys.argv[1:], '+h') - - for option, value in opts: - if option == '-h': - print(__doc__) - sys.exit(1) - - if not args or not args[0]: - raise ArgumentError("No Panda app specified. Use:\nrunp3d.py app.p3d") - - arg0 = args[0] - p3dFilename = Filename.fromOsSpecific(arg0) - if p3dFilename.exists(): - p3dFilename.makeAbsolute() - arg0 = p3dFilename.toOsSpecific() - - return [arg0] + args[1:] - -def runPackedApp(pathname): - runner = AppRunner() - runner.gotWindow = True - try: - runner.setP3DFilename(pathname, tokens = [], argv = [], - instanceId = 0, interactiveConsole = False) - except ArgumentError as e: - print(e.args[0]) - sys.exit(1) - -if __name__ == '__main__': - runner = AppRunner() - runner.gotWindow = True - try: - argv = parseSysArgs() - runner.setP3DFilename(argv[0], tokens = [], argv = argv, - instanceId = 0, interactiveConsole = False) - except ArgumentError as e: - print(e.args[0]) - sys.exit(1) - taskMgr.run() diff --git a/direct/src/p3d/thirdparty.pdef b/direct/src/p3d/thirdparty.pdef deleted file mode 100644 index bc001e427d..0000000000 --- a/direct/src/p3d/thirdparty.pdef +++ /dev/null @@ -1,167 +0,0 @@ -# This file defines a number of standard "packages" that correspond to -# a Panda3D distribution. These packages are built by passing this -# file to the ppackage utility, either as a packaged application, or -# as the module direct.p3d.ppackage. - -# The packages in this file define the thirdparty -# packages that could be used by a .p3d archive. - -# When needed, these packages are downloaded by the core API, from the -# host URL specified in a given p3d file, and not from any hardcoded -# URL. Thus, any custom version of Panda3D may be hosted on any -# server in the world, and any version of the plugin can download it. - -# Also see panda3d.pdef. - -from panda3d.core import Filename -import sys - -class wx(package): - config(display_name = "wxPython GUI Toolkit") - #config(gui_app = True) - require('panda3d') - - module('direct.showbase.WxGlobal') - module('direct.wxwidgets') - module('direct.wxwidgets.*') - module('wx', required = True) - module('wx.*') - -class tk(package): - config(display_name = "Tk GUI Toolkit") - #config(gui_app = True) - require('panda3d') - - if sys.version_info >= (3, 0): - module('tkinter', '_tkinter', required = True) - module('tkinter.colorchooser', 'tkinter.commondialog', - 'tkinter.constants', 'tkinter.dialog', 'tkinter.dnd', - 'tkinter.filedialog', 'tkinter.font', 'tkinter.messagebox', - 'tkinter.scrolledtext', 'tkinter.simpledialog', 'tkinter.tix', - 'tkinter.ttk') - else: - module('Tkinter', '_tkinter', required = True) - module('Tkconstants', 'Tkdnd', 'Tix', 'ScrolledText', 'turtle', - 'tkColorChooser', 'tkCommonDialog', 'tkFileDialog', - 'tkFont', 'tkMessageBox', 'tkSimpleDialog') - - module('direct.showbase.TkGlobal', - 'direct.tkpanels', - 'direct.tkwidgets') - - try: - if sys.version_info >= (3, 0): - from tkinter import Tcl - else: - from Tkinter import Tcl - tcl = Tcl() - dir = Filename.fromOsSpecific(tcl.eval("info library")) - ver = tcl.eval("info tclversion") - - file(Filename(dir, "*.tcl"), newDir = 'lib/tcl' + ver, extract = True) - except ImportError: - pass - - try: - import Pmw - except ImportError: - Pmw = None - - if Pmw is not None: - Pmw = sys.modules['_Pmw'] - sys.modules['Pmw'] = Pmw - sys.modules['Pmw'].__name__ = 'Pmw' - module('Pmw', 'Pmw.Pmw_1_3.lib.*') - path = Filename.fromOsSpecific(Pmw.__path__[0]) - file(Filename(path, 'Pmw_1_3/lib/Pmw.def'), newDir = 'Pmw/Pmw_1_3/lib') - -class sqlite(package): - config(display_name = "SQLite Interface") - require('panda3d') - - module('sqlite', 'sqlite3') - -class numpy(package): - config(display_name = "NumPy") - require('panda3d') - - module('numpy', required = True) - -class pygame(package): - config(display_name = "PyGame") - require('panda3d', 'numpy') - - module('pygame', required = True) - module('pygame.*', 'pygame._view') - -class twisted(package): - config(display_name = "Twisted") - require('panda3d') - - module('twisted', 'twisted._version', required = True) - module('twisted.application', 'twisted.conch', 'twisted.cred', - 'twisted.enterprise', 'twisted.internet', 'twisted.lore', - 'twisted.mail', 'twisted.manhole', 'twisted.names', - 'twisted.news', 'twisted.pair', 'twisted.persisted', - 'twisted.plugin', 'twisted.plugins', 'twisted.python', - 'twisted.runner', 'twisted.scripts', 'twisted.spread', - 'twisted.tap', 'twisted.trial', 'twisted.vfs', - 'twisted.web', 'twisted.web2', 'twisted.words') - - module('twisted.*', 'twisted.*.*') - -class pil(package): - config(display_name = "Python Imaging Library") - require('panda3d') - - module('Image', required = True) - module('PIL', 'ArgImagePlugin', 'BdfFontFile', - 'BmpImagePlugin', 'BufrStubImagePlugin', 'ContainerIO', - 'CurImagePlugin', 'DcxImagePlugin', 'EpsImagePlugin', - 'ExifTags', 'FitsStubImagePlugin', 'FliImagePlugin', - 'FontFile', 'FpxImagePlugin', 'GbrImagePlugin', 'GdImageFile', - 'GifImagePlugin', 'GimpGradientFile', 'GimpPaletteFile', - 'GribStubImagePlugin', 'Hdf5StubImagePlugin', - 'IcnsImagePlugin', 'IcoImagePlugin', 'ImImagePlugin', - 'ImageChops', 'ImageCms', 'ImageColor', 'ImageDraw', - 'ImageDraw2', 'ImageEnhance', 'ImageFile', 'ImageFileIO', - 'ImageFilter', 'ImageFont', 'ImageGL', 'ImageGrab', - 'ImageMath', 'ImageMode', 'ImageOps', 'ImagePalette', - 'ImagePath', 'ImageQt', 'ImageSequence', 'ImageShow', - 'ImageStat', 'ImageTk', 'ImageTransform', 'ImageWin', - 'ImtImagePlugin', 'IptcImagePlugin', 'JpegImagePlugin', - 'McIdasImagePlugin', 'MicImagePlugin', 'MpegImagePlugin', - 'MspImagePlugin', 'OleFileIO', 'PSDraw', 'PaletteFile', - 'PalmImagePlugin', 'PcdImagePlugin', 'PcfFontFile', - 'PcxImagePlugin', 'PdfImagePlugin', 'PixarImagePlugin', - 'PngImagePlugin', 'PpmImagePlugin', 'PsdImagePlugin', - 'SgiImagePlugin', 'SpiderImagePlugin', 'SunImagePlugin', - 'TarIO', 'TgaImagePlugin', 'TiffImagePlugin', 'TiffTags', - 'WalImageFile', 'WmfImagePlugin', 'XVThumbImagePlugin', - 'XbmImagePlugin', 'XpmImagePlugin', '_imaging', - '_imagingmath', '_imagingtk') - -class pyopengl(package): - config(display_name = "PyOpenGL") - require('panda3d', 'numpy') - - module('OpenGL', 'OpenGL.GL', required = True) - module('OpenGL.GLU', 'OpenGL.GLUT', 'OpenGL.GLE', 'OpenGL.GLX') - -class httplib2(package): - config(display_name = "httplib2", platform_specific = False) - require('panda3d') - - module('httplib2', required = True) - -class box2d(package): - config(display_name = "Box2D") - require('panda3d') - - module('Box2D', required = True) - -class pyglet(package): - config(display_name = "pyglet", platform_specific = False) - require('panda3d', 'morepy') - - module('pyglet', required = True) diff --git a/direct/src/particles/ParticleEffect.py b/direct/src/particles/ParticleEffect.py index c58b7b4d46..06b701b388 100644 --- a/direct/src/particles/ParticleEffect.py +++ b/direct/src/particles/ParticleEffect.py @@ -7,6 +7,12 @@ from . import Particles from . import ForceGroup from direct.directnotify import DirectNotifyGlobal +import sys + + +if sys.version_info < (3, 0): + FileNotFoundError = IOError + class ParticleEffect(NodePath): notify = DirectNotifyGlobal.directNotify.newCategory('ParticleEffect') @@ -163,49 +169,51 @@ class ParticleEffect(NodePath): def saveConfig(self, filename): filename = Filename(filename) - f = open(filename.toOsSpecific(), 'wb') - # Add a blank line - f.write('\n') + with open(filename.toOsSpecific(), 'w') as f: + # Add a blank line + f.write('\n') - # Make sure we start with a clean slate - f.write('self.reset()\n') + # Make sure we start with a clean slate + f.write('self.reset()\n') - pos = self.getPos() - hpr = self.getHpr() - scale = self.getScale() - f.write('self.setPos(%0.3f, %0.3f, %0.3f)\n' % - (pos[0], pos[1], pos[2])) - f.write('self.setHpr(%0.3f, %0.3f, %0.3f)\n' % - (hpr[0], hpr[1], hpr[2])) - f.write('self.setScale(%0.3f, %0.3f, %0.3f)\n' % - (scale[0], scale[1], scale[2])) + pos = self.getPos() + hpr = self.getHpr() + scale = self.getScale() + f.write('self.setPos(%0.3f, %0.3f, %0.3f)\n' % + (pos[0], pos[1], pos[2])) + f.write('self.setHpr(%0.3f, %0.3f, %0.3f)\n' % + (hpr[0], hpr[1], hpr[2])) + f.write('self.setScale(%0.3f, %0.3f, %0.3f)\n' % + (scale[0], scale[1], scale[2])) - # Save all the particles to file - num = 0 - for p in list(self.particlesDict.values()): - target = 'p%d' % num - num = num + 1 - f.write(target + ' = Particles.Particles(\'%s\')\n' % p.getName()) - p.printParams(f, target) - f.write('self.addParticles(%s)\n' % target) + # Save all the particles to file + num = 0 + for p in list(self.particlesDict.values()): + target = 'p%d' % num + num = num + 1 + f.write(target + ' = Particles.Particles(\'%s\')\n' % p.getName()) + p.printParams(f, target) + f.write('self.addParticles(%s)\n' % target) - # Save all the forces to file - num = 0 - for fg in list(self.forceGroupDict.values()): - target = 'f%d' % num - num = num + 1 - f.write(target + ' = ForceGroup.ForceGroup(\'%s\')\n' % \ - fg.getName()) - fg.printParams(f, target) - f.write('self.addForceGroup(%s)\n' % target) - - # Close the file - f.close() + # Save all the forces to file + num = 0 + for fg in list(self.forceGroupDict.values()): + target = 'f%d' % num + num = num + 1 + f.write(target + ' = ForceGroup.ForceGroup(\'%s\')\n' % \ + fg.getName()) + fg.printParams(f, target) + f.write('self.addForceGroup(%s)\n' % target) def loadConfig(self, filename): - data = vfs.readFile(filename, 1) - data = data.replace(b'\r', b'') + fn = Filename(filename) + vfs = VirtualFileSystem.getGlobalPtr() try: + if not vfs.resolveFilename(fn, getModelPath().value) and not fn.isRegularFile(): + raise FileNotFoundError("could not find particle file: %s" % (filename)) + + data = vfs.readFile(fn, True) + data = data.replace(b'\r', b'') exec(data) except: self.notify.warning('loadConfig: failed to load particle file: '+ repr(filename)) diff --git a/direct/src/particles/Particles.py b/direct/src/particles/Particles.py index 6ed19af896..d05700c3b1 100644 --- a/direct/src/particles/Particles.py +++ b/direct/src/particles/Particles.py @@ -22,6 +22,7 @@ from panda3d.physics import RingEmitter from panda3d.physics import SphereSurfaceEmitter from panda3d.physics import SphereVolumeEmitter from panda3d.physics import TangentRingEmitter +from panda3d.physics import SpriteAnim from . import SpriteParticleRendererExt diff --git a/direct/src/plugin/binaryXml.cxx b/direct/src/plugin/binaryXml.cxx deleted file mode 100644 index 7dc4a60268..0000000000 --- a/direct/src/plugin/binaryXml.cxx +++ /dev/null @@ -1,351 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file binaryXml.cxx - * @author drose - * @date 2009-07-13 - */ - -#include "binaryXml.h" -#include "p3d_lock.h" -#include - -using std::istream; -using std::ostream; -using std::ostringstream; -using std::string; - -static const bool debug_xml_output = false; - -static LOCK xml_lock; -static bool xml_lock_initialized = false; - -#define DO_BINARY_XML 1 - -enum NodeType { - NT_unknown, - NT_document, - NT_element, - NT_text, -}; - -// This typedef defines a 32-bit unsigned integer. It's used for passing -// values through the binary XML stream. -typedef unsigned int xml_uint32; - -// These are both prime numbers, though I don't know if that really matters. -// Mainly, they're big random numbers. -static const xml_uint32 length_nonce1 = 812311453; -static const xml_uint32 length_nonce2 = 612811373; - -/** - * Should be called before spawning any threads to ensure the lock is - * initialized. - */ -void -init_xml() { - if (!xml_lock_initialized) { - INIT_LOCK(xml_lock); - xml_lock_initialized = true; - } -} - -/** - * Recursively writes a node and all of its children to the given stream. - */ -static void -write_xml_node(ostream &out, TiXmlNode *xnode) { - const string &value = xnode->ValueStr(); - xml_uint32 value_length = value.length(); - xml_uint32 value_proof = (value_length + length_nonce1) * length_nonce2; - - // We write out not only value_length, but the same value again hashed by - // length_nonce1 and 2 (and truncated back to xml_uint32), just to prove to - // the reader that we're still on the same page. We do this only on the top - // node; we don't bother for the nested nodes. - out.write((char *)&value_length, sizeof(value_length)); - out.write((char *)&value_proof, sizeof(value_proof)); - out.write(value.data(), value_length); - - // Now write out the node type. - NodeType type = NT_element; - if (xnode->ToDocument() != nullptr) { - type = NT_document; - } else if (xnode->ToElement() != nullptr) { - type = NT_element; - } else if (xnode->ToText() != nullptr) { - type = NT_text; - } else { - type = NT_unknown; - } - - out.put((char)type); - // We don't bother to write any further data for the unknown types. - if (type == NT_unknown) { - return; - } - - if (type == NT_element) { - // Write the element attributes. - TiXmlElement *xelement = xnode->ToElement(); - assert(xelement != nullptr); - const TiXmlAttribute *xattrib = xelement->FirstAttribute(); - - while (xattrib != nullptr) { - // We have an attribute. - out.put((char)true); - - string name = xattrib->Name(); - xml_uint32 name_length = name.length(); - out.write((char *)&name_length, sizeof(name_length)); - out.write(name.data(), name_length); - - const string &value = xattrib->ValueStr(); - xml_uint32 value_length = value.length(); - out.write((char *)&value_length, sizeof(value_length)); - out.write(value.data(), value_length); - - xattrib = xattrib->Next(); - } - - // The end of the attributes list. - out.put((char)false); - } - - // Now write all of the children. - TiXmlNode *xchild = xnode->FirstChild(); - while (xchild != nullptr) { - // We have a child. - out.put((char)true); - write_xml_node(out, xchild); - xchild = xchild->NextSibling(); - } - - // The end of the children list. - out.put((char)false); -} - -/** - * Recursively reads a node and all of its children to the given stream. - * Returns the newly-allocated node. The caller is responsible for eventually - * deleting the return value. Returns NULL on error. - */ -static TiXmlNode * -read_xml_node(istream &in, char *&buffer, xml_uint32 &buffer_length, - ostream &logfile) { - xml_uint32 value_length; - in.read((char *)&value_length, sizeof(value_length)); - if (in.gcount() != sizeof(value_length)) { - return nullptr; - } - xml_uint32 value_proof_expect = (value_length + length_nonce1) * length_nonce2; - xml_uint32 value_proof; - in.read((char *)&value_proof, sizeof(value_proof)); - if (in.gcount() != sizeof(value_proof)) { - return nullptr; - } - if (value_proof != value_proof_expect) { - // Hey, we ran into garbage: the proof value didn't match our expected - // proof value. - logfile << "Garbage on XML stream!\n"; - - // Print out the garbage; maybe it will help the developer figure out - // where it came from. - logfile << "Begin garbage:\n"; - ostringstream strm; - strm.write((char *)&value_length, sizeof(value_length)); - strm.write((char *)&value_proof, sizeof(value_proof)); - logfile << strm.str(); - for (size_t i = 0; i < 100; ++i) { - int ch = in.get(); - if (ch != EOF) { - logfile.put(ch); - } - } - logfile << "\n"; - logfile << "End garbage.\n"; - return nullptr; - } - - if (value_length > buffer_length) { - delete[] buffer; - buffer_length = value_length; - buffer = new char[buffer_length]; - } - - in.read(buffer, value_length); - string value(buffer, value_length); - - // Read the node type. - NodeType type = (NodeType)in.get(); - if (type == NT_unknown) { - return nullptr; - } - - TiXmlNode *xnode = nullptr; - if (type == NT_element) { - xnode = new TiXmlElement(value); - } else if (type == NT_document) { - xnode = new TiXmlDocument; - } else if (type == NT_text) { - xnode = new TiXmlText(value); - } else { - assert(false); - } - - if (type == NT_element) { - // Read the element attributes. - TiXmlElement *xelement = xnode->ToElement(); - assert(xelement != nullptr); - bool got_attrib = (bool)(in.get() != 0); - - while (got_attrib && in && !in.eof()) { - // We have an attribute. - xml_uint32 name_length; - in.read((char *)&name_length, sizeof(name_length)); - if (in.gcount() != sizeof(name_length)) { - delete xnode; - return nullptr; - } - - if (name_length > buffer_length) { - delete[] buffer; - buffer_length = name_length; - buffer = new char[buffer_length]; - } - - in.read(buffer, name_length); - string name(buffer, name_length); - - xml_uint32 value_length; - in.read((char *)&value_length, sizeof(value_length)); - if (in.gcount() != sizeof(value_length)) { - delete xnode; - return nullptr; - } - - if (value_length > buffer_length) { - delete[] buffer; - buffer_length = value_length; - buffer = new char[buffer_length]; - } - - in.read(buffer, value_length); - string value(buffer, value_length); - - xelement->SetAttribute(name, value); - - got_attrib = (bool)(in.get() != 0); - } - } - - // Now read all of the children. - bool got_child = (bool)(in.get() != 0); - - while (got_child && in && !in.eof()) { - // We have a child. - TiXmlNode *xchild = read_xml_node(in, buffer, buffer_length, logfile); - if (xchild != nullptr) { - xnode->LinkEndChild(xchild); - } - - got_child = (bool)(in.get() != 0); - } - - return xnode; -} - - - -/** - * Writes the indicated TinyXml document to the given stream. - */ -void -write_xml(ostream &out, TiXmlDocument *doc, ostream &logfile) { - assert(xml_lock_initialized); - ACQUIRE_LOCK(xml_lock); - -#ifdef DO_BINARY_XML - // Binary write. - write_xml_node(out, doc); - -#else - // Formatted ASCII write. - - // We need a declaration to write it safely. - TiXmlDeclaration decl("1.0", "utf-8", ""); - doc->InsertBeforeChild(doc->FirstChild(), decl); - - out << *doc; -#endif - - out << flush; - - if (debug_xml_output) { - // Write via ostringstream, so it all goes in one operation, to help out - // the interleaving from multiple threads. - ostringstream logout; - logout << "sent: " << *doc << "\n"; - logfile << logout.str() << flush; - } - - RELEASE_LOCK(xml_lock); -} - -/** - * Reads a TinyXml document from the given stream, and returns it. If the - * document is not yet available, blocks until it is, or until there is an - * error condition on the input. - * - * The return value is NULL if there is an error, or the newly-allocated - * document if it is successfully read. If not NULL, the document has been - * allocated with new, and should be eventually freed by the caller with - * delete. - */ -TiXmlDocument * -read_xml(istream &in, ostream &logfile) { - // We don't acquire xml_lock while reading. We can't, because our XML - // readers are all designed to block until data is available, and they can't - // block while holding the lock. - - // Fortunately, there should be only one reader at a time, so a lock isn't - // really needed here. - -#if DO_BINARY_XML - // binary read. - xml_uint32 buffer_length = 128; - char *buffer = new char[buffer_length]; - TiXmlNode *xnode = read_xml_node(in, buffer, buffer_length, logfile); - delete[] buffer; - if (xnode == nullptr) { - return nullptr; - } - - TiXmlDocument *doc = xnode->ToDocument(); - assert(doc != nullptr); - -#else - // standard ASCII read. - TiXmlDocument *doc = new TiXmlDocument; - in >> *doc; - if (in.fail() || in.eof()) { - delete doc; - return nullptr; - } -#endif - - if (debug_xml_output) { - // Write via ostringstream, so it all goes in one operation, to help out - // the interleaving from multiple threads. - ostringstream logout; - logout << "received: " << *doc << "\n"; - logfile << logout.str() << flush; - } - - return doc; -} diff --git a/direct/src/plugin/binaryXml.h b/direct/src/plugin/binaryXml.h deleted file mode 100644 index 477c51e83f..0000000000 --- a/direct/src/plugin/binaryXml.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file binaryXml.h - * @author drose - * @date 2009-07-13 - */ - -#ifndef BINARYXML_H -#define BINARYXML_H - -#include "get_tinyxml.h" -#include "handleStream.h" -#include - -// A pair of functions to input and output the TinyXml constructs on the -// indicated streams. We could, of course, use the TinyXml output operators, -// but this is a smidge more efficient and gives us more control. - -void init_xml(); -void write_xml(std::ostream &out, TiXmlDocument *doc, std::ostream &logfile); -TiXmlDocument *read_xml(std::istream &in, std::ostream &logfile); - -#endif diff --git a/direct/src/plugin/failed.pgm b/direct/src/plugin/failed.pgm deleted file mode 100644 index c470d8fb6f..0000000000 Binary files a/direct/src/plugin/failed.pgm and /dev/null differ diff --git a/direct/src/plugin/failed.xcf b/direct/src/plugin/failed.xcf deleted file mode 100644 index 4f3a406f00..0000000000 Binary files a/direct/src/plugin/failed.xcf and /dev/null differ diff --git a/direct/src/plugin/fhandle.h b/direct/src/plugin/fhandle.h deleted file mode 100644 index 90459efbe1..0000000000 --- a/direct/src/plugin/fhandle.h +++ /dev/null @@ -1,31 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file fhandle.h - * @author drose - * @date 2009-08-29 - */ - -#ifndef FHANDLE_H -#define FHANDLE_H - -// This header file simply defines the FHandle type, which is used to pass -// around a handle to an open file object. - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -typedef HANDLE FHandle; -static const HANDLE invalid_fhandle = INVALID_HANDLE_VALUE; -#else -// On POSIX, we use a file descriptor as a "handle". -typedef int FHandle; -static const int invalid_fhandle = -1; -#endif - -#endif diff --git a/direct/src/plugin/fileSpec.I b/direct/src/plugin/fileSpec.I deleted file mode 100644 index 22e6f26f81..0000000000 --- a/direct/src/plugin/fileSpec.I +++ /dev/null @@ -1,102 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file fileSpec.I - * @author drose - * @date 2009-06-29 - */ - -/** - * Returns the relative path to this file on disk, within the package root - * directory. - */ -inline const std::string &FileSpec:: -get_filename() const { - return _filename; -} - -/** - * Changes the relative path to this file on disk, within the package root - * directory. - */ -inline void FileSpec:: -set_filename(const std::string &filename) { - _filename = filename; -} - -/** - * Returns the full path to this file on disk. - */ -inline std::string FileSpec:: -get_pathname(const std::string &package_dir) const { - return package_dir + "/" + _filename; -} - -/** - * Returns the expected size of this file on disk, in bytes. - */ -inline size_t FileSpec:: -get_size() const { - return _size; -} - -/** - * Returns the expected last-modify timestamp of this file on disk. - */ -inline time_t FileSpec:: -get_timestamp() const { - return _timestamp; -} - -/** - * Returns true if we have successfully read a hash value, false otherwise. - */ -inline bool FileSpec:: -has_hash() const { - return _got_hash; -} - -/** - * After a call to quick_verify() or full_verify(), this method *may* return a - * pointer to a FileSpec that represents the actual data read on disk, or it - * may return NULL. If this returns a non-NULL value, you may use it to - * extract the md5 hash of the existing file, thus saving the effort of - * performing the hash twice. - */ -inline const FileSpec *FileSpec:: -get_actual_file() const { - return _actual_file; -} - -/** - * Returns the integer value corresponding to the indicated hex digit. - * Returns -1 if it is not a hex digit. - */ -inline int FileSpec:: -decode_hexdigit(char c) { - if (isdigit(c)) { - return c - '0'; - } - c = tolower(c); - if (c >= 'a' && c <= 'f') { - return c - 'a' + 10; - } - - return -1; -} - -/** - * Returns the hex digit corresponding to the indicated integer value. - */ -inline char FileSpec:: -encode_hexdigit(int c) { - if (c >= 10) { - return c - 10 + 'a'; - } - return c + '0'; -} diff --git a/direct/src/plugin/fileSpec.cxx b/direct/src/plugin/fileSpec.cxx deleted file mode 100644 index fd7169c4d1..0000000000 --- a/direct/src/plugin/fileSpec.cxx +++ /dev/null @@ -1,486 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file fileSpec.cxx - * @author drose - * @date 2009-06-29 - */ - -#include "fileSpec.h" -#include "wstring_encode.h" -#include - -#include -#include -#include - -#include -#include -#include - -#ifdef _WIN32 -#include -#include -#define utimbuf _utimbuf - -#else -#include - -#endif - -using std::istream; -using std::ostream; -using std::string; -using std::wstring; - -/** - * - */ -FileSpec:: -FileSpec() { - _size = 0; - _timestamp = 0; - memset(_hash, 0, hash_size); - _got_hash = false; - _actual_file = nullptr; -} - -/** - * - */ -FileSpec:: -FileSpec(const FileSpec ©) : - _filename(copy._filename), - _size(copy._size), - _timestamp(copy._timestamp), - _got_hash(copy._got_hash) -{ - memcpy(_hash, copy._hash, hash_size); - _actual_file = nullptr; -} - -/** - * - */ -void FileSpec:: -operator = (const FileSpec ©) { - _filename = copy._filename; - _size = copy._size; - _timestamp = copy._timestamp; - memcpy(_hash, copy._hash, hash_size); - _got_hash = copy._got_hash; -} - -/** - * - */ -FileSpec:: -~FileSpec() { - if (_actual_file != nullptr) { - delete _actual_file; - } -} - -/** - * Reads the data from the indicated XML file. - */ -void FileSpec:: -load_xml(TiXmlElement *xelement) { - const char *filename = xelement->Attribute("filename"); - if (filename != nullptr) { - _filename = filename; - } - - const char *size = xelement->Attribute("size"); - if (size != nullptr) { - char *endptr; - _size = strtoul(size, &endptr, 10); - } - - const char *timestamp = xelement->Attribute("timestamp"); - if (timestamp != nullptr) { - char *endptr; - _timestamp = strtoul(timestamp, &endptr, 10); - } - - _got_hash = false; - const char *hash = xelement->Attribute("hash"); - if (hash != nullptr && strlen(hash) == (hash_size * 2)) { - // Decode the hex hash string. - _got_hash = decode_hex(_hash, hash, hash_size); - } -} - -/** - * Stores the data to the indicated XML file. - */ -void FileSpec:: -store_xml(TiXmlElement *xelement) { - if (!_filename.empty()) { - xelement->SetAttribute("filename", _filename); - } - if (_size != 0) { - xelement->SetAttribute("size", _size); - } - if (_timestamp != 0) { - xelement->SetAttribute("timestamp", (int)_timestamp); - } - if (_got_hash) { - char hash[hash_size * 2 + 1]; - encode_hex(hash, _hash, hash_size); - hash[hash_size * 2] = '\0'; - xelement->SetAttribute("hash", hash); - } -} - -/** - * Performs a quick test to ensure the file has not been modified. This test - * is vulnerable to people maliciously attempting to fool the program (by - * setting datestamps etc.). - * - * Returns true if it is intact, false if it needs to be redownloaded. - */ -bool FileSpec:: -quick_verify(const string &package_dir) { - return quick_verify_pathname(get_pathname(package_dir)); -} - -/** - * Works like quick_verify(), above, with an explicit pathname. Useful for - * verifying the copy of a file in a temporary location. - */ -bool FileSpec:: -quick_verify_pathname(const string &pathname) { - if (_actual_file != nullptr) { - delete _actual_file; - _actual_file = nullptr; - } - - int result = 1; -#ifdef _WIN32 - struct _stat st; - wstring pathname_w; - if (string_to_wstring(pathname_w, pathname)) { - result = _wstat(pathname_w.c_str(), &st); - } -#else // _WIN32 - struct stat st; - result = stat(pathname.c_str(), &st); -#endif // _WIN32 - - if (result != 0) { - // cerr << "file not found: " << _filename << "\n"; - return false; - } - - if (st.st_size != _size) { - // If the size is wrong, the file fails. cerr << "size wrong: " << - // _filename << "\n"; - return false; - } - - if (st.st_mtime == _timestamp) { - // If the size is right and the timestamp is right, the file passes. cerr - // << "file ok: " << _filename << "\n"; - return true; - } - - // cerr << "modification time wrong: " << _filename << "\n"; - - // If the size is right but the timestamp is wrong, the file soft-fails. We - // follow this up with a hash check. - if (!priv_check_hash(pathname, &st)) { - // Hard fail, the hash is wrong. cerr << "hash check wrong: " << - // _filename << "\n"; - return false; - } - - // cerr << "hash check ok: " << _filename << "\n"; - - // The hash is OK after all. Change the file's timestamp back to what we - // expect it to be, so we can quick-verify it successfully next time. - utimbuf utb; - utb.actime = st.st_atime; - utb.modtime = _timestamp; - -#ifdef _WIN32 - _wutime(pathname_w.c_str(), &utb); -#else // _WIN32 - utime(pathname.c_str(), &utb); -#endif // _WIN32 - - return true; -} - -/** - * Performs a more thorough test to ensure the file has not been modified. - * This test is less vulnerable to malicious attacks, since it reads and - * verifies the entire file. - * - * Returns true if it is intact, false if it needs to be redownloaded. - */ -bool FileSpec:: -full_verify(const string &package_dir) { - if (_actual_file != nullptr) { - delete _actual_file; - _actual_file = nullptr; - } - - string pathname = get_pathname(package_dir); - int result = 1; -#ifdef _WIN32 - struct _stat st; - wstring pathname_w; - if (string_to_wstring(pathname_w, pathname)) { - result = _wstat(pathname_w.c_str(), &st); - } -#else // _WIN32 - struct stat st; - result = stat(pathname.c_str(), &st); -#endif // _WIN32 - - if (result != 0) { - // cerr << "file not found: " << _filename << "\n"; - return false; - } - - if (st.st_size != _size) { - // If the size is wrong, the file fails. cerr << "size wrong: " << - // _filename << "\n"; - return false; - } - - if (!priv_check_hash(pathname, &st)) { - // Hard fail, the hash is wrong. cerr << "hash check wrong: " << - // _filename << "\n"; - return false; - } - - // cerr << "hash check ok: " << _filename << "\n"; - - // The hash is OK. If the timestamp is wrong, change it back to what we - // expect it to be, so we can quick-verify it successfully next time. - - if (st.st_mtime != _timestamp) { - utimbuf utb; - utb.actime = st.st_atime; - utb.modtime = _timestamp; -#ifdef _WIN32 - _wutime(pathname_w.c_str(), &utb); -#else // _WIN32 - utime(pathname.c_str(), &utb); -#endif // _WIN32 - } - - return true; -} - -/** - * Returns a FileSpec that represents the actual data read on disk. This will - * read the disk to determine the data if necessary. - */ -const FileSpec *FileSpec:: -force_get_actual_file(const string &pathname) { - if (_actual_file == nullptr) { -#ifdef _WIN32 - struct _stat st; - wstring pathname_w; - if (string_to_wstring(pathname_w, pathname)) { - _wstat(pathname_w.c_str(), &st); - } -#else // _WIN32 - struct stat st; - stat(pathname.c_str(), &st); -#endif // _WIN32 - - priv_check_hash(pathname, &st); - } - - return _actual_file; -} - -/** - * Returns true if the file has the expected md5 hash, false otherwise. - */ -bool FileSpec:: -check_hash(const string &pathname) const { - FileSpec other; - if (!other.read_hash(pathname)) { - return false; - } - - return (memcmp(_hash, other._hash, hash_size) == 0); -} - -/** - * Computes the hash from the indicated pathname and stores it within the - * FileSpec. - */ -bool FileSpec:: -read_hash(const string &pathname) { - memset(_hash, 0, hash_size); - _got_hash = false; - - ifstream stream; -#ifdef _WIN32 - wstring pathname_w; - if (string_to_wstring(pathname_w, pathname)) { - stream.open(pathname_w.c_str(), std::ios::in | std::ios::binary); - } -#else // _WIN32 - stream.open(pathname.c_str(), std::ios::in | std::ios::binary); -#endif // _WIN32 - - if (!stream) { - // cerr << "unable to read " << pathname << "\n"; - return false; - } - - MD5_CTX ctx; - MD5_Init(&ctx); - - static const int buffer_size = 4096; - char buffer[buffer_size]; - - stream.read(buffer, buffer_size); - size_t count = stream.gcount(); - while (count != 0) { - MD5_Update(&ctx, buffer, count); - stream.read(buffer, buffer_size); - count = stream.gcount(); - } - - MD5_Final(_hash, &ctx); - _got_hash = true; - - return true; -} - -/** - * Reads the hash from the next 16 bytes on the indicated istream, in the same - * unusual order observed by Panda's HashVal::read_stream() method. - */ -bool FileSpec:: -read_hash_stream(istream &in) { - for (int i = 0; i < hash_size; i += 4) { - unsigned int a = in.get(); - unsigned int b = in.get(); - unsigned int c = in.get(); - unsigned int d = in.get(); - _hash[i + 0] = d; - _hash[i + 1] = c; - _hash[i + 2] = b; - _hash[i + 3] = a; - } - - return !in.fail(); -} - -/** - * Returns < 0 if this hash sorts before the other hash, > 0 if it sorts - * after, 0 if they are the same. - */ -int FileSpec:: -compare_hash(const FileSpec &other) const { - return memcmp(_hash, other._hash, hash_size); -} - -/** - * Describes the data in the FileSpec. - */ -void FileSpec:: -write(ostream &out) const { - out << "filename: " << _filename << ", " << _size << " bytes, " - << asctime(localtime(&_timestamp)); - // asctime includes a newline. - out << "hash: "; - stream_hex(out, _hash, hash_size); - out << "\n"; -} - -/** - * Writes just the hash code. - */ -void FileSpec:: -output_hash(ostream &out) const { - stream_hex(out, _hash, hash_size); -} - -/** - * Returns true if the file has the expected md5 hash, false otherwise. - * Updates _actual_file with the data read from disk, including the hash, for - * future reference. - * - * The parameter stp is a pointer to a stat structure. It's declared as a - * void * to get around issues with the nonstandard declaration of this - * structure in Windows. - */ -bool FileSpec:: -priv_check_hash(const string &pathname, void *stp) { - const struct stat &st = *(const struct stat *)stp; - assert(_actual_file == nullptr); - _actual_file = new FileSpec; - _actual_file->_filename = pathname; - _actual_file->_size = st.st_size; - _actual_file->_timestamp = st.st_mtime; - - if (!_actual_file->read_hash(pathname)) { - return false; - } - - return (memcmp(_hash, _actual_file->_hash, hash_size) == 0); -} - -/** - * Decodes the hex string in source into the character array in dest. dest - * must have has least size bytes; source must have size * 2 bytes. - * - * Returns true on success, false if there was a non-hex digit in the string. - */ -bool FileSpec:: -decode_hex(unsigned char *dest, const char *source, size_t size) { - for (size_t i = 0; i < size; ++i) { - int high = decode_hexdigit(source[i * 2]); - int low = decode_hexdigit(source[i * 2 + 1]); - if (high < 0 || low < 0) { - return false; - } - dest[i] = (high << 4) | low; - } - - return true; -} - -/** - * Encodes a character array into a hex string for output. dest must have at - * least size * 2 bytes; source must have size bytes. The result is not null- - * terminated. - */ -void FileSpec:: -encode_hex(char *dest, const unsigned char *source, size_t size) { - for (size_t i = 0; i < size; ++i) { - int high = (source[i] >> 4) & 0xf; - int low = source[i] & 0xf; - dest[2 * i] = encode_hexdigit(high); - dest[2 * i + 1] = encode_hexdigit(low); - } -} - -/** - * Writes the indicated buffer as a string of hex characters to the given - * ostream. - */ -void FileSpec:: -stream_hex(ostream &out, const unsigned char *source, size_t size) { - for (size_t i = 0; i < size; ++i) { - int high = (source[i] >> 4) & 0xf; - int low = source[i] & 0xf; - out.put(encode_hexdigit(high)); - out.put(encode_hexdigit(low)); - } -} diff --git a/direct/src/plugin/fileSpec.h b/direct/src/plugin/fileSpec.h deleted file mode 100644 index 3a661ab603..0000000000 --- a/direct/src/plugin/fileSpec.h +++ /dev/null @@ -1,78 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file fileSpec.h - * @author drose - * @date 2009-06-29 - */ - -#ifndef FILESPEC_H -#define FILESPEC_H - -#include "get_tinyxml.h" -#include - -/** - * This simple class is used both within the core API in this module, as well - * as within the plugin_npapi plugin implementation, to represent a file on - * disk that may need to be verified or (re)downloaded. - */ -class FileSpec { -public: - FileSpec(); - FileSpec(const FileSpec ©); - void operator = (const FileSpec ©); - ~FileSpec(); - - void load_xml(TiXmlElement *xelement); - void store_xml(TiXmlElement *xelement); - - inline const std::string &get_filename() const; - inline void set_filename(const std::string &filename); - inline std::string get_pathname(const std::string &package_dir) const; - inline size_t get_size() const; - inline time_t get_timestamp() const; - inline bool has_hash() const; - - bool quick_verify(const std::string &package_dir); - bool quick_verify_pathname(const std::string &pathname); - bool full_verify(const std::string &package_dir); - inline const FileSpec *get_actual_file() const; - const FileSpec *force_get_actual_file(const std::string &pathname); - - bool check_hash(const std::string &pathname) const; - bool read_hash(const std::string &pathname); - bool read_hash_stream(std::istream &in); - int compare_hash(const FileSpec &other) const; - - void write(std::ostream &out) const; - void output_hash(std::ostream &out) const; - -private: - bool priv_check_hash(const std::string &pathname, void *stp); - static inline int decode_hexdigit(char c); - static inline char encode_hexdigit(int c); - - static bool decode_hex(unsigned char *dest, const char *source, size_t size); - static void encode_hex(char *dest, const unsigned char *source, size_t size); - static void stream_hex(std::ostream &out, const unsigned char *source, size_t size); - - enum { hash_size = 16 }; - - std::string _filename; - size_t _size; - time_t _timestamp; - unsigned char _hash[hash_size]; - bool _got_hash; - - FileSpec *_actual_file; -}; - -#include "fileSpec.I" - -#endif diff --git a/direct/src/plugin/find_root_dir.cxx b/direct/src/plugin/find_root_dir.cxx deleted file mode 100644 index 7d20461aa9..0000000000 --- a/direct/src/plugin/find_root_dir.cxx +++ /dev/null @@ -1,311 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file find_root_dir.cxx - * @author drose - * @date 2009-06-29 - */ - -#include "find_root_dir.h" -#include "mkdir_complete.h" -#include "get_tinyxml.h" -#include "wstring_encode.h" - -#ifdef _WIN32 -#include -#include -#else -#include -#include -#include -#include -#include -#endif - -using std::cerr; -using std::string; -using std::wstring; - -#ifdef _WIN32 -// From KnownFolders.h (part of Vista SDK): -#define DEFINE_KNOWN_FOLDER(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ - const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } -DEFINE_KNOWN_FOLDER(FOLDERID_LocalAppData, 0xF1B32785, 0x6FBA, 0x4FCF, 0x9D, 0x55, 0x7B, 0x8E, 0x7F, 0x15, 0x70, 0x91); -DEFINE_KNOWN_FOLDER(FOLDERID_LocalAppDataLow, 0xA520A1A4, 0x1780, 0x4FF6, 0xBD, 0x18, 0x16, 0x73, 0x43, 0xC5, 0xAF, 0x16); -DEFINE_KNOWN_FOLDER(FOLDERID_InternetCache, 0x352481E8, 0x33BE, 0x4251, 0xBA, 0x85, 0x60, 0x07, 0xCA, 0xED, 0xCF, 0x9D); -#endif // _WIN32 - - -#ifdef _WIN32 -/** - * A wrapper around SHGetSpecialFolderPath(), to return the Panda3D directory - * under the indicated CSIDL folder. - */ -static wstring -get_csidl_dir_w(int csidl) { - static const int buffer_size = MAX_PATH; - wchar_t buffer[buffer_size]; - if (SHGetSpecialFolderPathW(nullptr, buffer, csidl, true)) { - wstring root = buffer; - root += wstring(L"/Panda3D"); - - if (mkdir_complete_w(root, cerr)) { - return root; - } - } - - // Something went wrong. - return wstring(); -} -#endif // _WIN32 - -#ifdef _WIN32 -/** - * Wide-character implementation of find_root_dir_default(), only needed for - * Windows. - */ -static wstring -find_root_dir_default_w() { - // First, use IEIsProtectedModeProcess() to determine if we are running in - // IE's "protected mode" under Vista. - - wstring root; - bool is_protected = false; - HMODULE ieframe = LoadLibrary("ieframe.dll"); - if (ieframe != nullptr) { - typedef HRESULT STDAPICALLTYPE IEIsProtectedModeProcess(BOOL *pbResult); - IEIsProtectedModeProcess *func = (IEIsProtectedModeProcess *)GetProcAddress(ieframe, "IEIsProtectedModeProcess"); - if (func != nullptr) { - BOOL result = false; - HRESULT hr = (*func)(&result); - if (hr == S_OK) { - is_protected = (result != 0); - } - // Any other return value means some error, especially E_NOTIMPL, which - // means we're not running under Vista. In this case we can assume - // we're not running in protected mode. - } - - if (is_protected) { - // If we *are* running in protected mode, we need to use - // FOLDERID_LocalAppDataLow. - - // We should be able to use IEGetWriteableFolderPath() to query this - // folder, but for some reason, that function returns E_ACCESSDENIED on - // FOLDERID_LocalAppDataLow, even though this is certainly a folder we - // have write access to. - - // Well, SHGetKnownFolderPath() does work. This function only exists on - // Vista and above, though, so we still have to pull it out of the DLL - // instead of hard-linking it. - - HMODULE shell32 = LoadLibrary("shell32.dll"); - if (shell32 != nullptr) { - typedef HRESULT STDAPICALLTYPE SHGetKnownFolderPath(REFGUID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath); - SHGetKnownFolderPath *func = (SHGetKnownFolderPath *)GetProcAddress(shell32, "SHGetKnownFolderPath"); - if (func != nullptr) { - LPWSTR cache_path = nullptr; - HRESULT hr = (*func)(FOLDERID_LocalAppDataLow, 0, nullptr, &cache_path); - - if (SUCCEEDED(hr)) { - root = cache_path; - CoTaskMemFree(cache_path); - - root += wstring(L"/Panda3D"); - if (mkdir_complete_w(root, cerr)) { - FreeLibrary(shell32); - FreeLibrary(ieframe); - return root; - } - } - } - FreeLibrary(shell32); - } - - // Couldn't get FOLDERID_LocalAppDataLow for some reason. We're in - // fallback mode now. Use IEGetWriteableFolderPath to get the standard - // cache folder. - typedef HRESULT STDAPICALLTYPE IEGetWriteableFolderPath(REFGUID clsidFolderID, LPWSTR* lppwstrPath); - IEGetWriteableFolderPath *func = (IEGetWriteableFolderPath *)GetProcAddress(ieframe, "IEGetWriteableFolderPath"); - if (func != nullptr) { - LPWSTR cache_path = nullptr; - - // Since we're here, we'll start by asking for LocalAppDataLow, even - // though I know it doesn't work. - HRESULT hr = (*func)(FOLDERID_LocalAppDataLow, &cache_path); - if (FAILED(hr)) { - // This one should work. - hr = (*func)(FOLDERID_InternetCache, &cache_path); - } - - if (SUCCEEDED(hr)) { - root = cache_path; - CoTaskMemFree(cache_path); - root += wstring(L"/Panda3D"); - if (mkdir_complete_w(root, cerr)) { - FreeLibrary(ieframe); - return root; - } - } - } - } - - FreeLibrary(ieframe); - } - - // All right, here we are in the normal, unprotected mode. This is also the - // normal XP codepath. - - // e.g., c:Documents and SettingsLocal SettingsApplication - // DataPanda3D - root = get_csidl_dir_w(CSIDL_LOCAL_APPDATA); - if (!root.empty()) { - return root; - } - - // For some crazy reason, we can't get CSIDL_LOCAL_APPDATA. Fall back to - // the cache folder. - - // e.g. c:Documents and SettingsLocal SettingsTemporary Internet - // FilesPanda3D - root = get_csidl_dir_w(CSIDL_INTERNET_CACHE); - if (!root.empty()) { - return root; - } - - // If we couldn't get any of those folders, huh. Punt and try for the old - // standby GetTempPath, for lack of anything better. - static const int buffer_size = MAX_PATH; - wchar_t buffer[buffer_size]; - if (GetTempPathW(buffer_size, buffer) != 0) { - root = buffer; - root += wstring(L"Panda3D"); - if (mkdir_complete_w(root, cerr)) { - return root; - } - } - - return wstring(); -} -#endif // _WIN32 - - -/** - * Returns the path to the system-default for the root directory. This is - * where we look first. - */ -static string -find_root_dir_default() { -#ifdef _WIN32 - wstring root_w = find_root_dir_default_w(); - if (!root_w.empty()) { - string root; - if (wstring_to_string(root, root_w)) { - return root; - } - } - -#elif defined(__APPLE__) - // e.g., UsersLibraryCachesPanda3D - string root = find_osx_root_dir(); - if (!root.empty()) { - return root; - } - -#else // The Linux/*BSD case - // e.g., home.panda3d - - string root; - const passwd *pwdata = getpwuid(getuid()); - if (pwdata == nullptr) { - char *home = getenv("HOME"); - if (home == nullptr) { - // Beh. Let's hope it never gets to this point. - return "."; - } else { - root = home; - } - } else { - root = pwdata->pw_dir; - } - - root += "/.panda3d"; - if (mkdir(root.c_str(), 0700) == 0 || errno == EEXIST) { - return root; - } - -#endif - - // Couldn't find a directory. Punt. - return "."; -} - - -/** - * Returns the path to the installable Panda3D directory on the user's - * machine. - */ -static string -find_root_dir_actual() { - string root = find_root_dir_default(); - - // Now look for a config.xml file in that directory, which might redirect us - // elsewhere. - string config_filename = root + "/config.xml"; - TiXmlDocument doc(config_filename); - if (!doc.LoadFile()) { - // No config.xml found, or not valid xml. - return root; - } - - TiXmlElement *xconfig = doc.FirstChildElement("config"); - if (xconfig == nullptr) { - // No element within config.xml. - return root; - } - - const char *new_root = xconfig->Attribute("root_dir"); - if (new_root == nullptr || *new_root == '\0') { - // No root_dir specified. - return root; - } - - if (!mkdir_complete(new_root, cerr)) { - // The specified root_dir wasn't valid. - return root; - } - - // We've been redirected to another location. Respect that. - return new_root; -} - -/** - * This is the public interface to the above functions. - */ -string -find_root_dir() { - string root = find_root_dir_actual(); - -#ifdef _WIN32 - // Now map that (possibly utf-8) filename into its 8.3 equivalent, so we can - // safely pass it around to Python and other tools that might not understand - // Unicode filenames. Silly Windows, creating an entirely new and - // incompatible kind of filename. - wstring root_w; - string_to_wstring(root_w, root); - - DWORD length = GetShortPathNameW(root_w.c_str(), nullptr, 0); - wchar_t *short_name = new wchar_t[length]; - GetShortPathNameW(root_w.c_str(), short_name, length); - - wstring_to_string(root, short_name); - delete[] short_name; -#endif // _WIN32 - - return root; -} diff --git a/direct/src/plugin/find_root_dir.h b/direct/src/plugin/find_root_dir.h deleted file mode 100644 index e76be59af3..0000000000 --- a/direct/src/plugin/find_root_dir.h +++ /dev/null @@ -1,26 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file find_root_dir.h - * @author drose - * @date 2009-06-29 - */ - -#ifndef FIND_ROOT_DIR_H -#define FIND_ROOT_DIR_H - -#include -#include - -std::string find_root_dir(); - -#ifdef __APPLE__ -std::string find_osx_root_dir(); -#endif // __APPLE__ - -#endif diff --git a/direct/src/plugin/find_root_dir_assist.mm b/direct/src/plugin/find_root_dir_assist.mm deleted file mode 100644 index f27693d147..0000000000 --- a/direct/src/plugin/find_root_dir_assist.mm +++ /dev/null @@ -1,87 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file find_root_dir_assist.mm - * @author drose - * @date 2009-04-13 - */ - -#include "find_root_dir.h" - -#ifdef __APPLE__ - -#include -#include - -/** - * Copy the Objective-C string to a C++ string. - */ -static string -NSString_to_cpp_string(NSString *str) { - size_t length = [str length]; - string result; - for (size_t i = 0; i < length; ++i) { - result += (char)[str characterAtIndex: i]; - } - - return result; -} - -/** - * - */ -static string -call_NSSearchPathForDirectories(NSSearchPathDirectory dirkey, NSSearchPathDomainMask domain) { - // Ensure that Carbon has been initialized, and that we have an auto-release - // pool. - NSApplicationLoad(); - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSArray *paths = NSSearchPathForDirectoriesInDomains(dirkey, domain, YES); - string result; - if ([paths count] != 0) { - result = NSString_to_cpp_string([paths objectAtIndex:0]); - } - [pool release]; - - return result; -} - -/** - * - */ -static string -get_osx_home_directory() { - NSApplicationLoad(); - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSString *dir = NSHomeDirectory(); - string result = NSString_to_cpp_string(dir); - [pool release]; - - return result; -} - -/** - * - */ -std::string -find_osx_root_dir() { - string result = call_NSSearchPathForDirectories(NSCachesDirectory, NSUserDomainMask); - if (!result.empty()) { - return result + "/Panda3D"; - } - result = get_osx_home_directory(); - if (!result.empty()) { - return result + "/Panda3D"; - } - - return string(); -} - -#endif // __APPLE__ diff --git a/direct/src/plugin/get_tinyxml.h b/direct/src/plugin/get_tinyxml.h deleted file mode 100644 index 12f12f054d..0000000000 --- a/direct/src/plugin/get_tinyxml.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file get_tinyxml.h - * @author drose - * @date 2009-07-01 - */ - -#ifndef GET_TINYXML_H -#define GET_TINYXML_H - -// This header file exists just to include tinyxml.h safely. We need this -// since tinyxml.h requires having the symbol TIXML_USE_STL already defined -// before you include it. - -#ifndef TIXML_USE_STL -#define TIXML_USE_STL -#endif - -#include - -#endif diff --git a/direct/src/plugin/get_twirl_data.cxx b/direct/src/plugin/get_twirl_data.cxx deleted file mode 100644 index 8d1bbd73fd..0000000000 --- a/direct/src/plugin/get_twirl_data.cxx +++ /dev/null @@ -1,963 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file get_twirl_data.cxx - * @author drose - * @date 2011-08-24 - */ - -#include "get_twirl_data.h" -#include - -struct twirl_flip { - int _index; - bool _flip_x; - bool _flip_y; - bool _exchange; -}; - -static twirl_flip twirl_flip_table[twirl_num_steps + 1] = { - { 0, false, false, false }, // 0 - { 1, false, false, false }, // 30 - { 2, false, false, false }, // 60 - { 0, false, true, true }, // 90 - { 1, false, true, true }, // 120 - { 2, false, true, true }, // 150 - { 0, true, true, false }, // 180 - { 1, true, true, false }, // 210 - { 2, true, true, false }, // 240 - { 0, true, false, true }, // 270 - { 1, true, false, true }, // 300 - { 2, true, false, true }, // 330 - { 3, false, false, false } // the "failed" icon -}; - -// These tables are the raw data for the twirl icon data. They're extracted -// with bin2c from the three layers of twirl.xcf, in this directory. -static const unsigned char twirl_0_data[] = { - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, - 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xe4, 0xe4, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, - 0xff, 0xfe, 0xfd, 0xff, 0xa5, 0x1f, 0x1f, 0xa5, 0xff, 0xfd, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xd7, 0x16, 0x00, 0x00, - 0x16, 0xd7, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0xff, 0xeb, 0xba, 0xe6, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0x91, 0x00, 0x05, 0x05, 0x00, 0x91, 0xff, 0xff, 0xfe, 0xfe, 0xff, - 0xff, 0xfd, 0xfa, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xd7, 0x2f, 0x06, 0x23, 0xa6, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x66, 0x00, 0x04, 0x04, 0x00, 0x66, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xf8, 0xec, 0xe8, 0xed, 0xfb, 0xff, 0xff, - 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x80, 0x00, - 0x0a, 0x04, 0x0c, 0xb0, 0xff, 0xfe, 0xff, 0xfb, 0x5c, 0x00, 0x03, - 0x03, 0x00, 0x5c, 0xfb, 0xff, 0xfe, 0xff, 0xf9, 0xe9, 0xe8, 0xe8, - 0xe8, 0xf3, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0x7c, 0x08, 0x11, 0x14, 0x03, 0x30, 0xe6, 0xff, 0xff, - 0xfb, 0x5c, 0x00, 0x03, 0x03, 0x00, 0x5c, 0xfb, 0xff, 0xff, 0xfd, - 0xed, 0xe8, 0xe9, 0xe9, 0xe8, 0xf3, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xab, 0x0a, 0x0f, 0x15, 0x13, - 0x07, 0x79, 0xff, 0xff, 0xfd, 0x62, 0x00, 0x03, 0x03, 0x00, 0x61, - 0xfd, 0xff, 0xff, 0xf4, 0xe8, 0xe9, 0xea, 0xe9, 0xe9, 0xf7, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xf1, - 0x2c, 0x0a, 0x14, 0x15, 0x0d, 0x1e, 0xcb, 0xff, 0xff, 0x79, 0x00, - 0x04, 0x04, 0x00, 0x79, 0xff, 0xff, 0xfb, 0xeb, 0xe9, 0xea, 0xe9, - 0xe9, 0xeb, 0xfd, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0x97, 0x04, 0x14, 0x15, 0x18, 0x00, 0x82, - 0xff, 0xff, 0x99, 0x00, 0x03, 0x03, 0x00, 0x99, 0xff, 0xff, 0xf4, - 0xe7, 0xea, 0xea, 0xe9, 0xe8, 0xf5, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf2, 0x42, 0x03, - 0x17, 0x16, 0x0b, 0x39, 0xe9, 0xff, 0xb5, 0x01, 0x00, 0x00, 0x01, - 0xb6, 0xff, 0xfd, 0xee, 0xe9, 0xea, 0xea, 0xe8, 0xed, 0xfd, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xff, 0xff, 0xdd, 0x8b, 0x84, 0xad, 0xee, 0xff, 0xff, - 0xff, 0xff, 0xc2, 0x18, 0x0b, 0x15, 0x13, 0x09, 0xbf, 0xff, 0xda, - 0x25, 0x00, 0x00, 0x25, 0xd9, 0xff, 0xf9, 0xe9, 0xe9, 0xea, 0xe9, - 0xe9, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf0, 0xe7, 0xe8, 0xf8, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x48, 0x17, 0x1f, - 0x20, 0x3b, 0x98, 0xee, 0xff, 0xff, 0xff, 0xa2, 0x0f, 0x0e, 0x15, - 0x05, 0x7a, 0xff, 0xff, 0x54, 0x00, 0x00, 0x53, 0xff, 0xff, 0xf3, - 0xe8, 0xea, 0xe9, 0xe9, 0xf5, 0xff, 0xfe, 0xff, 0xfc, 0xec, 0xd9, - 0xd3, 0xd2, 0xd1, 0xd9, 0xfb, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xcc, 0x1f, 0x20, 0x28, 0x26, 0x22, 0x1a, 0x4c, 0xbf, 0xff, 0xff, - 0xff, 0x86, 0x02, 0x0e, 0x09, 0x41, 0xfa, 0xff, 0x8d, 0x00, 0x00, - 0x8d, 0xff, 0xfa, 0xee, 0xe8, 0xe9, 0xe8, 0xf3, 0xff, 0xff, 0xff, - 0xf4, 0xdd, 0xd2, 0xd3, 0xd3, 0xd4, 0xd3, 0xd2, 0xf2, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xf0, 0x3f, 0x1a, 0x2a, 0x2b, 0x2a, 0x2a, - 0x1c, 0x28, 0xa1, 0xff, 0xff, 0xff, 0x76, 0x02, 0x04, 0x1e, 0xee, - 0xff, 0xd7, 0x07, 0x07, 0xd7, 0xff, 0xfc, 0xeb, 0xe8, 0xe8, 0xf1, - 0xff, 0xff, 0xff, 0xee, 0xd5, 0xd1, 0xd4, 0xd4, 0xd5, 0xd4, 0xd1, - 0xd7, 0xfa, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xba, 0x28, - 0x1a, 0x28, 0x2b, 0x2a, 0x2d, 0x24, 0x23, 0x87, 0xff, 0xff, 0xff, - 0x80, 0x01, 0x0b, 0xc5, 0xff, 0xff, 0x24, 0x24, 0xff, 0xff, 0xfa, - 0xe9, 0xe7, 0xf2, 0xfe, 0xff, 0xff, 0xe9, 0xd4, 0xd3, 0xd5, 0xd5, - 0xd5, 0xd4, 0xd1, 0xd3, 0xef, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, - 0xfe, 0xff, 0xff, 0xc0, 0x4a, 0x1f, 0x22, 0x2e, 0x2c, 0x2b, 0x25, - 0x18, 0x79, 0xfb, 0xff, 0xff, 0x97, 0x0b, 0x7b, 0xff, 0xff, 0x9b, - 0x9b, 0xff, 0xff, 0xf4, 0xe8, 0xf4, 0xff, 0xff, 0xff, 0xe6, 0xd1, - 0xd3, 0xd5, 0xd5, 0xd5, 0xd3, 0xd2, 0xda, 0xf1, 0xff, 0xff, 0xfe, - 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xef, 0x8e, 0x38, - 0x17, 0x21, 0x29, 0x2b, 0x26, 0x17, 0x81, 0xff, 0xff, 0xff, 0xa0, - 0x94, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf5, 0xf5, 0xff, 0xff, - 0xff, 0xe8, 0xd1, 0xd3, 0xd5, 0xd4, 0xd3, 0xd1, 0xd7, 0xe7, 0xfa, - 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, - 0xfe, 0xff, 0xff, 0xd6, 0x95, 0x53, 0x22, 0x1c, 0x20, 0x1c, 0x15, - 0x96, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, - 0xfe, 0xff, 0xff, 0xff, 0xec, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xdb, - 0xe9, 0xf6, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xee, 0xca, - 0x8d, 0x58, 0x35, 0x24, 0x1e, 0x9f, 0xff, 0xfb, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xed, 0xd3, 0xd3, 0xd6, - 0xdc, 0xe7, 0xf3, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xff, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf3, 0xd1, 0x8f, 0x9c, 0xfc, - 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, - 0xec, 0xe7, 0xf5, 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xe1, 0xac, - 0x8d, 0x85, 0x85, 0x89, 0x9b, 0xb3, 0xc8, 0xe3, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, - 0xec, 0xe5, 0xdd, 0xd7, 0xd6, 0xd6, 0xd9, 0xe3, 0xf5, 0xff, 0xff, - 0xff, 0xbb, 0x50, 0x30, 0x2f, 0x33, 0x33, 0x32, 0x2f, 0x30, 0x41, - 0x5b, 0x7e, 0xaa, 0xe1, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xf5, 0xe3, 0xd4, 0xc8, 0xbf, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xb9, - 0xba, 0xc4, 0xe8, 0xff, 0xeb, 0x57, 0x31, 0x44, 0x42, 0x42, 0x42, - 0x42, 0x43, 0x42, 0x3e, 0x38, 0x30, 0x36, 0x45, 0x5c, 0xb4, 0xff, - 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xe6, 0xc8, 0xc0, 0xbb, 0xb9, 0xbc, 0xbe, 0xbf, 0xc0, - 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xb9, 0xc7, 0xf8, 0xeb, 0x57, 0x31, - 0x44, 0x42, 0x42, 0x42, 0x42, 0x43, 0x42, 0x3e, 0x38, 0x30, 0x36, - 0x46, 0x5c, 0xb4, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xe6, 0xc8, 0xc0, 0xbb, 0xb9, - 0xbc, 0xbe, 0xbf, 0xc0, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xb9, 0xc7, - 0xf8, 0xff, 0xbb, 0x50, 0x30, 0x2f, 0x33, 0x33, 0x32, 0x2f, 0x30, - 0x41, 0x5b, 0x7e, 0xaa, 0xe1, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xff, 0xf5, 0xe3, 0xd4, 0xc8, 0xbf, 0xb9, 0xb9, 0xba, 0xba, 0xba, - 0xb9, 0xba, 0xc4, 0xe8, 0xff, 0xff, 0xff, 0xe1, 0xac, 0x8d, 0x85, - 0x85, 0x89, 0x9b, 0xb3, 0xc8, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xec, 0xe5, - 0xdd, 0xd7, 0xd6, 0xd6, 0xd9, 0xe3, 0xf5, 0xff, 0xff, 0xfe, 0xfd, - 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfb, 0xf2, 0xd5, 0x9f, 0xb2, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xd7, 0xd2, 0xed, 0xfa, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xef, 0xd0, 0x9f, 0x74, 0x5b, 0x4e, 0x4d, 0xba, 0xff, 0xfc, - 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xd9, - 0xa5, 0xa7, 0xae, 0xbc, 0xd1, 0xea, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, - 0xfe, 0xff, 0xff, 0xda, 0xa4, 0x6f, 0x4c, 0x49, 0x4c, 0x48, 0x46, - 0xb3, 0xff, 0xff, 0xff, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xd5, 0xa1, 0xa4, 0xa5, 0xa4, 0xa6, 0xb9, - 0xd4, 0xef, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xed, 0x9e, 0x5c, 0x44, 0x4e, 0x53, - 0x55, 0x50, 0x47, 0xa3, 0xff, 0xff, 0xff, 0xbc, 0xba, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xcf, 0xd4, 0xff, 0xff, 0xff, 0xcd, 0xa2, - 0xa8, 0xaa, 0xa9, 0xa6, 0xa1, 0xaf, 0xd2, 0xf8, 0xff, 0xff, 0xfe, - 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xc5, 0x69, 0x4b, - 0x4f, 0x57, 0x56, 0x55, 0x50, 0x47, 0x9c, 0xff, 0xff, 0xff, 0xb5, - 0x61, 0xb0, 0xff, 0xff, 0xcd, 0xcd, 0xff, 0xff, 0xc3, 0x90, 0xd0, - 0xff, 0xff, 0xfd, 0xc9, 0xa2, 0xa7, 0xaa, 0xaa, 0xab, 0xa6, 0xa5, - 0xb6, 0xe5, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xbe, 0x4f, 0x48, 0x53, 0x55, 0x55, 0x56, 0x4e, 0x50, 0xa7, 0xff, - 0xff, 0xfc, 0xa6, 0x5b, 0x65, 0xde, 0xff, 0xff, 0x92, 0x92, 0xff, - 0xff, 0xe5, 0x90, 0x8c, 0xc6, 0xff, 0xff, 0xff, 0xcf, 0xa6, 0xa7, - 0xaa, 0xa9, 0xaa, 0xa9, 0xa3, 0xa9, 0xe3, 0xff, 0xff, 0xfe, 0xfe, - 0xfe, 0xff, 0xff, 0xed, 0x5f, 0x48, 0x54, 0x55, 0x54, 0x54, 0x47, - 0x57, 0xbb, 0xff, 0xff, 0xff, 0xa1, 0x5c, 0x5f, 0x72, 0xf7, 0xff, - 0xeb, 0x83, 0x83, 0xeb, 0xff, 0xf7, 0x99, 0x8d, 0x8c, 0xc1, 0xff, - 0xff, 0xff, 0xd9, 0xa9, 0xa4, 0xa9, 0xa9, 0xaa, 0xa9, 0xa3, 0xb2, - 0xf9, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xcd, 0x4a, 0x4d, 0x52, - 0x50, 0x4d, 0x49, 0x76, 0xd3, 0xff, 0xff, 0xff, 0xab, 0x5c, 0x66, - 0x62, 0x8a, 0xfd, 0xff, 0xc7, 0x79, 0x79, 0xc7, 0xff, 0xfd, 0xa8, - 0x8f, 0x92, 0x8c, 0xc8, 0xff, 0xff, 0xff, 0xe6, 0xb7, 0xa3, 0xa6, - 0xa7, 0xa8, 0xa5, 0xa5, 0xeb, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xf0, 0x67, 0x45, 0x4b, 0x4d, 0x65, 0xb3, 0xf5, 0xff, 0xff, 0xff, - 0xbd, 0x64, 0x66, 0x6a, 0x5f, 0xaf, 0xff, 0xff, 0xa9, 0x75, 0x75, - 0xa9, 0xff, 0xff, 0xc3, 0x8d, 0x95, 0x91, 0x92, 0xd5, 0xff, 0xff, - 0xff, 0xf8, 0xd5, 0xb0, 0xa5, 0xa5, 0xa2, 0xb5, 0xfa, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xe2, 0xa3, 0xa0, 0xc2, 0xf5, 0xff, - 0xff, 0xff, 0xff, 0xd2, 0x68, 0x65, 0x6a, 0x68, 0x64, 0xd9, 0xff, - 0xed, 0x92, 0x7a, 0x7a, 0x92, 0xed, 0xff, 0xe2, 0x90, 0x94, 0x95, - 0x90, 0x96, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xde, 0xcd, 0xd0, - 0xf1, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf3, 0x81, 0x5f, 0x6b, 0x6b, - 0x63, 0x86, 0xf3, 0xff, 0xda, 0x81, 0x7f, 0x7f, 0x81, 0xda, 0xff, - 0xf5, 0xa5, 0x90, 0x95, 0x96, 0x8c, 0xa9, 0xf9, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xb7, - 0x5e, 0x69, 0x69, 0x6c, 0x5b, 0xb4, 0xff, 0xff, 0xcc, 0x75, 0x81, - 0x81, 0x75, 0xcc, 0xff, 0xff, 0xc6, 0x8a, 0x96, 0x95, 0x94, 0x8d, - 0xcf, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xf3, 0x75, 0x63, 0x69, 0x6a, 0x64, 0x73, 0xe2, 0xff, - 0xff, 0xbc, 0x75, 0x81, 0x81, 0x75, 0xbc, 0xff, 0xff, 0xe7, 0x99, - 0x91, 0x95, 0x94, 0x90, 0x9f, 0xf8, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xc5, 0x62, 0x66, 0x6a, 0x68, - 0x62, 0xb0, 0xff, 0xff, 0xfe, 0xb0, 0x77, 0x81, 0x81, 0x77, 0xb0, - 0xfe, 0xff, 0xff, 0xc2, 0x8e, 0x94, 0x95, 0x92, 0x90, 0xd9, 0xff, - 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xa9, - 0x61, 0x68, 0x69, 0x5e, 0x80, 0xf4, 0xff, 0xff, 0xfd, 0xae, 0x77, - 0x81, 0x81, 0x77, 0xae, 0xfd, 0xff, 0xff, 0xf4, 0xa1, 0x8d, 0x94, - 0x93, 0x8f, 0xc4, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xad, 0x5c, 0x62, 0x5e, 0x68, 0xd3, 0xff, 0xfe, - 0xff, 0xfd, 0xae, 0x77, 0x81, 0x81, 0x77, 0xae, 0xfd, 0xff, 0xfe, - 0xff, 0xdb, 0x91, 0x8d, 0x90, 0x8b, 0xc5, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xe7, 0x7f, 0x62, 0x78, - 0xce, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb3, 0x75, 0x81, 0x81, 0x75, - 0xb3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd6, 0x9b, 0x8e, 0xa0, 0xed, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff, - 0xff, 0xf6, 0xdc, 0xf4, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xc8, - 0x75, 0x82, 0x82, 0x75, 0xc8, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xf4, 0xe0, 0xf6, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0xfe, 0xff, 0xeb, 0x8a, 0x76, 0x76, 0x8a, 0xeb, 0xff, 0xfe, - 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xd2, 0x8f, 0x8f, - 0xd2, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xf1, 0xf1, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff -}; - -static const unsigned char twirl_30_data[] = { - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, - 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xe7, 0xe7, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, - 0xff, 0xfe, 0xfd, 0xff, 0xac, 0x31, 0x31, 0xac, 0xff, 0xfd, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xda, 0x28, 0x02, 0x02, - 0x28, 0xda, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0xff, 0xed, 0xc1, 0xe9, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0x9a, 0x02, 0x1a, 0x1a, 0x02, 0x9a, 0xff, 0xff, 0xfe, 0xfe, 0xff, - 0xff, 0xed, 0xc2, 0xef, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xdb, 0x42, 0x1d, 0x37, 0xae, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x73, 0x01, 0x18, 0x18, 0x01, 0x73, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xab, 0x18, 0x00, 0x23, 0xd7, 0xff, 0xff, - 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x8c, 0x18, - 0x21, 0x1b, 0x23, 0xb7, 0xff, 0xfe, 0xff, 0xfb, 0x6a, 0x05, 0x17, - 0x17, 0x05, 0x6a, 0xfb, 0xff, 0xfe, 0xff, 0xb4, 0x00, 0x00, 0x00, - 0x00, 0x73, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0x88, 0x1f, 0x28, 0x2a, 0x1b, 0x44, 0xe8, 0xff, 0xff, - 0xfb, 0x6a, 0x05, 0x17, 0x17, 0x05, 0x6a, 0xfb, 0xff, 0xff, 0xec, - 0x25, 0x00, 0x00, 0x00, 0x00, 0x6b, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xb4, 0x21, 0x25, 0x2b, 0x29, - 0x1e, 0x86, 0xff, 0xff, 0xfd, 0x6e, 0x04, 0x17, 0x17, 0x04, 0x6e, - 0xfd, 0xff, 0xff, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xf3, - 0x3f, 0x21, 0x2a, 0x2b, 0x24, 0x34, 0xd0, 0xff, 0xff, 0x85, 0x00, - 0x18, 0x18, 0x00, 0x84, 0xff, 0xff, 0xce, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x13, 0xea, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xa1, 0x1b, 0x2a, 0x2b, 0x2e, 0x16, 0x8e, - 0xff, 0xff, 0xa2, 0x01, 0x18, 0x18, 0x01, 0xa2, 0xff, 0xff, 0x7f, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x83, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf3, 0x54, 0x1b, - 0x2d, 0x2c, 0x22, 0x4b, 0xeb, 0xff, 0xbc, 0x16, 0x13, 0x13, 0x16, - 0xbc, 0xff, 0xea, 0x2f, 0x00, 0x02, 0x02, 0x00, 0x28, 0xeb, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xff, 0xff, 0xe0, 0x96, 0x91, 0xb5, 0xf0, 0xff, 0xff, - 0xff, 0xff, 0xc8, 0x2e, 0x22, 0x2b, 0x29, 0x20, 0xc5, 0xff, 0xdd, - 0x37, 0x0b, 0x0b, 0x36, 0xdd, 0xff, 0xc0, 0x00, 0x00, 0x01, 0x00, - 0x00, 0xb2, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xf7, 0xf3, 0xf3, 0xfb, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x5a, 0x2e, 0x35, - 0x36, 0x4e, 0xa2, 0xf0, 0xff, 0xff, 0xff, 0xab, 0x26, 0x24, 0x2b, - 0x1c, 0x87, 0xff, 0xff, 0x62, 0x02, 0x02, 0x61, 0xff, 0xff, 0x76, - 0x00, 0x00, 0x00, 0x00, 0x8d, 0xff, 0xff, 0xff, 0xfd, 0xf6, 0xec, - 0xe8, 0xe8, 0xe8, 0xec, 0xfd, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xd1, 0x35, 0x36, 0x3d, 0x3b, 0x38, 0x31, 0x5d, 0xc5, 0xff, 0xff, - 0xff, 0x91, 0x1a, 0x25, 0x20, 0x52, 0xfb, 0xff, 0x97, 0x09, 0x09, - 0x97, 0xff, 0xfc, 0x36, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, - 0xf9, 0xee, 0xe8, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xf9, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xf2, 0x52, 0x31, 0x3f, 0x40, 0x3f, 0x3f, - 0x32, 0x3d, 0xaa, 0xff, 0xff, 0xff, 0x83, 0x19, 0x1b, 0x33, 0xef, - 0xff, 0xdb, 0x1c, 0x1c, 0xdb, 0xff, 0xf1, 0x0d, 0x00, 0x00, 0x5e, - 0xff, 0xff, 0xff, 0xf7, 0xea, 0xe8, 0xe9, 0xe9, 0xea, 0xe9, 0xe8, - 0xeb, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xc0, 0x3d, - 0x30, 0x3d, 0x40, 0x3f, 0x41, 0x39, 0x38, 0x93, 0xff, 0xff, 0xff, - 0x8c, 0x19, 0x22, 0xcb, 0xff, 0xff, 0x36, 0x37, 0xff, 0xff, 0xc8, - 0x00, 0x00, 0x68, 0xfb, 0xff, 0xff, 0xf4, 0xe9, 0xe9, 0xea, 0xea, - 0xea, 0xe9, 0xe8, 0xe9, 0xf7, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, - 0xfe, 0xff, 0xff, 0xc7, 0x5c, 0x35, 0x38, 0x42, 0x41, 0x40, 0x3b, - 0x2f, 0x86, 0xfb, 0xff, 0xff, 0xa1, 0x22, 0x88, 0xff, 0xff, 0xa3, - 0xa3, 0xff, 0xff, 0x77, 0x00, 0x81, 0xff, 0xff, 0xfe, 0xf2, 0xe8, - 0xe9, 0xea, 0xea, 0xea, 0xe9, 0xe8, 0xec, 0xf8, 0xff, 0xff, 0xfe, - 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xf0, 0x99, 0x4c, - 0x2e, 0x37, 0x3e, 0x40, 0x3b, 0x2e, 0x8e, 0xff, 0xff, 0xff, 0xa9, - 0x9e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x89, 0x8b, 0xff, 0xff, - 0xff, 0xf3, 0xe8, 0xe9, 0xea, 0xe9, 0xe9, 0xe8, 0xeb, 0xf3, 0xfd, - 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, - 0xfe, 0xff, 0xff, 0xda, 0x9f, 0x63, 0x38, 0x32, 0x36, 0x32, 0x2c, - 0xa0, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, - 0xfc, 0xff, 0xff, 0xff, 0xf5, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xed, - 0xf4, 0xfa, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xcf, - 0x99, 0x69, 0x4a, 0x3a, 0x34, 0xa9, 0xff, 0xfb, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfc, 0xff, 0xf6, 0xe9, 0xe9, 0xea, - 0xee, 0xf3, 0xf9, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xff, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xf4, 0xd5, 0x9a, 0xa6, 0xfc, - 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, - 0xf5, 0xf3, 0xf9, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xe4, 0xb5, - 0x99, 0x93, 0x92, 0x96, 0xa6, 0xbb, 0xce, 0xe6, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, - 0xf2, 0xee, 0xe8, 0xe5, 0xe4, 0xe4, 0xe6, 0xed, 0xf8, 0xff, 0xff, - 0xff, 0xc3, 0x63, 0x47, 0x46, 0x49, 0x49, 0x49, 0x46, 0x46, 0x56, - 0x6d, 0x8d, 0xb3, 0xe4, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xf8, 0xec, 0xe2, 0xdb, 0xd5, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd1, - 0xd1, 0xd8, 0xf0, 0xff, 0xed, 0x6a, 0x47, 0x58, 0x57, 0x57, 0x57, - 0x57, 0x57, 0x57, 0x53, 0x4e, 0x47, 0x4c, 0x5a, 0x6d, 0xbc, 0xff, - 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xee, 0xdb, 0xd6, 0xd2, 0xd1, 0xd3, 0xd4, 0xd5, 0xd5, - 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd1, 0xda, 0xfa, 0xed, 0x69, 0x47, - 0x58, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x53, 0x4e, 0x47, 0x4c, - 0x5a, 0x6d, 0xbc, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xee, 0xdb, 0xd6, 0xd2, 0xd1, - 0xd3, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd1, 0xda, - 0xfa, 0xff, 0xc3, 0x63, 0x47, 0x46, 0x49, 0x49, 0x49, 0x46, 0x46, - 0x56, 0x6d, 0x8d, 0xb3, 0xe4, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xff, 0xf8, 0xec, 0xe2, 0xdb, 0xd5, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, - 0xd1, 0xd1, 0xd8, 0xf0, 0xff, 0xff, 0xff, 0xe4, 0xb5, 0x99, 0x93, - 0x92, 0x96, 0xa6, 0xbb, 0xce, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xf2, 0xee, - 0xe8, 0xe5, 0xe4, 0xe4, 0xe6, 0xed, 0xf8, 0xff, 0xff, 0xfe, 0xfd, - 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfc, 0xf4, 0xda, 0xab, 0xbb, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xe1, 0xdd, 0xf1, 0xfb, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xf1, 0xd6, 0xaa, 0x86, 0x6f, 0x63, 0x63, 0xc2, 0xff, 0xfc, - 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xe2, - 0xbb, 0xbd, 0xc2, 0xcd, 0xdd, 0xef, 0xf9, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, - 0xfe, 0xff, 0xff, 0xde, 0xb0, 0x80, 0x62, 0x60, 0x62, 0x5f, 0x5d, - 0xbd, 0xff, 0xff, 0xff, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xdf, 0xb8, 0xba, 0xbb, 0xba, 0xbc, 0xca, - 0xdf, 0xf3, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xef, 0xaa, 0x70, 0x5b, 0x64, 0x69, - 0x6a, 0x66, 0x5e, 0xae, 0xff, 0xff, 0xff, 0xc5, 0xc4, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xd8, 0xdc, 0xff, 0xff, 0xff, 0xd9, 0xb9, - 0xbd, 0xbf, 0xbe, 0xbc, 0xb8, 0xc3, 0xdd, 0xfa, 0xff, 0xff, 0xfe, - 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xcd, 0x7b, 0x61, - 0x65, 0x6c, 0x6b, 0x6a, 0x65, 0x5e, 0xa8, 0xff, 0xff, 0xff, 0xc0, - 0x78, 0xbc, 0xff, 0xff, 0xd5, 0xd5, 0xff, 0xff, 0xcf, 0xa6, 0xd9, - 0xff, 0xff, 0xfd, 0xd6, 0xb9, 0xbd, 0xbf, 0xbf, 0xc0, 0xbc, 0xbb, - 0xc8, 0xec, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xc6, 0x64, 0x5e, 0x68, 0x6a, 0x6a, 0x6b, 0x64, 0x66, 0xb2, 0xff, - 0xff, 0xfc, 0xb3, 0x73, 0x7c, 0xe4, 0xff, 0xff, 0xa4, 0xa4, 0xff, - 0xff, 0xea, 0xa6, 0xa2, 0xd1, 0xff, 0xff, 0xff, 0xdb, 0xbc, 0xbc, - 0xbf, 0xbf, 0xbf, 0xbe, 0xb9, 0xbe, 0xe9, 0xff, 0xff, 0xfe, 0xfe, - 0xfe, 0xff, 0xff, 0xf0, 0x72, 0x5f, 0x69, 0x6a, 0x69, 0x69, 0x5e, - 0x6c, 0xc4, 0xff, 0xff, 0xff, 0xae, 0x74, 0x77, 0x86, 0xf8, 0xff, - 0xee, 0x98, 0x98, 0xee, 0xff, 0xf9, 0xad, 0xa3, 0xa3, 0xce, 0xff, - 0xff, 0xff, 0xe2, 0xbe, 0xba, 0xbe, 0xbe, 0xbf, 0xbe, 0xba, 0xc5, - 0xfa, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xd3, 0x60, 0x63, 0x67, - 0x66, 0x63, 0x5f, 0x87, 0xd8, 0xff, 0xff, 0xff, 0xb7, 0x74, 0x7d, - 0x79, 0x9b, 0xfd, 0xff, 0xd0, 0x8f, 0x8f, 0xd0, 0xff, 0xfd, 0xba, - 0xa5, 0xa7, 0xa3, 0xd3, 0xff, 0xff, 0xff, 0xec, 0xc9, 0xba, 0xbc, - 0xbd, 0xbe, 0xbb, 0xbb, 0xef, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xf2, 0x7a, 0x5c, 0x62, 0x63, 0x78, 0xbd, 0xf7, 0xff, 0xff, 0xff, - 0xc6, 0x7b, 0x7d, 0x80, 0x77, 0xbb, 0xff, 0xff, 0xb7, 0x8c, 0x8c, - 0xb7, 0xff, 0xff, 0xcf, 0xa4, 0xaa, 0xa7, 0xa7, 0xdd, 0xff, 0xff, - 0xff, 0xfa, 0xe0, 0xc4, 0xbb, 0xbb, 0xb9, 0xc8, 0xfb, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xe6, 0xae, 0xab, 0xca, 0xf6, 0xff, - 0xff, 0xff, 0xff, 0xd8, 0x7e, 0x7b, 0x80, 0x7f, 0x7b, 0xdf, 0xff, - 0xef, 0xa4, 0x90, 0x90, 0xa4, 0xef, 0xff, 0xe8, 0xa6, 0xa9, 0xaa, - 0xa6, 0xab, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xe6, 0xda, 0xdb, - 0xf5, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf5, 0x94, 0x77, 0x81, 0x81, - 0x7a, 0x97, 0xf5, 0xff, 0xe0, 0x95, 0x94, 0x94, 0x95, 0xe0, 0xff, - 0xf7, 0xb7, 0xa6, 0xaa, 0xaa, 0xa3, 0xba, 0xfa, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xc1, - 0x76, 0x7f, 0x7f, 0x81, 0x74, 0xbf, 0xff, 0xff, 0xd5, 0x8c, 0x96, - 0x96, 0x8c, 0xd5, 0xff, 0xff, 0xd1, 0xa1, 0xab, 0xaa, 0xa9, 0xa4, - 0xd9, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xf5, 0x89, 0x7a, 0x7f, 0x80, 0x7b, 0x88, 0xe6, 0xff, - 0xff, 0xc7, 0x8b, 0x96, 0x96, 0x8b, 0xc7, 0xff, 0xff, 0xec, 0xad, - 0xa7, 0xaa, 0xa9, 0xa6, 0xb2, 0xfa, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xce, 0x79, 0x7d, 0x80, 0x7e, - 0x79, 0xbb, 0xff, 0xff, 0xfe, 0xbd, 0x8d, 0x96, 0x96, 0x8d, 0xbd, - 0xfe, 0xff, 0xff, 0xce, 0xa5, 0xa9, 0xaa, 0xa7, 0xa6, 0xe1, 0xff, - 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xb5, - 0x78, 0x7e, 0x7f, 0x75, 0x92, 0xf5, 0xff, 0xff, 0xfd, 0xbb, 0x8d, - 0x96, 0x96, 0x8d, 0xbb, 0xfd, 0xff, 0xff, 0xf6, 0xb4, 0xa3, 0xa9, - 0xa8, 0xa5, 0xcf, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xb9, 0x74, 0x79, 0x76, 0x7e, 0xda, 0xff, 0xfe, - 0xff, 0xfd, 0xbb, 0x8d, 0x96, 0x96, 0x8d, 0xbb, 0xfd, 0xff, 0xfe, - 0xff, 0xe2, 0xa7, 0xa3, 0xa6, 0xa2, 0xd1, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xeb, 0x91, 0x78, 0x8c, - 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x8b, 0x96, 0x96, 0x8b, - 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xaf, 0xa4, 0xb3, 0xf0, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff, - 0xff, 0xf7, 0xe0, 0xf6, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xd1, - 0x8c, 0x97, 0x97, 0x8c, 0xd1, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xf6, 0xe6, 0xf8, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0xfe, 0xff, 0xee, 0x9e, 0x8c, 0x8c, 0x9e, 0xee, 0xff, 0xfe, - 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xd9, 0xa2, 0xa2, - 0xd9, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xf4, 0xf4, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff -}; - -static const unsigned char twirl_60_data[] = { - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, - 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xe9, 0xe9, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, - 0xff, 0xfe, 0xfd, 0xff, 0xb4, 0x45, 0x45, 0xb4, 0xff, 0xfd, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xde, 0x3d, 0x1a, 0x1a, - 0x3d, 0xde, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0xff, 0xef, 0xc7, 0xeb, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xa3, 0x1a, 0x2f, 0x2f, 0x1a, 0xa3, 0xff, 0xff, 0xfe, 0xfe, 0xff, - 0xff, 0xee, 0xc7, 0xf0, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xdf, 0x55, 0x34, 0x4b, 0xb6, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x80, 0x18, 0x2e, 0x2e, 0x18, 0x80, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xb2, 0x2c, 0x07, 0x35, 0xda, 0xff, 0xff, - 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x97, 0x2e, - 0x37, 0x32, 0x39, 0xbe, 0xff, 0xfe, 0xff, 0xfc, 0x78, 0x1c, 0x2d, - 0x2d, 0x1c, 0x78, 0xfc, 0xff, 0xfe, 0xff, 0xba, 0x12, 0x03, 0x09, - 0x00, 0x7e, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0x94, 0x35, 0x3d, 0x3f, 0x31, 0x57, 0xeb, 0xff, 0xff, - 0xfb, 0x77, 0x1c, 0x2d, 0x2d, 0x1c, 0x77, 0xfb, 0xff, 0xff, 0xed, - 0x37, 0x02, 0x14, 0x12, 0x07, 0x78, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xbb, 0x37, 0x3b, 0x40, 0x3e, - 0x34, 0x92, 0xff, 0xff, 0xfd, 0x7c, 0x1c, 0x2d, 0x2d, 0x1c, 0x7c, - 0xfd, 0xff, 0xff, 0x83, 0x08, 0x12, 0x15, 0x0f, 0x09, 0xa4, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xf4, - 0x52, 0x37, 0x3f, 0x40, 0x39, 0x47, 0xd5, 0xff, 0xff, 0x90, 0x18, - 0x2e, 0x2e, 0x18, 0x90, 0xff, 0xff, 0xd2, 0x24, 0x0b, 0x15, 0x14, - 0x0b, 0x26, 0xec, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xa9, 0x32, 0x3f, 0x40, 0x43, 0x2d, 0x9a, - 0xff, 0xff, 0xab, 0x19, 0x2e, 0x2e, 0x19, 0xab, 0xff, 0xff, 0x8a, - 0x00, 0x18, 0x14, 0x14, 0x03, 0x8d, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf4, 0x65, 0x31, - 0x41, 0x41, 0x38, 0x5d, 0xed, 0xff, 0xc2, 0x2c, 0x29, 0x29, 0x2c, - 0xc2, 0xff, 0xec, 0x40, 0x0a, 0x17, 0x17, 0x04, 0x39, 0xed, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xff, 0xff, 0xe4, 0xa2, 0x9c, 0xbd, 0xf1, 0xff, 0xff, - 0xff, 0xff, 0xcd, 0x42, 0x38, 0x40, 0x3e, 0x36, 0xca, 0xff, 0xe0, - 0x49, 0x22, 0x22, 0x49, 0xe0, 0xff, 0xc4, 0x0b, 0x13, 0x16, 0x0d, - 0x12, 0xb8, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xa4, 0x70, 0x74, 0xd4, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x6d, 0x45, 0x4b, - 0x4c, 0x61, 0xac, 0xf1, 0xff, 0xff, 0xff, 0xb3, 0x3b, 0x3a, 0x40, - 0x33, 0x93, 0xff, 0xff, 0x70, 0x1a, 0x1a, 0x70, 0xff, 0xff, 0x81, - 0x04, 0x15, 0x0f, 0x0c, 0x97, 0xff, 0xff, 0xff, 0xf1, 0x8e, 0x19, - 0x00, 0x00, 0x00, 0x1c, 0xe9, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xd6, 0x4b, 0x4c, 0x52, 0x50, 0x4e, 0x47, 0x6f, 0xcc, 0xff, 0xff, - 0xff, 0x9c, 0x31, 0x3b, 0x36, 0x64, 0xfb, 0xff, 0xa0, 0x20, 0x20, - 0xa0, 0xff, 0xfc, 0x47, 0x09, 0x10, 0x00, 0x7b, 0xff, 0xff, 0xff, - 0xbc, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xf3, 0x65, 0x47, 0x54, 0x55, 0x54, 0x54, - 0x49, 0x53, 0xb3, 0xff, 0xff, 0xff, 0x90, 0x30, 0x32, 0x47, 0xf1, - 0xff, 0xde, 0x31, 0x31, 0xde, 0xff, 0xf2, 0x21, 0x05, 0x00, 0x6b, - 0xff, 0xff, 0xff, 0x99, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0f, 0xe5, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xc7, 0x53, - 0x47, 0x53, 0x55, 0x54, 0x56, 0x4f, 0x4e, 0x9f, 0xff, 0xff, 0xff, - 0x97, 0x30, 0x38, 0xd0, 0xff, 0xff, 0x4a, 0x4a, 0xff, 0xff, 0xcc, - 0x0e, 0x00, 0x74, 0xfa, 0xff, 0xff, 0x7b, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, - 0xfe, 0xff, 0xff, 0xcd, 0x6e, 0x4b, 0x4e, 0x57, 0x56, 0x55, 0x50, - 0x46, 0x93, 0xfc, 0xff, 0xff, 0xaa, 0x38, 0x93, 0xff, 0xff, 0xac, - 0xac, 0xff, 0xff, 0x83, 0x06, 0x8b, 0xff, 0xff, 0xff, 0x6a, 0x00, - 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x1e, 0xa8, 0xff, 0xff, 0xfe, - 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xf2, 0xa4, 0x60, - 0x44, 0x4d, 0x53, 0x55, 0x51, 0x45, 0x9a, 0xff, 0xff, 0xff, 0xb1, - 0xa8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x92, 0x95, 0xff, 0xff, - 0xff, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0xe4, - 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, - 0xfe, 0xff, 0xff, 0xde, 0xa9, 0x74, 0x4e, 0x49, 0x4c, 0x49, 0x43, - 0xab, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, - 0xfc, 0xff, 0xff, 0xff, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, - 0x77, 0xc7, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xd4, - 0xa4, 0x79, 0x5d, 0x4f, 0x4b, 0xb2, 0xff, 0xfc, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfa, 0xff, 0x98, 0x00, 0x00, 0x0a, - 0x2f, 0x6e, 0xb9, 0xe7, 0xff, 0xff, 0xfd, 0xfd, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xf5, 0xda, 0xa5, 0xb0, 0xfd, - 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, - 0x8b, 0x70, 0xc0, 0xee, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xe8, 0xbe, - 0xa5, 0xa0, 0xa0, 0xa3, 0xb1, 0xc4, 0xd4, 0xe9, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, - 0xf9, 0xf6, 0xf4, 0xf2, 0xf1, 0xf1, 0xf2, 0xf6, 0xfb, 0xff, 0xff, - 0xff, 0xca, 0x76, 0x5e, 0x5d, 0x5f, 0x60, 0x5f, 0x5d, 0x5d, 0x6b, - 0x7f, 0x9b, 0xbd, 0xe7, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xfc, 0xf6, 0xf1, 0xed, 0xea, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, - 0xe8, 0xeb, 0xf7, 0xff, 0xef, 0x7c, 0x5e, 0x6d, 0x6c, 0x6b, 0x6b, - 0x6b, 0x6c, 0x6c, 0x69, 0x63, 0x5e, 0x62, 0x6e, 0x7f, 0xc4, 0xff, - 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xf6, 0xed, 0xea, 0xe9, 0xe8, 0xe9, 0xe9, 0xea, 0xea, - 0xea, 0xea, 0xea, 0xea, 0xea, 0xe8, 0xec, 0xfd, 0xef, 0x7c, 0x5e, - 0x6d, 0x6c, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c, 0x69, 0x63, 0x5e, 0x62, - 0x6e, 0x7f, 0xc4, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xf6, 0xed, 0xea, 0xe9, 0xe8, - 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xe8, 0xec, - 0xfd, 0xff, 0xca, 0x76, 0x5e, 0x5d, 0x5f, 0x60, 0x5f, 0x5d, 0x5d, - 0x6b, 0x7f, 0x9b, 0xbd, 0xe7, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xff, 0xfc, 0xf6, 0xf1, 0xed, 0xea, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, - 0xe8, 0xe8, 0xeb, 0xf7, 0xff, 0xff, 0xff, 0xe8, 0xbe, 0xa5, 0xa0, - 0xa0, 0xa3, 0xb1, 0xc4, 0xd4, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf9, 0xf6, - 0xf4, 0xf2, 0xf1, 0xf1, 0xf2, 0xf6, 0xfb, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfc, 0xf6, 0xdf, 0xb7, 0xc5, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xeb, 0xe8, 0xf6, 0xfd, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xf3, 0xdc, 0xb7, 0x98, 0x85, 0x7a, 0x7a, 0xcb, 0xff, 0xfd, - 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xec, - 0xd2, 0xd3, 0xd7, 0xde, 0xe8, 0xf4, 0xfb, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, - 0xfe, 0xff, 0xff, 0xe3, 0xbc, 0x94, 0x7a, 0x77, 0x79, 0x76, 0x75, - 0xc7, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xea, 0xd0, 0xd2, 0xd2, 0xd2, 0xd3, 0xdc, - 0xea, 0xf7, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xf1, 0xb6, 0x85, 0x73, 0x7a, 0x7f, - 0x80, 0x7c, 0x75, 0xba, 0xff, 0xff, 0xff, 0xcf, 0xcd, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xe2, 0xe5, 0xff, 0xff, 0xff, 0xe6, 0xd1, - 0xd4, 0xd5, 0xd4, 0xd3, 0xd1, 0xd8, 0xe8, 0xfb, 0xff, 0xff, 0xfe, - 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xd4, 0x8f, 0x78, - 0x7b, 0x82, 0x80, 0x80, 0x7c, 0x76, 0xb5, 0xff, 0xff, 0xff, 0xcb, - 0x8e, 0xc7, 0xff, 0xff, 0xde, 0xde, 0xff, 0xff, 0xda, 0xbc, 0xe3, - 0xff, 0xff, 0xfe, 0xe4, 0xd1, 0xd3, 0xd5, 0xd5, 0xd5, 0xd3, 0xd2, - 0xdb, 0xf2, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xcf, 0x7b, 0x76, 0x7e, 0x80, 0x80, 0x81, 0x7b, 0x7d, 0xbd, 0xff, - 0xff, 0xfd, 0xc0, 0x8a, 0x91, 0xe8, 0xff, 0xff, 0xb6, 0xb6, 0xff, - 0xff, 0xef, 0xbc, 0xb9, 0xdc, 0xff, 0xff, 0xff, 0xe7, 0xd3, 0xd3, - 0xd5, 0xd4, 0xd5, 0xd4, 0xd1, 0xd4, 0xf1, 0xff, 0xff, 0xfe, 0xfe, - 0xfe, 0xff, 0xff, 0xf2, 0x87, 0x76, 0x7f, 0x80, 0x7f, 0x7f, 0x76, - 0x81, 0xcc, 0xff, 0xff, 0xff, 0xbc, 0x8b, 0x8d, 0x9a, 0xf9, 0xff, - 0xf1, 0xac, 0xac, 0xf1, 0xff, 0xfa, 0xc1, 0xba, 0xb9, 0xda, 0xff, - 0xff, 0xff, 0xec, 0xd4, 0xd2, 0xd4, 0xd4, 0xd5, 0xd4, 0xd1, 0xd9, - 0xfc, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xda, 0x78, 0x7a, 0x7e, - 0x7c, 0x7a, 0x76, 0x98, 0xdd, 0xff, 0xff, 0xff, 0xc3, 0x8b, 0x92, - 0x8f, 0xab, 0xfd, 0xff, 0xd9, 0xa5, 0xa5, 0xd9, 0xff, 0xfd, 0xcb, - 0xbb, 0xbd, 0xba, 0xde, 0xff, 0xff, 0xff, 0xf2, 0xdb, 0xd1, 0xd3, - 0xd4, 0xd4, 0xd2, 0xd2, 0xf5, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xf4, 0x8e, 0x74, 0x79, 0x7a, 0x8c, 0xc7, 0xf8, 0xff, 0xff, 0xff, - 0xcf, 0x90, 0x92, 0x95, 0x8d, 0xc6, 0xff, 0xff, 0xc6, 0xa3, 0xa3, - 0xc6, 0xff, 0xff, 0xda, 0xba, 0xbf, 0xbd, 0xbd, 0xe5, 0xff, 0xff, - 0xff, 0xfb, 0xea, 0xd8, 0xd2, 0xd2, 0xd1, 0xda, 0xfc, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xea, 0xba, 0xb8, 0xd1, 0xf7, 0xff, - 0xff, 0xff, 0xff, 0xdf, 0x93, 0x91, 0x95, 0x94, 0x91, 0xe5, 0xff, - 0xf3, 0xb6, 0xa6, 0xa6, 0xb6, 0xf3, 0xff, 0xed, 0xbb, 0xbe, 0xbf, - 0xbc, 0xbf, 0xee, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xee, 0xe6, 0xe8, - 0xf8, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf6, 0xa5, 0x8d, 0x96, 0x95, - 0x90, 0xa8, 0xf6, 0xff, 0xe6, 0xaa, 0xa9, 0xa9, 0xaa, 0xe6, 0xff, - 0xf9, 0xc9, 0xbc, 0xbf, 0xbf, 0xba, 0xcb, 0xfb, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xcc, - 0x8d, 0x94, 0x94, 0x96, 0x8a, 0xc9, 0xff, 0xff, 0xdd, 0xa2, 0xab, - 0xab, 0xa2, 0xdd, 0xff, 0xff, 0xdd, 0xb9, 0xc0, 0xbf, 0xbe, 0xba, - 0xe2, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xf6, 0x9c, 0x90, 0x94, 0x95, 0x90, 0x9b, 0xea, 0xff, - 0xff, 0xd3, 0xa2, 0xab, 0xab, 0xa2, 0xd3, 0xff, 0xff, 0xf1, 0xc1, - 0xbc, 0xbf, 0xbe, 0xbc, 0xc5, 0xfb, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xd6, 0x8f, 0x92, 0x95, 0x93, - 0x8f, 0xc6, 0xff, 0xff, 0xfe, 0xca, 0xa4, 0xab, 0xab, 0xa4, 0xca, - 0xfe, 0xff, 0xff, 0xda, 0xbb, 0xbe, 0xbf, 0xbd, 0xbc, 0xe8, 0xff, - 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xc1, - 0x8f, 0x93, 0x94, 0x8c, 0xa4, 0xf7, 0xff, 0xff, 0xfd, 0xc8, 0xa4, - 0xaa, 0xaa, 0xa4, 0xc8, 0xfd, 0xff, 0xff, 0xf8, 0xc6, 0xba, 0xbe, - 0xbe, 0xbb, 0xdb, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xc5, 0x8b, 0x8f, 0x8c, 0x93, 0xe0, 0xff, 0xfe, - 0xff, 0xfd, 0xc9, 0xa4, 0xab, 0xab, 0xa4, 0xc9, 0xfd, 0xff, 0xfe, - 0xff, 0xe9, 0xbc, 0xba, 0xbb, 0xb9, 0xdc, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xee, 0xa3, 0x8f, 0x9f, - 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xa2, 0xab, 0xab, 0xa2, - 0xcc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xbb, 0xc6, 0xf4, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff, - 0xff, 0xf8, 0xe5, 0xf7, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xda, - 0xa3, 0xab, 0xab, 0xa3, 0xda, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xf8, 0xec, 0xf9, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0xfe, 0xff, 0xf1, 0xb1, 0xa3, 0xa3, 0xb1, 0xf1, 0xff, 0xfe, - 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xe1, 0xb4, 0xb4, - 0xe1, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xf6, 0xf6, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff -}; - -// Also, the "failed" icon. -const unsigned char failed_data[] = { - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, - 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfb, 0xfb, 0xfe, 0xff, 0xfe, - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, - 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfd, 0xff, 0xfa, 0x91, 0x93, 0xfd, 0xff, 0xfd, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xfe, 0xfc, 0xff, 0x8c, 0x00, 0x00, 0x8f, 0xff, - 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xcf, 0x0f, - 0x00, 0x00, 0x0f, 0xcf, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xff, 0x51, 0x00, 0x01, 0x01, 0x00, 0x51, 0xff, 0xff, 0xfe, - 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xa4, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xef, 0x1e, 0x00, - 0x00, 0x55, 0x54, 0x00, 0x00, 0x1f, 0xf0, 0xff, 0xfe, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, - 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0x6a, 0x00, 0x00, 0x1c, 0xbf, 0xbe, 0x1c, 0x00, 0x00, 0x68, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xfe, 0xfb, 0xff, 0xbb, 0x03, 0x00, 0x00, 0x7d, 0xd2, 0xd2, - 0x7e, 0x00, 0x00, 0x03, 0xbc, 0xff, 0xfb, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xf7, 0x3c, 0x00, 0x00, - 0x3f, 0xc6, 0xc4, 0xc4, 0xc6, 0x3e, 0x00, 0x00, 0x3d, 0xf8, 0xff, - 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, - 0x8b, 0x00, 0x00, 0x0c, 0xa1, 0xcd, 0xc0, 0xc0, 0xcd, 0xa1, 0x0c, - 0x00, 0x00, 0x89, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xcf, 0x09, 0x00, 0x00, 0x6c, 0xd4, 0xc2, 0xc0, - 0xc0, 0xc2, 0xd4, 0x6d, 0x00, 0x00, 0x09, 0xd0, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, - 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x4a, 0x00, 0x00, 0x29, - 0xc3, 0xca, 0xc7, 0xd0, 0xd0, 0xc7, 0xca, 0xc3, 0x29, 0x00, 0x00, - 0x4a, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xa5, - 0x00, 0x00, 0x00, 0x90, 0xd1, 0xc7, 0xb2, 0x52, 0x52, 0xb2, 0xc7, - 0xd1, 0x91, 0x00, 0x00, 0x00, 0xa6, 0xff, 0xfe, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfc, 0xff, 0xeb, 0x25, 0x00, 0x00, 0x53, 0xc9, 0xc4, 0xd2, 0x6b, - 0x00, 0x00, 0x6b, 0xd2, 0xc4, 0xc9, 0x53, 0x00, 0x00, 0x25, 0xec, - 0xff, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0x69, 0x00, 0x00, 0x20, 0xb3, - 0xcb, 0xbe, 0xd5, 0x58, 0x00, 0x00, 0x58, 0xd5, 0xbe, 0xcb, 0xb3, - 0x21, 0x00, 0x00, 0x69, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xb7, 0x01, - 0x00, 0x00, 0x7e, 0xd3, 0xc0, 0xbf, 0xd4, 0x5d, 0x00, 0x00, 0x5d, - 0xd4, 0xbf, 0xc0, 0xd3, 0x7f, 0x00, 0x00, 0x01, 0xb8, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xf5, 0x36, 0x00, 0x00, 0x3f, 0xc9, 0xc5, 0xc2, 0xbf, 0xd4, - 0x5d, 0x00, 0x00, 0x5d, 0xd4, 0xbf, 0xc2, 0xc5, 0xca, 0x3f, 0x00, - 0x00, 0x37, 0xf6, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0x87, 0x00, 0x00, 0x07, 0xa6, 0xcd, - 0xc2, 0xc3, 0xbf, 0xd4, 0x5d, 0x00, 0x00, 0x5d, 0xd4, 0xbf, 0xc3, - 0xc2, 0xcd, 0xa6, 0x07, 0x00, 0x00, 0x85, 0xff, 0xff, 0xfe, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, - 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xd4, 0x06, 0x00, - 0x00, 0x6c, 0xd3, 0xc5, 0xc2, 0xc3, 0xbf, 0xd4, 0x5d, 0x00, 0x00, - 0x5d, 0xd4, 0xbf, 0xc2, 0xc2, 0xc5, 0xd3, 0x6d, 0x00, 0x00, 0x06, - 0xd5, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0x4e, 0x00, 0x00, 0x2e, 0xbe, 0xcd, 0xc2, 0xc2, 0xc2, 0xbf, - 0xd4, 0x5d, 0x00, 0x00, 0x5d, 0xd4, 0xbf, 0xc3, 0xc2, 0xc2, 0xcd, - 0xbe, 0x2e, 0x00, 0x00, 0x4f, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xfe, 0xfb, 0xff, 0xa2, 0x00, 0x00, 0x02, 0x8f, 0xcf, 0xc3, - 0xc2, 0xc2, 0xc2, 0xbf, 0xd4, 0x5d, 0x00, 0x00, 0x5d, 0xd4, 0xbf, - 0xc3, 0xc2, 0xc2, 0xc3, 0xcf, 0x8f, 0x02, 0x00, 0x00, 0xa1, 0xff, - 0xfb, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfd, 0xff, 0xe6, 0x27, 0x00, 0x00, - 0x53, 0xcb, 0xc2, 0xc2, 0xc3, 0xc2, 0xc3, 0xbf, 0xd4, 0x5c, 0x00, - 0x00, 0x5c, 0xd4, 0xbf, 0xc3, 0xc2, 0xc3, 0xc2, 0xc1, 0xcc, 0x54, - 0x00, 0x00, 0x27, 0xe8, 0xff, 0xfd, 0xfe, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0x66, 0x00, 0x00, 0x20, 0xb7, 0xcc, 0xc1, 0xc3, 0xc2, 0xc2, 0xc3, - 0xbf, 0xd4, 0x59, 0x00, 0x00, 0x59, 0xd4, 0xbf, 0xc3, 0xc2, 0xc2, - 0xc3, 0xc1, 0xcc, 0xb7, 0x20, 0x00, 0x00, 0x66, 0xff, 0xff, 0xfe, - 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xfe, 0xff, 0xff, 0xb7, 0x00, 0x00, 0x00, 0x83, 0xd3, 0xc2, 0xc3, - 0xc3, 0xc2, 0xc2, 0xc2, 0xc0, 0xce, 0x86, 0x01, 0x01, 0x86, 0xce, - 0xc0, 0xc3, 0xc2, 0xc2, 0xc2, 0xc3, 0xc2, 0xd3, 0x84, 0x00, 0x00, - 0x00, 0xb8, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfc, 0x30, 0x00, 0x00, 0x3e, - 0xca, 0xc8, 0xc2, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, - 0x90, 0x90, 0xc3, 0xc3, 0xc2, 0xc3, 0xc2, 0xc2, 0xc2, 0xc3, 0xc2, - 0xc8, 0xca, 0x3d, 0x00, 0x00, 0x31, 0xfd, 0xff, 0xfe, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8a, - 0x00, 0x00, 0x0a, 0xa2, 0xcf, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, - 0xc3, 0xc3, 0xc2, 0xc3, 0xd3, 0xd3, 0xc3, 0xc2, 0xc3, 0xc3, 0xc2, - 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xcf, 0xa2, 0x0a, 0x00, 0x00, 0x8a, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, - 0xfc, 0xff, 0xcf, 0x0e, 0x00, 0x00, 0x6b, 0xd0, 0xc5, 0xc2, 0xc2, - 0xc3, 0xc3, 0xc2, 0xc2, 0xc3, 0xc3, 0xc1, 0xc9, 0xc5, 0xc5, 0xc9, - 0xc1, 0xc3, 0xc3, 0xc2, 0xc2, 0xc3, 0xc3, 0xc2, 0xc2, 0xc5, 0xd1, - 0x6e, 0x00, 0x00, 0x0e, 0xcf, 0xff, 0xfb, 0xff, 0xfe, 0xfe, 0xfe, - 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0x4e, 0x00, 0x00, 0x31, 0xbf, - 0xc8, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc9, - 0xab, 0x52, 0x52, 0xab, 0xc9, 0xc1, 0xc3, 0xc2, 0xc2, 0xc2, 0xc3, - 0xc2, 0xc2, 0xc2, 0xc8, 0xc0, 0x30, 0x00, 0x00, 0x4f, 0xff, 0xff, - 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xa0, 0x00, - 0x00, 0x04, 0x93, 0xd1, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc2, - 0xc2, 0xc2, 0xc0, 0xcf, 0x4c, 0x00, 0x00, 0x4c, 0xcf, 0xc0, 0xc3, - 0xc2, 0xc2, 0xc2, 0xc3, 0xc2, 0xc2, 0xc2, 0xc0, 0xd1, 0x92, 0x04, - 0x00, 0x00, 0x9e, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xe5, 0x1e, 0x00, 0x00, 0x54, 0xcd, 0xc3, 0xc3, 0xc3, 0xc2, - 0xc2, 0xc3, 0xc3, 0xc2, 0xc2, 0xc3, 0xc0, 0xd0, 0x4e, 0x00, 0x00, - 0x4e, 0xd0, 0xc0, 0xc3, 0xc2, 0xc2, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, - 0xc3, 0xc3, 0xce, 0x56, 0x00, 0x00, 0x20, 0xe6, 0xff, 0xfe, 0xfe, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00, 0x1b, 0xba, 0xcc, - 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1, - 0xc7, 0xae, 0x4c, 0x4c, 0xae, 0xc7, 0xc1, 0xc2, 0xc2, 0xc2, 0xc2, - 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xcc, 0xbb, 0x19, 0x00, 0x00, - 0x61, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xbd, 0x00, 0x00, - 0x00, 0x85, 0xd6, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, 0xcb, 0xd2, 0xd2, 0xcb, 0xc5, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, - 0xd6, 0x84, 0x00, 0x00, 0x00, 0xbb, 0xff, 0xfe, 0xff, 0xfd, 0xff, - 0xf7, 0x38, 0x00, 0x00, 0x42, 0xbf, 0xc6, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, - 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xc6, 0xc0, 0x42, 0x00, 0x00, 0x38, 0xf9, - 0xff, 0xfd, 0xfc, 0xff, 0x83, 0x00, 0x00, 0x00, 0x16, 0x24, 0x20, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x24, 0x16, - 0x00, 0x00, 0x00, 0x82, 0xff, 0xfd, 0xff, 0xfa, 0x34, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xf7, 0xff, 0xfe, - 0xff, 0x86, 0x12, 0x11, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x12, 0x18, - 0x8a, 0xff, 0xfe, 0xfe, 0xff, 0xfd, 0xd2, 0xc4, 0xc8, 0xc8, 0xc8, - 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, - 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, - 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, - 0xc8, 0xc8, 0xc5, 0xd8, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, - 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, - 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, - 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff -}; - -static const unsigned char * const twirl_data[4] = { - twirl_0_data, - twirl_30_data, - twirl_60_data, - failed_data -}; - - -/** - * Fills the indicated data array with a string of twirl_width * twirl_height - * * 3 bytes, representing the RGB pixel values of the twirl_width x - * twirl_height image at frame number step of twirl_num_steps frames. The - * specified fg and bg colors are applied to the array appropriately. - * - * Returns true on success, false on failure. On false, the array is - * initialized with zero. - * - * You must pass data_length = twirl_width * twirl_height * 3; this value is - * passed as a sanity check on array size. You should pass step so that 0 <= - * step < twirl_num_steps + 1. (The special value of step == twirl_num_steps - * returns the "failed" icon.) - */ -bool -get_twirl_data(unsigned char data[], size_t data_length, int step, - int fg_r, int fg_g, int fg_b, - int bg_r, int bg_g, int bg_b) { - - if (step < 0 || step >= twirl_num_steps + 1) { - memset(data, 0, data_length); - return false; - } - if (data_length != twirl_width * twirl_height) { - memset(data, 0, data_length); - return false; - } - - twirl_flip &flip = twirl_flip_table[step]; - const unsigned char *in_data = twirl_data[flip._index]; - - for (int yi = 0; yi < twirl_height; ++yi) { - const unsigned char *sp = &in_data[yi * twirl_width]; - for (int xi = 0; xi < twirl_width; ++xi) { - int xo = xi; - int yo = yi; - if (flip._flip_x) { - xo = twirl_width - 1 - xo; - } - if (flip._flip_y) { - yo = twirl_width - 1 - yo; - } - if (flip._exchange) { - int t = xo; - xo = yo; - yo = t; - } - unsigned char *dp = &data[yo * twirl_width * 3]; - dp += 3 * xo; - - double t = (double)(*sp) / 255.0; - ++sp; - - dp[0] = (unsigned char)(fg_r + t * (bg_r - fg_r)); - dp[1] = (unsigned char)(fg_g + t * (bg_g - fg_g)); - dp[2] = (unsigned char)(fg_b + t * (bg_b - fg_b)); - } - } - - return true; -} diff --git a/direct/src/plugin/get_twirl_data.h b/direct/src/plugin/get_twirl_data.h deleted file mode 100644 index 3b021a01e4..0000000000 --- a/direct/src/plugin/get_twirl_data.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file get_twirl_data.h - * @author drose - * @date 2011-08-24 - */ - -#ifndef GET_TWIRL_DATA_H -#define GET_TWIRL_DATA_H - -#include - -static const int twirl_width = 48; -static const int twirl_height = 48; -static const int twirl_num_steps = 12; - -bool get_twirl_data(unsigned char data[], size_t data_length, int step, - int fg_r, int fg_g, int fg_b, - int bg_r, int bg_g, int bg_b); - -#endif diff --git a/direct/src/plugin/handleStream.I b/direct/src/plugin/handleStream.I deleted file mode 100644 index 60021cee96..0000000000 --- a/direct/src/plugin/handleStream.I +++ /dev/null @@ -1,88 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file handleStream.I - * @author drose - * @date 2009-06-05 - */ - -/** - * - */ -inline HandleStream:: -HandleStream() : std::iostream(&_buf) { -} - -/** - * - */ -inline HandleStream:: -~HandleStream() { - close(); -} - -/** - * Attempts to open the given handle for input. The stream may not be - * simultaneously open for input and output. - */ -inline void HandleStream:: -open_read(FHandle handle) { - clear((std::ios::iostatetate)0); - _buf.open_read(handle); - if (!_buf.is_open_read()) { - clear(std::ios::failbit); - } -} - -/** - * Attempts to open the given handle for output. The stream may not be - * simultaneously open for input and output. - */ -inline void HandleStream:: -open_write(FHandle handle) { - clear((std::ios::iostatetate)0); - _buf.open_write(handle); - if (!_buf.is_open_write()) { - clear(std::ios::failbit); - } -} - -/** - * - */ -inline void HandleStream:: -close() { - _buf.close(); -} - -/** - * Closes the underlying handle, *without* attempting to flush the stream. - */ -inline void HandleStream:: -close_handle() { - _buf.close_handle(); -} - -/** - * Returns the handle that was passed to open_read() or open_write(). - */ -inline FHandle HandleStream:: -get_handle() const { - return _buf.get_handle(); -} - -/** - * Returns true if there is data in the stream's "get" buffer, meaning that at - * least one character can be extracted from the stream without making an OS - * read() call. Returns false if the get buffer is empty, meaning the next - * read call will hit the OS. - */ -inline bool HandleStream:: -has_gdata() const { - return _buf.has_gdata(); -} diff --git a/direct/src/plugin/handleStream.cxx b/direct/src/plugin/handleStream.cxx deleted file mode 100644 index 5d6580f493..0000000000 --- a/direct/src/plugin/handleStream.cxx +++ /dev/null @@ -1,14 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file handleStream.cxx - * @author drose - * @date 2009-06-05 - */ - -#include "handleStream.h" diff --git a/direct/src/plugin/handleStream.h b/direct/src/plugin/handleStream.h deleted file mode 100644 index b28dec5f6a..0000000000 --- a/direct/src/plugin/handleStream.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file handleStream.h - * @author drose - * @date 2009-06-05 - */ - -#ifndef HANDLESTREAM_H -#define HANDLESTREAM_H - -#include "handleStreamBuf.h" - -/** - * Implements a C++ stream object suitable for reading from and writing to - * Windows' HANDLE objects, or Posix file descriptors. This is necessary to - * map low-level pipes into an iostream for tinyxml. - */ -class HandleStream : public std::iostream { -public: - inline HandleStream(); - inline ~HandleStream(); - - inline void open_read(FHandle handle); - inline void open_write(FHandle handle); - inline void close(); - inline void close_handle(); - - inline FHandle get_handle() const; - inline bool has_gdata() const; - -private: - HandleStreamBuf _buf; -}; - -#include "handleStream.I" - -#endif diff --git a/direct/src/plugin/handleStreamBuf.I b/direct/src/plugin/handleStreamBuf.I deleted file mode 100644 index 24036ee9d3..0000000000 --- a/direct/src/plugin/handleStreamBuf.I +++ /dev/null @@ -1,31 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file handleStreamBuf.I - * @author drose - * @date 2009-06-05 - */ - -/** - * Returns the handle that was passed to open_read() or open_write(). - */ -inline FHandle HandleStreamBuf:: -get_handle() const { - return _handle; -} - -/** - * Returns true if there is data in the stream's "get" buffer, meaning that at - * least one character can be extracted from the stream without making an OS - * read() call. Returns false if the get buffer is empty, meaning the next - * read call will hit the OS. - */ -inline bool HandleStreamBuf:: -has_gdata() const { - return (egptr() != gptr()); -} diff --git a/direct/src/plugin/handleStreamBuf.cxx b/direct/src/plugin/handleStreamBuf.cxx deleted file mode 100644 index 55e74977b5..0000000000 --- a/direct/src/plugin/handleStreamBuf.cxx +++ /dev/null @@ -1,342 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file handleStreamBuf.cxx - * @author drose - * @date 2009-06-05 - */ - -#include "handleStreamBuf.h" - -#include -#include - -#ifndef _WIN32 -#include -#include -#include -#include -#include -#endif // _WIN32 - -#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__FreeBSD__) -#include -#endif // !_WIN32 && !__APPLE__ && !__FreeBSD__ - -using std::cerr; -using std::dec; -using std::hex; - -static const size_t handle_buffer_size = 4096; - -/** - * - */ -HandleStreamBuf:: -HandleStreamBuf() { - _is_open_read = false; - _is_open_write = false; - - _handle = invalid_fhandle; - - INIT_LOCK(_lock); - - _buffer = new char[handle_buffer_size]; - char *ebuf = _buffer + handle_buffer_size; - setg(_buffer, ebuf, ebuf); - setp(_buffer, ebuf); -} - -/** - * - */ -HandleStreamBuf:: -~HandleStreamBuf() { - close(); - - delete[] _buffer; - - DESTROY_LOCK(_lock); -} - -/** - * Attempts to open the given handle for input. The stream may not be - * simultaneously open for input and output. - */ -void HandleStreamBuf:: -open_read(FHandle handle) { - close(); - - _handle = handle; - _is_open_read = true; -} - -/** - * Attempts to open the given handle for output. The stream may not be - * simultaneously open for input and output. - */ -void HandleStreamBuf:: -open_write(FHandle handle) { - close(); - - _handle = handle; - _is_open_write = true; -} - -/** - * Returns true if the file is open for input, false otherwise. - */ -bool HandleStreamBuf:: -is_open_read() const { - return _is_open_read; -} - -/** - * Returns true if the file is open for output, false otherwise. - */ -bool HandleStreamBuf:: -is_open_write() const { - return _is_open_write; -} - -/** - * Empties the buffer and closes the file. - */ -void HandleStreamBuf:: -close() { - // Make sure the write buffer is flushed. - sync(); - - close_handle(); - - pbump(pbase() - pptr()); - gbump(egptr() - gptr()); -} - -/** - * Closes the underlying handle, *without* attempting to flush the stream. - */ -void HandleStreamBuf:: -close_handle() { -#ifdef _WIN32 - if (_handle != nullptr) { - CloseHandle(_handle); - } - _handle = nullptr; -#else - if (_handle != -1) { - ::close(_handle); - } - _handle = -1; -#endif // _WIN32 - - _is_open_read = false; - _is_open_write = false; -} - -/** - * Called by the system ostream implementation when its internal buffer is - * filled, plus one character. - */ -int HandleStreamBuf:: -overflow(int ch) { - ACQUIRE_LOCK(_lock); - - bool okflag = true; - - assert(pptr() >= pbase()); - size_t n = pptr() - pbase(); - if (n != 0) { - size_t wrote = write_chars(pbase(), n); - assert(wrote <= n); - pbump(-(int)wrote); - if (wrote != n) { - okflag = false; - } - } - - if (okflag && ch != EOF) { - if (pptr() != epptr()) { - // Store the extra character back in the buffer. - *(pptr()) = ch; - pbump(1); - } else { - // No room to store ch. - okflag = false; - } - } - - RELEASE_LOCK(_lock); - - if (!okflag) { - return EOF; - } - return 0; -} - -/** - * Called by the system iostream implementation to implement a flush - * operation. - */ -int HandleStreamBuf:: -sync() { - ACQUIRE_LOCK(_lock); - assert(pptr() >= pbase()); - size_t n = pptr() - pbase(); - - size_t wrote = write_chars(pbase(), n); - assert(wrote <= n); - pbump(-(int)wrote); - - RELEASE_LOCK(_lock); - - if (n != wrote) { - return EOF; - } - return 0; -} - -/** - * Called by the system istream implementation when its internal buffer needs - * more characters. - */ -int HandleStreamBuf:: -underflow() { - ACQUIRE_LOCK(_lock); - // Sometimes underflow() is called even if the buffer is not empty. - if (gptr() >= egptr()) { - // Mark the buffer filled (with buffer_size bytes). - size_t buffer_size = egptr() - eback(); - gbump(-(int)buffer_size); - - size_t num_bytes = buffer_size; - size_t read_count = read_chars(gptr(), buffer_size); - - if (read_count != num_bytes) { - // Oops, we didn't read what we thought we would. - if (read_count == 0) { - gbump(num_bytes); - RELEASE_LOCK(_lock); - return EOF; - } - - // Slide what we did read to the top of the buffer. - assert(read_count < num_bytes); - size_t delta = num_bytes - read_count; - memmove(gptr() + delta, gptr(), read_count); - gbump(delta); - } - } - - unsigned char next = *gptr(); - RELEASE_LOCK(_lock); - - return next; -} - -/** - * Attempts to extract the indicated number of characters from the current - * file position. Returns the number of characters extracted. - */ -size_t HandleStreamBuf:: -read_chars(char *start, size_t length) { - if (length == 0 || !_is_open_read) { - // Trivial no-op. - return 0; - } - - // Make sure the write buffer is flushed. - sync(); - - if (length == 0) { - return 0; - } - -#ifdef _WIN32 - // Windows case. - DWORD bytes_read = 0; - BOOL success = ReadFile(_handle, start, length, &bytes_read, nullptr); - if (!success) { - DWORD error = GetLastError(); - if (error != ERROR_HANDLE_EOF && error != ERROR_BROKEN_PIPE) { - cerr << "Error reading " << length - << " bytes, windows error code 0x" << hex - << error << dec << ".\n"; - return 0; - } - } - - length = bytes_read; - -#else - // Posix case. - ssize_t result = ::read(_handle, start, length); - if (result < 0) { - cerr << "Error reading " << length << " bytes\n"; - return 0; - } - - length = result; -#endif // _WIN32 - - return length; -} - -/** - * Outputs the indicated stream of characters to the current file position. - */ -size_t HandleStreamBuf:: -write_chars(const char *start, size_t length) { - if (length == 0) { - // Trivial no-op. - return 0; - } - - // Make sure the read buffer is flushed. - size_t n = egptr() - gptr(); - gbump(n); - - if (length == 0 || !_is_open_write) { - return 0; - } - -#ifdef _WIN32 - // Windows case. - DWORD bytes_written = 0; - BOOL success = WriteFile(_handle, start, length, &bytes_written, nullptr); - if (!success) { - assert(bytes_written <= length); - DWORD error = GetLastError(); - if (error != ERROR_NO_DATA && error != ERROR_BROKEN_PIPE) { - cerr << "Error writing " << length - << " bytes, windows error code 0x" << hex - << error << dec << ".\n"; - } - return bytes_written; - } - assert(bytes_written == length); - -#else - // Posix case. - size_t remaining = length; - while (remaining > 0) { - ssize_t result = ::write(_handle, start, remaining); - if (result < 0) { - if (errno != EPIPE) { - cerr << "Error writing " << remaining << " bytes\n"; - } - return length - remaining; - } - - start += result; - remaining -= result; - } -#endif // _WIN32 - - return length; -} diff --git a/direct/src/plugin/handleStreamBuf.h b/direct/src/plugin/handleStreamBuf.h deleted file mode 100644 index da3448d4d3..0000000000 --- a/direct/src/plugin/handleStreamBuf.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file handleStreamBuf.h - * @author drose - * @date 2009-06-05 - */ - -#ifndef HANDLESTREAMBUF_H -#define HANDLESTREAMBUF_H - -#include "fhandle.h" -#include "p3d_lock.h" -#include - -/** - * - */ -class HandleStreamBuf : public std::streambuf { -public: - HandleStreamBuf(); - virtual ~HandleStreamBuf(); - - void open_read(FHandle handle); - void open_write(FHandle handle); - bool is_open_read() const; - bool is_open_write() const; - void close(); - void close_handle(); - - inline FHandle get_handle() const; - inline bool has_gdata() const; - -protected: - virtual int overflow(int c); - virtual int sync(); - virtual int underflow(); - -private: - size_t read_chars(char *start, size_t length); - size_t write_chars(const char *start, size_t length); - -private: - bool _is_open_read; - bool _is_open_write; - - FHandle _handle; - LOCK _lock; - - char *_buffer; -}; - -#include "handleStreamBuf.I" - -#endif diff --git a/direct/src/plugin/is_pathsep.I b/direct/src/plugin/is_pathsep.I deleted file mode 100644 index 3dd35068b8..0000000000 --- a/direct/src/plugin/is_pathsep.I +++ /dev/null @@ -1,29 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file is_pathsep.I - * @author drose - * @date 2009-07-07 - */ - -/** - * Returns true if the indicated character is a path separator character (e.g. - * slash or backslash), false otherwise. - */ -inline bool -is_pathsep(int ch) { - if (ch == '/') { - return true; - } -#ifdef _WIN32 - if (ch == '\\') { - return true; - } -#endif - return false; -} diff --git a/direct/src/plugin/is_pathsep.h b/direct/src/plugin/is_pathsep.h deleted file mode 100644 index d1e326ee2d..0000000000 --- a/direct/src/plugin/is_pathsep.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file is_pathsep.h - * @author drose - * @date 2009-07-07 - */ - -#ifndef IS_PATHSEP_H -#define IS_PATHSEP_H - -inline bool -is_pathsep(int ch); - -#include "is_pathsep.I" - -#endif diff --git a/direct/src/plugin/load_plugin.cxx b/direct/src/plugin/load_plugin.cxx deleted file mode 100644 index a1575ff087..0000000000 --- a/direct/src/plugin/load_plugin.cxx +++ /dev/null @@ -1,476 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file load_plugin.cxx - * @author drose - * @date 2009-06-19 - */ - -#include "load_plugin.h" -#include "p3d_plugin_config.h" -#include "is_pathsep.h" -#include "wstring_encode.h" - -#include - -#include - -#ifndef _WIN32 -#include -#endif - -using std::string; - -#ifdef _WIN32 -static const string dll_ext = ".dll"; -#elif defined(__APPLE__) -static const string dll_ext = ".dylib"; -#else -static const string dll_ext = ".so"; -#endif - -static const string default_plugin_filename = "p3d_plugin"; - -P3D_initialize_func *P3D_initialize_ptr; -P3D_finalize_func *P3D_finalize_ptr; -P3D_set_plugin_version_func *P3D_set_plugin_version_ptr; -P3D_set_super_mirror_func *P3D_set_super_mirror_ptr; -P3D_new_instance_func *P3D_new_instance_ptr; -P3D_instance_start_func *P3D_instance_start_ptr; -P3D_instance_start_stream_func *P3D_instance_start_stream_ptr; -P3D_instance_finish_func *P3D_instance_finish_ptr; -P3D_instance_setup_window_func *P3D_instance_setup_window_ptr; - -P3D_object_get_type_func *P3D_object_get_type_ptr; -P3D_object_get_bool_func *P3D_object_get_bool_ptr; -P3D_object_get_int_func *P3D_object_get_int_ptr; -P3D_object_get_float_func *P3D_object_get_float_ptr; -P3D_object_get_string_func *P3D_object_get_string_ptr; -P3D_object_get_repr_func *P3D_object_get_repr_ptr; -P3D_object_get_property_func *P3D_object_get_property_ptr; -P3D_object_set_property_func *P3D_object_set_property_ptr; -P3D_object_has_method_func *P3D_object_has_method_ptr; -P3D_object_call_func *P3D_object_call_ptr; -P3D_object_eval_func *P3D_object_eval_ptr; -P3D_object_incref_func *P3D_object_incref_ptr; -P3D_object_decref_func *P3D_object_decref_ptr; - -P3D_make_class_definition_func *P3D_make_class_definition_ptr; -P3D_new_undefined_object_func *P3D_new_undefined_object_ptr; -P3D_new_none_object_func *P3D_new_none_object_ptr; -P3D_new_bool_object_func *P3D_new_bool_object_ptr; -P3D_new_int_object_func *P3D_new_int_object_ptr; -P3D_new_float_object_func *P3D_new_float_object_ptr; -P3D_new_string_object_func *P3D_new_string_object_ptr; -P3D_instance_get_panda_script_object_func *P3D_instance_get_panda_script_object_ptr; -P3D_instance_set_browser_script_object_func *P3D_instance_set_browser_script_object_ptr; - -P3D_instance_get_request_func *P3D_instance_get_request_ptr; -P3D_check_request_func *P3D_check_request_ptr; -P3D_request_finish_func *P3D_request_finish_ptr; -P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream_ptr; -P3D_instance_handle_event_func *P3D_instance_handle_event_ptr; - -#ifdef _WIN32 -static HMODULE module = nullptr; -#else -static void *module = nullptr; -#endif - -static bool plugin_loaded = false; -static bool dso_needs_unload = false; - - -/** - * Returns the default plugin filename, without any directory path (but - * including the extension appropriate to this platform). - */ -string -get_plugin_basename() { - return default_plugin_filename + dll_ext; -} - -/** - * Returns the position in the string of the dot before the filename - * extension; that is, the position of the rightmost dot that is right of the - * rightmost slash (or backslash, on Windows). Returns string::npos if there - * is no extension. - */ -static size_t -find_extension_dot(const string &filename) { - size_t p = filename.length(); - while (p > 0 && !is_pathsep(filename[p - 1])) { - --p; - if (filename[p] == '.') { - return p; - } - } - - return string::npos; -} - -// Forward reference for function defined below. -static void unload_dso(); - -/** - * Loads the plugin and assigns all of the function pointers. Returns true on - * success, false on failure. If load_plugin() has already been called - * successfully, this returns true immediately, without parsing any - * parameters. - * - * If p3d_plugin_filename is empty, the module is assumed to be already loaded - * (or statically linked in), and the symbols are located within the current - * address space. - */ -bool -load_plugin(const string &p3d_plugin_filename, - const string &contents_filename, const string &host_url, - P3D_verify_contents verify_contents, const string &platform, - const string &log_directory, const string &log_basename, - bool trusted_environment, bool console_environment, - const string &root_dir, const string &host_dir, - const string &start_dir, std::ostream &logfile) { - if (plugin_loaded) { - return true; - } - string filename = p3d_plugin_filename; - -#ifdef _WIN32 - assert(module == nullptr); - - if (filename.empty()) { - // If no filename is supplied, look within our existing address space. - module = GetModuleHandle(nullptr); - dso_needs_unload = false; - - } else { - // If a filename is supplied, attempt to load it as a dynamic library. - - // On Windows, the filename passed to LoadLibrary() must have an - // extension, or a default ".DLL" will be implicitly added. If the file - // actually has no extension, we must add "." to avoid this. - - // Check whether the filename has an extension. - size_t extension_dot = find_extension_dot(filename); - if (extension_dot == string::npos) { - // No extension. - filename += "."; - } - - SetErrorMode(0); - std::wstring filename_w; - if (string_to_wstring(filename_w, filename)) { - module = LoadLibraryW(filename_w.c_str()); - } - dso_needs_unload = true; - } - - if (module == nullptr) { - // Couldn't load the DLL. - logfile - << "Couldn't load " << filename << ", error = " - << GetLastError() << "\n"; - return false; - } - - #define get_func GetProcAddress - -#else // _WIN32 - // Posix case. - assert(module == nullptr); - if (filename.empty()) { - module = dlopen(nullptr, RTLD_LAZY | RTLD_LOCAL); - } else { - module = dlopen(filename.c_str(), RTLD_LAZY | RTLD_LOCAL); - } - if (module == nullptr) { - // Couldn't load the .so. - const char *message = dlerror(); - if (message == nullptr) { - message = "No error"; - } - logfile << "Couldn't load " << filename << ": " << message << "\n"; - - return false; - } - dso_needs_unload = true; - - #define get_func dlsym - -#endif // _WIN32 - - // Now get all of the function pointers. - P3D_initialize_ptr = (P3D_initialize_func *)get_func(module, "P3D_initialize"); - P3D_finalize_ptr = (P3D_finalize_func *)get_func(module, "P3D_finalize"); - P3D_set_plugin_version_ptr = (P3D_set_plugin_version_func *)get_func(module, "P3D_set_plugin_version"); - P3D_set_super_mirror_ptr = (P3D_set_super_mirror_func *)get_func(module, "P3D_set_super_mirror"); - P3D_new_instance_ptr = (P3D_new_instance_func *)get_func(module, "P3D_new_instance"); - P3D_instance_start_ptr = (P3D_instance_start_func *)get_func(module, "P3D_instance_start"); - P3D_instance_start_stream_ptr = (P3D_instance_start_stream_func *)get_func(module, "P3D_instance_start_stream"); - P3D_instance_finish_ptr = (P3D_instance_finish_func *)get_func(module, "P3D_instance_finish"); - P3D_instance_setup_window_ptr = (P3D_instance_setup_window_func *)get_func(module, "P3D_instance_setup_window"); - - P3D_object_get_type_ptr = (P3D_object_get_type_func *)get_func(module, "P3D_object_get_type"); - P3D_object_get_bool_ptr = (P3D_object_get_bool_func *)get_func(module, "P3D_object_get_bool"); - P3D_object_get_int_ptr = (P3D_object_get_int_func *)get_func(module, "P3D_object_get_int"); - P3D_object_get_float_ptr = (P3D_object_get_float_func *)get_func(module, "P3D_object_get_float"); - P3D_object_get_string_ptr = (P3D_object_get_string_func *)get_func(module, "P3D_object_get_string"); - P3D_object_get_repr_ptr = (P3D_object_get_repr_func *)get_func(module, "P3D_object_get_repr"); - P3D_object_get_property_ptr = (P3D_object_get_property_func *)get_func(module, "P3D_object_get_property"); - P3D_object_set_property_ptr = (P3D_object_set_property_func *)get_func(module, "P3D_object_set_property"); - P3D_object_has_method_ptr = (P3D_object_has_method_func *)get_func(module, "P3D_object_has_method"); - P3D_object_call_ptr = (P3D_object_call_func *)get_func(module, "P3D_object_call"); - P3D_object_eval_ptr = (P3D_object_eval_func *)get_func(module, "P3D_object_eval"); - P3D_object_incref_ptr = (P3D_object_incref_func *)get_func(module, "P3D_object_incref"); - P3D_object_decref_ptr = (P3D_object_decref_func *)get_func(module, "P3D_object_decref"); - P3D_make_class_definition_ptr = (P3D_make_class_definition_func *)get_func(module, "P3D_make_class_definition"); - P3D_new_undefined_object_ptr = (P3D_new_undefined_object_func *)get_func(module, "P3D_new_undefined_object"); - P3D_new_none_object_ptr = (P3D_new_none_object_func *)get_func(module, "P3D_new_none_object"); - P3D_new_bool_object_ptr = (P3D_new_bool_object_func *)get_func(module, "P3D_new_bool_object"); - P3D_new_int_object_ptr = (P3D_new_int_object_func *)get_func(module, "P3D_new_int_object"); - P3D_new_float_object_ptr = (P3D_new_float_object_func *)get_func(module, "P3D_new_float_object"); - P3D_new_string_object_ptr = (P3D_new_string_object_func *)get_func(module, "P3D_new_string_object"); - P3D_instance_get_panda_script_object_ptr = (P3D_instance_get_panda_script_object_func *)get_func(module, "P3D_instance_get_panda_script_object"); - P3D_instance_set_browser_script_object_ptr = (P3D_instance_set_browser_script_object_func *)get_func(module, "P3D_instance_set_browser_script_object"); - - P3D_instance_get_request_ptr = (P3D_instance_get_request_func *)get_func(module, "P3D_instance_get_request"); - P3D_check_request_ptr = (P3D_check_request_func *)get_func(module, "P3D_check_request"); - P3D_request_finish_ptr = (P3D_request_finish_func *)get_func(module, "P3D_request_finish"); - P3D_instance_feed_url_stream_ptr = (P3D_instance_feed_url_stream_func *)get_func(module, "P3D_instance_feed_url_stream"); - P3D_instance_handle_event_ptr = (P3D_instance_handle_event_func *)get_func(module, "P3D_instance_handle_event"); - - #undef get_func - - // Successfully loaded. - plugin_loaded = true; - - if (!init_plugin(contents_filename, host_url, - verify_contents, platform, - log_directory, log_basename, - trusted_environment, console_environment, - root_dir, host_dir, start_dir, logfile)) { - unload_dso(); - return false; - } - - return true; -} - -/** - * Ensures all the required function pointers have been set, and then calls - * P3D_initialize() on the recently-loaded plugin. Returns true on success, - * false on failure. - * - * It is not necessary to call this after calling load_plugin(); it is called - * implicitly. - */ -bool -init_plugin(const string &contents_filename, const string &host_url, - P3D_verify_contents verify_contents, const string &platform, - const string &log_directory, const string &log_basename, - bool trusted_environment, bool console_environment, - const string &root_dir, const string &host_dir, - const string &start_dir, std::ostream &logfile) { - - // Ensure that all of the function pointers have been found. - if (P3D_initialize_ptr == nullptr || - P3D_finalize_ptr == nullptr || - P3D_set_plugin_version_ptr == nullptr || - P3D_set_super_mirror_ptr == nullptr || - P3D_new_instance_ptr == nullptr || - P3D_instance_start_ptr == nullptr || - P3D_instance_start_stream_ptr == nullptr || - P3D_instance_finish_ptr == nullptr || - P3D_instance_setup_window_ptr == nullptr || - - P3D_object_get_type_ptr == nullptr || - P3D_object_get_bool_ptr == nullptr || - P3D_object_get_int_ptr == nullptr || - P3D_object_get_float_ptr == nullptr || - P3D_object_get_string_ptr == nullptr || - P3D_object_get_repr_ptr == nullptr || - P3D_object_get_property_ptr == nullptr || - P3D_object_set_property_ptr == nullptr || - P3D_object_has_method_ptr == nullptr || - P3D_object_call_ptr == nullptr || - P3D_object_eval_ptr == nullptr || - P3D_object_incref_ptr == nullptr || - P3D_object_decref_ptr == nullptr || - - P3D_make_class_definition_ptr == nullptr || - P3D_new_undefined_object_ptr == nullptr || - P3D_new_none_object_ptr == nullptr || - P3D_new_bool_object_ptr == nullptr || - P3D_new_int_object_ptr == nullptr || - P3D_new_float_object_ptr == nullptr || - P3D_new_string_object_ptr == nullptr || - P3D_instance_get_panda_script_object_ptr == nullptr || - P3D_instance_set_browser_script_object_ptr == nullptr || - - P3D_instance_get_request_ptr == nullptr || - P3D_check_request_ptr == nullptr || - P3D_request_finish_ptr == nullptr || - P3D_instance_feed_url_stream_ptr == nullptr || - P3D_instance_handle_event_ptr == nullptr) { - - logfile - << "Some function pointers not found:" - << "\nP3D_initialize_ptr = " << P3D_initialize_ptr - << "\nP3D_finalize_ptr = " << P3D_finalize_ptr - << "\nP3D_set_plugin_version_ptr = " << P3D_set_plugin_version_ptr - << "\nP3D_set_super_mirror_ptr = " << P3D_set_super_mirror_ptr - << "\nP3D_new_instance_ptr = " << P3D_new_instance_ptr - << "\nP3D_instance_start_ptr = " << P3D_instance_start_ptr - << "\nP3D_instance_start_stream_ptr = " << P3D_instance_start_stream_ptr - << "\nP3D_instance_finish_ptr = " << P3D_instance_finish_ptr - << "\nP3D_instance_setup_window_ptr = " << P3D_instance_setup_window_ptr - - << "\nP3D_object_get_type_ptr = " << P3D_object_get_type_ptr - << "\nP3D_object_get_bool_ptr = " << P3D_object_get_bool_ptr - << "\nP3D_object_get_int_ptr = " << P3D_object_get_int_ptr - << "\nP3D_object_get_float_ptr = " << P3D_object_get_float_ptr - << "\nP3D_object_get_string_ptr = " << P3D_object_get_string_ptr - << "\nP3D_object_get_repr_ptr = " << P3D_object_get_repr_ptr - << "\nP3D_object_get_property_ptr = " << P3D_object_get_property_ptr - << "\nP3D_object_set_property_ptr = " << P3D_object_set_property_ptr - << "\nP3D_object_has_method_ptr = " << P3D_object_has_method_ptr - << "\nP3D_object_call_ptr = " << P3D_object_call_ptr - << "\nP3D_object_eval_ptr = " << P3D_object_eval_ptr - << "\nP3D_object_incref_ptr = " << P3D_object_incref_ptr - << "\nP3D_object_decref_ptr = " << P3D_object_decref_ptr - - << "\nP3D_make_class_definition_ptr = " << P3D_make_class_definition_ptr - << "\nP3D_new_undefined_object_ptr = " << P3D_new_undefined_object_ptr - << "\nP3D_new_none_object_ptr = " << P3D_new_none_object_ptr - << "\nP3D_new_bool_object_ptr = " << P3D_new_bool_object_ptr - << "\nP3D_new_int_object_ptr = " << P3D_new_int_object_ptr - << "\nP3D_new_float_object_ptr = " << P3D_new_float_object_ptr - << "\nP3D_new_string_object_ptr = " << P3D_new_string_object_ptr - << "\nP3D_instance_get_panda_script_object_ptr = " << P3D_instance_get_panda_script_object_ptr - << "\nP3D_instance_set_browser_script_object_ptr = " << P3D_instance_set_browser_script_object_ptr - - << "\nP3D_instance_get_request_ptr = " << P3D_instance_get_request_ptr - << "\nP3D_check_request_ptr = " << P3D_check_request_ptr - << "\nP3D_request_finish_ptr = " << P3D_request_finish_ptr - << "\nP3D_instance_feed_url_stream_ptr = " << P3D_instance_feed_url_stream_ptr - << "\nP3D_instance_handle_event_ptr = " << P3D_instance_handle_event_ptr - << "\n"; - return false; - } - - // A bit of extra hand-hacked compatibility for using newer plug-ins with an - // older version of the core API. - int api_version = P3D_API_VERSION; - if (api_version == 17 && start_dir.empty()) { - api_version = 16; - if (host_dir.empty()) { - api_version = 15; - } - } - - if (!P3D_initialize_ptr(api_version, contents_filename.c_str(), - host_url.c_str(), verify_contents, platform.c_str(), - log_directory.c_str(), log_basename.c_str(), - trusted_environment, console_environment, - root_dir.c_str(), host_dir.c_str(), - start_dir.c_str())) { - // Oops, failure to initialize. - logfile - << "Failed to initialize plugin (passed API version " - << api_version << ")\n"; - return false; - } - - return true; -} - -/** - * Calls finalize, then removes the plugin from memory space and clears all of - * the pointers. - */ -void -unload_plugin(std::ostream &logfile) { - if (!plugin_loaded) { - return; - } - - P3D_finalize_ptr(); - unload_dso(); -} - -/** - * Removes the plugin from memory space and clears all of the pointers. This - * is only intended to be called by load_plugin(), above, in the specific case - * that the plugin loaded but could not successfully initialize itself. All - * user code should call unload_plugin(), above, which first calls - * P3D_finalize(). - */ -static void -unload_dso() { - if (dso_needs_unload) { - assert(module != nullptr); -#ifdef _WIN32 - FreeLibrary(module); -#else - dlclose(module); -#endif - module = nullptr; - dso_needs_unload = false; - } - - P3D_initialize_ptr = nullptr; - P3D_finalize_ptr = nullptr; - P3D_set_plugin_version_ptr = nullptr; - P3D_set_super_mirror_ptr = nullptr; - P3D_new_instance_ptr = nullptr; - P3D_instance_start_ptr = nullptr; - P3D_instance_start_stream_ptr = nullptr; - P3D_instance_finish_ptr = nullptr; - P3D_instance_setup_window_ptr = nullptr; - - P3D_object_get_type_ptr = nullptr; - P3D_object_get_bool_ptr = nullptr; - P3D_object_get_int_ptr = nullptr; - P3D_object_get_float_ptr = nullptr; - P3D_object_get_string_ptr = nullptr; - P3D_object_get_repr_ptr = nullptr; - P3D_object_get_property_ptr = nullptr; - P3D_object_set_property_ptr = nullptr; - P3D_object_has_method_ptr = nullptr; - P3D_object_call_ptr = nullptr; - P3D_object_eval_ptr = nullptr; - P3D_object_incref_ptr = nullptr; - P3D_object_decref_ptr = nullptr; - - P3D_make_class_definition_ptr = nullptr; - P3D_new_undefined_object_ptr = nullptr; - P3D_new_none_object_ptr = nullptr; - P3D_new_bool_object_ptr = nullptr; - P3D_new_int_object_ptr = nullptr; - P3D_new_float_object_ptr = nullptr; - P3D_new_string_object_ptr = nullptr; - P3D_instance_get_panda_script_object_ptr = nullptr; - P3D_instance_set_browser_script_object_ptr = nullptr; - - P3D_instance_get_request_ptr = nullptr; - P3D_check_request_ptr = nullptr; - P3D_request_finish_ptr = nullptr; - P3D_instance_feed_url_stream_ptr = nullptr; - P3D_instance_handle_event_ptr = nullptr; - - plugin_loaded = false; -} - -/** - * Returns true if the plugin has been loaded successfully by a previous call - * to load_plugin(), false otherwise. - */ -bool -is_plugin_loaded() { - return plugin_loaded; -} diff --git a/direct/src/plugin/load_plugin.h b/direct/src/plugin/load_plugin.h deleted file mode 100644 index 2e462ed9cd..0000000000 --- a/direct/src/plugin/load_plugin.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file load_plugin.h - * @author drose - * @date 2009-06-19 - */ - -#ifndef LOAD_PLUGIN_H -#define LOAD_PLUGIN_H - -#include "p3d_plugin.h" - -#include - -extern P3D_initialize_func *P3D_initialize_ptr; -extern P3D_finalize_func *P3D_finalize_ptr; -extern P3D_set_plugin_version_func *P3D_set_plugin_version_ptr; -extern P3D_set_super_mirror_func *P3D_set_super_mirror_ptr; -extern P3D_new_instance_func *P3D_new_instance_ptr; -extern P3D_instance_start_func *P3D_instance_start_ptr; -extern P3D_instance_start_stream_func *P3D_instance_start_stream_ptr; -extern P3D_instance_finish_func *P3D_instance_finish_ptr; -extern P3D_instance_setup_window_func *P3D_instance_setup_window_ptr; - -extern P3D_object_get_type_func *P3D_object_get_type_ptr; -extern P3D_object_get_bool_func *P3D_object_get_bool_ptr; -extern P3D_object_get_int_func *P3D_object_get_int_ptr; -extern P3D_object_get_float_func *P3D_object_get_float_ptr; -extern P3D_object_get_string_func *P3D_object_get_string_ptr; -extern P3D_object_get_repr_func *P3D_object_get_repr_ptr; -extern P3D_object_get_property_func *P3D_object_get_property_ptr; -extern P3D_object_set_property_func *P3D_object_set_property_ptr; -extern P3D_object_has_method_func *P3D_object_has_method_ptr; -extern P3D_object_call_func *P3D_object_call_ptr; -extern P3D_object_eval_func *P3D_object_eval_ptr; -extern P3D_object_incref_func *P3D_object_incref_ptr; -extern P3D_object_decref_func *P3D_object_decref_ptr; - -extern P3D_make_class_definition_func *P3D_make_class_definition_ptr; -extern P3D_new_undefined_object_func *P3D_new_undefined_object_ptr; -extern P3D_new_none_object_func *P3D_new_none_object_ptr; -extern P3D_new_bool_object_func *P3D_new_bool_object_ptr; -extern P3D_new_int_object_func *P3D_new_int_object_ptr; -extern P3D_new_float_object_func *P3D_new_float_object_ptr; -extern P3D_new_string_object_func *P3D_new_string_object_ptr; -extern P3D_instance_get_panda_script_object_func *P3D_instance_get_panda_script_object_ptr; -extern P3D_instance_set_browser_script_object_func *P3D_instance_set_browser_script_object_ptr; - -extern P3D_instance_get_request_func *P3D_instance_get_request_ptr; -extern P3D_check_request_func *P3D_check_request_ptr; -extern P3D_request_finish_func *P3D_request_finish_ptr; -extern P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream_ptr; -extern P3D_instance_handle_event_func *P3D_instance_handle_event_ptr; - -std::string get_plugin_basename(); -bool -load_plugin(const std::string &p3d_plugin_filename, - const std::string &contents_filename, const std::string &host_url, - P3D_verify_contents verify_contents, const std::string &platform, - const std::string &log_directory, const std::string &log_basename, - bool trusted_environment, bool console_environment, - const std::string &root_dir, const std::string &host_dir, - const std::string &start_dir, std::ostream &logfile); -bool -init_plugin(const std::string &contents_filename, const std::string &host_url, - P3D_verify_contents verify_contents, const std::string &platform, - const std::string &log_directory, const std::string &log_basename, - bool trusted_environment, bool console_environment, - const std::string &root_dir, const std::string &host_dir, - const std::string &start_dir, std::ostream &logfile); - -void unload_plugin(std::ostream &logfile); -bool is_plugin_loaded(); - -#endif diff --git a/direct/src/plugin/mkdir_complete.cxx b/direct/src/plugin/mkdir_complete.cxx deleted file mode 100644 index 6689584198..0000000000 --- a/direct/src/plugin/mkdir_complete.cxx +++ /dev/null @@ -1,224 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file mkdir_complete.cxx - * @author drose - * @date 2009-06-29 - */ - -#include "mkdir_complete.h" -#include "is_pathsep.h" -#include "wstring_encode.h" - -#ifdef _WIN32 -#include -#include // chmod() -#else -#include -#include // for mkdir() -#include -#include // strerror() -#include -#endif - -using std::ostream; -using std::string; -using std::wstring; - -/** - * Returns the directory component of the indicated pathname, or the empty - * string if there is no directory prefix. - */ -static string -get_dirname(const string &filename) { - size_t p = filename.length(); - while (p > 0) { - --p; - if (is_pathsep(filename[p])) { - return filename.substr(0, p); - } - } - - return string(); -} - -#ifdef _WIN32 -/** - * The wide-character implementation of get_dirname(). Only implemented (and - * needed) on Windows. - */ -static wstring -get_dirname_w(const wstring &filename) { - size_t p = filename.length(); - while (p > 0) { - --p; - if (is_pathsep(filename[p])) { - return filename.substr(0, p); - } - } - - return wstring(); -} -#endif // _WIN32 - - - -/** - * Creates a new directory, with normal access privileges. Returns true on - * success, false on failure. Will create intervening directories if - * necessary. - */ -bool -mkdir_complete(const string &dirname, ostream &logfile) { -#ifdef _WIN32 - wstring dirname_w; - if (!string_to_wstring(dirname_w, dirname)) { - return false; - } - return mkdir_complete_w(dirname_w, logfile); - -#else //_WIN32 - if (mkdir(dirname.c_str(), 0755) == 0) { - // Success! - return true; - } - - // Failed. - if (errno == EEXIST) { - // Not really an error: the directory is already there. - return true; - } - - if (errno == ENOENT || errno == EACCES) { - // We need to make the parent directory first. - string parent = get_dirname(dirname); - if (!parent.empty() && mkdir_complete(parent, logfile)) { - // Parent successfully created. Try again to make the child. - if (mkdir(dirname.c_str(), 0755) == 0) { - // Got it! - return true; - } - // Couldn't create the directory. :( - logfile - << "Couldn't create " << dirname << ": " << strerror(errno) << "\n"; - } - } - return false; - -#endif // _WIN32 -} - -/** - * Creates a new file with normal access priviledges. Returns true on - * success, false on failure. This will create intervening directories if - * needed. - */ -bool -mkfile_complete(const string &filename, ostream &logfile) { -#ifdef _WIN32 - wstring filename_w; - if (!string_to_wstring(filename_w, filename)) { - return false; - } - return mkfile_complete_w(filename_w, logfile); -#else // _WIN32 - // Make sure we delete any previously-existing file first. - unlink(filename.c_str()); - - int fd = creat(filename.c_str(), 0755); - if (fd == -1) { - // Try to make the parent directory first. - string parent = get_dirname(filename); - if (!parent.empty() && mkdir_complete(parent, logfile)) { - // Parent successfully created. Try again to make the file. - fd = creat(filename.c_str(), 0755); - } - if (fd == -1) { - logfile - << "Couldn't create " << filename << ": " << strerror(errno) << "\n"; - return false; - } - } - close(fd); - return true; - -#endif // _WIN32 -} - - -#ifdef _WIN32 -/** - * The wide-character implementation of mkdir_complete(). Only implemented - * (and needed) on Windows. - */ -bool -mkdir_complete_w(const wstring &dirname, ostream &logfile) { - if (CreateDirectoryW(dirname.c_str(), nullptr) != 0) { - // Success! - return true; - } - - // Failed. - DWORD last_error = GetLastError(); - if (last_error == ERROR_ALREADY_EXISTS) { - // Not really an error: the directory is already there. - return true; - } - - if (last_error == ERROR_PATH_NOT_FOUND) { - // We need to make the parent directory first. - wstring parent = get_dirname_w(dirname); - if (!parent.empty() && mkdir_complete_w(parent, logfile)) { - // Parent successfully created. Try again to make the child. - if (CreateDirectoryW(dirname.c_str(), nullptr) != 0) { - // Got it! - return true; - } - logfile - << "Couldn't create " << dirname << "\n"; - } - } - return false; -} -#endif // _WIN32 - -#ifdef _WIN32 -/** - * The wide-character implementation of mkfile_complete(). Only implemented - * (and needed) on Windows. - */ -bool -mkfile_complete_w(const wstring &filename, ostream &logfile) { - // Make sure we delete any previously-existing file first. - - // Windows can't delete a file if it's read-only. Weird. - _wchmod(filename.c_str(), 0644); - _wunlink(filename.c_str()); - - HANDLE file = CreateFileW(filename.c_str(), GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - if (file == INVALID_HANDLE_VALUE) { - // Try to make the parent directory first. - wstring parent = get_dirname_w(filename); - if (!parent.empty() && mkdir_complete_w(parent, logfile)) { - // Parent successfully created. Try again to make the file. - file = CreateFileW(filename.c_str(), GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - } - if (file == INVALID_HANDLE_VALUE) { - logfile - << "Couldn't create " << filename << "\n"; - return false; - } - } - CloseHandle(file); - return true; -} -#endif // _WIN32 diff --git a/direct/src/plugin/mkdir_complete.h b/direct/src/plugin/mkdir_complete.h deleted file mode 100644 index be79cf7e8e..0000000000 --- a/direct/src/plugin/mkdir_complete.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file mkdir_complete.h - * @author drose - * @date 2009-06-29 - */ - -#ifndef MKDIR_COMPLETE_H -#define MKDIR_COMPLETE_H - -#include -#include - -bool mkdir_complete(const std::string &dirname, std::ostream &logfile); -bool mkfile_complete(const std::string &dirname, std::ostream &logfile); - -#ifdef _WIN32 -bool mkdir_complete_w(const std::wstring &dirname, std::ostream &logfile); -bool mkfile_complete_w(const std::wstring &dirname, std::ostream &logfile); -#endif // _WIN32 - -#endif diff --git a/direct/src/plugin/p3dAuthSession.I b/direct/src/plugin/p3dAuthSession.I deleted file mode 100644 index 0e451a8f87..0000000000 --- a/direct/src/plugin/p3dAuthSession.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dAuthSession.I - * @author drose - * @date 2009-09-17 - */ diff --git a/direct/src/plugin/p3dAuthSession.cxx b/direct/src/plugin/p3dAuthSession.cxx deleted file mode 100644 index e76bbe0a07..0000000000 --- a/direct/src/plugin/p3dAuthSession.cxx +++ /dev/null @@ -1,456 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dAuthSession.cxx - * @author drose - * @date 2009-09-17 - */ - -#include "p3dAuthSession.h" -#include "p3dInstance.h" -#include "p3dInstanceManager.h" -#include "p3dMultifileReader.h" -#include "p3d_plugin_config.h" -#include "mkdir_complete.h" -#include "wstring_encode.h" - -#include - -#ifndef _WIN32 -#include -#include -#include -#include -#include -#include -#endif - -using std::string; - -/** - * - */ -P3DAuthSession:: -P3DAuthSession(P3DInstance *inst) : - _inst(inst) -{ - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - -#ifdef _WIN32 - _p3dcert_handle = INVALID_HANDLE_VALUE; -#else - _p3dcert_pid = -1; -#endif - _p3dcert_started = false; - _p3dcert_running = false; - _started_wait_thread = false; - INIT_THREAD(_wait_thread); - - // Write the cert chain to a temporary file. - _cert_filename = new P3DTemporaryFile(".crt"); - string filename = _cert_filename->get_filename(); - FILE *fp = fopen(filename.c_str(), "w"); - if (fp == nullptr) { - nout << "Couldn't write temporary file\n"; - return; - } - - if (inst->_mf_reader.get_num_signatures() > 0) { - const P3DMultifileReader::CertChain &cert_chain = - inst->_mf_reader.get_signature(0); - - if (cert_chain.size() > 0) { - // Save the cert_dir, this is where the p3dcert program will need to - // write the cert when it is approved. - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - _cert_dir = inst_mgr->get_cert_dir(cert_chain[0]._cert); - } - - P3DMultifileReader::CertChain::const_iterator ci; - for (ci = cert_chain.begin(); ci != cert_chain.end(); ++ci) { - X509 *c = (*ci)._cert; - PEM_write_X509(fp, c); - } - } - fclose(fp); - - start_p3dcert(); -} - - -/** - * - */ -P3DAuthSession:: -~P3DAuthSession() { - shutdown(false); - - if (_cert_filename != nullptr) { - delete _cert_filename; - } -} - -/** - * Terminates the session by killing the subprocess. - */ -void P3DAuthSession:: -shutdown(bool send_message) { - if (!send_message) { - // If we're not to send the instance the shutdown message as a result of - // this, then clear the _inst pointer now. - _inst = nullptr; - } - - if (_p3dcert_running) { - nout << "Killing p3dcert process\n"; -#ifdef _WIN32 - TerminateProcess(_p3dcert_handle, 2); - CloseHandle(_p3dcert_handle); - -#else // _WIN32 - kill(_p3dcert_pid, SIGKILL); - - // Wait a few milliseconds for the process to exit, and then get its - // return status to clean up the zombie status. If we don't wait long - // enough, don't sweat it. - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 100000; - select(0, nullptr, nullptr, nullptr, &tv); - int status; - waitpid(_p3dcert_pid, &status, WNOHANG); -#endif // _WIN32 - - _p3dcert_running = false; - } - _p3dcert_started = false; - - // Now that the process has stopped, the thread should stop itself quickly - // too. - join_wait_thread(); - - // We're no longer bound to any particular instance. - _inst = nullptr; -} - -/** - * Starts the p3dcert program running in a child process. - */ -void P3DAuthSession:: -start_p3dcert() { - if (_p3dcert_started) { - // Already started. - return; - } - - if (_inst->_p3dcert_package == nullptr) { - nout << "Couldn't start Python: no p3dcert package.\n"; - return; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - _start_dir = inst_mgr->get_start_dir(); - - string root_dir = _inst->_p3dcert_package->get_package_dir(); - mkdir_complete(_start_dir, nout); - - _p3dcert_exe = root_dir + "/p3dcert"; -#ifdef _WIN32 - _p3dcert_exe += ".exe"; -#endif -#ifdef __APPLE__ - // On OSX, run from the packaged bundle. - _p3dcert_exe = root_dir + "/P3DCert.app/Contents/MacOS/p3dcert"; -#endif - - // Populate the new process' environment. - -#ifdef _WIN32 - // These are the enviroment variables we forward from the current - // environment, if they are set. - const wchar_t *keep[] = { - L"TMP", L"TEMP", L"HOME", L"USER", - L"SYSTEMROOT", L"USERPROFILE", L"COMSPEC", - nullptr - }; - - std::wstring env_w; - - for (int ki = 0; keep[ki] != nullptr; ++ki) { - wchar_t *value = _wgetenv(keep[ki]); - if (value != nullptr) { - env_w += keep[ki]; - env_w += L"="; - env_w += value; - env_w += (wchar_t)'\0'; - } - } - - wstring_to_string(_env, env_w); - -#else // _WIN32 - - _env = string(); - - // These are the enviroment variables we forward from the current - // environment, if they are set. - const char *keep[] = { - "TMP", "TEMP", "HOME", "USER", - "LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG", -#ifdef HAVE_X11 - "DISPLAY", "XAUTHORITY", -#endif - nullptr - }; - for (int ki = 0; keep[ki] != nullptr; ++ki) { - char *value = getenv(keep[ki]); - if (value != nullptr) { - _env += keep[ki]; - _env += "="; - _env += value; - _env += '\0'; - } - } -#endif // _WIN32 - - // Define some new environment variables. - _env += "PATH="; - _env += root_dir; - _env += '\0'; - - _env += "LD_LIBRARY_PATH="; - _env += root_dir; - _env += '\0'; - - _env += "DYLD_LIBRARY_PATH="; - _env += root_dir; - _env += '\0'; - - _env += "P3DCERT_ROOT="; - _env += root_dir; - _env += '\0'; - - nout << "Setting environment:\n"; - write_env(); - - nout << "Attempting to start p3dcert from " << _p3dcert_exe << "\n"; - - bool started_p3dcert = false; -#ifdef _WIN32 - _p3dcert_handle = win_create_process(); - started_p3dcert = (_p3dcert_handle != INVALID_HANDLE_VALUE); -#else - _p3dcert_pid = posix_create_process(); - started_p3dcert = (_p3dcert_pid > 0); -#endif - if (!started_p3dcert) { - nout << "Failed to create process.\n"; - return; - } - - _p3dcert_started = true; - _p3dcert_running = true; - - spawn_wait_thread(); -} - -/** - * Starts the wait thread. This thread is responsible for waiting for the - * process to finish, and notifying the instance when it does. - */ -void P3DAuthSession:: -spawn_wait_thread() { - SPAWN_THREAD(_wait_thread, wt_thread_run, this); - _started_wait_thread = true; -} - -/** - * Waits for the wait thread to stop. - */ -void P3DAuthSession:: -join_wait_thread() { - if (!_started_wait_thread) { - return; - } - - JOIN_THREAD(_wait_thread); - _started_wait_thread = false; -} - -/** - * Writes _env, which is formatted as a string containing zero-byte-terminated - * environment defintions, to the nout stream, one definition per line. - */ -void P3DAuthSession:: -write_env() const { - size_t p = 0; - size_t zero = _env.find('\0', p); - while (zero != string::npos) { - nout << " "; - nout.write(_env.data() + p, zero - p); - nout << "\n"; - p = zero + 1; - zero = _env.find('\0', p); - } -} - -/** - * The main function for the wait thread. - */ -void P3DAuthSession:: -wt_thread_run() { - // All we do here is wait for the process to terminate. - nout << "wt_thread_run in " << this << " beginning\n"; -#ifdef _WIN32 - DWORD result = WaitForSingleObject(_p3dcert_handle, INFINITE); - if (result != 0) { - nout << "Wait for process failed: " << GetLastError() << "\n"; - } - CloseHandle(_p3dcert_handle); - _p3dcert_handle = INVALID_HANDLE_VALUE; - _p3dcert_running = false; - nout << "p3dcert process has successfully stopped.\n"; -#else - int status; - pid_t result = waitpid(_p3dcert_pid, &status, 0); - if (result == -1) { - perror("waitpid"); - } - _p3dcert_pid = -1; - _p3dcert_running = false; - - nout << "p3dcert process has successfully stopped.\n"; - if (WIFEXITED(status)) { - nout << " exited normally, status = " - << WEXITSTATUS(status) << "\n"; - } else if (WIFSIGNALED(status)) { - nout << " signalled by " << WTERMSIG(status) << ", core = " - << WCOREDUMP(status) << "\n"; - } else if (WIFSTOPPED(status)) { - nout << " stopped by " << WSTOPSIG(status) << "\n"; - } -#endif // _WIN32 - - // Notify the instance that we're done. - P3DInstance *inst = _inst; - if (inst != nullptr) { - inst->auth_finished_sub_thread(); - } - - nout << "Exiting wt_thread_run in " << this << "\n"; -} - -#ifdef _WIN32 -/** - * Creates a sub-process to run _p3dcert_exe, with the appropriate command- - * line arguments, and the environment string defined in _env. - * - * Returns the handle to the created process on success, or - * INVALID_HANDLE_VALUE on falure. - */ -HANDLE P3DAuthSession:: -win_create_process() { - // Make sure we see an error dialog if there is a missing DLL. - SetErrorMode(0); - - STARTUPINFO startup_info; - ZeroMemory(&startup_info, sizeof(startup_info)); - startup_info.cb = sizeof(startup_info); - - // Make sure the initial window is *shown* for this graphical app. - startup_info.wShowWindow = SW_SHOW; - startup_info.dwFlags |= STARTF_USESHOWWINDOW; - - const char *start_dir_cstr = _start_dir.c_str(); - - // Construct the command-line string, containing the quoted command-line - // arguments. - std::ostringstream stream; - stream << "\"" << _p3dcert_exe << "\" \"" - << _cert_filename->get_filename() << "\" \"" << _cert_dir << "\""; - - // I'm not sure why CreateProcess wants a non-const char pointer for its - // command-line string, but I'm not taking chances. It gets a non-const - // char array that it can modify. - string command_line_str = stream.str(); - char *command_line = new char[command_line_str.size() + 1]; - memcpy(command_line, command_line_str.c_str(), command_line_str.size() + 1); - - nout << "Command line: " << command_line_str << "\n"; - - // Something about p3dCert_wx tends to become crashy when we call it from - // CreateProcessW(). Something about the way wx parses the command-line - // parameters? Well, whatever, we don't really need the Unicode form - // anyway. - PROCESS_INFORMATION process_info; - BOOL result = CreateProcess - (_p3dcert_exe.c_str(), command_line, nullptr, nullptr, TRUE, - 0, (void *)_env.c_str(), - start_dir_cstr, &startup_info, &process_info); - bool started_program = (result != 0); - - delete[] command_line; - - if (!started_program) { - nout << "CreateProcess failed, error: " << GetLastError() << "\n"; - return INVALID_HANDLE_VALUE; - } - - CloseHandle(process_info.hThread); - return process_info.hProcess; -} -#endif // _WIN32 - - -#ifndef _WIN32 -/** - * Creates a sub-process to run _p3dcert_exe, with the appropriate command- - * line arguments, and the environment string defined in _env. - * - * Returns the pid of the created process on success, or -1 on falure. - */ -int P3DAuthSession:: -posix_create_process() { - // Fork and exec. - pid_t child = fork(); - if (child < 0) { - perror("fork"); - return -1; - } - - if (child == 0) { - // Here we are in the child process. - - if (chdir(_start_dir.c_str()) < 0) { - nout << "Could not chdir to " << _start_dir << "\n"; - // This is a warning, not an error. We don't actually care that much - // about the starting directory. - } - - // build up an array of char strings for the environment. - std::vector ptrs; - size_t p = 0; - size_t zero = _env.find('\0', p); - while (zero != string::npos) { - ptrs.push_back(_env.data() + p); - p = zero + 1; - zero = _env.find('\0', p); - } - ptrs.push_back(nullptr); - - execle(_p3dcert_exe.c_str(), _p3dcert_exe.c_str(), - _cert_filename->get_filename().c_str(), _cert_dir.c_str(), - (char *)0, &ptrs[0]); - nout << "Failed to exec " << _p3dcert_exe << "\n"; - _exit(1); - } - - return child; -} -#endif // _WIN32 diff --git a/direct/src/plugin/p3dAuthSession.h b/direct/src/plugin/p3dAuthSession.h deleted file mode 100644 index f37f24d9e3..0000000000 --- a/direct/src/plugin/p3dAuthSession.h +++ /dev/null @@ -1,82 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dAuthSession.h - * @author drose - * @date 2009-09-17 - */ - -#ifndef P3DAUTHSESSION_H -#define P3DAUTHSESSION_H - -#include "p3d_plugin_common.h" -#include "p3dPackage.h" -#include "get_tinyxml.h" -#include "p3dTemporaryFile.h" -#include "p3dReferenceCount.h" - -class P3DInstance; - -/** - * This is an instance of a p3dcert program running in a subprocess. There's - * no communication with the process, or none of that complicated stuff the - * P3DSession has to do; all we do here is fire off the process, then wait for - * it to exit. - */ -class P3DAuthSession : public P3DReferenceCount { -public: - P3DAuthSession(P3DInstance *inst); - ~P3DAuthSession(); - - void shutdown(bool send_message); - -private: - void start_p3dcert(); - - void spawn_wait_thread(); - void join_wait_thread(); - - void write_env() const; - -private: - // These methods run only within the read thread. - THREAD_CALLBACK_DECLARATION(P3DAuthSession, wt_thread_run); - void wt_thread_run(); - -#ifdef _WIN32 - HANDLE win_create_process(); -#else - int posix_create_process(); -#endif - -private: - P3DInstance *_inst; - std::string _start_dir; - - // This information is passed to create_process(). - P3DTemporaryFile *_cert_filename; - std::string _cert_dir; - std::string _p3dcert_exe; - std::string _env; - -#ifdef _WIN32 - HANDLE _p3dcert_handle; -#else - int _p3dcert_pid; -#endif - bool _p3dcert_started; - bool _p3dcert_running; - - // The remaining members are manipulated by or for the read thread. - bool _started_wait_thread; - THREAD _wait_thread; -}; - -#include "p3dAuthSession.I" - -#endif diff --git a/direct/src/plugin/p3dBoolObject.cxx b/direct/src/plugin/p3dBoolObject.cxx deleted file mode 100644 index 7740c2f669..0000000000 --- a/direct/src/plugin/p3dBoolObject.cxx +++ /dev/null @@ -1,68 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dBoolObject.cxx - * @author drose - * @date 2009-06-30 - */ - -#include "p3dBoolObject.h" - -/** - * - */ -P3DBoolObject:: -P3DBoolObject(bool value) : _value(value) { -} - -/** - * - */ -P3DBoolObject:: -P3DBoolObject(const P3DBoolObject ©) : - P3DObject(copy), - _value(copy._value) -{ -} - -/** - * Returns the fundamental type of this kind of object. - */ -P3D_object_type P3DBoolObject:: -get_type() { - return P3D_OT_bool; -} - -/** - * Returns the object value coerced to a boolean, if possible. - */ -bool P3DBoolObject:: -get_bool() { - return _value; -} - -/** - * Returns the object value coerced to an integer, if possible. - */ -int P3DBoolObject:: -get_int() { - return _value; -} - -/** - * Fills the indicated C++ string object with the value of this object coerced - * to a string. - */ -void P3DBoolObject:: -make_string(std::string &value) { - if (_value) { - value = "True"; - } else { - value = "False"; - } -} diff --git a/direct/src/plugin/p3dBoolObject.h b/direct/src/plugin/p3dBoolObject.h deleted file mode 100644 index 85eb0a127c..0000000000 --- a/direct/src/plugin/p3dBoolObject.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dBoolObject.h - * @author drose - * @date 2009-06-30 - */ - -#ifndef P3DBOOLOBJECT_H -#define P3DBOOLOBJECT_H - -#include "p3d_plugin_common.h" -#include "p3dObject.h" - -/** - * An object type that contains a boolean value. - */ -class P3DBoolObject : public P3DObject { -public: - P3DBoolObject(bool value); - P3DBoolObject(const P3DBoolObject ©); - -public: - virtual P3D_object_type get_type(); - virtual bool get_bool(); - virtual int get_int(); - virtual void make_string(std::string &value); - -private: - bool _value; -}; - -#endif diff --git a/direct/src/plugin/p3dCInstance.I b/direct/src/plugin/p3dCInstance.I deleted file mode 100644 index 34edfd6b62..0000000000 --- a/direct/src/plugin/p3dCInstance.I +++ /dev/null @@ -1,20 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dCInstance.I - * @author drose - * @date 2009-06-08 - */ - -/** - * Returns a unique integer for each instance in the system. - */ -inline int P3DCInstance:: -get_instance_id() const { - return _instance_id; -} diff --git a/direct/src/plugin/p3dCInstance.cxx b/direct/src/plugin/p3dCInstance.cxx deleted file mode 100644 index 0f77925b22..0000000000 --- a/direct/src/plugin/p3dCInstance.cxx +++ /dev/null @@ -1,32 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dCInstance.cxx - * @author drose - * @date 2009-06-08 - */ - -#include "p3dCInstance.h" - - -/** - * Constructs a new Instance from an XML description. - */ -P3DCInstance:: -P3DCInstance(TiXmlElement *xinstance) : - _func(nullptr) -{ - xinstance->Attribute("instance_id", &_instance_id); -} - -/** - * - */ -P3DCInstance:: -~P3DCInstance() { -} diff --git a/direct/src/plugin/p3dCInstance.h b/direct/src/plugin/p3dCInstance.h deleted file mode 100644 index 8d0ebc29c2..0000000000 --- a/direct/src/plugin/p3dCInstance.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dCInstance.h - * @author drose - * @date 2009-06-08 - */ - -#ifndef P3DCINSTANCE_H -#define P3DCINSTANCE_H - -#include "pandabase.h" - -#include "p3d_plugin.h" -#include "pvector.h" -#include "get_tinyxml.h" -#include "windowHandle.h" - -class P3DSession; - -/** - * This is an instance of a Panda3D window, as seen in the child-level - * process. - */ -class P3DCInstance : public P3D_instance { -public: - P3DCInstance(TiXmlElement *xinstance); - ~P3DCInstance(); - - inline int get_instance_id() const; - -public: - PT(WindowHandle) _parent_window_handle; - -private: - P3D_request_ready_func *_func; - - int _instance_id; - - friend class P3DPythonRun; -}; - -#include "p3dCInstance.I" - -#endif diff --git a/direct/src/plugin/p3dCert.cxx b/direct/src/plugin/p3dCert.cxx deleted file mode 100644 index e6868bdb80..0000000000 --- a/direct/src/plugin/p3dCert.cxx +++ /dev/null @@ -1,742 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dCert.cxx - * @author rdb - * @date 2011-03-08 - */ - -#include "p3dCert.h" -#include "p3dCert_strings.h" -#include "wstring_encode.h" -#include "mkdir_complete.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define BUTTON_WIDTH 180 // fit the Russian text -#define BUTTON_SPACE 10 - -#include "ca_bundle_data_src.c" - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include -#include - -#define snprintf sprintf_s -#endif - -#ifdef __APPLE__ -#include -#endif - -using std::cerr; -using std::string; -using std::wstring; - -static LanguageIndex li = LI_default; - -#if defined(_WIN32) -static LanguageIndex detect_language() { - // This function was introduced in Windows Vista; it may not be available on - // older systems. - typedef BOOL (*GUPL)(DWORD, PULONG, PZZWSTR, PULONG); - GUPL pGetUserPreferredUILanguages = (GUPL)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), - TEXT("GetUserPreferredUILanguages")); - if (pGetUserPreferredUILanguages != nullptr) { - ULONG num_langs = 0; - ULONG buffer_size = 0; - pGetUserPreferredUILanguages(8, &num_langs, nullptr, &buffer_size); - PZZWSTR buffer = (PZZWSTR)_alloca(buffer_size); - if (pGetUserPreferredUILanguages(8, &num_langs, buffer, &buffer_size)) { - for (ULONG i = 0; i < num_langs; ++i) { - size_t len = wcslen(buffer); - if (len >= 2 && (buffer[2] == 0 || buffer[2] == L'-')) { - // It may be a two-letter code; match it in our list. - for (int j = 0; j < LI_COUNT; ++j) { - const char *lang_code = language_codes[j]; - if (lang_code != nullptr && lang_code[0] == buffer[0] && - lang_code[1] == buffer[1]) { - return (LanguageIndex)j; - } - } - } - buffer += len + 1; - } - } - } - - // Fall back to the old Windows XP function. - LANGID lang = GetUserDefaultUILanguage() & 0x3ff; - if (lang == 0) { - return LI_default; - } - - for (int i = 0; i < LI_COUNT; ++i) { - if (language_ids[i] != 0 && language_ids[i] == lang) { - return (LanguageIndex)i; - } - } - return LI_default; -} - -#elif defined(__APPLE__) -static LanguageIndex detect_language() { - // Get and iterate through the list of preferred languages. - CFArrayRef langs = CFLocaleCopyPreferredLanguages(); - CFIndex num_langs = CFArrayGetCount(langs); - - for (long i = 0; i < num_langs; ++i) { - CFStringRef lang = (CFStringRef)CFArrayGetValueAtIndex(langs, i); - - CFIndex length = CFStringGetLength(lang); - if (length < 2) { - continue; - } - - CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; - char *buffer = (char *)alloca(max_size); - if (!CFStringGetCString(lang, buffer, max_size, kCFStringEncodingUTF8)) { - continue; - } - - if (isalnum(buffer[2])) { - // It's not a two-letter code. - continue; - } - - // See if we support this language. - for (int j = 0; j < LI_COUNT; ++j) { - const char *lang_code = language_codes[j]; - if (lang_code != nullptr && strncasecmp(buffer, lang_code, 2) == 0) { - CFRelease(langs); - return (LanguageIndex)j; - } - } - } - - CFRelease(langs); - return LI_default; -} - -#else -static LanguageIndex detect_language() { - // First consult the LANGUAGE variable, which is a GNU extension that can - // contain multiple languages in order of preference. - const char *lang = getenv("LANGUAGE"); - while (lang != nullptr && lang[0] != 0) { - size_t len; - const char *next = strchr(lang, ':'); - if (next == nullptr) { - len = strlen(lang); - } else { - len = (next - lang); - ++next; - } - - if (len >= 2 && !isalnum(lang[2])) { - // It may be a two-letter language code; match it in our list. - for (int i = 0; i < LI_COUNT; ++i) { - const char *lang_code = language_codes[i]; - if (lang_code != nullptr && strncasecmp(lang, lang_code, 2) == 0) { - return (LanguageIndex)i; - } - } - } - - lang = next; - } - - // Fall back to the C locale functions. - setlocale(LC_ALL, ""); - lang = setlocale(LC_MESSAGES, nullptr); - - if (lang == nullptr || lang[0] == 0 || strcmp(lang, "C") == 0) { - // Try the LANG variable. - lang = getenv("LANG"); - } - - if (lang == nullptr || strlen(lang) < 2 || isalnum(lang[2])) { - // Couldn't extract a meaningful two-letter code. - return LI_default; - } - - // It may be a two-letter language code; match it in our list. - for (int i = 0; i < LI_COUNT; ++i) { - const char *lang_code = language_codes[i]; - if (lang_code != nullptr && strncasecmp(lang, lang_code, 2) == 0) { - return (LanguageIndex)i; - } - } - return LI_default; -} -#endif - -#ifdef _WIN32 -int WINAPI -wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { - OpenSSL_add_all_algorithms(); - - LPWSTR *argv; - int argc; - argv = CommandLineToArgvW(pCmdLine, &argc); - if (argv == nullptr || argc != 2) { - cerr << "usage: p3dcert cert_filename cert_dir\n"; - return 1; - } - - li = detect_language(); - - wstring cert_filename (argv[0]); - wstring cert_dir (argv[1]); - LocalFree(argv); - - AuthDialog *dialog = new AuthDialog(cert_filename, cert_dir); - dialog->show(); - - return Fl::run(); -} - -#else // _WIN32 -int main(int argc, char **argv) { - OpenSSL_add_all_algorithms(); - - if (argc != 3) { - cerr << "usage: p3dcert cert_filename cert_dir\n"; - return 1; - } - - li = detect_language(); - - string cert_filename (argv[1]); - string cert_dir (argv[2]); - - AuthDialog *dialog = new AuthDialog(cert_filename, cert_dir); - dialog->show(1, argv); - - return Fl::run(); -} -#endif // _WIN32 - -/** - * - */ -#ifdef _WIN32 -AuthDialog:: -AuthDialog(const wstring &cert_filename, const wstring &cert_dir) : -#else -AuthDialog:: -AuthDialog(const string &cert_filename, const string &cert_dir) : -#endif - Fl_Window(435, 242, new_application_title[li]), - _cert_dir(cert_dir) -{ - _view_cert_dialog = nullptr; - - _cert = nullptr; - _stack = nullptr; - _verify_result = -1; - - // Center the window on the screen. - position((Fl::w() - w()) / 2, (Fl::h() - h()) / 2); - set_modal(); - - read_cert_file(cert_filename); - get_friendly_name(); - verify_cert(); - layout(); -} - -/** - * - */ -AuthDialog:: -~AuthDialog() { - if (_view_cert_dialog != nullptr) { - _view_cert_dialog->hide(); - } - - if (_cert != nullptr) { - X509_free(_cert); - _cert = nullptr; - } - if (_stack != nullptr) { - sk_X509_free(_stack); - _stack = nullptr; - } -} - -/** - * The user clicks the "Run" button. - */ -void AuthDialog:: -run_clicked(Fl_Widget *w, void *data) { - AuthDialog *dlg = (AuthDialog *) data; - dlg->approve_cert(); -} - -/** - * The user clicks the "View Certificate" button. - */ -void AuthDialog:: -view_cert_clicked(Fl_Widget *w, void *data) { - AuthDialog *dlg = (AuthDialog *) data; - - if (dlg->_view_cert_dialog != nullptr) { - dlg->_view_cert_dialog->hide(); - } - dlg->hide(); - dlg->_view_cert_dialog = new ViewCertDialog(dlg, dlg->_cert); - dlg->_view_cert_dialog->show(); -} - -/** - * The user clicks the "Cancel" button. - */ -void AuthDialog:: -cancel_clicked(Fl_Widget *w, void *data) { - AuthDialog *dlg = (AuthDialog *) data; - dlg->hide(); -} - -/** - * Writes the certificate into the _cert_dir, so that it will be found by the - * P3DInstanceManager and known to be approved. - */ -void AuthDialog:: -approve_cert() { - assert(_cert != nullptr); - - // Make sure the directory exists. -#ifdef _WIN32 - mkdir_complete_w(_cert_dir, cerr); -#else - mkdir_complete(_cert_dir, cerr); -#endif - - // Look for an unused filename. - int i = 1; - size_t buf_length = _cert_dir.length() + 100; - - // Sure, there's a slight race condition right now: another process might - // attempt to create the same filename. So what. - FILE *fp = nullptr; - -#ifdef _WIN32 - wchar_t *buf = new wchar_t[buf_length]; - - while (true) { - swprintf(buf, L"%s/p%d.crt", _cert_dir.c_str(), i); - assert(wcslen(buf) < buf_length); - - // Check if it already exists. If not, take it. - if (GetFileAttributesW(buf) == -1) { - break; - } - ++i; - } - fp = _wfopen(buf, L"w"); - -#else // _WIN32 - char *buf = new char[buf_length]; - - while (true) { - sprintf(buf, "%s/p%d.crt", _cert_dir.c_str(), i); - assert(strlen(buf) < buf_length); - - // Check if it already exists. If not, take it. - struct stat statbuf; - if (stat(buf, &statbuf) != 0) { - break; - } - ++i; - } - fp = fopen(buf, "w"); -#endif // _WIN32 - - if (fp != nullptr) { - PEM_write_X509(fp, _cert); - fclose(fp); - } - - hide(); -} - -/** - * Reads the list of certificates in the pem filename passed on the command - * line into _cert and _stack. - */ -#ifdef _WIN32 -void AuthDialog:: -read_cert_file(const wstring &cert_filename) { -#else -void AuthDialog:: -read_cert_file(const string &cert_filename) { -#endif - - FILE *fp = nullptr; -#ifdef _WIN32 - fp = _wfopen(cert_filename.c_str(), L"r"); -#else // _WIN32 - fp = fopen(cert_filename.c_str(), "r"); -#endif // _WIN32 - - if (fp == nullptr) { -#ifdef _WIN32 - wcerr << L"Couldn't read " << cert_filename.c_str() << L"\n"; -#else - cerr << "Couldn't read " << cert_filename.c_str() << "\n"; -#endif - return; - } - _cert = PEM_read_X509(fp, nullptr, nullptr, (void *)""); - if (_cert == nullptr) { -#ifdef _WIN32 - wcerr << L"Could not read certificate in " << cert_filename.c_str() << L".\n"; -#else - cerr << "Could not read certificate in " << cert_filename.c_str() << ".\n"; -#endif - fclose(fp); - return; - } - - // Build up a STACK of the remaining certificates in the file. - _stack = sk_X509_new(nullptr); - X509 *c = PEM_read_X509(fp, nullptr, nullptr, (void *)""); - while (c != nullptr) { - sk_X509_push(_stack, c); - c = PEM_read_X509(fp, nullptr, nullptr, (void *)""); - } - - fclose(fp); -} - -/** - * Extracts the "friendly name" from the certificate: the common name or email - * name. - */ -void AuthDialog:: -get_friendly_name() { - if (_cert == nullptr) { - _friendly_name.clear(); - return; - } - - - static const int nid_choices[] = { - NID_pkcs9_emailAddress, - NID_commonName, - -1, - }; - - // Choose the first NID that exists on the cert. - for (int ni = 0; nid_choices[ni] != -1; ++ni) { - int nid = nid_choices[ni]; - - // A complex OpenSSL interface to extract out the name in utf-8. - X509_NAME *xname = X509_get_subject_name(_cert); - if (xname != nullptr) { - int pos = X509_NAME_get_index_by_NID(xname, nid, -1); - if (pos != -1) { - // We just get the first common name. I guess it's possible to have - // more than one; not sure what that means in this context. - X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos); - if (xentry != nullptr) { - ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry); - if (data != nullptr) { - // We use "print" to dump the output to a memory BIO. Is there an - // easier way to decode the ASN1_STRING? Curse these incomplete - // docs. - BIO *mbio = BIO_new(BIO_s_mem()); - ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB); - - char *pp; - long pp_size = BIO_get_mem_data(mbio, &pp); - _friendly_name = string(pp, pp_size); - BIO_free(mbio); - return; - } - } - } - } - } -} - -/** - * Checks whether the certificate is valid by the chain and initializes - * _verify_status accordingly. - */ -void AuthDialog:: -verify_cert() { - if (_cert == nullptr) { - _verify_result = -1; - return; - } - - // Create a new X509_STORE. - X509_STORE *store = X509_STORE_new(); - X509_STORE_set_default_paths(store); - - // Add in the well-known certificate authorities. - load_certificates_from_der_ram(store, (const char *)ca_bundle_data, ca_bundle_data_len); - - // Create the X509_STORE_CTX for verifying the cert and chain. - X509_STORE_CTX *ctx = X509_STORE_CTX_new(); - X509_STORE_CTX_init(ctx, store, _cert, _stack); - X509_STORE_CTX_set_cert(ctx, _cert); - - if (X509_verify_cert(ctx)) { - _verify_result = 0; - } else { - _verify_result = X509_STORE_CTX_get_error(ctx); - } - - X509_STORE_CTX_free(ctx); - - X509_STORE_free(store); - - cerr << "Got certificate from " << _friendly_name.c_str() - << ", verify_result = " << _verify_result << "\n"; -} - -/** - * Reads a chain of trusted certificates from the indicated data buffer and - * adds them to the X509_STORE object. The data buffer should be DER- - * formatted. Returns the number of certificates read on success, or 0 on - * failure. - * - * You should call this only with trusted, locally-stored certificates; not - * with certificates received from an untrusted source. - */ -int AuthDialog:: -load_certificates_from_der_ram(X509_STORE *store, - const char *data, size_t data_size) { - int count = 0; - -#if OPENSSL_VERSION_NUMBER >= 0x00908000L - // Beginning in 0.9.8, d2i_X509() accepted a const unsigned char **. - const unsigned char *bp, *bp_end; -#else - // Prior to 0.9.8, d2i_X509() accepted an unsigned char **. - unsigned char *bp, *bp_end; -#endif - - bp = (unsigned char *)data; - bp_end = bp + data_size; - X509 *x509 = d2i_X509(nullptr, &bp, bp_end - bp); - while (x509 != nullptr) { - X509_STORE_add_cert(store, x509); - ++count; - x509 = d2i_X509(nullptr, &bp, bp_end - bp); - } - - return count; -} - -/** - * Arranges the text and controls within the dialog. - */ -void AuthDialog:: -layout() { - get_text(_header, sizeof _header, _text, sizeof _text); - - // Now replicate out any @ signs in the text to avoid FlTk's escape - // sequences. - int j = 0; - for (int i = 0; _text[i] != '\0'; ++i) { - _text_clean[j++] = _text[i]; - if (_text[i] == '@') { - _text_clean[j++] = _text[i]; - } - } - _text_clean[j] = '\0'; - assert(strlen(_text_clean) < sizeof(_text_clean)); - - int next_y = 35; - if (strlen(_header) > 0) { - Fl_Box *text0 = new Fl_Box(w() / 2, next_y, 0, 25, _header); - text0->align(FL_ALIGN_TOP | FL_ALIGN_CENTER); - text0->labelfont(FL_BOLD); - text0->labelsize(text0->labelsize() * 1.5); - next_y += 25; - } - - Fl_Box *text1 = new Fl_Box(17, next_y, 400, 180, _text_clean); - text1->align(FL_ALIGN_TOP | FL_ALIGN_INSIDE | FL_ALIGN_WRAP); - next_y += 180; - - short nbuttons = 1; - if (_cert != nullptr) { - nbuttons++; - if (_verify_result == 0) { - nbuttons++; - } - } - short bx = (w() - nbuttons * BUTTON_WIDTH - (nbuttons - 1) * BUTTON_SPACE) / 2; - - if (_verify_result == 0 && _cert != nullptr) { - Fl_Return_Button *run_button = new Fl_Return_Button(bx, next_y, BUTTON_WIDTH, 25, run_title[li]); - run_button->callback(this->run_clicked, this); - bx += BUTTON_WIDTH + BUTTON_SPACE; - } - - if (_cert != nullptr) { - Fl_Button *view_button = new Fl_Button(bx, next_y, BUTTON_WIDTH, 25, show_cert_title[li]); - view_button->callback(this->view_cert_clicked, this); - bx += BUTTON_WIDTH + BUTTON_SPACE; - } - - Fl_Button *cancel_button; - cancel_button = new Fl_Button(bx, next_y, BUTTON_WIDTH, 25, cancel_title[li]); - cancel_button->callback(this->cancel_clicked, this); - - next_y += 42; - size(435, next_y); - - end(); - set_modal(); -} - -/** - * Fills in the text appropriate to display in the dialog box, based on the - * certificate read so far. - */ -void AuthDialog:: -get_text(char *header, size_t hlen, char *text, size_t tlen) { - switch (_verify_result) { - case -1: - strncpy(header, no_cert_title[li], hlen); - strncpy(text, no_cert_text[li], tlen); - break; - - case 0: - snprintf(text, tlen, verified_cert_text[li], _friendly_name.c_str(), - _friendly_name.c_str(), _friendly_name.c_str()); - break; - - case X509_V_ERR_CERT_NOT_YET_VALID: - case X509_V_ERR_CERT_HAS_EXPIRED: - case X509_V_ERR_CRL_NOT_YET_VALID: - case X509_V_ERR_CRL_HAS_EXPIRED: - strncpy(header, expired_cert_title[li], hlen); - snprintf(text, tlen, expired_cert_text[li], _friendly_name.c_str()); - break; - - case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: - strncpy(header, unverified_cert_title[li], hlen); - strncpy(text, unknown_auth_cert_text[li], tlen); - break; - - case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: - case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: - strncpy(header, unverified_cert_title[li], hlen); - strncpy(text, self_signed_cert_text[li], tlen); - break; - - default: - strncpy(header, unverified_cert_title[li], hlen); - snprintf(text, tlen, generic_error_cert_text[li], _verify_result); - } -} - -/** - * - */ -ViewCertDialog:: -ViewCertDialog(AuthDialog *auth_dialog, X509 *cert) : - Fl_Window(600, 400, show_cert_title[li]), - _auth_dialog(auth_dialog), - _cert(cert) -{ - // Center the window on the screen. - position((Fl::w() - w()) / 2, (Fl::h() - h()) / 2); - set_modal(); - - layout(); -} - -/** - * - */ -ViewCertDialog:: -~ViewCertDialog() { - if (_auth_dialog != nullptr) { - _auth_dialog->_view_cert_dialog = nullptr; - } -} - -/** - * The user clicks the "Run" button. - */ -void ViewCertDialog:: -run_clicked(Fl_Widget *w, void *data) { - ViewCertDialog *dlg = (ViewCertDialog *) data; - if (dlg->_auth_dialog != nullptr){ - dlg->_auth_dialog->approve_cert(); - } - dlg->hide(); -} - -/** - * The user clicks the "Cancel" button. - */ -void ViewCertDialog:: -cancel_clicked(Fl_Widget *w, void *data) { - ViewCertDialog *dlg = (ViewCertDialog *) data; - if (dlg->_auth_dialog != nullptr){ - dlg->_auth_dialog->hide(); - } - dlg->hide(); -} - -/** - * Arranges the text and controls within the dialog. - */ -void ViewCertDialog:: -layout() { - // Format the certificate text for display in the dialog. - assert(_cert != nullptr); - - BIO *mbio = BIO_new(BIO_s_mem()); - X509_print(mbio, _cert); - - char *pp; - long pp_size = BIO_get_mem_data(mbio, &pp); - string cert_body(pp, pp_size); - BIO_free(mbio); - - Fl_Text_Buffer *buffer = new Fl_Text_Buffer; - buffer->append(cert_body.c_str()); - - Fl_Text_Display *text = new Fl_Text_Display(20, 20, 565, 320); - text->buffer(buffer); - - short bx = (w() - BUTTON_WIDTH * 2 - BUTTON_SPACE) / 2; - - Fl_Return_Button *run_button = new Fl_Return_Button(bx, 360, BUTTON_WIDTH, 25, run_title[li]); - run_button->callback(this->run_clicked, this); - - bx += BUTTON_WIDTH + BUTTON_SPACE; - - Fl_Button *cancel_button = new Fl_Button(bx, 360, BUTTON_WIDTH, 25, cancel_title[li]); - cancel_button->callback(this->cancel_clicked, this); - - end(); - set_modal(); -} diff --git a/direct/src/plugin/p3dCert.h b/direct/src/plugin/p3dCert.h deleted file mode 100644 index c17c8129e8..0000000000 --- a/direct/src/plugin/p3dCert.h +++ /dev/null @@ -1,117 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dCert.h - * @author rdb - * @date 2011-03-08 - */ - -#ifndef P3DCERT_H -#define P3DCERT_H - -#include -#include - -#define OPENSSL_NO_KRB5 -#include -#include -#include - -#include -#include -#include - -class ViewCertDialog; - -#ifndef STACK_OF - // At some point, presumably in 1.0.0, openssl went to the STACK_OF() macro - // system to typedef the contents of a stack. Unfortunately, that new API - // is different. We define some macros here here for backward compatiblity. - #define STACK_OF(type) STACK - #define sk_X509_push(stack, item) sk_push((stack), (char *)(item)) - #define sk_X509_free(stack) sk_free(stack) - #define sk_X509_new(cmp) sk_new(cmp) -#endif - -/** - * This is the primary dialog of this application. - * - * This dialog is presented to the user when he/she clicks on the red - * authorization button on the splash window. It tells the user the status of - * the application's signature, and invites the user to approve the signature - * or cancel. - */ -class AuthDialog : public Fl_Window { -public: -#ifdef _WIN32 - AuthDialog(const std::wstring &cert_filename, const std::wstring &cert_dir); -#else - AuthDialog(const std::string &cert_filename, const std::string &cert_dir); -#endif - virtual ~AuthDialog(); - - static void run_clicked(Fl_Widget *w, void *data); - static void view_cert_clicked(Fl_Widget *w, void *data); - static void cancel_clicked(Fl_Widget *w, void *data); - - void approve_cert(); - -private: -#ifdef _WIN32 - void read_cert_file(const std::wstring &cert_filename); -#else - void read_cert_file(const std::string &cert_filename); -#endif - void get_friendly_name(); - void verify_cert(); - int load_certificates_from_der_ram(X509_STORE *store, - const char *data, size_t data_size); - - void layout(); - void get_text(char *header, size_t hlen, char *text, size_t tlen); - -public: - ViewCertDialog *_view_cert_dialog; - -private: -#ifdef _WIN32 - std::wstring _cert_dir; -#else - std::string _cert_dir; -#endif - X509 *_cert; - STACK_OF(X509) *_stack; - - char _header[64]; - char _text[1024]; - char _text_clean[2048]; - - std::string _friendly_name; - int _verify_result; -}; - -/** - * This is the detailed view of the particular certificate. - */ -class ViewCertDialog : public Fl_Window { -public: - ViewCertDialog(AuthDialog *auth_dialog, X509 *cert); - virtual ~ViewCertDialog(); - - static void run_clicked(Fl_Widget *w, void *data); - static void cancel_clicked(Fl_Widget *w, void *data); - -private: - void layout(); - -private: - AuthDialog *_auth_dialog; - X509 *_cert; -}; - -#endif diff --git a/direct/src/plugin/p3dCert_strings.cxx b/direct/src/plugin/p3dCert_strings.cxx deleted file mode 100644 index 575dee34b0..0000000000 --- a/direct/src/plugin/p3dCert_strings.cxx +++ /dev/null @@ -1,567 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dCert_strings.cxx - * @author rdb - * @date 2015-03-25 - */ - -#include "p3dCert_strings.h" - -// Translations kindly provided by: eng: drwr nld: rdb deu: Sebastian Hoffmann -// spa: Imanol Celaya ita: Flavio Clava rus: montreal - -const char *language_codes[LI_COUNT] = - {"en", "nl", "de", "es", "it", "eo", "ru"}; - -// https:msdn.microsoft.comen- -// uslibrarywindowsdesktopdd318693%28v=vs.85%29.aspx -const unsigned char language_ids[LI_COUNT] = - {0x09, 0x13, 0x07, 0x0A, 0x10, 0x8F, 0x19}; - -const char * -run_title[LI_COUNT] = { - "Run", - "Uitvoeren", - "Starten", - "Ejecutar", - "Lancia", - "Lan\304\211i", - "\320\227\320\260\320\277\321\203\321\201\321\202\320\270\321\202\321\214", -}; - -const char * -cancel_title[LI_COUNT] = { - "Cancel", - "Annuleren", - "Abbrechen", - "Cancelar", - "Cancella", - "Nuligi", - "\320\236\321\202\320\274\320\265\320\275\320\260", -}; - -const char * -show_cert_title[LI_COUNT] = { - "Show Certificate", - "Toon Certificaat", - "Zertifikat anzeigen", - "Mostrar certificado", - "Mostra certificato", - "Montri Ateston", - "\320\237\320\276\320\272\320\260\320\267\320\260\321\202\321\214 \321\201" - "\320\265\321\200\321\202\320\270\321\204\320\270\320\272\320\260\321\202", -}; - -const char * -new_application_title[LI_COUNT] = { - "New Panda3D Application", - "Nieuwe Panda3D Applicatie", - "Neue Panda3D-Anwendung", - "Nueva aplicaci\303\263n de Panda3D", - "Nuova applicazione Panda3D", - "Nova aplika\304\265o de Panda3D", - "\320\235\320\276\320\262\320\276\320\265 Panda3D-\320\277\321\200\320\270" - "\320\273\320\276\320\266\320\265\320\275\320\270\320\265", -}; - -const char * -no_cert_title[LI_COUNT] = { - "No signature!", - "Geen handtekening!", - "Keine Signatur!", - "Sin firma!", - "Nessuna firma!", - "Neniu subskribo!", - "\320\235\320\265\321\202 \320\277\320\276\320\264\320\277\320\270\321\201" - "\320\270!", -}; - -const char * -unverified_cert_title[LI_COUNT] = { - "Unverified signature!", - "Ongeverifieerde handtekening!", - "Unbest\303\244tigte Signatur!", - "Firma sin verificar!", - "Firma non verificata!", - "Nekontrolita subskribo!", - "\320\235\320\265\320\277\321\200\320\276\320\262\320\265\321\200\320\265" - "\320\275\320\275\320\260\321\217 \320\277\320\276\320\264\320\277\320\270" - "\321\201\321\214!", -}; - -const char * -expired_cert_title[LI_COUNT] = { - "Expired signature!", - "Verlopen handtekening!", - "Abgelaufene Signatur!", - "Firma caducada!", - "Firma scaduta!", - "Eksvalidi\304\235inta subskribo!", - "\320\241\321\200\320\276\320\272 \320\264\320\265\320\271\321\201\321\202" - "\320\262\320\270\321\217 \320\277\320\276\320\264\320\277\320\270\321\201" - "\320\270 \320\270\321\201\321\202\321\221\320\272!", -}; - - -const char * -self_signed_cert_text[LI_COUNT] = { - // eng - "This Panda3D application uses a self-signed certificate. " - "This means the author's name can't be verified, and you have " - "no way of knowing for sure who wrote it.\n" - "\n" - "We recommend you click Cancel to avoid running this application.", - - // nld - "Deze Panda3D applicatie gebruikt een zelf-getekend certificaat. " - "Dit betekent dat de auteursnaam niet kan worden geverifieerd, en het " - "niet zeker is of de applicatie te vertrouwen is.\n" - "\n" - "Het is aanbevolen om op Annuleren te klikken om de applicatie af te " - "sluiten.", - - // deu - "Diese Panda3D-Anwendung benutzt ein selbst-signiertes Zertifikat. Dies " - "bedeutet, dass weder der Name des Autors \303\274berpr\303\274ft werden " - "kann, noch dass garantiert werden kann, dass tats\303\244chlich die " - "angegebene Person diese Anwendung geschrieben hat.\n" - "\n" - "Wir empfehlen, dass Sie Abbrechen dr\303\274cken um diese Anwendung nicht " - "auszuf\303\274hren.", - - // spa - "Esta aplicaci\303\263n de Panda3D usa un certificado autofirmado. " - "Esto significa que el nombre del autor no puede ser verificado y no se " - "puede conocer seguro quien la ha escrito.\n" - "\n" - "Se recomienda cancelar para evitar ejecutar esta aplicaci\303\263n.", - - // ita - "Questa applicazione Panda3D usa un certificato autofirmato. Ci\303\262 " - "significa che il nome dell'autore non pu\303\262 essere verificato, e " - "che non hai modo di assicurarti circa chi la abbia scritta.\n" - "\n" - "Raccomandiamo di cliccare su Cancella per evitare di lanciare questa " - "applicazione.", - - // epo - "\304\210i tiu aplika\304\265o de Panda3D uzas memsubskribitan ateston. " - "Tio signifas ke la nomo de la verkanto ne povas esti kontrolita, kaj vi " - "ne havas certan scimanieron pri la vera verkanto de la aplika\304\265o.\n" - "\n" - "Ni rekomendas ke vi premas la butonon 'Nuligi' por eviti lan\304\211on de " - "\304\211i tiu aplika\304\265o.", - - // rus - "\320\255\321\202\320\276 \320\277\321\200\320\270\320\273\320\276\320\266" - "\320\265\320\275\320\270\320\265 \320\270\321\201\320\277\320\276\320\273" - "\321\214\320\267\321\203\320\265\321\202 \321\201\320\260\320\274\320\276" - "\320\267\320\260\320\262\320\265\321\200\320\265\320\275\320\275\321\213" - "\320\271 \321\201\320\265\321\200\321\202\320\270\321\204\320\270\320\272" - "\320\260\321\202. \320\255\321\202\320\276 \320\276\320\267\320\275" - "\320\260\321\207\320\260\320\265\321\202, \321\207\321\202\320\276 " - "\320\270\320\274\321\217 \320\260\320\262\321\202\320\276\321\200\320\260 " - "\320\275\320\265 \320\274\320\276\320\266\320\265\321\202 \320\261\321\213" - "\321\202\321\214 \320\277\320\276\320\264\321\202\320\262\320\265\320\266" - "\320\264\320\265\320\275\320\276, \320\270 \320\262\321\213 \320\275" - "\320\265 \320\274\320\276\320\266\320\265\321\202\320\265 \320\267\320\275" - "\320\260\321\202\321\214, \320\272\321\202\320\276 \320\275\320\260" - "\320\277\320\270\321\201\320\260\320\273 \321\215\321\202\320\276 \320\277" - "\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265.\n" - "\n" - "\320\234\321\213 \321\200\320\265\320\272\320\276\320\274\320\265\320\275" - "\320\264\321\203\320\265\320\274 \320\262\320\260\320\274 \320\275\320\260" - "\320\266\320\260\321\202\321\214 \"\320\236\321\202\320\274\320\265" - "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277" - "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 " - "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270" - "\320\265.", -}; - - -const char * -unknown_auth_cert_text[LI_COUNT] = { - "This Panda3D application has been signed, but we don't recognize " - "the authority that verifies the signature. This means the author's " - "name can't be verified, and you have no way of knowing " - "for sure who wrote it.\n" - "\n" - "We recommend you click Cancel to avoid running this application.", - - // nld - "Deze Panda3D applicatie is ondertekend, maar de certificaatautoriteit " - "die het certificaat heeft uitgegeven wordt niet herkend. Dit betekent " - "dat de auteursnaam niet te vertrouwen is, en het niet zeker is wie de " - "applicatie gemaakt heeft.\n" - "\n" - "Het is aanbevolen om op Annuleren te klikken om de applicatie af te " - "sluiten.", - - // deu - "Diese Panda3D-Anwendung wurde signiert, aber die " - "\303\234berpr\303\274fungsstelle wurde nicht anerkannt. Dies bedeutet, " - "dass weder der Name des Autors \303\274berpr\303\274ft werden kann, noch " - "dass garantiert werden kann, dass tats\303\244chlich die angegebene " - "Person diese Anwendung geschrieben hat.\n" - "\n" - "Wir empfehlen, dass Sie Abbrechen dr\303\274cken um diese Anwendung nicht " - "auszuf\303\274hren.", - - // spa - "Esta aplicaci\303\263n de Panda3D esta firmada, pero no reconocemos la " - "autoridad que la verifica. Esto significa que el nombre del autor no " - "puede ser verificado y no se puede conocer seguro quien la ha escrito.\n" - "\n" - "Se recomienda cancelar para evitar ejecutar esta aplicaci\303\263n.", - - // ita - "Questa applicazione Panda3D \303\250 stata firmata, ma non riconosciamo " - "l'autorit\303\240 che verifica la firma. Ci\303\262 significa che il " - "nome dell'autore non pu\303\262 essere verificato, e che non hai modo " - "di assicurarti circa chi la abbia scritta.\n" - "\n" - "Raccomandiamo di cliccare su Cancella per evitare di lanciare questa " - "applicazione.", - - // epo - "\304\210i tiu aplika\304\265o estas subskribita, sed ni ne rekonas " - "la a\305\255toritaton, kiu kontrolas la subskribon. Tio signifas ke la " - "nomo de la verkanto ne povas esti konfidata, kaj vi ne havas certan " - "scimanieron pri la vera verkanto de la aplika\304\265o.\n" - "\n" - "Ni rekomendas ke vi premas la butonon 'Nuligi' por eviti lan\304\211on " - "de \304\211i tiu aplika\304\265o.", - - // rus - "\320\243 \321\215\321\202\320\276\320\263\320\276 \320\277\321\200\320\270" - "\320\273\320\276\320\266\320\265\320\275\320\270\321\217 \320\265\321\201" - "\321\202\321\214 \320\277\320\276\320\264\320\277\320\270\321\201\321\214," - " \320\277\320\276 \320\272\320\276\321\202\320\276\321\200\320\276\320\271" - " \320\274\321\213 \320\275\320\265 \320\274\320\276\320\266\320\265" - "\320\274 \321\200\320\260\321\201\320\277\320\276\320\267\320\275\320\260" - "\321\202\321\214 \320\262\320\273\320\260\320\264\320\265\320\273\321\214" - "\321\206\320\260. \320\255\321\202\320\276 \320\276\320\267\320\275" - "\320\260\321\207\320\260\320\265\321\202, \321\207\321\202\320\276 " - "\320\270\320\274\321\217 \320\260\320\262\321\202\320\276\321\200\320\260 " - "\320\275\320\265 \320\274\320\276\320\266\320\265\321\202 \320\261\321\213" - "\321\202\321\214 \320\276\320\277\321\200\320\265\320\264\320\265\320\273" - "\320\265\320\275\320\276, \320\270 \320\275\320\265\320\262\320\276" - "\320\267\320\274\320\276\320\266\320\275\320\276 \321\202\320\276\321\207" - "\320\275\320\276 \321\203\320\267\320\275\320\260\321\202\321\214, " - "\320\272\321\202\320\276 \320\275\320\260\320\277\320\270\321\201\320\260" - "\320\273 \321\215\321\202\320\276 \320\277\321\200\320\270\320\273\320\276" - "\320\266\320\265\320\275\320\270\320\265.\n" - "\n" - "\320\234\321\213 \321\200\320\265\320\272\320\276\320\274\320\265\320\275" - "\320\264\321\203\320\265\320\274 \320\262\320\260\320\274 \320\275\320\260" - "\320\266\320\260\321\202\321\214 \"\320\236\321\202\320\274\320\265" - "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277" - "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 " - "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270" - "\320\265.", -}; - - -const char * -verified_cert_text[LI_COUNT] = { - // eng - "This Panda3D application has been signed by %s. " - "If you trust %s, then click the Run button below " - "to run this application on your computer. This will also " - "automatically approve this and any other applications signed by " - "%s in the future.\n" - "\n" - "If you are unsure about this application, " - "you should click Cancel instead.", - - // nld - "Deze Panda3D applicatie is ondertekend door %s. " - "Als u %s vertrouwt, klik dan de onderstaande knop Uitvoeren om de applicatie " - "op uw computer uit te voeren. Dit zal ook deze en andere door %s getekende " - "applicaties in het vervolg toestaan.\n" - "\n" - "Als u niet zeker bent over deze applicatie is het aanbevolen om op " - "Annuleren te klikken.", - - // deu - "Diese Panda3D-Anwendung wurde von %s signiert. Falls Sie %s vertrauen, " - "dr\303\274cken Sie den Starten-Knopf, um diese Anwendung auszuf\303\274hren. " - "Zudem werden in der Zukunft diese und alle anderen von %s signierten " - "Anwendungen automatisch als vertrauensw\303\274rdig anerkannt.\n" - "\n" - "Falls Sie sich unsicher \303\274ber diese Anwendung sind, sollten Sie " - "Abbrechen dr\303\274cken.", - - // spa - "Esta aplicaci\303\263n de Panda3D ha sido firmada por %s. Si se considera %s" - "de confianza el bot\303\263n inferior ejecutar\303\241 la aplicaci\303\263n." - "Esto validara esta y cualquier otra applizacion firmada por %s en el futuro.\n" - "\n" - "Si se duda de la aplicaci\303\263nse recomienda cancelar." - - // ita - "Questa applicazione Panda3D \303\250 stata firmata da %s. Se %s \303\250 " - "un'entit\303\240 fidata, allora clicca il bottone Lancia sottostante per " - "lanciare questa applicazione sul tuo computer. Inoltre, ci\303\262 " - "approver\303\240 automaticamente questa e ogni altra applicazione " - "firmata da %s in futuro.\n" - "\n" - "Se non sei sicuro circa questa applicazione, dovresti invece cliccare " - "su Cancella.", - - // epo - "\304\210i tiu aplika\304\265o estas subskribita de %s. " - "Se %s estas fidinda la\305\255 vi, premu la butonon 'Lan\304\211i' sube por " - "lan\304\211i \304\211i tiun aplika\304\265on per via komputilo. Anka\305\255 " - "tio a\305\255tomate estonece aprobos \304\211i tiun kaj alian ajn " - "aplika\304\265on, kiu estas subskribita de %s.\n" - "\n" - "Se vi ne estas certa pri \304\211i tiu aplika\304\265o, " - "vi anstata\305\255e povus premi la butonon 'Nuligi'.", - - // rus - "\320\255\321\202\320\276 \320\277\321\200\320\270\320\273\320\276\320\266" - "\320\265\320\275\320\270\320\265 \320\277\320\276\320\264\320\277\320\270" - "\321\201\320\260\320\275\320\276 %s. " - "\320\225\321\201\320\273\320\270 \320\262\321\213 \320\264\320\276\320\262" - "\320\265\321\200\321\217\320\265\321\202\320\265 %s, \320\275\320\260" - "\320\266\320\274\320\270\321\202\320\265 \320\272\320\275\320\276\320\277" - "\320\272\321\203 \"\320\227\320\260\320\277\321\203\321\201\321\202" - "\320\270\321\202\321\214\" \320\262\320\275\320\270\320\267\321\203, " - "\321\207\321\202\320\276\320\261\321\213 \320\267\320\260\320\277\321\203" - "\321\201\321\202\320\270\321\202\321\214 \321\215\321\202\320\276 \320\277" - "\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265 " - "\320\275\320\260 \320\262\320\260\321\210\320\265\320\274 \320\272\320\276" - "\320\274\320\277\321\214\321\216\321\202\320\265\321\200\320\265. \320\255" - "\321\202\320\276 \321\202\320\260\320\272\320\266\320\265 \320\260\320\262" - "\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201" - "\320\272\320\270 \320\277\320\276\320\264\321\202\320\262\320\265\321\200" - "\320\264\320\270\321\202 \321\215\321\202\320\276 \320\270 \320\264" - "\321\200\321\203\320\263\320\270\320\265 \320\277\321\200\320\270\320\273" - "\320\276\320\266\320\265\320\275\320\270\321\217, \320\275\320\260\320\277" - "\320\270\321\201\320\260\320\275\320\275\321\213\320\265 %s \320\262 " - "\320\261\321\203\320\264\321\203\321\211\320\265\320\274.\n" - "\n" - "\320\225\321\201\320\273\320\270 \320\262\321\213 \320\275\320\265 " - "\321\203\320\262\320\265\321\200\320\265\320\275\321\213 \320\262 \321\215" - "\321\202\320\276\320\274 \320\277\321\200\320\270\320\273\320\276\320\266" - "\320\265\320\275\320\270\320\270, \320\275\320\260\320\266\320\274\320\270" - "\321\202\320\265 \320\272\320\275\320\276\320\277\320\272\321\203 \"" - "\320\236\321\202\320\274\320\265\320\275\320\260\".", -}; - - -const char * -expired_cert_text[LI_COUNT] = { - // eng - "This Panda3D application has been signed by %s, " - "but the certificate has expired.\n" - "\n" - "You should check the current date set on your computer's clock " - "to make sure it is correct.\n" - "\n" - "If your computer's date is correct, we recommend " - "you click Cancel to avoid running this application.", - - // nld - "Deze Panda3D applicatie is ondertekend door %s, maar de geldigheidsdatum " - "van het certificaat is verstreken.\n" - "\n" - "Controleer de datum op uw computerklok om te zorgen dat deze juist is " - "ingesteld.\n" - "\n" - "Als de datum op uw computer juist is, is het aanbevolen om op Annuleren te " - "klikken om de applicatie af te sluiten.", - - // deu - "Diese Anwendung wurde von %s signiert, aber das Zertifikat ist abgelaufen.\n" - "\n" - "Sie sollten die aktuelle Uhrzeit auf Ihrem Computer " - "\303\274berpr\303\274fen, um sicherzugehen, dass sie korrekt ist.\n" - "\n" - "Falls die Uhrzeit auf Ihrem Computer korrekt ist, empfehlen wir Ihnen " - "Abbrechen zu dr\303\274cken.", - - // spa - "Esta aplicaci\303\263n Panda3D ha sido firmada por %s pero el certificado ha" - "caducado.\n" - "\n" - "Se recomienda comprobar la fecha del reloj.\n" - "\n" - "Si la fecha del reloj es correcta se recomienda cancelar.", - - // ita - "Questa applicazione Panda3D \303\250 stata firmata da %s, ma il " - "certificato \303\250 scaduto.\n" - "\n" - "Dovresti controllare la data attuale impostata nell'orologio del tuo " - "computer per assicurarti che sia corretta.\n" - "\n" - "Se la data del tuo computer \303\250 corretta, raccomandiamo di cliccare " - "Cancella per evitare di lanciare questa applicazione." - - // epo - "\304\210i tiu aplika\304\265o de Panda3D estas subskribita de %s, " - "sed la atesto eksvalidi\304\235is.\n" - "\n" - "Vi povus kontroli la aktualan daton, kiu estas agordata sur la horlo\304\235o de " - "via komputilo por certigi ke \304\235i estas korekta.\n" - "\n" - "Se la dato de via komputilo estas korekta, ni rekomendas ke vi premas la " - "butonon 'Nuligi' por eviti lan\304\211on de \304\211i tiu aplika\304\265o.", - - // rus - "\320\255\321\202\320\276 \320\277\321\200\320\270\320\273\320\276\320\266" - "\320\265\320\275\320\270\320\265 \320\277\320\276\320\264\320\277\320\270" - "\321\201\320\260\320\275\320\276 %s, \320\276\320\264\320\275\320\260" - "\320\272\320\276 \321\201\321\200\320\276\320\272 \320\264\320\265\320\271" - "\321\201\321\202\320\262\320\270\321\217 \321\201\320\265\321\200\321\202" - "\320\270\321\204\320\270\320\272\320\260\321\202\320\260 \320\270\321\201" - "\321\202\321\221\320\272.\n" - "\n" - "\320\237\321\200\320\276\320\262\320\265\321\200\321\214\321\202\320\265, " - "\320\277\320\276\320\266\320\260\320\273\321\203\320\271\321\201\321\202" - "\320\260, \320\264\320\260\321\202\321\203 \320\275\320\260 \320\262" - "\320\260\321\210\320\265\320\274 \320\272\320\276\320\274\320\277\321\214" - "\321\216\321\202\320\265\321\200\320\265.\n" - "\n" - "\320\225\321\201\320\273\320\270 \320\275\320\260 \320\262\320\260\321\210" - "\320\265\320\274 \320\272\320\276\320\274\320\277\321\214\321\216\321\202" - "\320\265\321\200\320\265 \321\203\321\201\321\202\320\260\320\275\320\276" - "\320\262\320\273\320\265\320\275\320\276 \320\277\321\200\320\260\320\262" - "\320\270\320\273\321\214\320\275\320\276\320\265 \320\262\321\200\320\265" - "\320\274\321\217, \320\274\321\213 \321\200\320\265\320\272\320\276" - "\320\274\320\265\320\275\320\264\321\203\320\265\320\274 \320\262\320\260" - "\320\274 \320\275\320\260\320\266\320\260\321\202\321\214 \320\272\320\275" - "\320\276\320\277\320\272\321\203 \"\320\236\321\202\320\274\320\265" - "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277" - "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 " - "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270" - "\320\265.", -}; - - -const char * -generic_error_cert_text[LI_COUNT] = { - // eng - "This Panda3D application has been signed, but there is a problem " - "with the certificate (OpenSSL error code %d).\n" - "\n" - "We recommend you click Cancel to avoid running this application.", - - // nld - "Deze Panda3D applicatie is ondertekend, maar er is een probleem " - "met het certificaat opgetreden (OpenSSL foutcode %d).\n" - "\n" - "Het is aanbevolen om op Annuleren te klikken om de applicatie af te " - "sluiten.", - - // deu - "Diese Panda3D-Anwendung wurde signiert, aber es gibt ein Problem mit " - "dem Zertifikat (OpenSSL Fehlercode %d).\n" - "\n" - "Wir empfehlen, dass Sie Abbrechen dr\303\274cken um diese Anwendung nicht " - "auszuf\303\274hren.", - - // spa - "Esta aplicaci\303\263n de Panda3D esta firmada pero hay un problema con el " - "certificado (Error de OpenSSL %d).\n" - "\n" - "Se recomienda cancelar para evitar ejecutar esta aplicaci\303\263n.", - - // ita - "Questa applicazione Panda3D \303\250 stata firmata, ma c'\303\250 un " - "problema col certificato (codice di errore OpenSSL %s).\n" - "\n" - "Raccomandiamo di cliccare su Cancella per evitare di lanciare questa " - "applicazione.", - - // epo - "\304\210i tiu aplika\304\265o de Panda3D estas subskribita, sed la " - "atesto havas problemon (OpenSSL erarkodo %d).\n" - "\n" - "Ni rekomendas ke vi premas la butonon 'Nuligi' por eviti lan\304\211on " - "de \304\211i tiu aplika\304\265o.", - - // rus - "\320\243 \321\215\321\202\320\276\320\263\320\276 \320\277\321\200\320\270" - "\320\273\320\276\320\266\320\265\320\275\320\270\321\217 \320\265\321\201" - "\321\202\321\214 \320\277\320\276\320\264\320\277\320\270\321\201\321\214," - " \320\275\320\276 \320\262\320\276\320\267\320\275\320\270\320\272\320\273" - "\320\260 \320\277\321\200\320\276\320\261\320\273\320\265\320\274\320\260 " - "\321\201 \321\201\320\265\321\200\321\202\320\270\321\204\320\270\320\272" - "\320\260\321\202\320\276\320\274 (\320\232\320\276\320\264 \320\276" - "\321\210\320\270\320\261\320\272\320\270 OpenSSL %d).\n" - "\n" - "\320\234\321\213 \321\200\320\265\320\272\320\276\320\274\320\265\320\275" - "\320\264\321\203\320\265\320\274 \320\262\320\260\320\274 \320\275\320\260" - "\320\266\320\260\321\202\321\214 \"\320\236\321\202\320\274\320\265" - "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277" - "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 " - "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270" - "\320\265.", -}; - - -const char * -no_cert_text[LI_COUNT] = { - "This Panda3D application has not been signed. This means you have " - "no way of knowing for sure who wrote it.\n" - "\n" - "Click Cancel to avoid running this application.", - - // nld - "Deze Panda3D applicatie is niet ondertekend. Dit betekent dat het niet " - "mogelijk is om de auteur te verifi\303\253ren.\n" - "\n" - "Klik op Annuleren om de applicatie af te sluiten.", - - // deu - "Diese Panda3D-Anwendung wurde nicht signiert. Es gibt keine " - "M\303\266glichkeit festzustellen, wer diese entwickelt hat.\n" - "\n" - "Dr\303\274cken Sie Abbrechen, um diese Anwendung nicht auszuf\303\274hren.", - - // spa - "Esta aplicaci\303\263n de Panda3D no esta firmada, no hay forma de conocer " - "quien la ha escrito.\n" - "\n" - "Cancelar para evitar ejecutar la aplicaci\303\263n.", - - // ita - "Questa applicazione Panda3D non \303\250 stata firmata. Ci\303\262 " - "significa che non hai modo di assicurarti circa chi la abbia scritta.\n" - "\n" - "Clicca Cancella per evitare di lanciare questa applicazione.", - - // epo - "\304\210i tiu aplika\304\265o de Panda3D ne estas subskribita. Tio " - "signifas ke vi ne havas certan scimanieron pri la vera verkanto de " - "la aplika\304\265o.\n" - "\n" - "Premu la butonon 'Nuligi' por eviti lan\304\211on de \304\211i tiu " - "aplika\304\265o.", - - // rus - "\320\243 \321\215\321\202\320\276\320\263\320\276 \320\277\321\200\320\270" - "\320\273\320\276\320\266\320\265\320\275\320\270\321\217 \320\275\320\265" - "\321\202 \320\277\320\276\320\264\320\277\320\270\321\201\320\270. " - "\320\255\321\202\320\276 \320\276\320\267\320\275\320\260\321\207\320\260" - "\320\265\321\202, \321\207\321\202\320\276 \320\262\321\213 \320\275" - "\320\265 \320\274\320\276\320\266\320\265\321\202\320\265 \320\267\320\275" - "\320\260\321\202\321\214, \320\272\321\202\320\276 \320\265\320\263" - "\320\276 \320\275\320\260\320\277\320\270\321\201\320\260\320\273.\n" - "\n" - "\320\235\320\260\320\266\320\274\320\270\321\202\320\265 \"\320\236" - "\321\202\320\274\320\265\320\275\320\260\", \321\207\321\202\320\276" - "\320\261\321\213 \320\275\320\265 \320\267\320\260\320\277\321\203\321\201" - "\320\272\320\260\321\202\321\214 \320\277\321\200\320\270\320\273\320\276" - "\320\266\320\265\320\275\320\270\320\265.", -}; diff --git a/direct/src/plugin/p3dCert_strings.h b/direct/src/plugin/p3dCert_strings.h deleted file mode 100644 index 7e6d391e2a..0000000000 --- a/direct/src/plugin/p3dCert_strings.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dCert_strings.h - * @author rdb - * @date 2015-03-25 - */ - -enum LanguageIndex { - LI_en, // English - LI_nl, // Dutch - LI_de, // German - LI_es, // Spanish - LI_it, // Italian - LI_eo, // Esperanto - LI_ru, // Russian - LI_COUNT, - - LI_default = LI_en -}; - -extern const char *language_codes[LI_COUNT]; -extern const unsigned char language_ids[LI_COUNT]; - -extern const char *run_title[LI_COUNT]; -extern const char *cancel_title[LI_COUNT]; -extern const char *show_cert_title[LI_COUNT]; - -extern const char *new_application_title[LI_COUNT]; -extern const char *no_cert_title[LI_COUNT]; -extern const char *unverified_cert_title[LI_COUNT]; -extern const char *expired_cert_title[LI_COUNT]; - -extern const char *self_signed_cert_text[LI_COUNT]; -extern const char *unknown_auth_cert_text[LI_COUNT]; -extern const char *verified_cert_text[LI_COUNT]; -extern const char *expired_cert_text[LI_COUNT]; -extern const char *generic_error_cert_text[LI_COUNT]; -extern const char *no_cert_text[LI_COUNT]; diff --git a/direct/src/plugin/p3dCert_wx.cxx b/direct/src/plugin/p3dCert_wx.cxx deleted file mode 100644 index e7002db46a..0000000000 --- a/direct/src/plugin/p3dCert_wx.cxx +++ /dev/null @@ -1,618 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dCert_wx.cxx - * @author drose - * @date 2009-09-11 - */ - -#include "p3dCert_wx.h" -#include "wstring_encode.h" -#include "mkdir_complete.h" - -#include -#include - -#include "ca_bundle_data_src.c" - -using std::cerr; - -static const wxString -self_signed_cert_text = - _T("This Panda3D application uses a self-signed certificate. ") - _T("This means the author's name can't be verified, and you have ") - _T("no way of knowing for sure who wrote it.\n\n") - - _T("We recommend you click Cancel to avoid running this application."); - -static const wxString -unknown_auth_cert_text = - _T("This Panda3D application has been signed, but we don't recognize ") - _T("the authority that verifies the signature. This means the author's ") - _T("name can't be trusted, and you have no way of knowing ") - _T("for sure who wrote it.\n\n") - - _T("We recommend you click Cancel to avoid running this application."); - -static const wxString -verified_cert_text = - _T("This Panda3D application has been signed by %s. ") - _T("If you trust %s, then click the Run button below ") - _T("to run this application on your computer. This will also ") - _T("automatically approve this and any other applications signed by ") - _T("%s in the future.\n\n") - - _T("If you are unsure about this application, ") - _T("you should click Cancel instead."); - -static const wxString -expired_cert_text = - _T("This Panda3D application has been signed by %s, ") - _T("but the certificate has expired.\n\n") - - _T("You should check the current date set on your computer's clock ") - _T("to make sure it is correct.\n\n") - - _T("If your computer's date is correct, we recommend ") - _T("you click Cancel to avoid running this application."); - -static const wxString -generic_error_cert_text = - _T("This Panda3D application has been signed, but there is a problem ") - _T("with the certificate (OpenSSL error code %d).\n\n") - - _T("We recommend you click Cancel to avoid running this application."); - -static const wxString -no_cert_text = - _T("This Panda3D application has not been signed. This means you have ") - _T("no way of knowing for sure who wrote it.\n\n") - - _T("Click Cancel to avoid running this application."); - -// wxWidgets boilerplate macro to define main() and start up the application. -IMPLEMENT_APP(P3DCertApp) - -/** - * The "main" of a wx application. This is the first entry point. - */ -bool P3DCertApp:: -OnInit() { - // call the base class initialization method, currently it only parses a few - // common command-line options but it could be do more in the future - if (!wxApp::OnInit()) { - return false; - } - - OpenSSL_add_all_algorithms(); - - AuthDialog *dialog = new AuthDialog(_cert_filename, _cert_dir); - SetTopWindow(dialog); - dialog->Show(true); - dialog->SetFocus(); - dialog->Raise(); - - // Return true to enter the main loop and wait for user input. - return true; -} - -/** - * A callback to initialize the parser with the command line options. - */ -void P3DCertApp:: -OnInitCmdLine(wxCmdLineParser &parser) { - parser.AddParam(); - parser.AddParam(); -} - -/** - * A callback after the successful parsing of the command line. - */ -bool P3DCertApp:: -OnCmdLineParsed(wxCmdLineParser &parser) { - _cert_filename = (const char *)parser.GetParam(0).mb_str(); - _cert_dir = (const char *)parser.GetParam(1).mb_str(); - - return true; -} - - -// The event table for AuthDialog. -#define VIEW_CERT_BUTTON (wxID_HIGHEST + 1) -BEGIN_EVENT_TABLE(AuthDialog, wxDialog) - EVT_BUTTON(wxID_OK, AuthDialog::run_clicked) - EVT_BUTTON(VIEW_CERT_BUTTON, AuthDialog::view_cert_clicked) - EVT_BUTTON(wxID_CANCEL, AuthDialog::cancel_clicked) -END_EVENT_TABLE() - -/** - * - */ -AuthDialog:: -AuthDialog(const std::string &cert_filename, const std::string &cert_dir) : - // I hate stay-on-top dialogs, but if we don't set this flag, it doesn't - // come to the foreground on OSX, and might be lost behind the browser - // window. - wxDialog(nullptr, wxID_ANY, _T("New Panda3D Application"), wxDefaultPosition, - wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxSTAY_ON_TOP), - _cert_dir(cert_dir) -{ - _view_cert_dialog = nullptr; - - _cert = nullptr; - _stack = nullptr; - _verify_result = -1; - - read_cert_file(cert_filename); - get_friendly_name(); - verify_cert(); - layout(); -} - -/** - * - */ -AuthDialog:: -~AuthDialog() { - if (_view_cert_dialog != nullptr) { - _view_cert_dialog->Destroy(); - } - - if (_cert != nullptr) { - X509_free(_cert); - _cert = nullptr; - } - if (_stack != nullptr) { - sk_X509_free(_stack); - _stack = nullptr; - } -} - -/** - * The user clicks the "Run" button. - */ -void AuthDialog:: -run_clicked(wxCommandEvent &event) { - approve_cert(); -} - -/** - * The user clicks the "View Certificate" button. - */ -void AuthDialog:: -view_cert_clicked(wxCommandEvent &event) { - if (_view_cert_dialog != nullptr) { - _view_cert_dialog->Destroy(); - } - Hide(); - _view_cert_dialog = new ViewCertDialog(this, _cert); - _view_cert_dialog->Show(); -} - -/** - * The user clicks the "Cancel" button. - */ -void AuthDialog:: -cancel_clicked(wxCommandEvent &event) { - Destroy(); -} - -/** - * Writes the certificate into the _cert_dir, so that it will be found by the - * P3DInstanceManager and known to be approved. - */ -void AuthDialog:: -approve_cert() { - assert(_cert != nullptr); - - // Make sure the directory exists. - mkdir_complete(_cert_dir, cerr); - - // Look for an unused filename. - int i = 1; - size_t buf_length = _cert_dir.length() + 100; - char *buf = new char[buf_length]; -#ifdef _WIN32 - std::wstring buf_w; -#endif // _WIN32 - - while (true) { - sprintf(buf, "%s/p%d.crt", _cert_dir.c_str(), i); - assert(strlen(buf) < buf_length); - - // Check if it already exists. If not, take it. -#ifdef _WIN32 - DWORD results = 0; - if (string_to_wstring(buf_w, buf)) { - results = GetFileAttributesW(buf_w.c_str()); - } - if (results == -1) { - break; - } -#else - struct stat statbuf; - if (stat(buf, &statbuf) != 0) { - break; - } -#endif - ++i; - } - - // Sure, there's a slight race condition right now: another process might - // attempt to create the same filename. So what. - FILE *fp = nullptr; -#ifdef _WIN32 - fp = _wfopen(buf_w.c_str(), L"w"); -#else // _WIN32 - fp = fopen(buf, "w"); -#endif // _WIN32 - if (fp != nullptr) { - PEM_write_X509(fp, _cert); - fclose(fp); - } - - Destroy(); -} - -/** - * Reads the list of certificates in the pem filename passed on the command - * line into _cert and _stack. - */ -void AuthDialog:: -read_cert_file(const std::string &cert_filename) { - FILE *fp = nullptr; -#ifdef _WIN32 - std::wstring cert_filename_w; - if (string_to_wstring(cert_filename_w, cert_filename)) { - fp = _wfopen(cert_filename_w.c_str(), L"r"); - } -#else // _WIN32 - fp = fopen(cert_filename.c_str(), "r"); -#endif // _WIN32 - - if (fp == nullptr) { - cerr << "Couldn't read " << cert_filename << "\n"; - return; - } - _cert = PEM_read_X509(fp, nullptr, nullptr, (void *)""); - if (_cert == nullptr) { - cerr << "Could not read certificate in " << cert_filename << ".\n"; - fclose(fp); - return; - } - - // Build up a STACK of the remaining certificates in the file. - _stack = sk_X509_new(nullptr); - X509 *c = PEM_read_X509(fp, nullptr, nullptr, (void *)""); - while (c != nullptr) { - sk_X509_push(_stack, c); - c = PEM_read_X509(fp, nullptr, nullptr, (void *)""); - } - - fclose(fp); -} - -/** - * Extracts the "friendly name" from the certificate: the common name or email - * name. - */ -void AuthDialog:: -get_friendly_name() { - if (_cert == nullptr) { - _friendly_name.clear(); - return; - } - - - static const int nid_choices[] = { - NID_pkcs9_emailAddress, - NID_commonName, - -1, - }; - - // Choose the first NID that exists on the cert. - for (int ni = 0; nid_choices[ni] != -1; ++ni) { - int nid = nid_choices[ni]; - - // A complex OpenSSL interface to extract out the name in utf-8. - X509_NAME *xname = X509_get_subject_name(_cert); - if (xname != nullptr) { - int pos = X509_NAME_get_index_by_NID(xname, nid, -1); - if (pos != -1) { - // We just get the first common name. I guess it's possible to have - // more than one; not sure what that means in this context. - X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos); - if (xentry != nullptr) { - ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry); - if (data != nullptr) { - // We use "print" to dump the output to a memory BIO. Is there an - // easier way to decode the ASN1_STRING? Curse these incomplete - // docs. - BIO *mbio = BIO_new(BIO_s_mem()); - ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB); - - char *pp; - long pp_size = BIO_get_mem_data(mbio, &pp); - _friendly_name = wxString(pp, wxConvUTF8, pp_size); - BIO_free(mbio); - return; - } - } - } - } - } -} - -/** - * Checks whether the certificate is valid by the chain and initializes - * _verify_status accordingly. - */ -void AuthDialog:: -verify_cert() { - if (_cert == nullptr) { - _verify_result = -1; - return; - } - - // Create a new X509_STORE. - X509_STORE *store = X509_STORE_new(); - X509_STORE_set_default_paths(store); - - // Add in the well-known certificate authorities. - load_certificates_from_der_ram(store, (const char *)ca_bundle_data, ca_bundle_data_len); - - // Create the X509_STORE_CTX for verifying the cert and chain. - X509_STORE_CTX *ctx = X509_STORE_CTX_new(); - X509_STORE_CTX_init(ctx, store, _cert, _stack); - X509_STORE_CTX_set_cert(ctx, _cert); - - if (X509_verify_cert(ctx)) { - _verify_result = 0; - } else { - _verify_result = X509_STORE_CTX_get_error(ctx); - } - - X509_STORE_CTX_free(ctx); - - X509_STORE_free(store); - - cerr << "Got certificate from " << _friendly_name.mb_str() - << ", verify_result = " << _verify_result << "\n"; -} - -/** - * Reads a chain of trusted certificates from the indicated data buffer and - * adds them to the X509_STORE object. The data buffer should be DER- - * formatted. Returns the number of certificates read on success, or 0 on - * failure. - * - * You should call this only with trusted, locally-stored certificates; not - * with certificates received from an untrusted source. - */ -int AuthDialog:: -load_certificates_from_der_ram(X509_STORE *store, - const char *data, size_t data_size) { - int count = 0; - -#if OPENSSL_VERSION_NUMBER >= 0x00908000L - // Beginning in 0.9.8, d2i_X509() accepted a const unsigned char **. - const unsigned char *bp, *bp_end; -#else - // Prior to 0.9.8, d2i_X509() accepted an unsigned char **. - unsigned char *bp, *bp_end; -#endif - - bp = (unsigned char *)data; - bp_end = bp + data_size; - X509 *x509 = d2i_X509(nullptr, &bp, bp_end - bp); - while (x509 != nullptr) { - X509_STORE_add_cert(store, x509); - ++count; - x509 = d2i_X509(nullptr, &bp, bp_end - bp); - } - - return count; -} - -/** - * Arranges the text and controls within the dialog. - */ -void AuthDialog:: -layout() { - wxString header, text; - get_text(header, text); - - wxPanel *panel = new wxPanel(this); - wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL); - - wxFont font = panel->GetFont(); - wxFont *bold_font = wxTheFontList->FindOrCreateFont - ((int)(font.GetPointSize() * 1.5), - font.GetFamily(), font.GetStyle(), wxFONTWEIGHT_BOLD); - - if (!header.IsEmpty()) { - wxStaticText *text0 = new wxStaticText - (panel, wxID_ANY, header, wxDefaultPosition, wxDefaultSize, - wxALIGN_CENTER); - text0->SetFont(*bold_font); - vsizer->Add(text0, 0, wxCENTER | wxALL, 10); - } - - wxStaticText *text1 = new wxStaticText - (panel, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); - text1->Wrap(400); - vsizer->Add(text1, 0, wxCENTER | wxALL, 10); - - // Create the run cancel buttons. - wxBoxSizer *bsizer = new wxBoxSizer(wxHORIZONTAL); - - if (_verify_result == 0 && _cert != nullptr) { - wxButton *run_button = new wxButton(panel, wxID_OK, _T("Run")); - bsizer->Add(run_button, 0, wxALIGN_CENTER | wxALL, 5); - } - - if (_cert != nullptr) { - wxButton *view_button = new wxButton(panel, VIEW_CERT_BUTTON, _T("View Certificate")); - bsizer->Add(view_button, 0, wxALIGN_CENTER | wxALL, 5); - } - - wxButton *cancel_button = new wxButton(panel, wxID_CANCEL, _T("Cancel")); - bsizer->Add(cancel_button, 0, wxALIGN_CENTER | wxALL, 5); - - vsizer->Add(bsizer, 0, wxALIGN_CENTER | wxALL, 5); - - panel->SetSizer(vsizer); - panel->SetAutoLayout(true); - vsizer->Fit(this); -} - -/** - * Fills in the text appropriate to display in the dialog box, based on the - * certificate read so far. - */ -void AuthDialog:: -get_text(wxString &header, wxString &text) { - switch (_verify_result) { - case -1: - header = _T("No signature!"); - text = no_cert_text; - break; - - case 0: - text.Printf(verified_cert_text, _friendly_name.c_str(), _friendly_name.c_str(), _friendly_name.c_str()); - break; - - case X509_V_ERR_CERT_NOT_YET_VALID: - case X509_V_ERR_CERT_HAS_EXPIRED: - case X509_V_ERR_CRL_NOT_YET_VALID: - case X509_V_ERR_CRL_HAS_EXPIRED: - header = _T("Expired signature!"); - text.Printf(expired_cert_text, _friendly_name.c_str()); - break; - - case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: - header = _T("Unverified signature!"); - text.Printf(unknown_auth_cert_text, _friendly_name.c_str()); - break; - - case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: - case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: - header = _T("Unverified signature!"); - text = self_signed_cert_text; - break; - - default: - header = _T("Unverified signature!"); - text.Printf(generic_error_cert_text, _verify_result); - } -} - - -// The event table for ViewCertDialog. -BEGIN_EVENT_TABLE(ViewCertDialog, wxDialog) - EVT_BUTTON(wxID_OK, ViewCertDialog::run_clicked) - EVT_BUTTON(wxID_CANCEL, ViewCertDialog::cancel_clicked) -END_EVENT_TABLE() - -/** - * - */ -ViewCertDialog:: -ViewCertDialog(AuthDialog *auth_dialog, X509 *cert) : -wxDialog(nullptr, wxID_ANY, _T("View Certificate"), wxDefaultPosition, - wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), - _auth_dialog(auth_dialog), - _cert(cert) -{ - layout(); -} - -/** - * - */ -ViewCertDialog:: -~ViewCertDialog() { - if (_auth_dialog != nullptr) { - _auth_dialog->_view_cert_dialog = nullptr; - } -} - -/** - * The user clicks the "Run" button. - */ -void ViewCertDialog:: -run_clicked(wxCommandEvent &event) { - if (_auth_dialog != nullptr){ - _auth_dialog->approve_cert(); - } - Destroy(); -} - -/** - * The user clicks the "Cancel" button. - */ -void ViewCertDialog:: -cancel_clicked(wxCommandEvent &event) { - if (_auth_dialog != nullptr){ - _auth_dialog->Destroy(); - } - Destroy(); -} - -/** - * Arranges the text and controls within the dialog. - */ -void ViewCertDialog:: -layout() { - // Format the certificate text for display in the dialog. - assert(_cert != nullptr); - - BIO *mbio = BIO_new(BIO_s_mem()); - X509_print(mbio, _cert); - - char *pp; - long pp_size = BIO_get_mem_data(mbio, &pp); - wxString cert_body(pp, wxConvUTF8, pp_size); - BIO_free(mbio); - - wxPanel *panel = new wxPanel(this); - wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL); - - wxScrolledWindow *slwin = new wxScrolledWindow - (panel, -1, wxDefaultPosition, wxDefaultSize, wxVSCROLL | wxHSCROLL | wxBORDER_SUNKEN); - slwin->SetScrollRate(20, 20); - - wxBoxSizer *slsizer = new wxBoxSizer(wxVERTICAL); - - wxStaticText *text1 = new wxStaticText - (slwin, wxID_ANY, cert_body, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - slsizer->Add(text1, 0, wxEXPAND, 0); - slwin->SetSizer(slsizer); - - vsizer->Add(slwin, 1, wxEXPAND | wxALL, 10); - - // Create the run cancel buttons. - wxBoxSizer *bsizer = new wxBoxSizer(wxHORIZONTAL); - - wxButton *run_button = new wxButton(panel, wxID_OK, _T("Run")); - bsizer->Add(run_button, 0, wxALIGN_CENTER | wxALL, 5); - - wxButton *cancel_button = new wxButton(panel, wxID_CANCEL, _T("Cancel")); - bsizer->Add(cancel_button, 0, wxALIGN_CENTER | wxALL, 5); - - vsizer->Add(bsizer, 0, wxALIGN_CENTER | wxALL, 5); - - panel->SetSizer(vsizer); - panel->SetAutoLayout(true); - vsizer->Fit(this); - - // Make sure the resulting window is at least a certain size. - int width, height; - GetSize(&width, &height); - SetSize(std::max(width, 600), std::max(height, 400)); -} diff --git a/direct/src/plugin/p3dCert_wx.h b/direct/src/plugin/p3dCert_wx.h deleted file mode 100644 index b0b3dda12e..0000000000 --- a/direct/src/plugin/p3dCert_wx.h +++ /dev/null @@ -1,119 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dCert_wx.h - * @author drose - * @date 2009-09-11 - */ - -#ifndef P3DCERT_WX_H -#define P3DCERT_WX_H - -#include - -#define OPENSSL_NO_KRB5 -#include -#include -#include - -#include -#include -#include - -class ViewCertDialog; - -#ifndef STACK_OF - // At some point, presumably in 1.0.0, openssl went to the STACK_OF() macro - // system to typedef the contents of a stack. Unfortunately, that new API - // is different. We define some macros here here for backward compatiblity. - #define STACK_OF(type) STACK - #define sk_X509_push(stack, item) sk_push((stack), (char *)(item)) - #define sk_X509_free(stack) sk_free(stack) - #define sk_X509_new(cmp) sk_new(cmp) -#endif - -/** - * This is the wxApp that drives this application. - */ -class P3DCertApp : public wxApp { -public: - virtual bool OnInit(); - virtual void OnInitCmdLine(wxCmdLineParser &parser); - virtual bool OnCmdLineParsed(wxCmdLineParser &parser); - -private: - std::string _cert_filename; - std::string _cert_dir; -}; - -/** - * This is the primary dialog of this application. - * - * This dialog is presented to the user when he/she clicks on the red - * authorization button on the splash window. It tells the user the status of - * the application's signature, and invites the user to approve the signature - * or cancel. - */ -class AuthDialog : public wxDialog { -public: - AuthDialog(const std::string &cert_filename, const std::string &cert_dir); - virtual ~AuthDialog(); - - void run_clicked(wxCommandEvent &event); - void view_cert_clicked(wxCommandEvent &event); - void cancel_clicked(wxCommandEvent &event); - - void approve_cert(); - -private: - void read_cert_file(const std::string &cert_filename); - void get_friendly_name(); - void verify_cert(); - int load_certificates_from_der_ram(X509_STORE *store, - const char *data, size_t data_size); - - void layout(); - void get_text(wxString &header, wxString &text); - -public: - ViewCertDialog *_view_cert_dialog; - -private: - // any class wishing to process wxWidgets events must use this macro - DECLARE_EVENT_TABLE() - - std::string _cert_dir; - X509 *_cert; - STACK_OF(X509) *_stack; - - wxString _friendly_name; - int _verify_result; -}; - -/** - * This is the detailed view of the particular certificate. - */ -class ViewCertDialog : public wxDialog { -public: - ViewCertDialog(AuthDialog *auth_dialog, X509 *cert); - virtual ~ViewCertDialog(); - - void run_clicked(wxCommandEvent &event); - void cancel_clicked(wxCommandEvent &event); - -private: - void layout(); - -private: - DECLARE_EVENT_TABLE() - - AuthDialog *_auth_dialog; - X509 *_cert; -}; - -#endif diff --git a/direct/src/plugin/p3dConcreteSequence.cxx b/direct/src/plugin/p3dConcreteSequence.cxx deleted file mode 100644 index ec4291cb7b..0000000000 --- a/direct/src/plugin/p3dConcreteSequence.cxx +++ /dev/null @@ -1,213 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dConcreteSequence.cxx - * @author drose - * @date 2009-06-30 - */ - -#include "p3dConcreteSequence.h" -#include "p3dSession.h" - -/** - * - */ -P3DConcreteSequence:: -P3DConcreteSequence() { -} - -/** - * - */ -P3DConcreteSequence:: -~P3DConcreteSequence() { - Elements::iterator ei; - for (ei = _elements.begin(); ei != _elements.end(); ++ei) { - P3D_OBJECT_DECREF(*ei); - } -} - -/** - * Returns true if this is actually an instance of a P3DConcreteSequence, - * false otherwise. - */ -bool P3DConcreteSequence:: -is_sequence_object() { - return true; -} - -/** - * Returns the fundamental type of this kind of object. - */ -P3D_object_type P3DConcreteSequence:: -get_type() { - return P3D_OT_object; -} - -/** - * Returns the object value coerced to a boolean, if possible. - */ -bool P3DConcreteSequence:: -get_bool() { - return !_elements.empty(); -} - -/** - * Fills the indicated C++ string object with the value of this object coerced - * to a string. - */ -void P3DConcreteSequence:: -make_string(std::string &value) { - std::ostringstream strm; - strm << "["; - if (!_elements.empty()) { - strm << *_elements[0]; - for (size_t i = 1; i < _elements.size(); ++i) { - strm << ", " << *_elements[i]; - } - } - strm << "]"; - - value = strm.str(); -} - -/** - * Returns the named property element in the object. The return value is a - * new-reference P3D_object, or NULL on error. - */ -P3D_object *P3DConcreteSequence:: -get_property(const std::string &property) { - // We only understand integer "property" names. - char *endptr; - int index = strtoul(property.c_str(), &endptr, 10); - if (*endptr != '\0') { - return nullptr; - } - - return get_element(index); -} - -/** - * Modifies (or deletes, if value is NULL) the named property element in the - * object. Returns true on success, false on failure. - */ -bool P3DConcreteSequence:: -set_property(const std::string &property, P3D_object *value) { - // We only understand integer "property" names. - char *endptr; - int index = strtoul(property.c_str(), &endptr, 10); - if (*endptr != '\0') { - return false; - } - - return set_element(index, value); -} - -/** - * If this object has a valid XML representation for the indicated session - * (that hasn't already been implemented by the generic code in P3DSession), - * this method will apply it to the indicated "value" element and return true. - * Otherwise, this method will leave the element unchanged and return false. - */ -bool P3DConcreteSequence:: -fill_xml(TiXmlElement *xvalue, P3DSession *session) { - xvalue->SetAttribute("type", "concrete_sequence"); - Elements::const_iterator ei; - for (ei = _elements.begin(); ei != _elements.end(); ++ei) { - xvalue->LinkEndChild(session->p3dobj_to_xml(*ei)); - } - - return true; -} - -/** - * Returns a pointer to the array of objects represented by this object. Most - * objects represent only themselves, but a P3DConcreteSequence represents its - * list. - */ -P3D_object **P3DConcreteSequence:: -get_object_array() { - if (_elements.empty()) { - return nullptr; - } - return &_elements[0]; -} - -/** - * Returns the number of elements in the array returned by get_object_array(). - */ -int P3DConcreteSequence:: -get_object_array_size() { - return _elements.size(); -} - -/** - * Returns the number of items in the sequence. - */ -int P3DConcreteSequence:: -get_length() const { - return _elements.size(); -} - -/** - * Returns the nth item in the sequence. The return value is a new-reference - * P3DObject object, or NULL on error. - */ -P3D_object *P3DConcreteSequence:: -get_element(int n) const { - if (n >= 0 && n < (int)_elements.size()) { - P3D_OBJECT_INCREF(_elements[n]); - return _elements[n]; - } - - return nullptr; -} - -/** - * Modifies (or deletes, if value is NULL) the nth item in the sequence. - * Returns true on success, false on failure. - */ -bool P3DConcreteSequence:: -set_element(int n, P3D_object *value) { - if (value == nullptr) { - // Delete an element. - if (n < 0 || n >= (int)_elements.size()) { - // Invalid index. - return false; - } - P3D_OBJECT_DECREF(_elements[n]); - _elements.erase(_elements.begin() + n); - return true; - - } else if (n == _elements.size()) { - // Append an element. - append(value); - return true; - - } else { - // Replace an element. - if (n < 0 || n >= (int)_elements.size()) { - // Invalid index. - return false; - } - - P3D_OBJECT_INCREF(value); - P3D_OBJECT_DECREF(_elements[n]); - _elements[n] = value; - return true; - } -} - -/** - * Adds a new element to the end of the list. - */ -void P3DConcreteSequence:: -append(P3D_object *value) { - _elements.push_back(value); - P3D_OBJECT_INCREF(value); -} diff --git a/direct/src/plugin/p3dConcreteSequence.h b/direct/src/plugin/p3dConcreteSequence.h deleted file mode 100644 index 09b489c5ba..0000000000 --- a/direct/src/plugin/p3dConcreteSequence.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dConcreteSequence.h - * @author drose - * @date 2009-06-30 - */ - -#ifndef P3DCONCRETESEQUENCE_H -#define P3DCONCRETESEQUENCE_H - -#include "p3d_plugin_common.h" -#include "p3dObject.h" - -/** - * An object type that contains a sequence of objects, which is passed by - * value between Python and JavaScript, so may be more optimal for small lists - * that are accessed repeatedly. - * - * This is converted from a Python "tuple" object. - */ -class P3DConcreteSequence : public P3DObject { -public: - P3DConcreteSequence(); - virtual ~P3DConcreteSequence(); - - virtual bool is_sequence_object(); - - virtual P3D_object_type get_type(); - virtual bool get_bool(); - - virtual void make_string(std::string &value); - - virtual P3D_object *get_property(const std::string &property); - virtual bool set_property(const std::string &property, P3D_object *value); - - virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session); - virtual P3D_object **get_object_array(); - virtual int get_object_array_size(); - - int get_length() const; - P3D_object *get_element(int n) const; - bool set_element(int n, P3D_object *value); - void append(P3D_object *value); - -private: - typedef std::vector Elements; - Elements _elements; -}; - -#endif diff --git a/direct/src/plugin/p3dConcreteStruct.cxx b/direct/src/plugin/p3dConcreteStruct.cxx deleted file mode 100644 index fa9917d955..0000000000 --- a/direct/src/plugin/p3dConcreteStruct.cxx +++ /dev/null @@ -1,178 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dConcreteStruct.cxx - * @author drose - * @date 2009-07-14 - */ - -#include "p3dConcreteStruct.h" - -using std::string; - -/** - * - */ -P3DConcreteStruct:: -P3DConcreteStruct() { -} - -/** - * - */ -P3DConcreteStruct:: -~P3DConcreteStruct() { - Elements::iterator ei; - for (ei = _elements.begin(); ei != _elements.end(); ++ei) { - P3D_OBJECT_DECREF((*ei).second); - } -} - -/** - * Returns the fundamental type of this kind of object. - */ -P3D_object_type P3DConcreteStruct:: -get_type() { - return P3D_OT_object; -} - -/** - * Returns the object value coerced to a boolean, if possible. - */ -bool P3DConcreteStruct:: -get_bool() { - return !_elements.empty(); -} - -/** - * Fills the indicated C++ string object with the value of this object coerced - * to a string. - */ -void P3DConcreteStruct:: -make_string(string &value) { - std::ostringstream strm; - strm << "{"; - if (!_elements.empty()) { - Elements::iterator ei; - ei = _elements.begin(); - strm << (*ei).first << ": " << *(*ei).second; - ++ei; - while (ei != _elements.end()) { - strm << ", " << (*ei).first << ": " << *(*ei).second; - ++ei; - } - } - strm << "}"; - - value = strm.str(); -} - -/** - * Returns the named property element in the object. The return value is a - * new-reference P3D_object, or NULL on error. - */ -P3D_object *P3DConcreteStruct:: -get_property(const string &property) { - Elements::const_iterator ei = _elements.find(property); - if (ei != _elements.end()) { - P3D_OBJECT_INCREF((*ei).second); - return (*ei).second; - } - - return nullptr; -} - -/** - * Modifies (or deletes, if value is NULL) the named property element in the - * object. Returns true on success, false on failure. - */ -bool P3DConcreteStruct:: -set_property(const string &property, P3D_object *value) { - if (value == nullptr) { - // Delete an element. - Elements::iterator ei = _elements.find(property); - if (ei == _elements.end()) { - // Invalid property. - return false; - } - P3D_OBJECT_DECREF((*ei).second); - _elements.erase(ei); - return true; - - } else { - // Replace or insert an element. - P3D_OBJECT_INCREF(value); - std::pair result = _elements.insert(Elements::value_type(property, value)); - if (!result.second) { - // Replacing an element. - Elements::iterator ei = result.first; - P3D_OBJECT_DECREF((*ei).second); - (*ei).second = value; - } - return true; - } -} - -/** - * Returns true if the named method exists on this object, false otherwise. - */ -bool P3DConcreteStruct:: -has_method(const string &method_name) { - if (method_name == "toString") { - return true; - } - - return false; -} - -/** - * Invokes the named method on the object, passing the indicated parameters. - * If the method name is empty, invokes the object itself. - * - * If needs_response is true, the return value is a new-reference P3D_object - * on success, or NULL on failure. If needs_response is false, the return - * value is always NULL, and there is no way to determine success or failure. - */ -P3D_object *P3DConcreteStruct:: -call(const string &method_name, bool needs_response, - P3D_object *params[], int num_params) { - P3D_object *result = nullptr; - - if (method_name == "toString") { - string value; - make_string(value); - result = P3D_new_string_object(value.data(), value.length()); - } - - if (result != nullptr && !needs_response) { - P3D_OBJECT_DECREF(result); - result = nullptr; - } - - return result; -} - - -/** - * If this object has a valid XML representation for the indicated session - * (that hasn't already been implemented by the generic code in P3DSession), - * this method will apply it to the indicated "value" element and return true. - * Otherwise, this method will leave the element unchanged and return false. - */ -bool P3DConcreteStruct:: -fill_xml(TiXmlElement *xvalue, P3DSession *session) { - xvalue->SetAttribute("type", "concrete_struct"); - Elements::const_iterator ei; - for (ei = _elements.begin(); ei != _elements.end(); ++ei) { - TiXmlElement *xitem = session->p3dobj_to_xml((*ei).second); - xitem->SetAttribute("key", (*ei).first); - xvalue->LinkEndChild(xitem); - } - - return true; -} diff --git a/direct/src/plugin/p3dConcreteStruct.h b/direct/src/plugin/p3dConcreteStruct.h deleted file mode 100644 index cccd35bbeb..0000000000 --- a/direct/src/plugin/p3dConcreteStruct.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dConcreteStruct.h - * @author drose - * @date 2009-07-14 - */ - -#ifndef P3DCONCRETESTRUCT_H -#define P3DCONCRETESTRUCT_H - -#include "p3d_plugin_common.h" -#include "p3dObject.h" - -/** - * A simple object that contains a standard mapping of string -> element. It - * is passed by value between Python and Javascript, so it may be more optimal - * for relatively small objects. - * - * Methods are not supported, other than built-in methods like toString(). - */ -class P3DConcreteStruct : public P3DObject { -public: - P3DConcreteStruct(); - virtual ~P3DConcreteStruct(); - - virtual P3D_object_type get_type(); - virtual bool get_bool(); - - virtual void make_string(std::string &value); - - virtual P3D_object *get_property(const std::string &property); - virtual bool set_property(const std::string &property, P3D_object *value); - - virtual bool has_method(const std::string &method_name); - virtual P3D_object *call(const std::string &method_name, bool needs_response, - P3D_object *params[], int num_params); - - virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session); - -private: - typedef std::map Elements; - Elements _elements; -}; - -#endif diff --git a/direct/src/plugin/p3dConditionVar.I b/direct/src/plugin/p3dConditionVar.I deleted file mode 100644 index 08ae4ffe7e..0000000000 --- a/direct/src/plugin/p3dConditionVar.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dConditionVar.I - * @author drose - * @date 2009-07-02 - */ diff --git a/direct/src/plugin/p3dConditionVar.cxx b/direct/src/plugin/p3dConditionVar.cxx deleted file mode 100644 index 22133c0781..0000000000 --- a/direct/src/plugin/p3dConditionVar.cxx +++ /dev/null @@ -1,171 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dConditionVar.cxx - * @author drose - * @date 2009-07-02 - */ - -#include "p3dConditionVar.h" - -#ifndef _WIN32 -#include -#include -#endif -#include - -/** - * - */ -P3DConditionVar:: -P3DConditionVar() { -#ifdef _WIN32 - InitializeCriticalSection(&_lock); - - // Create an auto-reset event. - _event_signal = CreateEvent(nullptr, false, false, nullptr); - -#else // _WIN32 - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); - int result = pthread_mutex_init(&_lock, &attr); - pthread_mutexattr_destroy(&attr); - assert(result == 0); - - result = pthread_cond_init(&_cvar, nullptr); - assert(result == 0); - -#endif // _WIN32 -} - -/** - * - */ -P3DConditionVar:: -~P3DConditionVar() { -#ifdef _WIN32 - DeleteCriticalSection(&_lock); - CloseHandle(_event_signal); - -#else // _WIN32 - int result = pthread_mutex_destroy(&_lock); - assert(result == 0); - - result = pthread_cond_destroy(&_cvar); - assert(result == 0); - -#endif // _WIN32 -} - -/** - * Acquires the internal lock. The lock should be held during any calls to - * wait() or notify(). - */ -void P3DConditionVar:: -acquire() { -#ifdef _WIN32 - EnterCriticalSection(&_lock); - -#else // _WIN32 - int result = pthread_mutex_lock(&_lock); - assert(result == 0); - -#endif // _WIN32 -} - -/** - * Requires the lock to be held on entry. Releases the lock, waits for - * another thread to call notify(), then reacquires the lock on exit. - */ -void P3DConditionVar:: -wait() { -#ifdef _WIN32 - LeaveCriticalSection(&_lock); - - DWORD result = WaitForSingleObject(_event_signal, INFINITE); - assert(result == WAIT_OBJECT_0); - - EnterCriticalSection(&_lock); - -#else // _WIN32 - int result = pthread_cond_wait(&_cvar, &_lock); - assert(result == 0); - -#endif // _WIN32 -} - -/** - * As above, but waits no longer than timeout seconds before returning. - */ -void P3DConditionVar:: -wait(double timeout) { -#ifdef _WIN32 - LeaveCriticalSection(&_lock); - - DWORD result = WaitForSingleObject(_event_signal, (DWORD)(timeout * 1000.0)); - assert(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT); - - EnterCriticalSection(&_lock); -#else // _WIN32 - - struct timeval now; - gettimeofday(&now, nullptr); - - // Convert from timeval to timespec - struct timespec ts; - ts.tv_sec = now.tv_sec; - ts.tv_nsec = now.tv_usec * 1000; - - int seconds = (int)floor(timeout); - ts.tv_sec += seconds; - ts.tv_nsec += (int)((timeout - seconds) * 1000000.0); - if (ts.tv_nsec > 1000000) { - ts.tv_nsec -= 1000000; - ++ts.tv_sec; - } - - int result = pthread_cond_timedwait(&_cvar, &_lock, &ts); - if (result != 0 && result != ETIMEDOUT) { - errno = result; - perror("pthread_cond_timedwait"); - } - -#endif // _WIN32 -} - -/** - * Waits a single thread blocked on wait(), if any. If no threads are - * waiting, the event is lost. The lock should be held during this call. - */ -void P3DConditionVar:: -notify() { -#ifdef _WIN32 - SetEvent(_event_signal); - -#else // _WIN32 - int result = pthread_cond_signal(&_cvar); - assert(result == 0); - -#endif // _WIN32 -} - -/** - * Releases the internal lock. - */ -void P3DConditionVar:: -release() { -#ifdef _WIN32 - LeaveCriticalSection(&_lock); - -#else // _WIN32 - int result = pthread_mutex_unlock(&_lock); - assert(result == 0); - -#endif // _WIN32 -} diff --git a/direct/src/plugin/p3dConditionVar.h b/direct/src/plugin/p3dConditionVar.h deleted file mode 100644 index f4de8f549d..0000000000 --- a/direct/src/plugin/p3dConditionVar.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dConditionVar.h - * @author drose - * @date 2009-07-02 - */ - -#ifndef P3DCONDITIONVAR_H -#define P3DCONDITIONVAR_H - -#include "p3d_plugin_common.h" - -/** - * A simple condition-variable like object. It doesn't support the full - * condition-var semantics, but it works well enough with one waiter and one - * signaller. - */ -class P3DConditionVar { -public: - P3DConditionVar(); - ~P3DConditionVar(); - - void acquire(); - void wait(); - void wait(double timeout); - void notify(); - void release(); - -private: -#ifdef _WIN32 - CRITICAL_SECTION _lock; - HANDLE _event_signal; - -#else // _WIN32 - pthread_mutex_t _lock; - pthread_cond_t _cvar; -#endif // _WIN32 -}; - -#include "p3dConditionVar.I" - -#endif diff --git a/direct/src/plugin/p3dDownload.I b/direct/src/plugin/p3dDownload.I deleted file mode 100644 index c248948612..0000000000 --- a/direct/src/plugin/p3dDownload.I +++ /dev/null @@ -1,126 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dDownload.I - * @author drose - * @date 2009-06-11 - */ - -/** - * Returns the URL that we are querying. - */ -const std::string &P3DDownload:: -get_url() const { - return _url; -} - -/** - * Specifies the particular P3DInstance that is responsible for downloading - * this object. - */ -inline void P3DDownload:: -set_instance(P3DInstance *instance) { - _instance = instance; -} - -/** - * Returns the particular P3DInstance that is responsible for downloading this - * object. - */ -inline P3DInstance *P3DDownload:: -get_instance() const { - return _instance; -} - -/** - * Returns an indication of the progress through the download file, 0.0 to - * 1.0. Returns 1.0 if the size of the file is not known. - */ -inline double P3DDownload:: -get_download_progress() const { - if (_total_expected_data == 0) { - return 1.0; - } - - return (double)_total_data / (double)_total_expected_data; -} - -/** - * Returns true if the download progress is known, or false if it is unknown - * because the server hasn't told us the total size it will be feeding us. If - * this is false, get_download_progress() will generally always return 1.0; - * use get_total_bytes() to measure progress in this case. - */ -inline bool P3DDownload:: -is_download_progress_known() const { - return _progress_known; -} - -/** - * Returns the total number of bytes downloaded so far. - */ -inline size_t P3DDownload:: -get_total_data() const { - return _total_data; -} - -/** - * Sets the total number of bytes expected to be downloaded. This is used to - * compute the progress. Normally, this can be set from the download server, - * but there may be cases when the download server doesn't accurately report - * it. - */ -inline void P3DDownload:: -set_total_expected_data(size_t expected_data) { - _total_expected_data = expected_data; - _progress_known = true; -} - -/** - * Returns true if the download has finished, either successfully or - * otherwise, or false if it is still in progress. - */ -inline bool P3DDownload:: -get_download_finished() const { - return _status != P3D_RC_in_progress; -} - -/** - * Returns true if the download has finished successfully, or false if it is - * still in progress or if it has failed. - */ -inline bool P3DDownload:: -get_download_success() const { - return _status == P3D_RC_done; -} - -/** - * Returns true if the download has failed because the instance is about to be - * shut down, or false if it hasn't failed, or failed for some other reason. - */ -inline bool P3DDownload:: -get_download_terminated() const { - return _status == P3D_RC_shutdown; -} - -/** - * Called only by P3DInstance to set a unique ID for this particular download - * object. - */ -inline void P3DDownload:: -set_download_id(int download_id) { - _download_id = download_id; -} - -/** - * Returns the unique ID set by the P3DInstance. - */ -inline int P3DDownload:: -get_download_id() const { - return _download_id; -} diff --git a/direct/src/plugin/p3dDownload.cxx b/direct/src/plugin/p3dDownload.cxx deleted file mode 100644 index 3b6a6cca7a..0000000000 --- a/direct/src/plugin/p3dDownload.cxx +++ /dev/null @@ -1,192 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dDownload.cxx - * @author drose - * @date 2009-06-11 - */ - -#include "p3dDownload.h" - -/** - * - */ -P3DDownload:: -P3DDownload() { - _status = P3D_RC_in_progress; - _http_status_code = 0; - _total_data = 0; - _total_expected_data = 0; - _last_reported_time = 0; - _progress_known = false; - - _canceled = false; - _download_id = 0; - _instance = nullptr; -} - -/** - * - */ -P3DDownload:: -P3DDownload(const P3DDownload ©) : - _url(copy._url), - _total_expected_data(copy._total_expected_data), - _progress_known(copy._progress_known) -{ - _status = P3D_RC_in_progress; - _http_status_code = 0; - _total_data = 0; - _last_reported_time = 0; - - _canceled = false; - _download_id = 0; - _instance = nullptr; -} - -/** - * - */ -P3DDownload:: -~P3DDownload() { -} - -/** - * Supplies the source URL for the download. - */ -void P3DDownload:: -set_url(const std::string &url) { - _url = url; -} - -/** - * Cancels a running download. download_finished() will not be called, but - * the P3DDownload object itself will eventually be deleted by its owning - * P3DInstance. - */ -void P3DDownload:: -cancel() { - _canceled = true; - if (_status == P3D_RC_in_progress) { - _status = P3D_RC_generic_error; - } -} - -/** - * Resets the download to its initial state, for re-trying the same download. - */ -void P3DDownload:: -clear() { - _status = P3D_RC_in_progress; - _http_status_code = 0; - _total_data = 0; - _last_reported_time = 0; - - _canceled = false; - _download_id = 0; -} - -/** - * Called by P3DInstance as more data arrives from the host. Returns true on - * success, false if the download should be aborted. - */ -bool P3DDownload:: -feed_url_stream(P3D_result_code result_code, - int http_status_code, - size_t total_expected_data, - const unsigned char *this_data, - size_t this_data_size) { - if (_canceled) { - return false; - } - - _status = result_code; - _http_status_code = http_status_code; - - if (this_data_size != 0) { - if (!receive_data(this_data, this_data_size)) { - // Failure writing to disk or some such. - _status = P3D_RC_generic_error; - download_finished(false); - return false; - } - - _total_data += this_data_size; - } - - total_expected_data = std::max(total_expected_data, _total_data); - if (total_expected_data > _total_expected_data) { - // If the expected data grows during the download, we don't really know - // how much we're getting. - _progress_known = false; - _total_expected_data = total_expected_data; - } - if (_total_expected_data > 0 && - (double)_total_data / (double)_total_expected_data < 0.9) { - // But if we're not close to our target yet, let's say we do know (at - // least until we get there and the target moves again). - _progress_known = true; - } - - download_progress(); - - if (_status != P3D_RC_in_progress) { - // We're done. - if (get_download_success()) { - _progress_known = true; - } - download_finished(get_download_success()); - } - - return true; -} - - -/** - * Called as new data is downloaded. Returns true on success, false on - * failure. - */ -bool P3DDownload:: -receive_data(const unsigned char *this_data, size_t this_data_size) { - return false; -} - -/** - * Intended to be overloaded to generate an occasional callback as new data - * comes in. - */ -void P3DDownload:: -download_progress() { - time_t now = time(nullptr); - if (now - _last_reported_time > 10) { - _last_reported_time = now; - nout << "Downloading " << get_url() << ": "; - if (_progress_known) { - nout << int(get_download_progress() * 1000.0) / 10.0 << "%"; - } else { - nout << int((double)get_total_data() / 104857.6) / 10.0 << "M"; - } - nout << ", " << this << "\n"; - } -} - -/** - * Intended to be overloaded to generate a callback when the download - * finishes, either successfully or otherwise. The bool parameter is true if - * the download was successful. - */ -void P3DDownload:: -download_finished(bool success) { - nout << "Downloaded " << get_url() << ": "; - if (_progress_known) { - nout << int(get_download_progress() * 1000.0) / 10.0 << "%"; - } else { - nout << int((double)get_total_data() / 104857.6) / 10.0 << "M"; - } - nout << ", " << this << ", success = " << success << "\n"; -} diff --git a/direct/src/plugin/p3dDownload.h b/direct/src/plugin/p3dDownload.h deleted file mode 100644 index ad2cd872a5..0000000000 --- a/direct/src/plugin/p3dDownload.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dDownload.h - * @author drose - * @date 2009-06-11 - */ - -#ifndef P3DDOWNLOAD_H -#define P3DDOWNLOAD_H - -#include "p3d_plugin_common.h" -#include "p3dReferenceCount.h" -class P3DInstance; - -#include - -/** - * This represents a request to download a single file from a URL, with no - * particular destination. It is intended to be used as an abstract base - * class; to use it, subclass it and redefine the appropriate callback - * methods. - */ -class P3DDownload : public P3DReferenceCount { -public: - P3DDownload(); - P3DDownload(const P3DDownload ©); - virtual ~P3DDownload(); - - void set_url(const std::string &url); - inline const std::string &get_url() const; - - inline void set_instance(P3DInstance *instance); - inline P3DInstance *get_instance() const; - - inline double get_download_progress() const; - inline bool is_download_progress_known() const; - inline bool get_download_finished() const; - inline bool get_download_success() const; - inline bool get_download_terminated() const; - inline size_t get_total_data() const; - inline void set_total_expected_data(size_t expected_data); - - void cancel(); - void clear(); - -public: - // These are intended to be called only by P3DInstance. - inline void set_download_id(int download_id); - inline int get_download_id() const; - - bool feed_url_stream(P3D_result_code result_code, - int http_status_code, - size_t total_expected_data, - const unsigned char *this_data, - size_t this_data_size); - -protected: - virtual bool receive_data(const unsigned char *this_data, - size_t this_data_size); - virtual void download_progress(); - virtual void download_finished(bool success); - -protected: - P3D_result_code _status; - int _http_status_code; - - size_t _total_data; - size_t _total_expected_data; - time_t _last_reported_time; - bool _progress_known; - -private: - bool _canceled; - int _download_id; - std::string _url; - P3DInstance *_instance; -}; - -#include "p3dDownload.I" - -#endif diff --git a/direct/src/plugin/p3dFileDownload.I b/direct/src/plugin/p3dFileDownload.I deleted file mode 100644 index af4e2b1ec8..0000000000 --- a/direct/src/plugin/p3dFileDownload.I +++ /dev/null @@ -1,20 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dFileDownload.I - * @author drose - * @date 2009-06-11 - */ - -/** - * Returns the filename that we are downloading into. - */ -const std::string &P3DFileDownload:: -get_filename() const { - return _filename; -} diff --git a/direct/src/plugin/p3dFileDownload.cxx b/direct/src/plugin/p3dFileDownload.cxx deleted file mode 100644 index a978c3640e..0000000000 --- a/direct/src/plugin/p3dFileDownload.cxx +++ /dev/null @@ -1,107 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dFileDownload.cxx - * @author drose - * @date 2009-06-11 - */ - -#include "p3dFileDownload.h" -#include "p3dInstanceManager.h" -#include "mkdir_complete.h" -#include "wstring_encode.h" - -/** - * - */ -P3DFileDownload:: -P3DFileDownload() { -} - -/** - * - */ -P3DFileDownload:: -P3DFileDownload(const P3DFileDownload ©) : - P3DDownload(copy) -{ - // We don't copy the filename. You have to copy it yourself. -} - -/** - * Supplies the target local filename for the download. Returns true on - * success, false on failure. - */ -bool P3DFileDownload:: -set_filename(const std::string &filename) { - _filename = filename; - - return open_file(); -} - -/** - * Opens the local file for receiving the download. Returns true on success, - * false on failure. - */ -bool P3DFileDownload:: -open_file() { - if (!mkfile_complete(_filename, nout)) { - nout << "Failed to create " << _filename << "\n"; - return false; - } - - _file.clear(); -#ifdef _WIN32 - std::wstring filename_w; - if (string_to_wstring(filename_w, _filename)) { - _file.open(filename_w.c_str(), std::ios::out | std::ios::trunc | std::ios::binary); - } -#else // _WIN32 - _file.open(_filename.c_str(), std::ios::out | std::ios::trunc | std::ios::binary); -#endif // _WIN32 - if (!_file) { - nout << "Failed to open " << _filename << " in write mode\n"; - return false; - } - - return true; -} - -/** - * Closes the local file. - */ -void P3DFileDownload:: -close_file() { - _file.close(); -} - -/** - * Called as new data is downloaded. Returns true on success, false on - * failure. - */ -bool P3DFileDownload:: -receive_data(const unsigned char *this_data, size_t this_data_size) { - _file.write((const char *)this_data, this_data_size); - - if (!_file) { - return false; - } - - return true; -} - -/** - * Intended to be overloaded to generate a callback when the download - * finishes, either successfully or otherwise. The bool parameter is true if - * the download was successful. - */ -void P3DFileDownload:: -download_finished(bool success) { - P3DDownload::download_finished(success); - _file.close(); -} diff --git a/direct/src/plugin/p3dFileDownload.h b/direct/src/plugin/p3dFileDownload.h deleted file mode 100644 index 923f8ec0ec..0000000000 --- a/direct/src/plugin/p3dFileDownload.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dFileDownload.h - * @author drose - * @date 2009-06-11 - */ - -#ifndef P3DFILEDOWNLOAD_H -#define P3DFILEDOWNLOAD_H - -#include "p3d_plugin_common.h" -#include "p3dDownload.h" - -#include - -/** - * This is a specialization on P3DDownload that specifically writes a disk - * file. - */ -class P3DFileDownload : public P3DDownload { -public: - P3DFileDownload(); - P3DFileDownload(const P3DFileDownload ©); - - bool set_filename(const std::string &filename); - inline const std::string &get_filename() const; - -protected: - virtual bool open_file(); - void close_file(); - virtual bool receive_data(const unsigned char *this_data, - size_t this_data_size); - virtual void download_finished(bool success); - -protected: - ofstream _file; - -private: - std::string _filename; -}; - -#include "p3dFileDownload.I" - -#endif diff --git a/direct/src/plugin/p3dFileParams.I b/direct/src/plugin/p3dFileParams.I deleted file mode 100644 index 8565c47a90..0000000000 --- a/direct/src/plugin/p3dFileParams.I +++ /dev/null @@ -1,63 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dFileParams.I - * @author drose - * @date 2009-06-23 - */ - -/** - * Returns the filename that was passed to set_p3d_filename(). - */ -inline const std::string &P3DFileParams:: -get_p3d_filename() const { - return _p3d_filename; -} - -/** - * Returns the p3d file offset, the location in the file where the p3d data - * starts. - */ -inline int P3DFileParams:: -get_p3d_offset() const { - return _p3d_offset; -} - -/** - * Returns the string that was passed to set_p3d_url(). - */ -inline const std::string &P3DFileParams:: -get_p3d_url() const { - return _p3d_url; -} - -/** - * Returns the number of tokens in the params. - */ -inline int P3DFileParams:: -get_num_tokens() const { - return _tokens.size(); -} - -/** - * Returns the keyword of the nth token. - */ -inline const std::string &P3DFileParams:: -get_token_keyword(int n) const { - assert(n >= 0 && n < (int)_tokens.size()); - return _tokens[n]._keyword; -} - -/** - * Returns the value of the nth token. - */ -inline const std::string &P3DFileParams:: -get_token_value(int n) const { - assert(n >= 0 && n < (int)_tokens.size()); - return _tokens[n]._value; -} diff --git a/direct/src/plugin/p3dFileParams.cxx b/direct/src/plugin/p3dFileParams.cxx deleted file mode 100644 index a32ede4b95..0000000000 --- a/direct/src/plugin/p3dFileParams.cxx +++ /dev/null @@ -1,193 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dFileParams.cxx - * @author drose - * @date 2009-06-23 - */ - -#include "p3dFileParams.h" -#include - -using std::string; - -/** - * - */ -P3DFileParams:: -P3DFileParams() : _p3d_offset(0) { -} - -/** - * - */ -P3DFileParams:: -P3DFileParams(const P3DFileParams ©) : - _p3d_filename(copy._p3d_filename), - _p3d_offset(copy._p3d_offset), - _p3d_url(copy._p3d_url), - _tokens(copy._tokens), - _args(copy._args) -{ -} - -/** - * - */ -void P3DFileParams:: -operator = (const P3DFileParams &other) { - _p3d_filename = other._p3d_filename; - _p3d_offset = other._p3d_offset; - _p3d_url = other._p3d_url; - _tokens = other._tokens; - _args = other._args; -} - -/** - * Specifies the file that contains the instance data. - */ -void P3DFileParams:: -set_p3d_filename(const string &p3d_filename) { - _p3d_filename = p3d_filename; -} - -/** - * Specifies the location in the file where the p3d file data starts. - */ -void P3DFileParams:: -set_p3d_offset(const int &p3d_offset) { - _p3d_offset = p3d_offset; -} - -/** - * Specifies the original URL that hosted the p3d file, if any. This is for - * documentation purposes only; it is communicated to the child Panda3D - * process. - */ -void P3DFileParams:: -set_p3d_url(const string &p3d_url) { - _p3d_url = p3d_url; -} - -/** - * Replaces all the tokens associated with the instance. - */ -void P3DFileParams:: -set_tokens(const P3D_token tokens[], size_t num_tokens) { - _tokens.clear(); - - for (size_t i = 0; i < num_tokens; ++i) { - set_token(tokens[i]._keyword, tokens[i]._value); - } -} - -/** - * Sets an individual token value. - */ -void P3DFileParams:: -set_token(const char *keyword, const char *value) { - Token token; - if (keyword != nullptr) { - // Make the token lowercase, since HTML is case-insensitive but we're not. - for (const char *p = keyword; *p; ++p) { - token._keyword += tolower(*p); - } - } - if (value != nullptr) { - token._value = value; - } - _tokens.push_back(token); -} - -/** - * Specifies the command-line arguments associated with the instance. - */ -void P3DFileParams:: -set_args(int argc, const char *argv[]) { - _args.clear(); - - for (int i = 0; i < argc; ++i) { - const char *arg = argv[i]; - if (arg == nullptr) { - arg = ""; - } - _args.push_back(arg); - } -} - -/** - * Returns the value associated with the first appearance of the named token, - * or empty string if the token does not appear. - */ -string P3DFileParams:: -lookup_token(const string &keyword) const { - Tokens::const_iterator ti; - for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) { - if ((*ti)._keyword == keyword) { - return (*ti)._value; - } - } - - return string(); -} - -/** - * Returns the integer value associated with the first appearance of the named - * token, or zero if the token does not appear or is not an integer. - */ -int P3DFileParams:: -lookup_token_int(const string &keyword) const { - string value = lookup_token(keyword); - return atoi(value.c_str()); -} - -/** - * Returns true if the named token appears in the list, false otherwise. - */ -bool P3DFileParams:: -has_token(const string &keyword) const { - Tokens::const_iterator ti; - for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) { - if ((*ti)._keyword == keyword) { - return true; - } - } - - return false; -} - -/** - * Returns a newly-allocated XML structure that corresponds to the file - * parameter data within this instance. - */ -TiXmlElement *P3DFileParams:: -make_xml() { - TiXmlElement *xfparams = new TiXmlElement("fparams"); - - xfparams->SetAttribute("p3d_filename", _p3d_filename); - xfparams->SetAttribute("p3d_offset", _p3d_offset); - xfparams->SetAttribute("p3d_url", _p3d_url); - - Tokens::const_iterator ti; - for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) { - const Token &token = (*ti); - TiXmlElement *xtoken = new TiXmlElement("token"); - xtoken->SetAttribute("keyword", token._keyword); - xtoken->SetAttribute("value", token._value); - xfparams->LinkEndChild(xtoken); - } - - Args::const_iterator ai; - for (ai = _args.begin(); ai != _args.end(); ++ai) { - TiXmlElement *xarg = new TiXmlElement("arg"); - xarg->SetAttribute("value", (*ai)); - xfparams->LinkEndChild(xarg); - } - - return xfparams; -} diff --git a/direct/src/plugin/p3dFileParams.h b/direct/src/plugin/p3dFileParams.h deleted file mode 100644 index 53b2630680..0000000000 --- a/direct/src/plugin/p3dFileParams.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dFileParams.h - * @author drose - * @date 2009-06-23 - */ - -#ifndef P3DFILEPARAMS_H -#define P3DFILEPARAMS_H - -#include "p3d_plugin_common.h" -#include "get_tinyxml.h" -#include - -/** - * Encapsulates the file parameters: the p3d_filename, and extra tokens. - */ -class P3DFileParams { -public: - P3DFileParams(); - P3DFileParams(const P3DFileParams ©); - void operator = (const P3DFileParams &other); - - void set_p3d_filename(const std::string &p3d_filename); - void set_p3d_offset(const int &p3d_offset); - void set_p3d_url(const std::string &p3d_url); - void set_tokens(const P3D_token tokens[], size_t num_tokens); - void set_token(const char *keyword, const char *value); - void set_args(int argc, const char *argv[]); - - inline const std::string &get_p3d_filename() const; - inline int get_p3d_offset() const; - inline const std::string &get_p3d_url() const; - std::string lookup_token(const std::string &keyword) const; - int lookup_token_int(const std::string &keyword) const; - bool has_token(const std::string &keyword) const; - - inline int get_num_tokens() const; - inline const std::string &get_token_keyword(int n) const; - inline const std::string &get_token_value(int n) const; - - TiXmlElement *make_xml(); - -private: - class Token { - public: - std::string _keyword; - std::string _value; - }; - typedef std::vector Tokens; - typedef std::vector Args; - - std::string _p3d_filename; - int _p3d_offset; - std::string _p3d_url; - Tokens _tokens; - Args _args; -}; - -#include "p3dFileParams.I" - -#endif diff --git a/direct/src/plugin/p3dFloatObject.cxx b/direct/src/plugin/p3dFloatObject.cxx deleted file mode 100644 index 3262d45a54..0000000000 --- a/direct/src/plugin/p3dFloatObject.cxx +++ /dev/null @@ -1,74 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dFloatObject.cxx - * @author drose - * @date 2009-06-30 - */ - -#include "p3dFloatObject.h" - -/** - * - */ -P3DFloatObject:: -P3DFloatObject(double value) : _value(value) { -} - -/** - * - */ -P3DFloatObject:: -P3DFloatObject(const P3DFloatObject ©) : - P3DObject(copy), - _value(copy._value) -{ -} - -/** - * Returns the fundamental type of this kind of object. - */ -P3D_object_type P3DFloatObject:: -get_type() { - return P3D_OT_float; -} - -/** - * Returns the object value coerced to a boolean, if possible. - */ -bool P3DFloatObject:: -get_bool() { - return (_value != 0.0); -} - -/** - * Returns the object value coerced to an integer, if possible. - */ -int P3DFloatObject:: -get_int() { - return (int)_value; -} - -/** - * Returns the object value coerced to a floating-point value, if possible. - */ -double P3DFloatObject:: -get_float() { - return _value; -} - -/** - * Fills the indicated C++ string object with the value of this object coerced - * to a string. - */ -void P3DFloatObject:: -make_string(std::string &value) { - std::ostringstream strm; - strm << _value; - value = strm.str(); -} diff --git a/direct/src/plugin/p3dFloatObject.h b/direct/src/plugin/p3dFloatObject.h deleted file mode 100644 index 13f7ba8358..0000000000 --- a/direct/src/plugin/p3dFloatObject.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dFloatObject.h - * @author drose - * @date 2009-06-30 - */ - -#ifndef P3DFLOATOBJECT_H -#define P3DFLOATOBJECT_H - -#include "p3d_plugin_common.h" -#include "p3dObject.h" - -/** - * An object type that contains a floating-point value. - */ -class P3DFloatObject : public P3DObject { -public: - P3DFloatObject(double value); - P3DFloatObject(const P3DFloatObject ©); - -public: - virtual P3D_object_type get_type(); - virtual bool get_bool(); - virtual int get_int(); - virtual double get_float(); - virtual void make_string(std::string &value); - -private: - double _value; -}; - -#endif diff --git a/direct/src/plugin/p3dHost.I b/direct/src/plugin/p3dHost.I deleted file mode 100644 index 9fb3672205..0000000000 --- a/direct/src/plugin/p3dHost.I +++ /dev/null @@ -1,106 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dHost.I - * @author drose - * @date 2009-08-21 - */ - -/** - * Returns true if the host_dir has already been set, false if not. If this - * returns true it is safe to call get_host_dir(). - */ -inline bool P3DHost:: -has_host_dir() const { - return (!_host_dir.empty()); -} - -/** - * Returns the local directory into which files downloaded from this host will - * be installed. It may not be safe to call this before the host has fully - * bootstrapped; if there is some danger of calling this early in the - * initialization process, you should check has_host_dir() first. - */ -inline const std::string &P3DHost:: -get_host_dir() const { - assert(has_host_dir()); - return _host_dir; -} - -/** - * Returns the root URL of this particular host, as passed from the package - * file. This is a unique string that identifies each host. - */ -inline const std::string &P3DHost:: -get_host_url() const { - return _host_url; -} - -/** - * Returns the root URL of this host, for constructing the URL to download - * contents.xml only. This is the same as get_host_url(), except it is - * guaranteed to end in a slash character. - * - * Also see get_download_url_prefix(). - */ -inline const std::string &P3DHost:: -get_host_url_prefix() const { - return _host_url_prefix; -} - -/** - * Returns the root URL of this host, for downloading everything other than - * the contents.xml file. This is often the same as get_host_url_prefix(), - * but it may be different in the case of an https server for contents.xml. - */ -inline const std::string &P3DHost:: -get_download_url_prefix() const { - return _download_url_prefix; -} - -/** - * Returns the descriptive name provided for this host, if any. Returns the - * url if no descriptive name is provided. This will be available after - * read_contents_file() has been called. - */ -inline const std::string &P3DHost:: -get_descriptive_name() const { - return _descriptive_name; -} - -/** - * Returns true if a contents.xml file has been successfully read for this - * host, false otherwise. - */ -inline bool P3DHost:: -has_contents_file() const { - return (_xcontents != nullptr); -} - -/** - * Returns a number that increments whenever a new version of the contents.xml - * file has been read. This number is local to the session only; it has - * nothing to do with the "seq" value written into the contents.xml file - * itself. - * - * This can be used by packages to determine whether they need to redownload - * from scratch. - */ -inline int P3DHost:: -get_contents_iseq() const { - return _contents_iseq; -} - -/** - * Returns true if the indicated pathname has the same md5 hash as the - * contents.xml file (as provided by the server), false otherwise. - */ -inline bool P3DHost:: -check_contents_hash(const std::string &pathname) const { - return _contents_spec.check_hash(pathname); -} diff --git a/direct/src/plugin/p3dHost.cxx b/direct/src/plugin/p3dHost.cxx deleted file mode 100644 index db189e14c4..0000000000 --- a/direct/src/plugin/p3dHost.cxx +++ /dev/null @@ -1,1056 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dHost.cxx - * @author drose - * @date 2009-08-21 - */ - -#include "p3dHost.h" -#include "p3dInstanceManager.h" -#include "p3dPackage.h" -#include "mkdir_complete.h" -#include "wstring_encode.h" -#include "xml_helpers.h" -#include - -#include - -#ifndef _WIN32 -#include -#endif - -using std::ios; -using std::ostringstream; -using std::string; -using std::wstring; - -/** - * Use P3DInstanceManager::get_host() to construct a new P3DHost. - */ -P3DHost:: -P3DHost(const string &host_url, const string &host_dir) : - _host_url(host_url), - _host_dir(host_dir) -{ - // Ensure that the download URL ends with a slash. - _host_url_prefix = _host_url; - if (!_host_url_prefix.empty() && _host_url_prefix[_host_url_prefix.size() - 1] != '/') { - _host_url_prefix += "/"; - } - _download_url_prefix = _host_url_prefix; - - _descriptive_name = _host_url; - - _xcontents = nullptr; - _contents_expiration = 0; - _contents_iseq = 0; -} - -/** - * - */ -P3DHost:: -~P3DHost() { - if (_xcontents != nullptr) { - delete _xcontents; - } - - Packages::iterator mi; - for (mi = _packages.begin(); mi != _packages.end(); ++mi) { - PackageMap &package_map = (*mi).second; - PackageMap::iterator pi; - for (pi = package_map.begin(); pi != package_map.end(); ++pi) { - PlatformPackages &ppackages = (*pi).second; - PlatformPackages::iterator ppi; - for (ppi = ppackages.begin(); ppi != ppackages.end(); ++ppi) { - delete (*ppi); - } - } - } - _packages.clear(); - - FailedPackages::iterator pi; - for (pi = _failed_packages.begin(); pi != _failed_packages.end(); ++pi) { - delete (*pi); - } - _failed_packages.clear(); -} - -/** - * Returns the pre-defined alternate host with the indicated token, if one is - * defined for this token, or the original host if there is no alternate host - * defined for this token. - * - * This is intended to implement test versions and the like, for instance in - * which a particular p3d file may reference a package on one particular host, - * but there is an alternate version to be tested on a different host. The - * HTML code that embeds the p3d file can choose to set the alt_host token to - * redirect the p3d file to the alternate host. - * - * The actual URL for the alternate host is embedded within the host's - * contents.xml as a security measure, to prevent people from tricking a p3d - * file into running untrusted code by redirecting it to an arbitrary URL. - */ -P3DHost *P3DHost:: -get_alt_host(const string &alt_host) { - assert(_xcontents != nullptr); - - AltHosts::iterator hi; - hi = _alt_hosts.find(alt_host); - if (hi != _alt_hosts.end() && (*hi).second != _host_url) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - return inst_mgr->get_host((*hi).second); - } - return this; -} - -/** - * Returns true if a contents.xml file has been successfully read for this - * host and is still current, false otherwise. - */ -bool P3DHost:: -has_current_contents_file(P3DInstanceManager *inst_mgr) const { - if (inst_mgr->get_verify_contents() == P3D_VC_never - || inst_mgr->get_verify_contents() == P3D_VC_none) { - // If we're not asking to verify contents, then contents.xml files never - // expire. - return has_contents_file(); - } - - time_t now = time(nullptr); - return now < _contents_expiration && (_xcontents != nullptr); -} - -/** - * Reads the contents.xml file in the standard filename, if possible. - * - * Returns true on success, false on failure. - */ -bool P3DHost:: -read_contents_file() { - if (_host_dir.empty()) { - // If we haven't got a host_dir yet, we can't read the contents. - return false; - } - - string standard_filename = _host_dir + "/contents.xml"; - return read_contents_file(standard_filename, false); -} - -/** - * Reads the contents.xml file in the indicated filename. On success, writes - * the contents.xml file into the standard location (if it's not there - * already). - * - * Returns true on success, false on failure. - */ -bool P3DHost:: -read_contents_file(const string &contents_filename, bool fresh_download) { - TiXmlDocument doc(contents_filename.c_str()); - if (!doc.LoadFile()) { - return false; - } - - TiXmlElement *xcontents = doc.FirstChildElement("contents"); - if (xcontents == nullptr) { - return false; - } - - if (_xcontents != nullptr) { - delete _xcontents; - } - _xcontents = (TiXmlElement *)xcontents->Clone(); - ++_contents_iseq; - _contents_spec = FileSpec(); - - int max_age = P3D_CONTENTS_DEFAULT_MAX_AGE; - xcontents->Attribute("max_age", &max_age); - - // Get the latest possible expiration time, based on the max_age indication. - // Any expiration time later than this is in error. - time_t now = time(nullptr); - _contents_expiration = now + (time_t)max_age; - - if (fresh_download) { - _contents_spec.read_hash(contents_filename); - - // Update the XML with the new download information. - TiXmlElement *xorig = xcontents->FirstChildElement("orig"); - while (xorig != nullptr) { - xcontents->RemoveChild(xorig); - xorig = xcontents->FirstChildElement("orig"); - } - - xorig = new TiXmlElement("orig"); - xcontents->LinkEndChild(xorig); - _contents_spec.store_xml(xorig); - - xorig->SetAttribute("expiration", (int)_contents_expiration); - - } else { - // Read the download hash and expiration time from the XML. - int expiration = 0; - TiXmlElement *xorig = xcontents->FirstChildElement("orig"); - if (xorig != nullptr) { - _contents_spec.load_xml(xorig); - xorig->Attribute("expiration", &expiration); - } - if (!_contents_spec.has_hash()) { - _contents_spec.read_hash(contents_filename); - } - - _contents_expiration = std::min(_contents_expiration, (time_t)expiration); - } - - nout << "read contents.xml, max_age = " << max_age - << ", expires in " << std::max(_contents_expiration, now) - now - << " s\n"; - - TiXmlElement *xhost = _xcontents->FirstChildElement("host"); - if (xhost != nullptr) { - const char *url = xhost->Attribute("url"); - if (url != nullptr && _host_url == string(url)) { - // We're the primary host. This is the normal case. - read_xhost(xhost); - - // Build up the list of alternate hosts. - TiXmlElement *xalthost = xhost->FirstChildElement("alt_host"); - while (xalthost != nullptr) { - const char *keyword = xalthost->Attribute("keyword"); - const char *url = xalthost->Attribute("url"); - if (keyword != nullptr && url != nullptr) { - _alt_hosts[keyword] = url; - } - xalthost = xalthost->NextSiblingElement("alt_host"); - } - - } else { - // We're not the primary host; perhaps we're an alternate host. - TiXmlElement *xalthost = xhost->FirstChildElement("alt_host"); - while (xalthost != nullptr) { - const char *url = xalthost->Attribute("url"); - if (url != nullptr && _host_url == string(url)) { - // Yep, we're this alternate host. - read_xhost(xalthost); - break; - } - xalthost = xalthost->NextSiblingElement("alt_host"); - } - } - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - if (_host_dir.empty()) { - determine_host_dir(""); - } - assert(!_host_dir.empty()); - - string standard_filename = _host_dir + "/contents.xml"; - - if (inst_mgr->get_verify_contents() != P3D_VC_never) { - mkdir_complete(_host_dir, nout); - - if (fresh_download) { - if (!save_xml_file(&doc, standard_filename)) { - nout << "Couldn't save to " << standard_filename << "\n"; - } - } else { - if (standardize_filename(standard_filename) != - standardize_filename(contents_filename)) { - if (!copy_file(contents_filename, standard_filename)) { - nout << "Couldn't copy to " << standard_filename << "\n"; - } - } - } - - if (_host_url == inst_mgr->get_host_url()) { - // If this is also the plugin host, then copy the contents.xml file into - // the root Panda directory as well, for the next plugin iteration. - string top_filename = inst_mgr->get_root_dir() + "/contents.xml"; - if (standardize_filename(top_filename) != - standardize_filename(standard_filename)) { - if (!copy_file(standard_filename, top_filename)) { - nout << "Couldn't copy to " << top_filename << "\n"; - } - } - } - } - - return true; -} - -/** - * Reads the host data from the (or ) entry in the - * contents.xml file, or from a p3d_info.xml file. - */ -void P3DHost:: -read_xhost(TiXmlElement *xhost) { - const char *descriptive_name = xhost->Attribute("descriptive_name"); - if (descriptive_name != nullptr && _descriptive_name.empty()) { - _descriptive_name = descriptive_name; - } - - const char *host_dir_basename = xhost->Attribute("host_dir"); - if (host_dir_basename == nullptr) { - host_dir_basename = ""; - } - if (_host_dir.empty()) { - determine_host_dir(host_dir_basename); - } - - // Get the "download" URL, which is the source from which we download - // everything other than the contents.xml file. - const char *download_url = xhost->Attribute("download_url"); - if (download_url != nullptr) { - _download_url_prefix = download_url; - } - if (!_download_url_prefix.empty()) { - if (_download_url_prefix[_download_url_prefix.size() - 1] != '/') { - _download_url_prefix += "/"; - } - } else { - _download_url_prefix = _host_url_prefix; - } - - TiXmlElement *xmirror = xhost->FirstChildElement("mirror"); - while (xmirror != nullptr) { - const char *url = xmirror->Attribute("url"); - if (url != nullptr) { - add_mirror(url); - } - xmirror = xmirror->NextSiblingElement("mirror"); - } -} - -/** - * Returns a (possibly shared) pointer to the indicated package. - * - * The package_seq value should be the expected minimum package_seq value for - * the indicated package. If the given seq value is higher than the - * package_seq value in the contents.xml file cached for the host, it is a - * sign that the contents.xml file is out of date and needs to be - * redownloaded. - */ -P3DPackage *P3DHost:: -get_package(const string &package_name, const string &package_version, - const string &package_platform, const string &package_seq, - const string &alt_host) { - if (!alt_host.empty()) { - if (_xcontents != nullptr) { - // If we're asking for an alt host and we've already read our - // contents.xml file, then we already know all of our hosts, and we can - // start the package off with the correct host immediately. - P3DHost *new_host = get_alt_host(alt_host); - return new_host->get_package(package_name, package_version, - package_platform, package_seq); - } - - // If we haven't read contents.xml yet, we need to create the package - // first, then let it be responsible for downloading our contents.xml, and - // it can migrate to its alt_host after that. - } - - string key = package_name + "_" + package_version; - PlatformPackages &ppackages = _packages[alt_host][key]; - PlatformPackages::iterator ppi; - P3DPackage *package = nullptr; - - // First, look for an exact match of the platform. - for (ppi = ppackages.begin(); ppi != ppackages.end(); ++ppi) { - if ((*ppi)->get_package_platform() == package_platform) { - package = *ppi; - break; - } - } - - // If an exact match isn't found, look for a generic platform. - if (package == nullptr) { - for (ppi = ppackages.begin(); ppi != ppackages.end(); ++ppi) { - if ((*ppi)->get_package_platform().empty()) { - package = *ppi; - break; - } - } - } - - if (package != nullptr) { - if (package->get_failed()) { - // If the package has previously failed, move it aside and try again - // (maybe it just failed because the user interrupted it). - nout << "Package " << key << " has previously failed; trying again.\n"; - _failed_packages.push_back(package); - ppackages.erase(ppi); - package = nullptr; - } - } - - if (package == nullptr) { - package = - new P3DPackage(this, package_name, package_version, package_platform, alt_host); - ppackages.push_back(package); - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (!package_seq.empty() && has_current_contents_file(inst_mgr)) { - // If we were given a specific package_seq file to verify, and we believe - // we have a valid contents.xml file, then check the seq value in the - // contents. - FileSpec desc_file; - string seq; - bool solo; - if (get_package_desc_file(desc_file, seq, solo, - package_name, package_version, package_platform)) { - nout << package_name << ": asked for seq " << package_seq - << ", we have seq " << seq << "\n"; - if (compare_seq(package_seq, seq) > 0) { - // The requested seq value is higher than the one we have on file; our - // contents.xml file must be out of date after all. - nout << "expiring contents.xml for " << get_host_url() << "\n"; - _contents_expiration = 0; - } - } - } - - return package; -} - -/** - * Chooses the most appropriate platform for the indicated package based on - * what this hardware supports and what is actually available. Also fills in - * per_platform, which is a boolean value indicating whether the directory - * structure contains the platform directory or not. - */ -bool P3DHost:: -choose_suitable_platform(string &selected_platform, - bool &per_platform, - const string &package_name, - const string &package_version, - const string &package_platform) { - if (_xcontents == nullptr) { - return false; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - TiXmlElement *xpackage; - - // If the platform is initially unspecified, we allow searching for any - // available supported platform. - if (package_platform == "") { - int num_supported_platforms = inst_mgr->get_num_supported_platforms(); - for (int pi = 0; pi < num_supported_platforms; ++pi) { - string supported_platform = inst_mgr->get_supported_platform(pi); - xpackage = _xcontents->FirstChildElement("package"); - while (xpackage != nullptr) { - const char *name = xpackage->Attribute("name"); - const char *platform = xpackage->Attribute("platform"); - const char *version = xpackage->Attribute("version"); - if (platform == nullptr) { - platform = ""; - } - if (version == nullptr) { - version = ""; - } - if (name != nullptr && - package_name == name && - supported_platform == platform && - package_version == version) { - // Here's the matching package definition. - selected_platform = platform; - per_platform = parse_bool_attrib(xpackage, "per_platform", false); - return true; - } - - xpackage = xpackage->NextSiblingElement("package"); - } - } - } - - // Now, we look for an exact match for the expected platform. - xpackage = _xcontents->FirstChildElement("package"); - while (xpackage != nullptr) { - const char *name = xpackage->Attribute("name"); - const char *platform = xpackage->Attribute("platform"); - const char *version = xpackage->Attribute("version"); - if (platform == nullptr) { - platform = ""; - } - if (version == nullptr) { - version = ""; - } - if (name != nullptr && - package_name == name && - package_platform == platform && - package_version == version) { - // Here's the matching package definition. - selected_platform = platform; - per_platform = parse_bool_attrib(xpackage, "per_platform", false); - return true; - } - - xpackage = xpackage->NextSiblingElement("package"); - } - - // Look one more time, this time looking for a non-platform-specific - // version. - xpackage = _xcontents->FirstChildElement("package"); - while (xpackage != nullptr) { - const char *name = xpackage->Attribute("name"); - const char *platform = xpackage->Attribute("platform"); - const char *version = xpackage->Attribute("version"); - if (platform == nullptr) { - platform = ""; - } - if (version == nullptr) { - version = ""; - } - if (name != nullptr && - package_name == name && - *platform == '\0' && - package_version == version) { - selected_platform = platform; - per_platform = parse_bool_attrib(xpackage, "per_platform", false); - return true; - } - - xpackage = xpackage->NextSiblingElement("package"); - } - - // Couldn't find a suitable platform. - return false; -} - -/** - * Fills the indicated FileSpec with the hash information for the package's - * desc file, and also determines the package's platform. Returns true if - * successful, false if the package is unknown. This requires - * has_contents_file() to return true in order to be successful. - */ -bool P3DHost:: -get_package_desc_file(FileSpec &desc_file, // out - string &package_seq, // out - bool &package_solo, // out - const string &package_name, // in - const string &package_version, // in - const string &package_platform) { // in - if (_xcontents == nullptr) { - return false; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - // Scan the contents data for the indicated package. We expect to match the - // platform precisely, because we previously called - // choose_suitable_platform(). - TiXmlElement *xpackage = _xcontents->FirstChildElement("package"); - while (xpackage != nullptr) { - const char *name = xpackage->Attribute("name"); - const char *platform = xpackage->Attribute("platform"); - const char *version = xpackage->Attribute("version"); - const char *seq = xpackage->Attribute("seq"); - const char *solo = xpackage->Attribute("solo"); - if (platform == nullptr) { - platform = ""; - } - if (version == nullptr) { - version = ""; - } - if (seq == nullptr) { - seq = ""; - } - if (name != nullptr && platform != nullptr && - package_name == name && - package_platform == platform && - package_version == version) { - // Here's the matching package definition. - desc_file.load_xml(xpackage); - package_seq = seq; - package_solo = false; - if (solo != nullptr) { - package_solo = (atoi(solo) != 0); - } - return true; - } - - xpackage = xpackage->NextSiblingElement("package"); - } - - // Couldn't find the named package. - return false; -} - -/** - * Removes the indicated package from the cache of packages known by this - * host. This is invoked from the Python side by AppRunner.deletePackages(), - * so that we remove the package before deleting its files. - */ -void P3DHost:: -forget_package(P3DPackage *package, const string &alt_host) { - string key = package->get_package_name() + "_" + package->get_package_version(); - nout << "Forgetting package " << key << "\n"; - - PlatformPackages &ppackages = _packages[alt_host][key]; - - PlatformPackages::iterator ppi = find(ppackages.begin(), ppackages.end(), package); - if (ppi != ppackages.end()) { - // Hmm, this is a memory leak. But we allow it to remain, since it's an - // unusual circumstance (uninstalling), and it's safer to leak than to - // risk a floating pointer. - ppackages.erase(ppi); - } -} - -/** - * This is called by P3DPackage when it migrates from this host to its final - * alt_host, after downloading the contents.xml file for this file and - * learning the true URL for its target alt_host. - */ -void P3DHost:: -migrate_package_host(P3DPackage *package, const string &alt_host, P3DHost *new_host) { - assert(new_host != this); - assert(new_host == get_alt_host(alt_host)); - - string key = package->get_package_name() + "_" + package->get_package_version(); - - PlatformPackages &ppackages = _packages[alt_host][key]; - - PlatformPackages::iterator ppi = find(ppackages.begin(), ppackages.end(), package); - - assert(ppi != ppackages.end()); - ppackages.erase(ppi); - - PlatformPackages &new_ppackages = new_host->_packages[""][key]; - new_ppackages.push_back(package); -} - -/** - * Selects num_mirrors elements, chosen at random, from the _mirrors list. - * Adds the selected mirrors to result. If there are fewer than num_mirrors - * elements in the list, adds only as many mirrors as we can get. - */ -void P3DHost:: -choose_random_mirrors(std::vector &result, int num_mirrors) { - std::vector selected; - - size_t num_to_select = std::min(_mirrors.size(), (size_t)num_mirrors); - while (num_to_select > 0) { - size_t i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size()); - while (find(selected.begin(), selected.end(), i) != selected.end()) { - // Already found this i, find a new one. - i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size()); - } - selected.push_back(i); - result.push_back(_mirrors[i]); - --num_to_select; - } -} - -/** - * Adds a new URL to serve as a mirror for this host. The mirrors will be - * consulted first, before consulting the host directly. - */ -void P3DHost:: -add_mirror(string mirror_url) { - // Ensure the URL ends in a slash. - if (!mirror_url.empty() && mirror_url[mirror_url.size() - 1] != '/') { - mirror_url += '/'; - } - - // Add it to the _mirrors list, but only if it's not already there. - if (find(_mirrors.begin(), _mirrors.end(), mirror_url) == _mirrors.end()) { - _mirrors.push_back(mirror_url); - } -} - -/** - * Removes the host directory and all its contents from the user's hard disk. - */ -void P3DHost:: -uninstall() { - if (_host_dir.empty()) { - nout << "Cannot uninstall " << _descriptive_name << ": host directory not yet known.\n"; - return; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - // Check if we're even allowed to. - if (inst_mgr->get_verify_contents() == P3D_VC_never) { - nout << "Not allowed to uninstall " << _descriptive_name << ".\n"; - return; - } - - // First, explicitly uninstall each of our packages. - Packages::iterator mi; - for (mi = _packages.begin(); mi != _packages.end(); ++mi) { - PackageMap &package_map = (*mi).second; - PackageMap::iterator pi; - for (pi = package_map.begin(); pi != package_map.end(); ++pi) { - PlatformPackages &ppackages = (*pi).second; - PlatformPackages::iterator ppi; - for (ppi = ppackages.begin(); ppi != ppackages.end(); ++ppi) { - P3DPackage *package = (*ppi); - package->uninstall(); - } - } - } - - // Then, uninstall the host itself. - nout << "Uninstalling " << _descriptive_name << " from " << _host_dir << "\n"; - inst_mgr->delete_directory_recursively(_host_dir); - inst_mgr->forget_host(this); -} - -/** - * Hashes the host_url into a (mostly) unique directory string, which will be - * the root of the host's install tree. Stores the result in _host_dir. - * - * This code is duplicated in Python, in HostInfo.determineHostDir(). - */ -void P3DHost:: -determine_host_dir(const string &host_dir_basename) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - _host_dir = inst_mgr->get_root_dir(); - _host_dir += "/hosts"; - - if (!host_dir_basename.empty()) { - // If the contents.xml specified a host_dir parameter, use it. - inst_mgr->append_safe_dir(_host_dir, host_dir_basename); - return; - } - - // If we didn't get a host_dir parameter, we have to make one up. - _host_dir += "/"; - string hostname; - - // Look for a server name in the URL. Including this string in the - // directory name makes it friendlier for people browsing the directory. - - // We can't use URLSpec here, because we don't link with Panda3D. We have to - // do it by hand. - size_t p = _host_url.find("://"); - if (p != string::npos) { - size_t start = p + 3; - size_t end = _host_url.find("/", start); - // Now start .. end is something like "username@host:port". - - size_t at = _host_url.find("@", start); - if (at < end) { - start = at + 1; - } - - size_t colon = _host_url.find(":", start); - if (colon < end) { - end = colon; - } - - // Now start .. end is just the hostname. - hostname = _host_url.substr(start, end - start); - } - -/* - * Now build a hash string of the whole URL. We'll use MD5 to get a pretty - * good hash, with a minimum chance of collision. Even if there is a hash - * collision, though, it's not the end of the world; it just means that both - * hosts will dump their packages into the same directory, and they'll fight - * over the toplevel contents.xml file. Assuming they use different version - * numbers (which should be safe since they have the same hostname), there - * will be minimal redownloading. - */ - - static const size_t hash_size = 16; - unsigned char md[hash_size]; - - size_t keep_hash = hash_size; - - if (!hostname.empty()) { - _host_dir += hostname; - _host_dir += "_"; - - // If we successfully got a hostname, we don't really need the full hash. - // We'll keep half of it. - keep_hash = keep_hash / 2; - } - - MD5_CTX ctx; - MD5_Init(&ctx); - MD5_Update(&ctx, _host_url.data(), _host_url.size()); - MD5_Final(md, &ctx); - - for (size_t i = 0; i < keep_hash; ++i) { - int high = (md[i] >> 4) & 0xf; - int low = md[i] & 0xf; - _host_dir += P3DInstanceManager::encode_hexdigit(high); - _host_dir += P3DInstanceManager::encode_hexdigit(low); - } -} - - -/** - * Attempts to change the filename into some standard form for comparison with - * other filenames. On a case-insensitive filesystem, this converts the - * filename to lowercase. On Windows, it further replaces forward slashes - * with backslashes. - */ -string P3DHost:: -standardize_filename(const string &filename) { -#if defined(_WIN32) || defined(__APPLE__) - string new_filename; - for (string::const_iterator si = filename.begin(); - si != filename.end(); - ++si) { - char ch = *si; -#ifdef _WIN32 - if (ch == '/') { - ch = '\\'; - } -#endif // _WIN32 - new_filename += tolower(ch); - } - return new_filename; -#else // _WIN32 || __APPLE__ - return filename; -#endif // _WIN32 || __APPLE__ -} - -/** - * Copies the data in the file named by from_filename into the file named by - * to_filename. - */ -bool P3DHost:: -copy_file(const string &from_filename, const string &to_filename) { -#ifdef _WIN32 - ifstream in; - wstring from_filename_w; - if (string_to_wstring(from_filename_w, from_filename)) { - in.open(from_filename_w.c_str(), ios::in | ios::binary); - } - - // Copy to a temporary file first, in case (a) the filenames actually refer - // to the same file, or (b) in case we have different processes writing to - // the same file, and (c) to prevent partially overwriting the file should - // something go wrong. - ostringstream strm; - strm << to_filename << ".t"; - strm << GetCurrentProcessId() << "_" << GetCurrentThreadId(); - string temp_filename = strm.str(); - ofstream out; - wstring temp_filename_w; - if (string_to_wstring(temp_filename_w, temp_filename)) { - out.open(temp_filename_w.c_str(), ios::out | ios::binary); - } - - static const size_t buffer_size = 4096; - char buffer[buffer_size]; - - in.read(buffer, buffer_size); - std::streamsize count = in.gcount(); - while (count != 0) { - out.write(buffer, count); - if (out.fail()) { - unlink(temp_filename.c_str()); - return false; - } - in.read(buffer, buffer_size); - count = in.gcount(); - } - out.close(); - - wstring to_filename_w; - string_to_wstring(to_filename_w, to_filename); - - if (!in.eof()) { - _wunlink(temp_filename_w.c_str()); - return false; - } - - if (_wrename(temp_filename_w.c_str(), to_filename_w.c_str()) == 0) { - return true; - } - - _wunlink(to_filename_w.c_str()); - if (_wrename(temp_filename_w.c_str(), to_filename_w.c_str()) == 0) { - return true; - } - - _wunlink(temp_filename_w.c_str()); - return false; - -#else // _WIN32 - - ifstream in; - in.open(from_filename.c_str(), ios::in | ios::binary); - - // Copy to a temporary file first, in case (a) the filenames actually refer - // to the same file, or (b) in case we have different processes writing to - // the same file, and (c) to prevent partially overwriting the file should - // something go wrong. - ostringstream strm; - strm << to_filename << ".t"; - strm << getpid(); - - string temp_filename = strm.str(); - ofstream out; - out.open(temp_filename.c_str(), ios::out | ios::binary); - - static const size_t buffer_size = 4096; - char buffer[buffer_size]; - - in.read(buffer, buffer_size); - size_t count = in.gcount(); - while (count != 0) { - out.write(buffer, count); - if (out.fail()) { - unlink(temp_filename.c_str()); - return false; - } - in.read(buffer, buffer_size); - count = in.gcount(); - } - out.close(); - - if (!in.eof()) { - unlink(temp_filename.c_str()); - return false; - } - - if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) { - return true; - } - - unlink(to_filename.c_str()); - if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) { - return true; - } - - unlink(temp_filename.c_str()); - return false; -#endif // _WIN32 -} - -/** - * Stores the XML document to the file named by to_filename, safely. - */ -bool P3DHost:: -save_xml_file(TiXmlDocument *doc, const string &to_filename) { - // Save to a temporary file first, in case (a) we have different processes - // writing to the same file, and (b) to prevent partially overwriting the - // file should something go wrong. - -#ifdef _WIN32 - ostringstream strm; - strm << to_filename << ".t"; - strm << GetCurrentProcessId() << "_" << GetCurrentThreadId(); - string temp_filename = strm.str(); - - wstring temp_filename_w; - string_to_wstring(temp_filename_w, temp_filename); - wstring to_filename_w; - string_to_wstring(to_filename_w, to_filename); - - if (!doc->SaveFile(temp_filename.c_str())) { - _wunlink(temp_filename_w.c_str()); - return false; - } - - if (_wrename(temp_filename_w.c_str(), to_filename_w.c_str()) == 0) { - return true; - } - - _wunlink(to_filename_w.c_str()); - if (_wrename(temp_filename_w.c_str(), to_filename_w.c_str()) == 0) { - return true; - } - - _wunlink(temp_filename_w.c_str()); - return false; - -#else // _WIN32 - ostringstream strm; - strm << to_filename << ".t"; - strm << getpid(); - string temp_filename = strm.str(); - - if (!doc->SaveFile(temp_filename.c_str())) { - unlink(temp_filename.c_str()); - return false; - } - - if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) { - return true; - } - - unlink(to_filename.c_str()); - if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) { - return true; - } - - unlink(temp_filename.c_str()); - return false; -#endif // _WIN32 -} - -/** - * Compares the two dotted-integer sequence values numerically. Returns -1 if - * seq_a sorts first, 1 if seq_b sorts first, 0 if they are equivalent. - */ -int P3DHost:: -compare_seq(const string &seq_a, const string &seq_b) { - const char *num_a = seq_a.c_str(); - const char *num_b = seq_b.c_str(); - int comp = compare_seq_int(num_a, num_b); - while (comp == 0) { - if (*num_a != '.') { - if (*num_b != '.') { - // Both strings ran out together. - return 0; - } - // a ran out first. - return -1; - } else if (*num_b != '.') { - // b ran out first. - return 1; - } - - // Increment past the dot. - ++num_a; - ++num_b; - comp = compare_seq(num_a, num_b); - } - - return comp; -} - -/** - * Numerically compares the formatted integer value at num_a with num_b. - * Increments both num_a and num_b to the next character following the valid - * integer. - */ -int P3DHost:: -compare_seq_int(const char *&num_a, const char *&num_b) { - long int a; - char *next_a; - long int b; - char *next_b; - - a = strtol((char *)num_a, &next_a, 10); - b = strtol((char *)num_b, &next_b, 10); - - num_a = next_a; - num_b = next_b; - - if (a < b) { - return -1; - } else if (b < a) { - return 1; - } else { - return 0; - } -} diff --git a/direct/src/plugin/p3dHost.h b/direct/src/plugin/p3dHost.h deleted file mode 100644 index fd880ccde0..0000000000 --- a/direct/src/plugin/p3dHost.h +++ /dev/null @@ -1,115 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dHost.h - * @author drose - * @date 2009-08-21 - */ - -#ifndef P3DHOST_H -#define P3DHOST_H - -#include "p3d_plugin_common.h" -#include "fileSpec.h" -#include - -class FileSpec; -class P3DInstanceManager; -class P3DPackage; - -/** - * Represents a particular download host serving up Panda3D packages. - */ -class P3DHost { -private: - P3DHost(const std::string &host_url, const std::string &host_dir = ""); - ~P3DHost(); - -public: - inline bool has_host_dir() const; - inline const std::string &get_host_dir() const; - inline const std::string &get_host_url() const; - inline const std::string &get_host_url_prefix() const; - inline const std::string &get_download_url_prefix() const; - inline const std::string &get_descriptive_name() const; - - P3DHost *get_alt_host(const std::string &alt_host); - - inline bool has_contents_file() const; - bool has_current_contents_file(P3DInstanceManager *inst_mgr) const; - inline int get_contents_iseq() const; - inline bool check_contents_hash(const std::string &pathname) const; - - bool read_contents_file(); - bool read_contents_file(const std::string &contents_filename, bool fresh_download); - void read_xhost(TiXmlElement *xhost); - - P3DPackage *get_package(const std::string &package_name, - const std::string &package_version, - const std::string &package_platform, - const std::string &package_seq, - const std::string &alt_host = ""); - bool choose_suitable_platform(std::string &selected_platform, - bool &per_platform, - const std::string &package_name, - const std::string &package_version, - const std::string &package_platform); - bool get_package_desc_file(FileSpec &desc_file, - std::string &package_seq, - bool &package_solo, - const std::string &package_name, - const std::string &package_version, - const std::string &package_platform); - - void forget_package(P3DPackage *package, const std::string &alt_host = ""); - void migrate_package_host(P3DPackage *package, const std::string &alt_host, P3DHost *new_host); - - void choose_random_mirrors(std::vector &result, int num_mirrors); - void add_mirror(std::string mirror_url); - - void uninstall(); - -private: - void determine_host_dir(const std::string &host_dir_basename); - - static std::string standardize_filename(const std::string &filename); - static bool copy_file(const std::string &from_filename, const std::string &to_filename); - static bool save_xml_file(TiXmlDocument *doc, const std::string &to_filename); - static int compare_seq(const std::string &seq_a, const std::string &seq_b); - static int compare_seq_int(const char *&num_a, const char *&num_b); - -private: - std::string _host_dir; - std::string _host_url; - std::string _host_url_prefix; - std::string _download_url_prefix; - std::string _descriptive_name; - TiXmlElement *_xcontents; - time_t _contents_expiration; - int _contents_iseq; - FileSpec _contents_spec; - - typedef std::vector Mirrors; - Mirrors _mirrors; - - typedef std::map AltHosts; - AltHosts _alt_hosts; - - typedef std::vector PlatformPackages; - typedef std::map PackageMap; - typedef std::map Packages; - Packages _packages; - typedef std::vector FailedPackages; - FailedPackages _failed_packages; - - friend class P3DInstanceManager; -}; - -#include "p3dHost.I" - -#endif diff --git a/direct/src/plugin/p3dInstance.I b/direct/src/plugin/p3dInstance.I deleted file mode 100644 index 63765832d3..0000000000 --- a/direct/src/plugin/p3dInstance.I +++ /dev/null @@ -1,147 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dInstance.I - * @author drose - * @date 2009-05-29 - */ - -/** - * Returns the current file parameters. - */ -inline const P3DFileParams &P3DInstance:: -get_fparams() const { - return _fparams; -} - -/** - * Returns the current window parameters. - */ -inline const P3DWindowParams &P3DInstance:: -get_wparams() const { - return _wparams; -} - -/** - * Returns a unique integer for each instance in the system. - */ -inline int P3DInstance:: -get_instance_id() const { - return _instance_id; -} - -/** - * Returns a string that uniquely identifies this session. This is a - * constructed string that includes the supplied session_name, the python and - * panda version, and the publisher, as well as any other relevant details; it - * is guaranteed to be unique for each unique session required for different - * P3DInstances. - */ -inline const std::string &P3DInstance:: -get_session_key() const { - return _session_key; -} - -/** - * Returns the platform of this particular session. Before the panda3d - * package has been seen, this is the empty string; once we have downloaded - * the info file for the panda3d package, it is filled in with whatever - * platform is provided (that we're also runtime-compatible with). - * - * Presumably all of the platform-specific packages that are downloaded - * subsequently must be of the exact same platform. - */ -inline const std::string &P3DInstance:: -get_session_platform() const { - return _session_platform; -} - -/** - * Returns the P3DSession that is hosting this instance, or NULL if the - * instance is not running. - */ -inline P3DSession *P3DInstance:: -get_session() const { - return _session; -} - -/** - * Returns a pointer to the asynchronous notification function that was passed - * to the constructor, if any, or NULL if asynchronous notifications are not - * required. - */ -inline P3D_request_ready_func *P3DInstance:: -get_request_ready_func() const { - return _func; -} - -/** - * Returns true if this instance's p3d file is trusted and ready to launch, - * false if it needs to be approved by the user. - */ -inline bool P3DInstance:: -is_trusted() const { - return _p3d_trusted; -} - -/** - * Returns true if this instance is allowed to be scripted by its embedding - * web page, false otherwise. This may not be known until the p3d file has - * been fully downloaded and opened. - */ -inline bool P3DInstance:: -get_matches_script_origin() const { - return _matches_script_origin; -} - -/** - * Returns true if this instance has already been started within some session, - * false otherwise. - */ -inline bool P3DInstance:: -is_started() const { - return (_session != nullptr); -} - -/** - * Returns true if this instance has tried and failed to launch for some - * reason. - */ -inline bool P3DInstance:: -is_failed() const { - return _failed; -} - -/** - * - */ -inline P3DInstance::ImageFile:: -ImageFile() { - _use_standard_image = true; - _temp_filename = nullptr; - _image_placement = P3DSplashWindow::IP_none; -} - -/** - * - */ -inline P3DInstance::ImageFile:: -~ImageFile() { - cleanup(); -} - -/** - * Removes the temporary file, if any. - */ -inline void P3DInstance::ImageFile:: -cleanup() { - if (_temp_filename != nullptr) { - delete _temp_filename; - _temp_filename = nullptr; - } -} diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx deleted file mode 100644 index 220e28c667..0000000000 --- a/direct/src/plugin/p3dInstance.cxx +++ /dev/null @@ -1,3936 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dInstance.cxx - * @author drose - * @date 2009-05-29 - */ - -#include "p3dInstance.h" -#include "p3dInstanceManager.h" -#include "p3dDownload.h" -#include "p3dSession.h" -#include "p3dPackage.h" -#include "p3dSplashWindow.h" -#include "p3dWinSplashWindow.h" -#include "p3dOsxSplashWindow.h" -#include "p3dX11SplashWindow.h" -#include "p3dObject.h" -#include "p3dMainObject.h" -#include "p3dUndefinedObject.h" -#include "p3dMultifileReader.h" -#include "p3dTemporaryFile.h" -#include "parse_color.h" - -#include -#include - -#ifdef __APPLE__ -#include -#include - -using std::max; -using std::min; -using std::ostream; -using std::ostringstream; -using std::stringstream; -using std::string; -using std::vector; - -// Lifted from NSEvent.h (which is Objective-C). -enum { - NSAlphaShiftKeyMask = 1 << 16, - NSShiftKeyMask = 1 << 17, - NSControlKeyMask = 1 << 18, - NSAlternateKeyMask = 1 << 19, - NSCommandKeyMask = 1 << 20, - NSNumericPadKeyMask = 1 << 21, - NSHelpKeyMask = 1 << 22, - NSFunctionKeyMask = 1 << 23, - NSDeviceIndependentModifierFlagsMask = 0xffff0000U -}; - -#endif // __APPLE__ - -#ifdef _WIN32 -typedef P3DWinSplashWindow SplashWindowType; -#elif defined(__APPLE__) && !__LP64__ -typedef P3DOsxSplashWindow SplashWindowType; -#elif defined(HAVE_X11) -typedef P3DX11SplashWindow SplashWindowType; -#else -typedef P3DSplashWindow SplashWindowType; -#endif - -// The amount of time (in seconds) over which we average the total download -// time, for smoothing out the time estimate. -static const double time_average = 10.0; - -// These are the various image files we might download for use in the splash -// window. This list must match the ImageType enum. -const char *P3DInstance::_image_type_names[P3DInstance::IT_num_image_types] = { - "download", - "unauth", - "ready", - "failed", - "launch", - "active", - "auth_ready", - "auth_rollover", - "auth_click", - "play_ready", - "play_rollover", - "play_click", - "none", // Not really used. -}; - -static void -write_str(ostream &out, const wchar_t *str) { - const wchar_t *p = str; - while (*p != 0) { - out << (int)*p << ' '; - ++p; - } -} - -/** - * - */ -P3DInstance:: -P3DInstance(P3D_request_ready_func *func, - const P3D_token tokens[], size_t num_tokens, - int argc, const char *argv[], void *user_data) : - _func(func) -{ - _dom_object = nullptr; - _main_object = new P3DMainObject; - _main_object->set_instance(this); - _user_data = user_data; - _request_pending = false; - _total_time_reports = 0; - _temp_p3d_filename = nullptr; - _image_package = nullptr; - _current_background_image = IT_none; - _current_button_image = IT_none; - _got_fparams = false; - _got_wparams = false; - _p3d_trusted = false; - _xpackage = nullptr; - _certlist_package = nullptr; - _p3dcert_package = nullptr; - - _fparams.set_tokens(tokens, num_tokens); - _fparams.set_args(argc, argv); - - nout << "Creating P3DInstance " << this << ": "; - for (int i = 0; i < _fparams.get_num_tokens(); ++i) { - nout << " " << _fparams.get_token_keyword(i) - << "=\"" << _fparams.get_token_value(i) << "\""; - } - nout << "\n"; - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - _instance_id = inst_mgr->get_unique_id(); - _hidden = (_fparams.lookup_token_int("hidden") != 0); - _matches_run_origin = true; - _matches_script_origin = false; - _allow_python_dev = false; - _keep_user_env = (_fparams.lookup_token_int("keep_user_env") != 0); - _auto_start = (_fparams.lookup_token_int("auto_start") != 0); - _stop_on_ready = (_fparams.lookup_token_int("stop_on_ready") != 0); - _auto_install = true; - if (_fparams.has_token("auto_install")) { - _auto_install = (_fparams.lookup_token_int("auto_install") != 0); - } - _auth_button_clicked = false; - _failed = false; - _session = nullptr; - _auth_session = nullptr; - _panda3d_package = nullptr; - _splash_window = nullptr; - _instance_window_opened = false; - _instance_window_attached = false; - _stuff_to_download = false; - _download_package_index = 0; - _prev_downloaded = 0; - _total_download_size = 0; - _total_downloaded = 0; - _packages_specified = false; - _download_started = false; - _download_complete = false; - _instance_started = false; - - INIT_LOCK(_request_lock); - _requested_stop = false; - -#ifdef __APPLE__ - _shared_fd = -1; - _shared_mmap_size = 0; - _swbuffer = nullptr; - _reversed_buffer = nullptr; - _buffer_data = nullptr; - _data_provider = nullptr; - _buffer_color_space = nullptr; - _buffer_image = nullptr; - - // We have to start with _mouse_active true; firefox doesn't send activate - // events. - _mouse_active = true; - _modifiers = 0; - _frame_timer = nullptr; -#endif // __APPLE__ - - // Set some initial properties. - _main_object->set_float_property("instanceDownloadProgress", 0.0); - _main_object->set_float_property("downloadProgress", 0.0); - _main_object->set_undefined_property("downloadElapsedSeconds"); - _main_object->set_undefined_property("downloadElapsedFormatted"); - _main_object->set_undefined_property("downloadRemainingSeconds"); - _main_object->set_undefined_property("downloadRemainingFormatted"); - _main_object->set_string_property("downloadPackageName", ""); - _main_object->set_string_property("downloadPackageDisplayName", ""); - _main_object->set_bool_property("downloadComplete", false); - _main_object->set_string_property("status", "initial"); - - ostringstream stream; - stream << inst_mgr->get_plugin_major_version() << "." - << inst_mgr->get_plugin_minor_version() << "." - << inst_mgr->get_plugin_sequence_version(); - if (!inst_mgr->get_plugin_official_version()) { - stream << "c"; - } - - // The plugin version as a single number, with three digits reserved for - // each component. - int numeric_version = - inst_mgr->get_plugin_major_version() * 1000000 + - inst_mgr->get_plugin_minor_version() * 1000 + - inst_mgr->get_plugin_sequence_version(); - if (!inst_mgr->get_plugin_official_version()) { - // Subtract 1 if we are not an official version. - --numeric_version; - } - - _main_object->set_string_property("pluginVersionString", stream.str()); - _main_object->set_int_property("pluginMajorVersion", inst_mgr->get_plugin_major_version()); - _main_object->set_int_property("pluginMinorVersion", inst_mgr->get_plugin_minor_version()); - _main_object->set_int_property("pluginSequenceVersion", inst_mgr->get_plugin_sequence_version()); - _main_object->set_bool_property("pluginOfficialVersion", inst_mgr->get_plugin_official_version()); - _main_object->set_int_property("pluginNumericVersion", numeric_version); - _main_object->set_string_property("pluginDistributor", inst_mgr->get_plugin_distributor()); - _main_object->set_string_property("coreapiHostUrl", inst_mgr->get_coreapi_host_url()); - time_t timestamp = inst_mgr->get_coreapi_timestamp(); - _main_object->set_int_property("coreapiTimestamp", (int)timestamp); - const char *timestamp_string = ctime(×tamp); - if (timestamp_string == nullptr) { - timestamp_string = ""; - } - _main_object->set_string_property("coreapiTimestampString", timestamp_string); - _main_object->set_string_property("coreapiVersionString", inst_mgr->get_coreapi_set_ver()); - - _main_object->set_bool_property("trustedEnvironment", (int)inst_mgr->get_trusted_environment()); - _main_object->set_bool_property("consoleEnvironment", (int)inst_mgr->get_console_environment()); - - // We'll start off with the "download" image displayed in the splash window - // (when it opens), until we get stuff downloaded. - set_background_image(IT_download); - - // We'd better ask for the image package up front, even if it turns out we - // don't need it for this particular app. We'll probably use it eventually, - // and it's good to have it loaded early, so we can put up a splash image - // (for instance, the above IT_download image) while we download the real - // contents. - P3DHost *host = inst_mgr->get_host(inst_mgr->get_host_url()); - _image_package = host->get_package("images", "", "", ""); - if (_image_package != nullptr) { - _image_package->add_instance(this); - } - - // Check if the window size has been explicitly set to 0. This means we - // have an explicitly hidden plugin, and we should be prepared not to get a - // wparams from the browser. - if (_fparams.has_token("width") && _fparams.has_token("height") && - (_fparams.lookup_token_int("width") == 0 || - _fparams.lookup_token_int("height") == 0)) { - P3D_window_handle dummy_handle; - memset(&dummy_handle, 0, sizeof(dummy_handle)); - P3DWindowParams wparams(P3D_WT_hidden, 0, 0, 0, 0, dummy_handle); - set_wparams(wparams); - } -} - -/** - * - */ -P3DInstance:: -~P3DInstance() { - assert(_session == nullptr); - cleanup(); - - if (_dom_object != nullptr) { - P3D_OBJECT_DECREF(_dom_object); - _dom_object = nullptr; - } - - if (_main_object != nullptr) { - nout << "panda_script_object ref = " - << _main_object->_ref_count << "\n"; - _main_object->set_instance(nullptr); - P3D_OBJECT_DECREF(_main_object); - _main_object = nullptr; - } - - Downloads::iterator di; - for (di = _downloads.begin(); di != _downloads.end(); ++di) { - P3DDownload *download = (*di).second; - if (download->get_instance() == this) { - download->set_instance(nullptr); - } - p3d_unref_delete(download); - } - _downloads.clear(); - - DESTROY_LOCK(_request_lock); - - // TODO: Is it possible for someone to delete an instance while a download - // is still running? Who will crash when this happens? -} - -/** - * Invalidates the instance and removes any structures prior to deleting. - */ -void P3DInstance:: -cleanup() { - _failed = true; - - if (_auth_session != nullptr) { - _auth_session->shutdown(false); - p3d_unref_delete(_auth_session); - _auth_session = nullptr; - } - - for (int i = 0; i < (int)IT_num_image_types; ++i) { - _image_files[i].cleanup(); - } - - // Tell all of the packages that we're no longer in business for them. - Packages::iterator pi; - for (pi = _packages.begin(); pi != _packages.end(); ++pi) { - (*pi)->remove_instance(this); - } - _packages.clear(); - if (_image_package != nullptr) { - _image_package->remove_instance(this); - _image_package = nullptr; - } - - if (_certlist_package != nullptr) { - _certlist_package->remove_instance(this); - _certlist_package = nullptr; - } - - if (_p3dcert_package != nullptr) { - _p3dcert_package->remove_instance(this); - _p3dcert_package = nullptr; - } - - if (_splash_window != nullptr) { - delete _splash_window; - _splash_window = nullptr; - } - - if (_temp_p3d_filename != nullptr) { - delete _temp_p3d_filename; - _temp_p3d_filename = nullptr; - } - - if (_xpackage != nullptr) { - delete _xpackage; - _xpackage = nullptr; - } - -#ifdef __APPLE__ - if (_frame_timer != nullptr) { - CFRunLoopTimerInvalidate(_frame_timer); - CFRelease(_frame_timer); - _frame_timer = nullptr; - } - - free_swbuffer(); -#endif - - TiXmlDocument *doc = nullptr; - ACQUIRE_LOCK(_request_lock); - RawRequests::iterator ri; - for (ri = _raw_requests.begin(); ri != _raw_requests.end(); ++ri) { - doc = (*ri); - delete doc; - } - _raw_requests.clear(); - RELEASE_LOCK(_request_lock); - - BakedRequests::iterator bi; - for (bi = _baked_requests.begin(); bi != _baked_requests.end(); ++bi) { - P3D_request *request = (*bi); - finish_request(request, false); - } - _baked_requests.clear(); - - Downloads::iterator di; - for (di = _downloads.begin(); di != _downloads.end(); ++di) { - P3DDownload *download = (*di).second; - download->cancel(); - } -} - - -/** - * Specifies a URL that should be contacted to download the instance data. - * Normally this, or set_p3d_filename() or make_p3d_stream(), is only called - * once. - * - * The instance data at the other end of this URL is key. We can't start the - * instance until we have downloaded the instance file and examined the - * p3d_info.xml, and we know what Python version we need and so forth. - */ -void P3DInstance:: -set_p3d_url(const string &p3d_url) { - if (p3d_url.empty()) { - nout << "No p3d URL specified. Cannot run.\n"; - set_failed(); - return; - } - _fparams.set_p3d_url(p3d_url); - - // Save the last part of the URL as the p3d_basename, for reporting purposes - // or whatever. - determine_p3d_basename(p3d_url); - - // Make a temporary file to receive the instance data. - assert(_temp_p3d_filename == nullptr); - _temp_p3d_filename = new P3DTemporaryFile(".p3d"); - _stuff_to_download = true; - - // Maybe it's time to open a splash window now. - make_splash_window(); - - // Mark the time we started downloading, so we'll know when to reveal the - // progress bar, and we can predict the total download time. -#ifdef _WIN32 - _start_dl_tick = GetTickCount(); -#else - gettimeofday(&_start_dl_timeval, nullptr); -#endif - _show_dl_instance_progress = false; - - // Start downloading the data. - InstanceDownload *download = new InstanceDownload(this); - download->set_url(p3d_url); - download->set_filename(_temp_p3d_filename->get_filename()); - if (_fparams.has_token("p3d_size")) { - download->set_total_expected_data(_fparams.lookup_token_int("p3d_size")); - } - - _main_object->set_string_property("status", "downloading_instance"); - start_download(download); -} - -/** - * Indicates an intention to transmit the p3d data as a stream. Should return - * a new unique stream ID to receive it. - */ -int P3DInstance:: -make_p3d_stream(const string &p3d_url) { - _fparams.set_p3d_url(p3d_url); - - // Save the last part of the URL as the p3d_basename, for reporting purposes - // or whatever. - determine_p3d_basename(p3d_url); - - // Make a temporary file to receive the instance data. - assert(_temp_p3d_filename == nullptr); - _temp_p3d_filename = new P3DTemporaryFile(".p3d"); - _stuff_to_download = true; - - // Maybe it's time to open a splash window now. - make_splash_window(); - - // Mark the time we started downloading, so we'll know when to reveal the - // progress bar. -#ifdef _WIN32 - _start_dl_tick = GetTickCount(); -#else - gettimeofday(&_start_dl_timeval, nullptr); -#endif - _show_dl_instance_progress = false; - - // Start downloading the data. - InstanceDownload *download = new InstanceDownload(this); - download->set_url(p3d_url); - download->set_filename(_temp_p3d_filename->get_filename()); - if (_fparams.has_token("p3d_size")) { - download->set_total_expected_data(_fparams.lookup_token_int("p3d_size")); - } - - _main_object->set_string_property("status", "downloading_instance"); - return start_download(download, false); -} - -/** - * Specifies the file that contains the instance data. Normally this is only - * called once. - */ -void P3DInstance:: -set_p3d_filename(const string &p3d_filename, const int &p3d_offset) { - determine_p3d_basename(p3d_filename); - priv_set_p3d_filename(p3d_filename, p3d_offset); -} - -/** - * Changes the window parameters, e.g. to resize or reposition the window; or - * sets the parameters for the first time, creating the initial window. - */ -void P3DInstance:: -set_wparams(const P3DWindowParams &wparams) { - bool prev_got_wparams = _got_wparams; - _got_wparams = true; - _wparams = wparams; - - nout << "set_wparams: " << wparams.get_window_type() - << " " << wparams.get_win_width() << " " << wparams.get_win_height() - << "\n"; - - if (_hidden || _wparams.get_win_width() == 0 || _wparams.get_win_height() == 0) { - // If we're a hidden app, or if the window has no size, then it is really - // a hidden window, regardless of what type it claims to be. - _wparams.set_window_type(P3D_WT_hidden); - } - - if (_wparams.get_window_type() != P3D_WT_hidden) { - // Update or create the splash window. - if (_splash_window != nullptr) { - _splash_window->set_wparams(_wparams); - } else { - make_splash_window(); - } - } - - // It doesn't make much sense to go further than this point if the instance - // is already in the failed state. - if (is_failed()) { - return; - } - - if (_wparams.get_window_type() != P3D_WT_hidden) { -#ifdef __APPLE__ - // On Mac, we have to communicate the results of the rendering back via - // shared memory, instead of directly parenting windows to the browser. - // Set up this mechanism. - int x_size = _wparams.get_win_width(); - int y_size = _wparams.get_win_height(); - if (x_size != 0 && y_size != 0) { - if (_swbuffer == nullptr || _swbuffer->get_x_size() != x_size || - _swbuffer->get_y_size() != y_size) { - // We need to open a new shared buffer. - alloc_swbuffer(); - } - - if (_swbuffer == nullptr) { - nout << "Could not open swbuffer\n"; - } - } -#endif // __APPLE__ - } - - // Update the instance in the sub-process. - if (_session != nullptr) { - TiXmlDocument *doc = new TiXmlDocument; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "setup_window"); - xcommand->SetAttribute("instance_id", get_instance_id()); - TiXmlElement *xwparams = _wparams.make_xml(this); - - doc->LinkEndChild(xcommand); - xcommand->LinkEndChild(xwparams); - - _session->send_command(doc); - } - - if (!prev_got_wparams) { - // If this was the first set_wparams call, try to start the app. - if (_p3d_trusted && get_packages_ready()) { - ready_to_start(); - } - } -} - -/** - * Returns a pointer to the top-level scriptable object of the instance, to be - * used by JavaScript code in the browser to control this program. - */ -P3D_object *P3DInstance:: -get_panda_script_object() const { - nout << "get_panda_script_object\n"; - return _main_object; -} - -/** - * Stores a pointer to the top-level window object of the browser, to be used - * by Panda code to control JavaScript. The new object's reference count is - * incremented, and the previous object's is decremented. - */ -void P3DInstance:: -set_browser_script_object(P3D_object *browser_script_object) { - nout << "set_browser_script_object\n"; - if (browser_script_object != _dom_object) { - P3D_OBJECT_XDECREF(_dom_object); - _dom_object = browser_script_object; - if (_dom_object != nullptr) { - P3D_OBJECT_INCREF(_dom_object); - } - - if (_session != nullptr) { - send_browser_script_object(); - } - } - - // Query the origin: protocol, hostname, and port. We'll use this to limit - // access to the scripting interfaces for a particular p3d file. - _origin_protocol.clear(); - _origin_hostname.clear(); - _origin_port.clear(); - if (_dom_object != nullptr) { - P3D_object *location = P3D_OBJECT_GET_PROPERTY(_dom_object, "location"); - if (location != nullptr) { - P3D_object *protocol = P3D_OBJECT_GET_PROPERTY(location, "protocol"); - if (protocol != nullptr) { - int size = P3D_OBJECT_GET_STRING(protocol, nullptr, 0); - char *buffer = new char[size]; - P3D_OBJECT_GET_STRING(protocol, buffer, size); - _origin_protocol = string(buffer, size); - delete [] buffer; - P3D_OBJECT_DECREF(protocol); - } - - P3D_object *hostname = P3D_OBJECT_GET_PROPERTY(location, "hostname"); - if (hostname != nullptr) { - int size = P3D_OBJECT_GET_STRING(hostname, nullptr, 0); - char *buffer = new char[size]; - P3D_OBJECT_GET_STRING(hostname, buffer, size); - _origin_hostname = string(buffer, size); - delete [] buffer; - P3D_OBJECT_DECREF(hostname); - } - - P3D_object *port = P3D_OBJECT_GET_PROPERTY(location, "port"); - if (port != nullptr) { - int size = P3D_OBJECT_GET_STRING(port, nullptr, 0); - char *buffer = new char[size]; - P3D_OBJECT_GET_STRING(port, buffer, size); - _origin_port = string(buffer, size); - delete [] buffer; - P3D_OBJECT_DECREF(port); - } - - if (_origin_hostname.empty() && _origin_protocol == "file:") { - _origin_hostname = "localhost"; - } - - if (_origin_port.empty()) { - // Maybe the actual URL doesn't include the port, in which case it is - // implicit. - if (_origin_protocol == "http:") { - _origin_port = "80"; - } else if (_origin_protocol == "https:") { - _origin_port = "443"; - } - } - - P3D_OBJECT_DECREF(location); - } - } - - nout << "origin is " << _origin_protocol << "//" << _origin_hostname; - if (!_origin_port.empty()) { - nout << ":" << _origin_port; - } - nout << "\n"; -} - - -/** - * Returns true if the instance has any pending requests at the time of this - * call, false otherwise. - */ -bool P3DInstance:: -has_request() { - return _request_pending; -} - -/** - * Returns a newly-allocated P3D_request corresponding to the pending request - * for the host, or NULL if there is no pending request. If the return value - * is non-NULL, it should eventually be passed back to finish_request() for - * cleanup. - */ -P3D_request *P3DInstance:: -get_request() { - bake_requests(); - if (_baked_requests.empty()) { - // No requests ready. - _request_pending = false; - return nullptr; - } - - P3D_request *request = _baked_requests.front(); - _baked_requests.pop_front(); - _request_pending = !_baked_requests.empty(); - - if (request != nullptr) { - switch (request->_request_type) { - case P3D_RT_notify: - { - // Also eval the associated HTML token, if any. - string message = request->_request._notify._message; - string expression = _fparams.lookup_token(message); - nout << "notify: " << message << " " << expression << "\n"; - if (!expression.empty() && _dom_object != nullptr) { - P3D_object *result = P3D_OBJECT_EVAL(_dom_object, expression.c_str()); - P3D_OBJECT_XDECREF(result); - } - } - break; - - case P3D_RT_stop: - { - // We also send an implicit message when Python requests itself to - // shutdown. - _main_object->set_pyobj(nullptr); - _main_object->set_string_property("status", "stopped"); - - string message = "onpythonstop"; - string expression = _fparams.lookup_token(message); - nout << "notify: " << message << " " << expression << "\n"; - if (!expression.empty() && _dom_object != nullptr) { - P3D_object *result = P3D_OBJECT_EVAL(_dom_object, expression.c_str()); - P3D_OBJECT_XDECREF(result); - } - } - break; - - case P3D_RT_callback: - { - // And when the callback request is extracted, we make the callback. - P3D_callback_func *func = request->_request._callback._func; - void *data = request->_request._callback._data; - (*func)(data); - } - break; - - case P3D_RT_forget_package: - { - // We're being asked to forget a particular package. - const char *host_url = request->_request._forget_package._host_url; - const char *package_name = request->_request._forget_package._package_name; - const char *package_version = request->_request._forget_package._package_version; - if (package_version == nullptr) { - package_version = ""; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3DHost *host = inst_mgr->get_host(host_url); - if (package_name != nullptr) { - P3DPackage *package = host->get_package(package_name, package_version, _session_platform, ""); - host->forget_package(package); - } else { - // If a NULL package name is given, forget the whole host. - inst_mgr->forget_host(host); - } - } - break; - - default: - break; - } - } - - return request; -} - -/** - * Copies requests from the _raw_requests queue, which is built up in one or - * more sub-threads, into the _baked_requests queue, which is publicly - * presented to the browser. Along the way, some requests (like script - * requests) are handled immediately. - * - * At the end of this call, _baked_requests will contain the current set of - * requests pending for the browser. - * - * This method should only be called in the main thread. - */ -void P3DInstance:: -bake_requests() { - while (true) { - // Get the latest request from the read thread. - TiXmlDocument *doc = nullptr; - ACQUIRE_LOCK(_request_lock); - if (!_raw_requests.empty()) { - doc = _raw_requests.front(); - _raw_requests.pop_front(); - } - RELEASE_LOCK(_request_lock); - - if (doc == nullptr) { - // No more requests to process right now. - return; - } - - // Now we've got a request in XML form; convert it to P3D_request form. - TiXmlElement *xrequest = doc->FirstChildElement("request"); - assert(xrequest != nullptr); - P3D_request *request = make_p3d_request(xrequest); - delete doc; - - if (request != nullptr) { - _baked_requests.push_back(request); - } - } -} - -/** - * May be called in any thread to add a new XML request to the pending_request - * queue for this instance. The XML document will be deleted when the request - * is eventually handled. - */ -void P3DInstance:: -add_raw_request(TiXmlDocument *doc) { - ACQUIRE_LOCK(_request_lock); - _raw_requests.push_back(doc); - _request_pending = true; - RELEASE_LOCK(_request_lock); - - // We don't decode the XML yet, since we might be running in any thread - // here. We'll decode it in the main thread, where it's safe. - - // Tell the world we've got a new request. - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - inst_mgr->signal_request_ready(this); - if (_session != nullptr) { - _session->signal_request_ready(this); - } -} - -/** - * May be called in the main thread only to add a new request to the - * baked_request queue. This request queue is directly passed on the browser - * without further processing at this level. - */ -void P3DInstance:: -add_baked_request(P3D_request *request) { - assert(request->_instance == nullptr); - request->_instance = this; - ref(); - - _baked_requests.push_back(request); - _request_pending = true; - - // Tell the world we've got a new request. - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - inst_mgr->signal_request_ready(this); -} - -/** - * Deallocates a previously-returned request from get_request(). If handled - * is true, the request has been handled by the host; otherwise, it has been - * ignored. - */ -void P3DInstance:: -finish_request(P3D_request *request, bool handled) { - assert(request != nullptr); - if (request->_instance == nullptr) { - nout << "Ignoring empty request " << request << "\n"; - return; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (inst_mgr->validate_instance(request->_instance) == nullptr) { - // nout << "Ignoring unknown request " << request << "\n"; - return; - } - - switch (request->_request_type) { - case P3D_RT_stop: - break; - - case P3D_RT_get_url: - if (request->_request._get_url._url != nullptr) { - free((char *)request->_request._get_url._url); - request->_request._get_url._url = nullptr; - } - break; - - case P3D_RT_notify: - if (request->_request._notify._message != nullptr) { - free((char *)request->_request._notify._message); - request->_request._notify._message = nullptr; - } - break; - - case P3D_RT_forget_package: - if (request->_request._forget_package._host_url != nullptr) { - free((char *)request->_request._forget_package._host_url); - request->_request._forget_package._host_url = nullptr; - } - if (request->_request._forget_package._package_name != nullptr) { - free((char *)request->_request._forget_package._package_name); - request->_request._forget_package._package_name = nullptr; - } - if (request->_request._forget_package._package_version != nullptr) { - free((char *)request->_request._forget_package._package_version); - request->_request._forget_package._package_version = nullptr; - } - break; - - default: - break; - } - - p3d_unref_delete(((P3DInstance *)request->_instance)); - request->_instance = nullptr; - - delete request; -} - -/** - * Called by the host in response to a get_url request, this sends the data - * retrieved from the requested URL, a piece at a time. - */ -bool P3DInstance:: -feed_url_stream(int unique_id, - P3D_result_code result_code, - int http_status_code, - size_t total_expected_data, - const unsigned char *this_data, - size_t this_data_size) { - Downloads::iterator di = _downloads.find(unique_id); - if (di == _downloads.end()) { - nout << "Unexpected feed_url_stream for " << unique_id << "\n"; - // Don't know this request. - return false; - } - - P3DDownload *download = (*di).second; - assert(download->get_instance() == this); - bool download_ok = download->feed_url_stream - (result_code, http_status_code, total_expected_data, - this_data, this_data_size); - - if (!download_ok || download->get_download_finished()) { - // All done. - if (download->get_instance() == this) { - download->set_instance(nullptr); - } - _downloads.erase(di); - p3d_unref_delete(download); - } - - return download_ok; -} - -/** - * Responds to the os-generated window event. Returns true if the event is - * handled, false if ignored. - */ -bool P3DInstance:: -handle_event(const P3D_event_data &event) { - bool retval = false; - if (_splash_window != nullptr) { - if (_splash_window->handle_event(event)) { - retval = true; - } - } - -#if defined(__APPLE__) - if (event._event_type == P3D_ET_osx_event_record) { - retval = handle_event_osx_event_record(event); - } else if (event._event_type == P3D_ET_osx_cocoa) { - retval = handle_event_osx_cocoa(event); - } else { - assert(false); - } -#endif // __APPLE__ - - return retval; -} - -/** - * Returns the log filename for this particular session, if the session was - * started and if it has a log file. Returns empty string if the session - * never started or if it lacks a log file. - * - * This is the same value returned by P3DSession::get_log_pathname(), except - * that it remains valid even after the session has closed. - */ -const string &P3DInstance:: -get_log_pathname() const { - if (_session != nullptr) { - return _session->_log_pathname; - } - return _log_pathname; -} - -/** - * Adds the package to the list of packages used by this instance. The - * instance will share responsibility for downloading the package with any of - * the other instances that use the same package. - * - * The seq value should be the expected minimum package_seq value for the - * indicated package. If the given seq value is higher than the package_seq - * value in the contents.xml file cached for the host, it is a sign that the - * contents.xml file is out of date and needs to be redownloaded. - */ -void P3DInstance:: -add_package(const string &name, const string &version, const string &seq, - P3DHost *host) { - string alt_host = _fparams.lookup_token("alt_host"); - - // Look up in the p3d_info.xml file to see if this p3d file has a specific - // alt_host indication for this host_url. - string alt_host_url = find_alt_host_url(host->get_host_url(), alt_host); - if (!alt_host_url.empty()) { - // If it does, we go ahead and switch to that host now, instead of - // bothering to contact the original host. - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - host = inst_mgr->get_host(alt_host_url); - alt_host.clear(); - } - - if (!host->has_contents_file()) { - // Since we haven't downloaded this host's contents.xml file yet, get its - // additional host information. - get_host_info(host); - } - - P3DPackage *package = host->get_package(name, version, _session_platform, - seq, alt_host); - add_package(package); -} - -/** - * Adds the package to the list of packages used by this instance. The - * instance will share responsibility for downloading the package with any of - * the other instances that use the same package. - */ -void P3DInstance:: -add_package(P3DPackage *package) { - if (find(_packages.begin(), _packages.end(), package) != _packages.end()) { - // Already have this package. - return; - } - - if (package->get_package_name() == "panda3d") { - _panda3d_package = package; - } - - _packages.push_back(package); - - // This call must be at the end of this method, because it might ultimately - // start the application before it returns (if this was the last required - // package). - package->add_instance(this); -} - -/** - * Indicates that the given package is destructing and this instance should no - * longer retain a pointer to it. This is normally called only by the - * P3DPackage destructor, and it invalidates the instance. - */ -void P3DInstance:: -remove_package(P3DPackage *package) { - Packages::iterator pi = find(_packages.begin(), _packages.end(), package); - if (pi != _packages.end()) { - _packages.erase(pi); - } - pi = find(_downloading_packages.begin(), _downloading_packages.end(), package); - if (pi != _downloading_packages.end()) { - _downloading_packages.erase(pi); - } - if (package == _image_package) { - _image_package = nullptr; - } - if (package == _certlist_package) { - _certlist_package = nullptr; - } - if (package == _p3dcert_package) { - _p3dcert_package = nullptr; - } - if (package == _panda3d_package) { - _panda3d_package = nullptr; - } - - set_failed(); -} - -/** - * Returns true if all of the packages required by the instance have their - * information available and are ready to be downloaded, false if one or more - * of them is still waiting for information (or has failed). - */ -bool P3DInstance:: -get_packages_info_ready() const { - if (!_packages_specified) { - // We haven't even specified the full set of required packages yet. - return false; - } - - Packages::const_iterator pi; - for (pi = _packages.begin(); pi != _packages.end(); ++pi) { - if (!(*pi)->get_info_ready()) { - return false; - } - } - - return true; -} - -/** - * Returns true if all of the packages required by the instance (as specified - * in previous calls to add_package()) have been fully downloaded and are - * ready to run, or false if one or more of them still requires downloading. - */ -bool P3DInstance:: -get_packages_ready() const { - if (!_packages_specified) { - // We haven't even specified the full set of required packages yet. - return false; - } - - Packages::const_iterator pi; - for (pi = _packages.begin(); pi != _packages.end(); ++pi) { - if (!(*pi)->get_ready()) { - return false; - } - } - - return true; -} - -/** - * Returns true if any of the packages required by the instance have failed to - * download (and thus we will never be ready). - */ -bool P3DInstance:: -get_packages_failed() const { - Packages::const_iterator pi; - for (pi = _packages.begin(); pi != _packages.end(); ++pi) { - if ((*pi)->get_failed()) { - return true; - } - } - - return false; -} - -/** - * Adds a newly-allocated P3DDownload object to the download queue, and issues - * the request to start it downloading. As the download data comes in, it - * will be fed to the download object. - * - * This increments the P3DDownload object's reference count, and will - * decrement it (and possibly delete the object) after download_finished() has - * been called. - * - * add_request should be true to actually request the URL from the plugin, or - * false not to. Normally, this should always be set true, except in the one - * special case of make_p3d_stream(), in which case the plugin is already - * prepared to send the stream and doesn't need to have it requested. - * - * Returns the unique ID of this stream. - */ -int P3DInstance:: -start_download(P3DDownload *download, bool add_request) { - assert(download->get_download_id() == 0); - assert(!download->get_url().empty()); - - if (is_failed()) { - // Can't download anything more after failure. - download->cancel(); - return 0; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - int download_id = inst_mgr->get_unique_id(); - download->set_download_id(download_id); - download->set_instance(this); - - download->ref(); - bool inserted = _downloads.insert(Downloads::value_type(download_id, download)).second; - assert(inserted); - - // add_request will be false only for the initial p3d stream, which the - // plugin already knows about. For all other download streams, add_request - // is true in order to ask the plugin for the stream. - if (add_request) { - P3D_request *request = new P3D_request; - request->_instance = nullptr; - request->_request_type = P3D_RT_get_url; - request->_request._get_url._url = strdup(download->get_url().c_str()); - request->_request._get_url._unique_id = download_id; - - add_baked_request(request); - } - - return download_id; -} - -/** - * Asks the host to shut down this particular instance, presumably because the - * user has indicated it should exit. This call may be made in any thread. - */ -void P3DInstance:: -request_stop_sub_thread() { - // Atomically check _requested_stop. - bool add_request = false; - ACQUIRE_LOCK(_request_lock); - if (!_requested_stop) { - _requested_stop = true; - add_request = true; - } - RELEASE_LOCK(_request_lock); - - // If we haven't requested a stop already, do it now. - if (add_request) { - TiXmlDocument *doc = new TiXmlDocument; - TiXmlElement *xrequest = new TiXmlElement("request"); - xrequest->SetAttribute("rtype", "stop"); - doc->LinkEndChild(xrequest); - - add_raw_request(doc); - } -} - -/** - * Asks the host to shut down this particular instance, presumably because the - * user has indicated it should exit. This call may only be made in the main - * thread. - */ -void P3DInstance:: -request_stop_main_thread() { - // Atomically check _requested_stop. - bool add_request = false; - ACQUIRE_LOCK(_request_lock); - if (!_requested_stop) { - _requested_stop = true; - add_request = true; - } - RELEASE_LOCK(_request_lock); - - // If we haven't requested a stop already, do it now. - if (add_request) { - _requested_stop = true; - P3D_request *request = new P3D_request; - request->_instance = nullptr; - request->_request_type = P3D_RT_stop; - add_baked_request(request); - } -} - -/** - * Asks the host to refresh the plugin window. This is only relevant for - * windowless plugins, for instance, the way OSX plugins always run. - */ -void P3DInstance:: -request_refresh() { - P3D_request *request = new P3D_request; - request->_instance = nullptr; - request->_request_type = P3D_RT_refresh; - add_baked_request(request); -} - -/** - * Asks the host to make a callback later. - */ -void P3DInstance:: -request_callback(P3D_callback_func *func, void *data) { - P3D_request *request = new P3D_request; - request->_instance = nullptr; - request->_request_type = P3D_RT_callback; - request->_request._callback._func = func; - request->_request._callback._data = data; - add_baked_request(request); -} - -/** - * Returns a newly-allocated XML structure that corresponds to the data within - * this instance. - */ -TiXmlElement *P3DInstance:: -make_xml() { - assert(_got_fparams); - - TiXmlElement *xinstance = new TiXmlElement("instance"); - xinstance->SetAttribute("instance_id", _instance_id); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - xinstance->SetAttribute("root_dir", inst_mgr->get_root_dir()); - xinstance->SetAttribute("log_directory", inst_mgr->get_log_directory()); - xinstance->SetAttribute("verify_contents", (int)inst_mgr->get_verify_contents()); - - // Tell the Panda process that it was started by a plugin that knows about - // the new per_platform flag. - xinstance->SetAttribute("respect_per_platform", 1); - - if (!inst_mgr->get_super_mirror().empty()) { - xinstance->SetAttribute("super_mirror", inst_mgr->get_super_mirror()); - } - - - TiXmlElement *xfparams = _fparams.make_xml(); - xinstance->LinkEndChild(xfparams); - - if (_got_wparams) { - TiXmlElement *xwparams = _wparams.make_xml(this); - xinstance->LinkEndChild(xwparams); - } - - Packages::const_iterator pi; - for (pi = _packages.begin(); pi != _packages.end(); ++pi) { - TiXmlElement *xpackage = (*pi)->make_xml(); - xinstance->LinkEndChild(xpackage); - } - - TiXmlElement *xmain = _session->p3dobj_to_xml(_main_object); - xmain->SetValue("main"); - xinstance->LinkEndChild(xmain); - - return xinstance; -} - -/** - * Called by the P3DSplashWindow code (maybe in a sub-thread) when the user - * clicks the button visible on the splash window. This will forward the - * event to the main thread via the request callback mechanism. - */ -void P3DInstance:: -splash_button_clicked_sub_thread() { - TiXmlDocument *doc = new TiXmlDocument; - TiXmlElement *xrequest = new TiXmlElement("request"); - xrequest->SetAttribute("rtype", "notify"); - xrequest->SetAttribute("message", "buttonclick"); - doc->LinkEndChild(xrequest); - - add_raw_request(doc); -} - -/** - * Called only in the main thread, indirectly from - * splash_button_clicked_sub_thread(), as the result of the user clicking on - * the button visible in the splash window. - */ -void P3DInstance:: -splash_button_clicked_main_thread() { - if (is_failed()) { - // Can't click the button after we've failed. - nout << "Ignoring click for failed instance\n"; - return; - } - - if (!_p3d_trusted) { - auth_button_clicked(); - } else if (_session == nullptr) { - play_button_clicked(); - } else { - nout << "Ignoring click for already-started instance\n"; - } -} - -/** - * Called to authorize the p3d file by the user clicking the red "auth" - * button. - */ -void P3DInstance:: -auth_button_clicked() { - // Delete the previous session and create a new one. - if (_auth_session != nullptr) { - _auth_session->shutdown(false); - p3d_unref_delete(_auth_session); - _auth_session = nullptr; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - _auth_session = inst_mgr->authorize_instance(this); - _auth_session->ref(); -} - -/** - * Called to start the game by the user clicking the green "play" button, or - * by JavaScript calling play(). - */ -void P3DInstance:: -play_button_clicked() { - if (_session == nullptr && _p3d_trusted) { - set_button_image(IT_none); - if (!_download_started) { - // Now we initiate the download. - _auto_install = true; - _auto_start = true; - if (get_packages_info_ready()) { - ready_to_install(); - } - - } else { - set_background_image(IT_launch); - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - inst_mgr->start_instance(this); - } - } -} - -/** - * Called by the P3DAuthSession code in a sub-thread when the auth dialog - * exits (for instance, because the user approved the certificate, or - * cancelled). - */ -void P3DInstance:: -auth_finished_sub_thread() { - TiXmlDocument *doc = new TiXmlDocument; - TiXmlElement *xrequest = new TiXmlElement("request"); - xrequest->SetAttribute("rtype", "notify"); - xrequest->SetAttribute("message", "authfinished"); - doc->LinkEndChild(xrequest); - - add_raw_request(doc); -} - -/** - * Called only in the main thread, indirectly from auth_finished_sub_thread(), - * as the result of the user closing the auth dialog. - */ -void P3DInstance:: -auth_finished_main_thread() { - // Set this flag to indicate that the user has clicked on the red "auth" - // button. This eliminates the need to click on the green "start" button. - _auth_button_clicked = true; - - // After the authorization program has returned, check the signature again. - check_p3d_signature(); -} - -/** - * Stops the instance (if it is running) and deletes any packages referenced - * by the instance. This is normally called by JavaScript, via - * P3DMainObject::call_uninstall(). - */ -bool P3DInstance:: -uninstall_packages() { - if (_packages.empty()) { - // If we have no packages (for instance, because we're untrusted), we - // can't uninstall anything. - nout << "Uninstall failed: no packages.\n"; - return false; - } - - nout << "Uninstalling " << _packages.size() << " packages\n"; - - Packages::const_iterator pi; - for (pi = _packages.begin(); pi != _packages.end(); ++pi) { - P3DPackage *package = (*pi); - if (package != _image_package && package != _certlist_package && - package != _p3dcert_package) { - package->uninstall(); - } - } - - // Also clean up the start directory, if we have a custom start dir. We - // won't do this if verify_contents is 'none'. - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (inst_mgr->get_verify_contents() != P3D_VC_never) { - string start_dir_suffix = get_start_dir_suffix(); - if (!start_dir_suffix.empty()) { - string start_dir = inst_mgr->get_start_dir() + start_dir_suffix; - nout << "Cleaning up start directory " << start_dir << "\n"; - inst_mgr->delete_directory_recursively(start_dir); - } - } - - return true; -} - -/** - * Stops the instance (if it is running) and deletes all packages downloaded - * from any of the host(s) referenced by the instance. This is a more - * aggressive uninstall than uninstall_packages(). This is normally called by - * JavaScript, via P3DMainObject::call_uninstall(). - */ -bool P3DInstance:: -uninstall_host() { - if (_packages.empty()) { - // If we have no packages (for instance, because we're untrusted), we - // can't uninstall anything. - nout << "Uninstall failed: no packages.\n"; - return false; - } - - uninstall_packages(); - - // Collect the set of hosts referenced by this instance. - std::set hosts; - Packages::const_iterator pi; - for (pi = _packages.begin(); pi != _packages.end(); ++pi) { - P3DPackage *package = (*pi); - if (package != _image_package && package != _certlist_package && - package != _p3dcert_package) { - hosts.insert(package->get_host()); - } - } - nout << "Uninstalling " << hosts.size() << " hosts\n"; - - // Uninstall all of them. - std::set::iterator hi; - for (hi = hosts.begin(); hi != hosts.end(); ++hi) { - P3DHost *host = (*hi); - host->uninstall(); - } - - return true; -} - -/** - * The private implementation of set_p3d_filename(), this does all the work - * except for updating p3d_basename. It is intended to be called internally, - * and might be passed a temporary filename. - */ -void P3DInstance:: -priv_set_p3d_filename(const string &p3d_filename, const int &p3d_offset) { - if (!_fparams.get_p3d_filename().empty()) { - nout << "p3d_filename already set to: " << _fparams.get_p3d_filename() - << ", trying to set to " << p3d_filename << "\n"; - return; - } - - _fparams.set_p3d_filename(p3d_filename); - // The default for p3d_offset is -1, which means not to change it. - if (p3d_offset >= 0) { - _fparams.set_p3d_offset(p3d_offset); - } - _got_fparams = true; - - _main_object->set_float_property("instanceDownloadProgress", 1.0); - - // Generate a special notification: onpluginload, indicating the plugin has - // read its parameters and is ready to be queried (even if Python has not - // yet started). - send_notify("onpluginload"); - - if (!_mf_reader.open_read(_fparams.get_p3d_filename(), _fparams.get_p3d_offset())) { - if (_fparams.get_p3d_offset() == 0) { - nout << "Couldn't read " << _fparams.get_p3d_filename() << "\n"; - } else { - nout << "Couldn't read " << _fparams.get_p3d_filename() - << " at offset " << _fparams.get_p3d_offset() << "\n"; - } - set_failed(); - return; - } - - check_p3d_signature(); -} - -/** - * Determines _p3d_basename from the indicated URL. - */ -void P3DInstance:: -determine_p3d_basename(const string &p3d_url) { - string file_part = p3d_url; - size_t question = file_part.find('?'); - if (question != string::npos) { - file_part = file_part.substr(0, question); - } - size_t slash = file_part.rfind('/'); - if (slash != string::npos) { - file_part = file_part.substr(slash + 1); - } - _p3d_basename = file_part; - - nout << "p3d_basename = " << _p3d_basename << "\n"; -} - -/** - * Returns true if the indicated origin_match string, one of either run_origin - * or script_origin from the p3d_info.xml file, matches the origin of the page - * that embedded the p3d file. - */ -bool P3DInstance:: -check_matches_origin(const string &origin_match) { - // First, separate the string up at the semicolons. - size_t p = 0; - size_t semicolon = origin_match.find(';'); - while (semicolon != string::npos) { - if (check_matches_origin_one(origin_match.substr(p, semicolon - p))) { - return true; - } - p = semicolon + 1; - semicolon = origin_match.find(';', p); - } - if (check_matches_origin_one(origin_match.substr(p))) { - return true; - } - - // It doesn't match any of the semicolon-delimited strings within - // origin_match. - return false; -} - -/** - * Called for each semicolon-delimited string within origin_match passed to - * check_matches_origin(). - */ -bool P3DInstance:: -check_matches_origin_one(const string &origin_match) { - // Do we have a protocol? - size_t p = 0; - size_t colon = origin_match.find(':'); - if (colon + 1 < origin_match.length() && origin_match[colon + 1] == '/') { - // Yes. It should therefore match the protocol we have in the origin. - string protocol = origin_match.substr(0, colon + 1); - if (!check_matches_component(_origin_protocol, protocol)) { - return false; - } - p = colon + 2; - // We'll support both http:hostname and http:hostname, in case the user is - // sloppy. - if (p < origin_match.length() && origin_match[p] == '/') { - ++p; - } - colon = origin_match.find(':', p); - } - - // Do we have a port? - if (colon < origin_match.length() && isdigit(origin_match[colon + 1])) { - // Yes. It should therefore match the port we have in the origin. - string port = origin_match.substr(colon + 1); - if (!check_matches_component(_origin_port, port)) { - return false; - } - } - - // The hostname should also match what we have in the origin. - string hostname = origin_match.substr(p, colon - p); - if (!check_matches_hostname(_origin_hostname, hostname)) { - return false; - } - - // Everything matches. - return true; -} - -/** - * Matches the hostname of check_matches_origin: the individual components of - * the hostname are matched independently, with '**.' allowed at the beginning - * to indicate zero or more prefixes. Returns true on match, false on - * failure. - */ -bool P3DInstance:: -check_matches_hostname(const string &orig, const string &match) { - // First, separate both strings up at the dots. - vector orig_components; - separate_components(orig_components, orig); - - vector match_components; - separate_components(match_components, match); - - // If the first component of match is "**", it means we accept any number, - // zero or more, of components at the beginning of the hostname. - if (!match_components.empty() && match_components[0] == "**") { - // Remove the leading "**" - match_components.erase(match_components.begin()); - // Then remove any extra components from the beginning of orig_components; - // we won't need to check them. - if (orig_components.size() > match_components.size()) { - size_t num_to_remove = orig_components.size() - match_components.size(); - orig_components.erase(orig_components.begin(), orig_components.begin() + num_to_remove); - } - } - - // Now match the remaining components one-to-one. - if (match_components.size() != orig_components.size()) { - return false; - } - - vector::const_iterator p = orig_components.begin(); - vector::const_iterator p2 = match_components.begin(); - - while (p != orig_components.end()) { - assert(p2 != match_components.end()); - if (!check_matches_component(*p, *p2)) { - return false; - } - ++p; - ++p2; - } - - assert(p2 == match_components.end()); - return true; -} - -/** - * Separates the indicated hostname into its components at the dots. - */ -void P3DInstance:: -separate_components(vector &components, const string &str) { - size_t p = 0; - size_t dot = str.find('.'); - while (dot != string::npos) { - components.push_back(str.substr(p, dot - p)); - p = dot + 1; - dot = str.find('.', p); - } - components.push_back(str.substr(p)); -} - -/** - * Matches a single component of check_matches_origin: either protocol or - * port, or a single component of the hostname. Case-insensitive, and - * supports the '*' wildcard operator to match the entire component. Returns - * true on match, false on failure. - */ -bool P3DInstance:: -check_matches_component(const string &orig, const string &match) { - if (match == "*") { - return true; - } - - // Case-insensitive compare. - if (orig.length() != match.length()) { - return false; - } - - string::const_iterator p = orig.begin(); - string::const_iterator p2 = match.begin(); - - while (p != orig.end()) { - assert(p2 != match.end()); - if (tolower(*p) != tolower(*p2)) { - return false; - } - ++p; - ++p2; - } - - assert(p2 == match.end()); - return true; -} - -/** - * Checks the signature(s) encoded in the p3d file, and looks to see if any of - * them are recognized. - * - * If the signature is recognized, calls mark_p3d_trusted(); otherwise, calls - * mark_p3d_untrusted(). - */ -void P3DInstance:: -check_p3d_signature() { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (inst_mgr->get_trusted_environment()) { - // If we're in a trusted environment (e.g. the panda3d command line, - // where we've already downloaded the p3d file separately), then - // everything is approved. - mark_p3d_trusted(); - return; - } - - // See if we've previously approved the certificate--any certificate--that's - // signing this p3d file. - int num_signatures = _mf_reader.get_num_signatures(); - for (int i = 0; i < num_signatures; ++i) { - const P3DMultifileReader::CertChain &chain = _mf_reader.get_signature(i); - - // Here's a certificate that has signed this multifile. - X509 *cert = chain[0]._cert; - - // Look up the certificate to see if we've stored a copy in our certs dir. - if (inst_mgr->find_cert(cert)) { - mark_p3d_trusted(); - return; - } - } - - // Check the list of pre-approved certificates. - if (_certlist_package == nullptr) { - // We have to go download this package. - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3DHost *host = inst_mgr->get_host(inst_mgr->get_host_url()); - _certlist_package = host->get_package("certlist", "", "", ""); - if (_certlist_package != nullptr) { - _certlist_package->add_instance(this); - } - - // When the package finishes downloading, we will come back here. - return; - } - - if (!_certlist_package->get_ready() && !_certlist_package->get_failed()) { - // Wait for it to finish downloading. - return; - } - - // Couldn't find any approved certificates. - mark_p3d_untrusted(); - return; -} - -/** - * This is called internally when it has been determined that the p3d file - * can't (yet) be trusted, for instance because it lacks a signature, or - * because it is signed by an unrecognized certificate. This puts up the red - * "auth" button and waits for the user to approve the app before continuing. - */ -void P3DInstance:: -mark_p3d_untrusted() { - // Failed test. - nout << "p3d untrusted\n"; - if (is_failed()) { - return; - } - - if (_p3dcert_package == nullptr) { - // We have to go download this package. - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3DHost *host = inst_mgr->get_host(inst_mgr->get_host_url()); - _p3dcert_package = host->get_package("p3dcert", "", "", ""); - if (_p3dcert_package != nullptr) { - _p3dcert_package->add_instance(this); - } - - // When the package finishes downloading, we will come back here. - return; - } - - if (_p3dcert_package->get_failed()) { - // Oh, too bad for us. We're dependent on this package which we weren't - // able to download for some reason. - set_failed(); - } - - if (!_p3dcert_package->get_ready()) { - // Wait for it to finish downloading. - return; - } - - // OK, we've got the authorization program; we can put up the red button - // now. - - // Notify JS that we've got no trust of the p3d file. - _main_object->set_bool_property("trusted", false); - send_notify("onunauth"); - set_background_image(IT_unauth); - set_button_image(IT_auth_ready); - make_splash_window(); -} - -/** - * This is called internally when it has been determined that the p3d file can - * be trusted and started. When this is called, the p3d file will be examined - * and made ready to start; it will not be started until this is called. - */ -void P3DInstance:: -mark_p3d_trusted() { - nout << "p3d trusted\n"; - // Only call this once. - if (_p3d_trusted) { - nout << "mark_p3d_trusted() called twice on " << _fparams.get_p3d_filename() - << "\n"; - return; - } - - // Extract the application desc file from the p3d file. - stringstream sstream; - if (!_mf_reader.extract_one(sstream, "p3d_info.xml")) { - nout << "No p3d_info.xml file found in " << _fparams.get_p3d_filename() << "\n"; - set_failed(); - - } else { - sstream.seekg(0); - TiXmlDocument doc; - sstream >> doc; - - scan_app_desc_file(&doc); - } - - // Now we've got no further need to keep the _mf_reader open. - _mf_reader.close(); - - // For the moment, all sessions will be unique. TODO: support multiple - // instances per session. - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - ostringstream strm; - strm << inst_mgr->get_unique_id(); - _session_key = strm.str(); - - // Notify JS that we've accepted the trust of the p3d file. - _main_object->set_bool_property("trusted", true); - send_notify("onauth"); - - // Now that we're all set up, grab the panda3d package. We need to examine - // this before we can start to download the remaining packages. - add_panda3d_package(); -} - -/** - * Reads the p3d_info.xml file at instance startup, to determine the set of - * required packages and so forth. - */ -void P3DInstance:: -scan_app_desc_file(TiXmlDocument *doc) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - TiXmlElement *xpackage = doc->FirstChildElement("package"); - if (xpackage == nullptr) { - return; - } - assert(_xpackage == nullptr); - _xpackage = (TiXmlElement *)xpackage->Clone(); - - TiXmlElement *xconfig = _xpackage->FirstChildElement("config"); - if (xconfig != nullptr) { - int hidden = 0; - if (xconfig->QueryIntAttribute("hidden", &hidden) == TIXML_SUCCESS) { - if (hidden != 0) { - _hidden = true; - } - } - - const char *log_basename = xconfig->Attribute("log_basename"); - if (log_basename != nullptr) { - _log_basename = log_basename; - } - - const char *prc_name = xconfig->Attribute("prc_name"); - if (prc_name != nullptr) { - _prc_name = prc_name; - } - - const char *start_dir = xconfig->Attribute("start_dir"); - if (start_dir != nullptr) { - _start_dir = start_dir; - } - - const char *run_origin = xconfig->Attribute("run_origin"); - if (run_origin != nullptr) { - _matches_run_origin = check_matches_origin(run_origin); - } - - const char *script_origin = xconfig->Attribute("script_origin"); - if (script_origin != nullptr) { - _matches_script_origin = check_matches_origin(script_origin); - } - - int allow_python_dev = 0; - if (xconfig->QueryIntAttribute("allow_python_dev", &allow_python_dev) == TIXML_SUCCESS) { - _allow_python_dev = (allow_python_dev != 0); - } - - int keep_user_env = 0; - if (xconfig->QueryIntAttribute("keep_user_env", &keep_user_env) == TIXML_SUCCESS) { - _keep_user_env = (keep_user_env != 0); - } - - int auto_start = 0; - if (xconfig->QueryIntAttribute("auto_start", &auto_start) == TIXML_SUCCESS) { - _auto_start = (auto_start != 0); - } - - int auto_install = 0; - if (xconfig->QueryIntAttribute("auto_install", &auto_install) == TIXML_SUCCESS) { - _auto_install = (auto_install != 0); - } - } - - nout << "_matches_run_origin = " << _matches_run_origin << "\n"; - nout << "_matches_script_origin = " << _matches_script_origin << "\n"; - - if (inst_mgr->get_trusted_environment()) { - // If we're in a trusted environment, it is as if the origin always - // matches. - _matches_run_origin = true; - _matches_script_origin = true; - } - - if (_auth_button_clicked) { - // But finally, if the user has already clicked through the red "auth" - // button, no need to present himher with another green "play" button as - // well. - _auto_install = true; - _auto_start = true; - } - - nout << "_auto_install = " << _auto_install - << ", _auto_start = " << _auto_start - << ", _stop_on_ready = " << _stop_on_ready - << "\n"; - - if (_hidden && _got_wparams) { - _wparams.set_window_type(P3D_WT_hidden); - } - - if (!_matches_run_origin) { - nout << "Cannot run " << _p3d_basename << " from origin " - << _origin_protocol << "//" << _origin_hostname - << ":" << _origin_port << "\n"; - set_failed(); - } -} - -/** - * Adds the "panda3d" package only. This package must be downloaded first, - * and its desc file examined, before we can begin downloading the other - * packages. - */ -void P3DInstance:: -add_panda3d_package() { - assert(!_packages_specified); - assert(_panda3d_package == nullptr); - if (_xpackage == nullptr) { - return; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - TiXmlElement *xrequires = _xpackage->FirstChildElement("requires"); - while (xrequires != nullptr) { - const char *name = xrequires->Attribute("name"); - const char *host_url = xrequires->Attribute("host"); - if (name != nullptr && host_url != nullptr && strcmp(name, "panda3d") == 0) { - const char *version = xrequires->Attribute("version"); - if (version == nullptr) { - version = ""; - } - const char *seq = xrequires->Attribute("seq"); - if (seq == nullptr) { - seq = ""; - } - P3DHost *host = inst_mgr->get_host(host_url); - add_package(name, version, seq, host); - return; - } - - xrequires = xrequires->NextSiblingElement("requires"); - } - - nout << "No panda3d package found in " << _fparams.get_p3d_filename() << "\n"; - set_failed(); -} - -/** - * Adds the set of packages required by this p3d file to the _packages member. - * If _auto_install is true, this will also start downloading them. - */ -void P3DInstance:: -add_packages() { - assert(!_packages_specified); - if (_xpackage == nullptr) { - return; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - TiXmlElement *xrequires = _xpackage->FirstChildElement("requires"); - while (xrequires != nullptr) { - const char *name = xrequires->Attribute("name"); - const char *host_url = xrequires->Attribute("host"); - if (name != nullptr && host_url != nullptr) { - const char *version = xrequires->Attribute("version"); - if (version == nullptr) { - version = ""; - } - const char *seq = xrequires->Attribute("seq"); - if (seq == nullptr) { - seq = ""; - } - P3DHost *host = inst_mgr->get_host(host_url); - add_package(name, version, seq, host); - } - - xrequires = xrequires->NextSiblingElement("requires"); - } - - _packages_specified = true; - - consider_start_download(); - - // Now that we've scanned the p3d file, and prepared the list of packages, - // it's safe to set the trusted flag. - _p3d_trusted = true; - - // If the packages are already downloaded, start the instance rolling. - if (get_packages_ready()) { - mark_download_complete(); - } -} - - -/** - * Looks in the p3d_info.xml file for the alt_host associated with the - * indicated host_url, if any. Returns empty string if there is no match. - */ -string P3DInstance:: -find_alt_host_url(const string &host_url, const string &alt_host) { - TiXmlElement *xhost = _xpackage->FirstChildElement("host"); - while (xhost != nullptr) { - const char *url = xhost->Attribute("url"); - if (url != nullptr && host_url == url) { - // This matches the host. Now do we have a matching alt_host keyword - // for this host? - TiXmlElement *xalt_host = xhost->FirstChildElement("alt_host"); - while (xalt_host != nullptr) { - const char *keyword = xalt_host->Attribute("keyword"); - if (keyword != nullptr && alt_host == keyword) { - const char *alt_host_url = xalt_host->Attribute("url"); - if (alt_host_url != nullptr) { - return alt_host_url; - } - } - xalt_host = xalt_host->NextSiblingElement("alt_host"); - } - } - xhost = xhost->NextSiblingElement("host"); - } - - return string(); -} - -/** - * Looks in the p3d_info.xml file for the auxiliary host information for the - * selected host. Some of this information is helpful to have before the host - * has read its own contents.xml file (particularly the host_dir - * specification). - */ -void P3DInstance:: -get_host_info(P3DHost *host) { - // We should only call this function if we haven't already read the host's - // more-authoritative contents.xml file. - assert(!host->has_contents_file()); - - TiXmlElement *xhost = _xpackage->FirstChildElement("host"); - while (xhost != nullptr) { - const char *url = xhost->Attribute("url"); - if (url != nullptr && host->get_host_url() == url) { - // Found the entry for this particular host. - host->read_xhost(xhost); - return; - } - xhost = xhost->NextSiblingElement("host"); - } - - // Didn't find an entry for this host; oh well. -} - -/** - * Determines the local path to the appropriate start directory for this - * instance, within the generic "start" directory. Returns empty string if - * this instance doesn't specify a custom start directory. - * - * If this is nonempty, it will begin with a slash--the intention is to append - * this to the end of the generic start_dir path. - */ -string P3DInstance:: -get_start_dir_suffix() const { - string start_dir_suffix; - - string start_dir = get_fparams().lookup_token("start_dir"); - if (start_dir.empty()) { - start_dir = _start_dir; - - if (!start_dir.empty()) { - // If the start_dir is taken from the p3d file (and not from the HTML - // tokens), then we also append the alt_host name to the start_dir, so - // that each alt_host variant will run in a different directory. - string alt_host = get_fparams().lookup_token("alt_host"); - if (!alt_host.empty()) { - start_dir += "_"; - start_dir += alt_host; - } - } - } - if (!start_dir.empty()) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - inst_mgr->append_safe_dir(start_dir_suffix, start_dir); - } - - return start_dir_suffix; -} - -/** - * Sends the XML sequence to inform the session of our browser's toplevel - * window object. - */ -void P3DInstance:: -send_browser_script_object() { - TiXmlDocument *doc = new TiXmlDocument; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "pyobj"); - xcommand->SetAttribute("op", "set_browser_script_object"); - if (_dom_object != nullptr) { - xcommand->LinkEndChild(_session->p3dobj_to_xml(_dom_object)); - } - - doc->LinkEndChild(xcommand); - - _session->send_command(doc); -} - -/** - * Creates a new P3D_request structure from the XML. Returns NULL if no - * request is needed. - */ -P3D_request *P3DInstance:: -make_p3d_request(TiXmlElement *xrequest) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3D_request *request = nullptr; - - const char *rtype = xrequest->Attribute("rtype"); - if (rtype != nullptr) { - if (strcmp(rtype, "notify") == 0) { - const char *message = xrequest->Attribute("message"); - if (message != nullptr) { - // A notify message from Python code. - request = new P3D_request; - request->_instance = nullptr; - request->_request_type = P3D_RT_notify; - request->_request._notify._message = strdup(message); - handle_notify_request(message); - } - - } else if (strcmp(rtype, "script") == 0) { - // We don't actually build a P3D_request for a script request; we always - // just handle it immediately. - const char *operation = xrequest->Attribute("operation"); - TiXmlElement *xobject = xrequest->FirstChildElement("object"); - const char *property_name = xrequest->Attribute("property_name"); - int needs_response = 0; - xrequest->Attribute("needs_response", &needs_response); - int unique_id = 0; - xrequest->Attribute("unique_id", &unique_id); - - P3D_object *value = nullptr; - TiXmlElement *xvalue = xrequest->FirstChildElement("value"); - if (xvalue != nullptr) { - value = _session->xml_to_p3dobj(xvalue); - } - if (value == nullptr) { - value = inst_mgr->new_none_object(); - } - - if (operation != nullptr && xobject != nullptr) { - P3D_object *object = _session->xml_to_p3dobj(xobject); - - if (property_name == nullptr) { - property_name = ""; - } - - handle_script_request(operation, object, property_name, value, - (needs_response != 0), unique_id); - } - - P3D_OBJECT_DECREF(value); - - } else if (strcmp(rtype, "drop_p3dobj") == 0) { - int object_id; - if (xrequest->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) { - // We no longer need to keep this reference. - _session->drop_p3dobj(object_id); - } - - } else if (strcmp(rtype, "stop") == 0) { - // A stop request from Python code. This is kind of weird, but OK. - request = new P3D_request; - request->_instance = nullptr; - request->_request_type = P3D_RT_stop; - - } else if (strcmp(rtype, "forget_package") == 0) { - const char *host_url = xrequest->Attribute("host_url"); - if (host_url != nullptr) { - // A Python-level request to remove a package from the cache. - request = new P3D_request; - request->_instance = nullptr; - request->_request_type = P3D_RT_forget_package; - request->_request._forget_package._host_url = strdup(host_url); - request->_request._forget_package._package_name = nullptr; - request->_request._forget_package._package_version = nullptr; - - const char *package_name = xrequest->Attribute("package_name"); - const char *package_version = xrequest->Attribute("package_version"); - if (package_name != nullptr) { - request->_request._forget_package._package_name = strdup(package_name); - if (package_version != nullptr) { - request->_request._forget_package._package_version = strdup(package_version); - } - } - } - - } else { - nout << "Ignoring request of type " << rtype << "\n"; - } - } - - if (request != nullptr) { - assert(request->_instance == nullptr); - request->_instance = this; - ref(); - } - return request; -} - -/** - * Called (in the main thread) when a notify request is received from the - * subprocess. - */ -void P3DInstance:: -handle_notify_request(const string &message) { - // We look for certain notify events that have particular meaning to this - // instance. - if (message == "onpythonload") { - // Once Python is up and running, we can get the actual main object from - // the Python side, and merge it with our own. - - TiXmlDocument *doc = new TiXmlDocument; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "pyobj"); - xcommand->SetAttribute("op", "get_panda_script_object"); - doc->LinkEndChild(xcommand); - TiXmlDocument *response = _session->command_and_response(doc); - - P3D_object *result = nullptr; - if (response != nullptr) { - TiXmlElement *xresponse = response->FirstChildElement("response"); - if (xresponse != nullptr) { - TiXmlElement *xvalue = xresponse->FirstChildElement("value"); - if (xvalue != nullptr) { - result = _session->xml_to_p3dobj(xvalue); - } - } - delete response; - } - - if (result != nullptr) { - if (_matches_script_origin) { - // We only actually merge the objects if this web page is allowed to - // call our scripting functions. - _main_object->set_pyobj(result); - } else { - // Otherwise, we just do a one-time application of the toplevel - // properties down to Python. - _main_object->apply_properties(result); - } - P3D_OBJECT_DECREF(result); - } - - _main_object->set_string_property("status", "starting"); - - } else if (message == "onwindowopen") { - // The process told us that it just successfully opened its window, for - // the first time. Hide the splash window. - _instance_window_opened = true; - if (_splash_window != nullptr) { - _splash_window->set_visible(false); - } - - // Guess we won't be using (most of) these images any more. - for (int i = 0; i < (int)IT_num_image_types; ++i) { - if (i != IT_active) { - _image_files[i].cleanup(); - } - } - - _main_object->set_string_property("status", "open"); - - } else if (message == "onwindowattach") { - // The graphics window has been attached to the browser frame (maybe - // initially, maybe later). Hide the splash window. - - // We don't actually hide the splash window immediately on OSX, because on - // that platform, we can hide it as soon as we render the first frame, - // avoiding an empty frame in that little period of time between the - // window opening and the first frame being drawn. - _instance_window_attached = true; -#ifndef __APPLE__ - if (_splash_window != nullptr) { - _splash_window->set_visible(false); - } -#endif // __APPLE__ - -#ifdef __APPLE__ - // Start a timer to update the frame repeatedly. This seems to be - // steadier than waiting for nullEvent. - if (_frame_timer == nullptr) { - CFRunLoopTimerContext timer_context; - memset(&timer_context, 0, sizeof(timer_context)); - timer_context.info = this; - _frame_timer = CFRunLoopTimerCreate - (nullptr, 0, 1.0 / 60.0, 0, 0, timer_callback, &timer_context); - CFRunLoopRef run_loop = CFRunLoopGetCurrent(); - CFRunLoopAddTimer(run_loop, _frame_timer, kCFRunLoopCommonModes); - } -#endif // __APPLE__ - - } else if (message == "onwindowdetach") { - // The graphics window has been removed from the browser frame. Restore - // the splash window. - _instance_window_opened = true; - _instance_window_attached = false; - set_background_image(IT_active); - if (_splash_window != nullptr) { - _splash_window->set_visible(true); - } - -#ifdef __APPLE__ - // Stop the frame timer; we don't need it any more. - if (_frame_timer != nullptr) { - CFRunLoopTimerInvalidate(_frame_timer); - CFRelease(_frame_timer); - _frame_timer = nullptr; - } -#endif // __APPLE__ - - } else if (message == "buttonclick") { - // We just got a special "button click" message from the sub-thread. This - // case is a little unusual, as it came from the splash window and not - // from Python (we presumably haven't even started Python yet). We use - // this as a sneaky way to forward the event from the sub-thread to the - // main thread. - splash_button_clicked_main_thread(); - - } else if (message == "authfinished") { - // Similarly for the "auth finished" message. - auth_finished_main_thread(); - - } else if (message == "keyboardfocus") { - if (_splash_window != nullptr) { - _splash_window->request_keyboard_focus(); - } - } -} - -/** - * Called (in the main thread) when a script request is received from the - * subprocess. - */ -void P3DInstance:: -handle_script_request(const string &operation, P3D_object *object, - const string &property_name, P3D_object *value, - bool needs_response, int unique_id) { - - TiXmlDocument *doc = new TiXmlDocument; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "script_response"); - xcommand->SetAttribute("unique_id", unique_id); - - doc->LinkEndChild(xcommand); - - if (operation == "get_property") { - P3D_object *result = P3D_OBJECT_GET_PROPERTY(object, property_name.c_str()); - - // We've got the property value; feed it back down to the subprocess. - - if (result != nullptr) { - xcommand->LinkEndChild(_session->p3dobj_to_xml(result)); - P3D_OBJECT_DECREF(result); - } - - } else if (operation == "set_property") { - bool result = - P3D_OBJECT_SET_PROPERTY(object, property_name.c_str(), true, value); - - TiXmlElement *xvalue = new TiXmlElement("value"); - xvalue->SetAttribute("type", "bool"); - xvalue->SetAttribute("value", (int)result); - xcommand->LinkEndChild(xvalue); - - } else if (operation == "del_property") { - bool result = P3D_OBJECT_SET_PROPERTY(object, property_name.c_str(), true, nullptr); - - TiXmlElement *xvalue = new TiXmlElement("value"); - xvalue->SetAttribute("type", "bool"); - xvalue->SetAttribute("value", (int)result); - xcommand->LinkEndChild(xvalue); - - } else if (operation == "has_method") { - bool result = P3D_OBJECT_HAS_METHOD(object, property_name.c_str()); - - TiXmlElement *xvalue = new TiXmlElement("value"); - xvalue->SetAttribute("type", "bool"); - xvalue->SetAttribute("value", (int)result); - xcommand->LinkEndChild(xvalue); - - } else if (operation == "call") { - // Convert the single value parameter into an array of parameters. - P3D_object **values = &value; - int num_values = 1; - if (value->_class == &P3DObject::_object_class) { - P3DObject *p3dobj = (P3DObject *)value; - if (p3dobj->get_object_array_size() != -1) { - values = p3dobj->get_object_array(); - num_values = p3dobj->get_object_array_size(); - } - } - - P3D_object *result = - P3D_OBJECT_CALL(object, property_name.c_str(), needs_response, - values, num_values); - - if (result != nullptr) { - xcommand->LinkEndChild(_session->p3dobj_to_xml(result)); - P3D_OBJECT_DECREF(result); - } - - } else if (operation == "eval") { - P3D_object *result; - int size = P3D_OBJECT_GET_STRING(value, nullptr, 0); - char *buffer = new char[size + 1]; - P3D_OBJECT_GET_STRING(value, buffer, size + 1); - result = P3D_OBJECT_EVAL(object, buffer); - delete[] buffer; - - if (result != nullptr) { - xcommand->LinkEndChild(_session->p3dobj_to_xml(result)); - P3D_OBJECT_DECREF(result); - } - } - - if (needs_response) { - _session->send_command(doc); - } else { - delete doc; - } -} - -/** - * Sets the "failed" indication to display sadness to the user--we're unable - * to launch the instance for some reason. - */ -void P3DInstance:: -set_failed() { - set_button_image(IT_none); - set_background_image(IT_failed); - _main_object->set_string_property("status", "failed"); - - if (!_failed) { - _failed = true; - make_splash_window(); - send_notify("onfail"); - } -} - -/** - * Creates the splash window to be displayed at startup, if it's time. - */ -void P3DInstance:: -make_splash_window() { - // Should we make the splash window visible? - bool make_visible = true; - if (_instance_window_opened) { - // Not once we've opened the main window. - make_visible = false; - } - - if (_wparams.get_window_type() != P3D_WT_embedded && - !_stuff_to_download && _auto_install && - (_auto_start || _stop_on_ready) && _p3d_trusted) { - // If it's a toplevel or fullscreen window, then we don't want a splash - // window unless we have stuff to download, or a button to display. - make_visible = false; - } - - if (is_failed()) { - // But, if we've failed to launch somehow, we need to let the user know. - make_visible = true; - } - - if (_splash_window != nullptr) { - // Already got one. - _splash_window->set_visible(make_visible); - return; - } - if (!_got_wparams) { - // Don't know where to put it yet. - return; - } - if (_wparams.get_window_type() == P3D_WT_hidden && !is_failed()) { - // We're hidden, and so is the splash window. (But if we've got a failure - // case to report, we don't care and create the splash window anyway.) - return; - } - - _splash_window = new SplashWindowType(this, make_visible); - - // Get the splash window colors. We must set these *before* we call - // set_wparams. - if (_fparams.has_token("fgcolor")) { - int r, g, b; - if (parse_color(r, g, b, _fparams.lookup_token("fgcolor"))) { - _splash_window->set_fgcolor(r, g, b); - } else { - nout << "parse failure on fgcolor " << _fparams.lookup_token("fgcolor") << "\n"; - } - } - if (_fparams.has_token("bgcolor")) { - int r, g, b; - if (parse_color(r, g, b, _fparams.lookup_token("bgcolor"))) { - _splash_window->set_bgcolor(r, g, b); - _splash_window->set_bar_bgcolor(r, g, b); - } else { - nout << "parse failure on bgcolor " << _fparams.lookup_token("bgcolor") << "\n"; - } - } - if (_fparams.has_token("barcolor")) { - int r, g, b; - if (parse_color(r, g, b, _fparams.lookup_token("barcolor"))) { - _splash_window->set_barcolor(r, g, b); - } else { - nout << "parse failure on barcolor " << _fparams.lookup_token("barcolor") << "\n"; - } - } - if (_fparams.has_token("bar_bgcolor")) { - int r, g, b; - if (parse_color(r, g, b, _fparams.lookup_token("bar_bgcolor"))) { - _splash_window->set_bar_bgcolor(r, g, b); - } else { - nout << "parse failure on bar_bgcolor " << _fparams.lookup_token("bar_bgcolor") << "\n"; - } - } - if (_fparams.has_token("bar_border")) { - int border = _fparams.lookup_token_int("bar_border"); - _splash_window->set_bar_border(border); - } - if (_fparams.has_token("bar_bottom")) { - int bottom = _fparams.lookup_token_int("bar_bottom"); - _splash_window->set_bar_bottom(bottom); - } - if (_fparams.has_token("bar_width")) { - string bar_width = _fparams.lookup_token("bar_width"); - char *endptr; - long width = strtol(bar_width.c_str(), &endptr, 10); - bool percent = false; - - if (*endptr == '%') { - percent = true; - ++endptr; - } - if (*endptr == '\0' && width >= 0 && width < 65536) { - _splash_window->set_bar_width((int)width, percent); - } else { - nout << "parse failure on bar_width " << bar_width << "\n"; - } - } - if (_fparams.has_token("bar_height")) { - string bar_height = _fparams.lookup_token("bar_height"); - char *endptr; - long height = strtol(bar_height.c_str(), &endptr, 10); - bool percent = false; - - if (*endptr == '%') { - percent = true; - ++endptr; - } - if (*endptr == '\0' && height >= 0 && height < 65536) { - _splash_window->set_bar_height((int)height, percent); - } else { - nout << "parse failure on bar_height " << bar_height << "\n"; - } - } - if (_fparams.has_token("font_family")) { - string family = _fparams.lookup_token("font_family"); - _splash_window->set_font_family(family); - } - if (_fparams.has_token("font_size")) { - int size = _fparams.lookup_token_int("font_size"); - _splash_window->set_font_size(size); - } - if (_fparams.has_token("font_style")) { - string style = _fparams.lookup_token("font_style"); - - if (style == "normal") { - _splash_window->set_font_style(P3DSplashWindow::FS_normal); - } else if (style == "oblique") { - _splash_window->set_font_style(P3DSplashWindow::FS_oblique); - } else if (style == "italic") { - _splash_window->set_font_style(P3DSplashWindow::FS_italic); - } else { - nout << "parse_failure on font_style " << style << "\n"; - } - } - if (_fparams.has_token("font_weight")) { - string weight = _fparams.lookup_token("font_weight"); - - if (weight == "normal") { - _splash_window->set_font_weight(400); - } else if (weight == "bold") { - _splash_window->set_font_weight(700); - } else if (weight == "bolder") { - _splash_window->set_font_weight(700); - } else if (weight == "lighter") { - _splash_window->set_font_weight(100); - } else if (weight.size() == 3 && - weight[0] >= '1' && weight[0] <= '9' && - weight[1] == '0' && weight[2] == '0') { - _splash_window->set_font_weight(((int)weight[0] - 48) * 100); - } else { - nout << "parse_failure on font_weight " << weight << "\n"; - } - } - - _splash_window->set_wparams(_wparams); - _splash_window->set_install_label(_install_label); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - // Go get the required images. - for (int i = 0; i < (int)IT_none; ++i) { - string token_keyword = string(_image_type_names[i]) + "_img"; - if (!_fparams.has_token(token_keyword) && i < (int)IT_auth_ready) { - token_keyword = "splash_img"; - } - - string image_url = _fparams.lookup_token(token_keyword); - if (image_url.empty()) { - // No specific image for this type is specified; get the default image. - // We do this via the P3DPackage interface, so we can use the cached - // version on disk if it's good. - _image_files[i]._use_standard_image = true; - - } else { - // We have an explicit image specified for this slot, so just download - // it directly. This one won't be cached locally (though the browser - // might be free to cache it). - _image_files[i]._use_standard_image = false; - _image_files[i]._filename.clear(); - - // Make a temporary file to receive the splash image. - assert(_image_files[i]._temp_filename == nullptr); - _image_files[i]._temp_filename = new P3DTemporaryFile(".jpg"); - - // Start downloading the requested image. - ImageDownload *download = new ImageDownload(this, i); - download->set_url(image_url); - download->set_filename(_image_files[i]._temp_filename->get_filename()); - - start_download(download); - } - } - - if (_current_background_image != IT_none) { - _splash_window->set_image_filename(_image_files[_current_background_image]._filename, P3DSplashWindow::IP_background); - } - - if (_current_button_image != IT_none) { - _splash_window->set_image_filename(_image_files[_current_button_image]._filename, P3DSplashWindow::IP_button_ready); - _splash_window->set_image_filename(_image_files[_current_button_image + 1]._filename, P3DSplashWindow::IP_button_rollover); - _splash_window->set_image_filename(_image_files[_current_button_image + 2]._filename, P3DSplashWindow::IP_button_click); - _splash_window->set_button_active(true); - } -} - -/** - * Specifies the particular image that should be displayed as the background - * image in the splash window. Specify IT_none to take the background image - * away. - */ -void P3DInstance:: -set_background_image(ImageType image_type) { - if (is_failed()) { - // Can't change the background again after we've failed. - return; - } - - if (image_type != _current_background_image) { - nout << "setting background to " << _image_type_names[image_type] - << ", splash_window = " << _splash_window << "\n"; - // Remove the previous image. - _image_files[_current_background_image]._image_placement = P3DSplashWindow::IP_none; - - // Install the new image. - _current_background_image = image_type; - if (_current_background_image != IT_none) { - _image_files[_current_background_image]._image_placement = P3DSplashWindow::IP_background; - } - - // Update the splash window. - if (_splash_window != nullptr) { - _splash_window->set_image_filename(_image_files[_current_background_image]._filename, P3DSplashWindow::IP_background); - } - } -} - -/** - * Specifies the particular image that should be displayed as the button image - * in the splash window. Specify IT_none to take the button image away. - * - * This actually defines a trilogy of button images: ready, rollover, click. - */ -void P3DInstance:: -set_button_image(ImageType image_type) { - if (is_failed()) { - // Can't set the button again after we've failed. - return; - } - - if (image_type != _current_button_image) { - nout << "setting button to " << _image_type_names[image_type] << "\n"; - // Remove the previous image. - _image_files[_current_button_image]._image_placement = P3DSplashWindow::IP_none; - if (_current_button_image != IT_none) { - _image_files[_current_button_image + 1]._image_placement = P3DSplashWindow::IP_none; - _image_files[_current_button_image + 2]._image_placement = P3DSplashWindow::IP_none; - } - - // Install the new image. - _current_button_image = image_type; - if (_current_button_image != IT_none) { - _image_files[_current_button_image]._image_placement = P3DSplashWindow::IP_button_ready; - _image_files[_current_button_image + 1]._image_placement = P3DSplashWindow::IP_button_rollover; - _image_files[_current_button_image + 2]._image_placement = P3DSplashWindow::IP_button_click; - } - - // Update the splash window. - if (_splash_window != nullptr) { - if (_current_button_image != IT_none) { - _splash_window->set_image_filename(_image_files[_current_button_image]._filename, P3DSplashWindow::IP_button_ready); - _splash_window->set_image_filename(_image_files[_current_button_image + 1]._filename, P3DSplashWindow::IP_button_rollover); - _splash_window->set_image_filename(_image_files[_current_button_image + 2]._filename, P3DSplashWindow::IP_button_click); - _splash_window->set_button_active(true); - } else { - _splash_window->set_button_active(false); - } - } - - } else { - // We're not changing the button graphic, but we might be re-activating - // it. - if (_splash_window != nullptr) { - if (_current_button_image != IT_none) { - _splash_window->set_button_active(true); - } else { - _splash_window->set_button_active(false); - } - } - } -} - -/** - * Notified when a package information has been successfully downloaded and - * the package is idle, waiting for activate_download() to be called. - */ -void P3DInstance:: -report_package_info_ready(P3DPackage *package) { - nout << "report_package_info_ready: " << package->get_package_name() << "\n"; - if (package == _image_package || package == _certlist_package || - package == _p3dcert_package) { - // A special case: these packages get immediately downloaded, without - // waiting for anything else. - if (package == _certlist_package || package == _p3dcert_package) { - // If we're downloading one of the two cert packages, though, put up a - // progress bar. - make_splash_window(); - if (_splash_window != nullptr) { - _splash_window->set_install_progress(0.0, true, 0); - } - if (package == _certlist_package) { - set_install_label("Getting Certificates"); - } else { - set_install_label("Getting Authorization Dialog"); - } - } - - package->activate_download(); - return; - } - - if (package == _panda3d_package && !_packages_specified) { - // Another special case. Once the special panda3d package is ready to - // download (and we know what platform it belongs to), we can begin to - // download the remaining required packages. - string package_platform = package->get_package_platform(); - if (!package_platform.empty() && _session_platform.empty()) { - // From now on, all platform-specific files downloaded by this session - // will be for this platform. - _session_platform = package_platform; - } - if (_session_platform != package_platform) { - nout << "Error: session is " << _session_platform - << ", but we somehow got panda3d for " << package_platform << "\n"; - set_failed(); - return; - } - - add_packages(); - } - - // Now that the package's info is ready, we know its set of required - // packages, and we can add these to the list. - P3DPackage::Requires::const_iterator ri; - for (ri = package->_requires.begin(); ri != package->_requires.end(); ++ri) { - const P3DPackage::RequiredPackage &rp = (*ri); - add_package(rp._package_name, rp._package_version, rp._package_seq, - rp._host); - } - - consider_start_download(); -} - -/** - * When all package info files have been obtained, begins downloading stuff. - */ -void P3DInstance:: -consider_start_download() { - if (get_packages_info_ready()) { - // All packages are ready to go. Let's start some download action. - _downloading_packages.clear(); - _prev_downloaded = 0; - _total_download_size = 0; - Packages::const_iterator pi; - for (pi = _packages.begin(); pi != _packages.end(); ++pi) { - P3DPackage *package = (*pi); - if (package->get_info_ready()) { - if (!package->get_ready()) { - _downloading_packages.push_back(package); - _total_download_size += package->get_download_size(); - } else { - _prev_downloaded += package->get_download_size(); - } - } - } - ready_to_install(); - } -} - -/** - * Called when it's time to start the package download process. - */ -void P3DInstance:: -ready_to_install() { - if (_downloading_packages.empty() && _download_complete) { - // We have already been here. Ignore it. - - } else if (!_auto_install && !_download_started) { - // Not authorized to download yet. We're waiting for the user to - // acknowledge the download. - set_background_image(IT_ready); - set_button_image(IT_play_ready); - - } else { - _download_started = true; - _download_complete = false; - _download_package_index = 0; - _total_downloaded = 0; - - // Record the time we started the package download, so we can report - // downloadElapsedTime and predict downloadRemainingTime. -#ifdef _WIN32 - _start_dl_tick = GetTickCount(); -#else - gettimeofday(&_start_dl_timeval, nullptr); -#endif - - nout << "Beginning install of " << _downloading_packages.size() - << " packages, total " << _total_download_size - << " bytes required (" << _prev_downloaded - << " previously downloaded).\n"; - - if (_downloading_packages.size() > 0) { - _stuff_to_download = true; - - // Maybe it's time to open a splash window now. - make_splash_window(); - } - - _main_object->set_string_property("status", "downloading"); - _main_object->set_int_property("numDownloadingPackages", _downloading_packages.size()); - _main_object->set_int_property("totalDownloadSize", _total_download_size); - _main_object->set_int_property("downloadElapsedSeconds", 0); - _main_object->set_undefined_property("downloadRemainingSeconds"); - - double progress = 0.0; - if (_prev_downloaded != 0) { - // We might start off with more than 0 progress, if we've already - // downloaded some of it previously. - progress = (_prev_downloaded) / (_total_download_size + _prev_downloaded); - progress = min(progress, 1.0); - - _main_object->set_float_property("downloadProgress", progress); - } - if (_splash_window != nullptr) { - _splash_window->set_install_progress(progress, true, 0); - } - - send_notify("ondownloadbegin"); - - start_next_download(); - } -} - -/** - * Checks whether all packages are ready and waiting to be downloaded; if so, - * starts the next package in sequence downloading. - */ -void P3DInstance:: -start_next_download() { - while (_download_package_index < (int)_downloading_packages.size()) { - P3DPackage *package = _downloading_packages[_download_package_index]; - if (package->get_failed()) { - send_notify("ondownloadfail"); - set_failed(); - return; - } - - if (!package->get_ready()) { - // This package is ready to download. Begin. - string name = package->get_formatted_name(); - _main_object->set_string_property("downloadPackageName", package->get_package_name()); - _main_object->set_string_property("downloadPackageDisplayName", name); - _main_object->set_int_property("downloadPackageNumber", _download_package_index + 1); - _main_object->set_int_property("downloadPackageSize", package->get_download_size()); - set_install_label("Installing " + name); - - nout << "Installing " << package->get_package_name() - << ", package " << _download_package_index + 1 - << " of " << _downloading_packages.size() - << ", " << package->get_download_size() - << " bytes.\n"; - - package->activate_download(); - send_notify("ondownloadnext"); - return; - } - - // This package has been downloaded. Move to the next. - _total_downloaded += package->get_download_size(); - ++_download_package_index; - } - - // Looks like we're all done downloading. Launch! - _downloading_packages.clear(); - - if (get_packages_ready()) { - mark_download_complete(); - } -} - -/** - * Called internally when all files needed to launch have been downloaded. - */ -void P3DInstance:: -mark_download_complete() { - if (_failed) { - return; - } - - if (!_download_complete) { - _download_complete = true; - _main_object->set_bool_property("downloadComplete", true); - _main_object->set_string_property("status", "downloadcomplete"); - send_notify("ondownloadcomplete"); - } - - // Take down the download progress bar. - if (_splash_window != nullptr) { - _splash_window->set_install_progress(0.0, true, 0); - } - set_install_label(""); - - if (_got_wparams && _p3d_trusted) { - ready_to_start(); - } -} - - -/** - * Called internally when we have got the wparams and fparams and we have - * downloaded all required packages. - */ -void P3DInstance:: -ready_to_start() { - if (_instance_started || is_failed()) { - // Already started--or never mind. - return; - } - - _main_object->set_string_property("status", "ready"); - send_notify("onready"); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (_stop_on_ready) { - // If we've got the "stop_on_ready" token, then exit abruptly now, instead - // of displaying the splash window. - request_stop_main_thread(); - return; - } - - if (_auto_start) { - set_background_image(IT_launch); - inst_mgr->start_instance(this); - - } else { - // We're fully downloaded, and waiting for the user to click play. - set_background_image(IT_ready); - set_button_image(IT_play_ready); - } -} - -/** - * Notified as the instance file is downloaded. - */ -void P3DInstance:: -report_instance_progress(double progress, bool is_progress_known, - size_t received_data) { - if (!_show_dl_instance_progress) { - // If we haven't yet set the download label, set it after a full second - // has elapsed. We don't want to set it too soon, because we're not - // really sure how long it will take to download (the instance file might - // be already in the browser cache). -#ifdef _WIN32 - int now = GetTickCount(); - double elapsed = (double)(now - _start_dl_tick) * 0.001; -#else - struct timeval now; - gettimeofday(&now, nullptr); - double elapsed = (double)(now.tv_sec - _start_dl_timeval.tv_sec) + - (double)(now.tv_usec - _start_dl_timeval.tv_usec) / 1000000.0; -#endif - - // Put up the progress bar after 2 seconds have elapsed, if we've still - // got some distance to go; or after 5 seconds have elapsed regardless. - if ((elapsed > 2.0 && progress < 0.7) || - (elapsed > 5.0)) { - _show_dl_instance_progress = true; - if (_fparams.has_token("p3d_install_label")) { - set_install_label(_fparams.lookup_token("p3d_install_label")); - } else { - set_install_label("Getting " + _p3d_basename); - } - } - } - - if (_splash_window != nullptr && _show_dl_instance_progress) { - _splash_window->set_install_progress(progress, is_progress_known, received_data); - } - _main_object->set_float_property("instanceDownloadProgress", progress); -} - -/** - * Notified as the packages required by the instance file are downloaded. - */ -void P3DInstance:: -report_package_progress(P3DPackage *package, double progress) { - if (package == _image_package) { - // Ignore this. - return; - } - if (package == _certlist_package || package == _p3dcert_package) { - // This gets its own progress bar. - if (_splash_window != nullptr) { - _splash_window->set_install_progress(progress, true, 0); - } - return; - } - - if (_download_package_index >= (int)_downloading_packages.size() || - package != _downloading_packages[_download_package_index]) { - // Quietly ignore a download progress report from an unexpected package. - return; - } - - // Scale the progress into the range appropriate to this package. - progress = (progress * package->get_download_size() + _total_downloaded + _prev_downloaded) / (_total_download_size + _prev_downloaded); - progress = min(progress, 1.0); - - if (_splash_window != nullptr) { - _splash_window->set_install_progress(progress, true, 0); - } - _main_object->set_float_property("downloadProgress", progress); - - static const size_t buffer_size = 256; - char buffer[buffer_size]; - - // Get the floating-point elapsed time. -#ifdef _WIN32 - int now = GetTickCount(); - double elapsed = (double)(now - _start_dl_tick) * 0.001; -#else - struct timeval now; - gettimeofday(&now, nullptr); - double elapsed = (double)(now.tv_sec - _start_dl_timeval.tv_sec) + - (double)(now.tv_usec - _start_dl_timeval.tv_usec) / 1000000.0; -#endif - - int ielapsed = (int)elapsed; - _main_object->set_int_property("downloadElapsedSeconds", ielapsed); - - sprintf(buffer, "%d:%02d", ielapsed / 60, ielapsed % 60); - _main_object->set_string_property("downloadElapsedFormatted", buffer); - - if (progress > 0 && (elapsed > 5.0 || progress > 0.2)) { - double this_total = elapsed / progress; - double this_remaining = max(this_total - elapsed, 0.0); - - // Age out any old time reports. - double old = elapsed - min(time_average, this_remaining); - while (!_time_reports.empty() && _time_reports.front()._report_time < old) { - TimeReport &tr0 = _time_reports.front(); - _total_time_reports -= tr0._total; - _time_reports.pop_front(); - } - if (_time_reports.empty()) { - _total_time_reports = 0.0; - } - - // Add a new time report. - TimeReport tr; - tr._total = this_total; - tr._report_time = elapsed; - _time_reports.push_back(tr); - _total_time_reports += tr._total; - - // Now get the average report. - if (!_time_reports.empty()) { - double total = _total_time_reports / (double)_time_reports.size(); - double remaining = max(total - elapsed, 0.0); - int iremaining = (int)(remaining + 0.5); - _main_object->set_int_property("downloadRemainingSeconds", iremaining); - sprintf(buffer, "%d:%02d", iremaining / 60, iremaining % 60); - _main_object->set_string_property("downloadRemainingFormatted", buffer); - } - } -} - -/** - * Notified when a required package is fully downloaded, or failed. - */ -void P3DInstance:: -report_package_done(P3DPackage *package, bool success) { - nout << "Done installing " << package->get_package_name() - << ": success = " << success << "\n"; - - if (package == _image_package) { - // A special case: we just downloaded the image package, so get the image - // files out of it and point them to the splash window. - string package_dir = package->get_package_dir(); - const TiXmlElement *xconfig = package->get_xconfig(); - if (xconfig == nullptr) { - nout << "No entry in image package\n"; - return; - } - package->mark_used(); - - for (int i = 0; i < (int)IT_none; ++i) { - if (_image_files[i]._use_standard_image) { - // This image indexes into the package. Go get the standard image - // filename. - string token = string(_image_type_names[i]) + "_img"; - const string *basename = xconfig->Attribute(token); - if (basename == nullptr) { - nout << "No entry in image package for " << token << "\n"; - } else { - string image_filename = package_dir + "/" + *basename; - _image_files[i]._filename = image_filename; - - // If the image should be on the window now, and the window still - // exists, put it up. - if (_splash_window != nullptr && - _image_files[i]._image_placement != P3DSplashWindow::IP_none) { - P3DSplashWindow::ImagePlacement image_placement = _image_files[i]._image_placement; - _splash_window->set_image_filename(image_filename, image_placement); - } - } - } - } - return; - } - - if (package == _certlist_package) { - // Another special case: successfully downloading certlist (or failing to - // download it) means we can finish checking the authenticity of the p3d - // file. - - package->mark_used(); - - // Take down the download progress. - if (_splash_window != nullptr) { - _splash_window->set_install_progress(0.0, true, 0); - } - set_install_label(""); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - inst_mgr->read_certlist(package); - check_p3d_signature(); - return; - } - - if (package == _p3dcert_package) { - // Another special case: successfully downloading p3dcert means we can - // enable the auth button. - - package->mark_used(); - - // Take down the download progress. - if (_splash_window != nullptr) { - _splash_window->set_install_progress(0.0, true, 0); - } - set_install_label(""); - mark_p3d_untrusted(); - return; - } - - if (success) { - report_package_progress(package, 1.0); - start_next_download(); - } else { - send_notify("ondownloadfail"); - set_failed(); - } -} - -/** - * Sets the install label that will be displayed on the splash window, if it - * is present. - */ -void P3DInstance:: -set_install_label(const string &install_label) { - _install_label = install_label; - if (_splash_window != nullptr) { - _splash_window->set_install_label(_install_label); - } -} - -/** - * Actually paints the rendered image to the browser window. This is only - * needed for OSX, where the child process isn't allowed to do it directly. - */ -void P3DInstance:: -paint_window() { -#ifdef __APPLE__ - const P3D_window_handle &handle = _wparams.get_parent_window(); - if (handle._window_handle_type == P3D_WHT_osx_port) { -#if !__LP64__ - paint_window_osx_port(); -#endif - - } else if (handle._window_handle_type == P3D_WHT_osx_cgcontext) { - const P3D_window_handle &handle = _wparams.get_parent_window(); - assert(handle._window_handle_type == P3D_WHT_osx_cgcontext); - CGContextRef context = handle._handle._osx_cgcontext._context; - - paint_window_osx_cgcontext(context); - } -#endif // __APPLE__ -} - -#if defined(__APPLE__) && !__LP64__ -/** - * Fills _reversed_buffer with the pixels from the current frame, suitable for - * rendering via the old QuickDraw interface. Returns true on success, or - * false if there is no Panda3D window visible. Only needed on OSX. - */ -bool P3DInstance:: -get_framebuffer_osx_port() { - if (_swbuffer == nullptr || !_instance_window_attached) { - // We don't have a Panda3D window yet. - return false; - } - - // blit rendered framebuffer into window backing store - int x_size = min(_wparams.get_win_width(), _swbuffer->get_x_size()); - int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size()); - size_t rowsize = _swbuffer->get_row_size(); - - if (_swbuffer->ready_for_read()) { - // Copy the new framebuffer image from the child process. - const void *framebuffer = _swbuffer->open_read_framebuffer(); - - // We have to reverse the image vertically first (different conventions - // between Panda and Mac). - for (int yi = 0; yi < y_size; ++yi) { -#ifndef __BIG_ENDIAN__ - // On a little-endian machine, we only have to reverse the order of the - // rows. - memcpy(_reversed_buffer + (y_size - 1 - yi) * rowsize, - (char *)framebuffer + yi * rowsize, - rowsize); - -#else // __BIG_ENDIAN__ - // On a big-endian machine, we need to do more work. - - // It appears that kBGRAPixelFormat, below, is ignored on big-endian - // machines, and it is treated as KARGBPixelFormat regardless of what we - // specify. Vexing. To compensate for this, we have to reverse the - // color channels ourselves on big-endian machines. - - const char *source = (const char *)framebuffer + yi * rowsize; - const char *stop = source + x_size * 4; - char *dest = (_reversed_buffer + (y_size - 1 - yi) * rowsize); - while (source < stop) { - char b = source[0]; - char g = source[1]; - char r = source[2]; - char a = source[3]; - dest[0] = a; - dest[1] = r; - dest[2] = g; - dest[3] = b; - dest += 4; - source += 4; - } -#endif - } - - _swbuffer->close_read_framebuffer(); - - if (_splash_window != nullptr && _splash_window->get_visible()) { - // If the splash window is up, time to hide it. We've just rendered a - // real frame. - _splash_window->set_visible(false); - } - - } else { - // No frame ready. Just re-paint the frame we had saved last time. - } - - return true; -} -#endif // __APPLE__ - -#ifdef __APPLE__ -/** - * Fills _reversed_buffer with the pixels from the current frame, suitable for - * rendering via the new CoreGraphics interface. Returns true on success, or - * false if there is no Panda3D window visible. Only needed on OSX. - */ -bool P3DInstance:: -get_framebuffer_osx_cgcontext() { - if (_swbuffer == nullptr || !_instance_window_attached) { - // We don't have a Panda3D window yet. - return false; - } - - // blit rendered framebuffer into window backing store - int x_size = min(_wparams.get_win_width(), _swbuffer->get_x_size()); - int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size()); - size_t rowsize = _swbuffer->get_row_size(); - - if (_swbuffer->ready_for_read()) { - // Copy the new framebuffer image from the child process. - const void *framebuffer = _swbuffer->open_read_framebuffer(); - memcpy(_reversed_buffer, framebuffer, y_size * rowsize); - _swbuffer->close_read_framebuffer(); - - if (_splash_window != nullptr && _splash_window->get_visible()) { - // If the splash window is up, time to hide it. We've just rendered a - // real frame. - _splash_window->set_visible(false); - } - - } else { - // No frame ready. Just re-paint the frame we had saved last time. - } - - return true; -} -#endif // __APPLE__ - -#if defined(__APPLE__) && !__LP64__ -/** - * Actually paints the rendered image to the browser window, using the OSX - * deprecated QuickDraw interfaces. - */ -void P3DInstance:: -paint_window_osx_port() { - if (!get_framebuffer_osx_port()) { - // No Panda3D window is showing. - return; - } - - int x_size = min(_wparams.get_win_width(), _swbuffer->get_x_size()); - int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size()); - size_t rowsize = _swbuffer->get_row_size(); - - Rect src_rect = {0, 0, (short)y_size, (short)x_size}; - Rect ddrc_rect = {0, 0, (short)y_size, (short)x_size}; - - QDErr err; - - GWorldPtr pGWorld; - err = NewGWorldFromPtr(&pGWorld, k32BGRAPixelFormat, &src_rect, 0, 0, 0, - _reversed_buffer, rowsize); - if (err != noErr) { - nout << " error in NewGWorldFromPtr, called from paint_window()\n"; - return; - } - - const P3D_window_handle &handle = _wparams.get_parent_window(); - assert(handle._window_handle_type == P3D_WHT_osx_port); - GrafPtr out_port = handle._handle._osx_port._port; - GrafPtr port_save = nullptr; - Boolean port_changed = QDSwapPort(out_port, &port_save); - - // Make sure the clipping rectangle isn't in the way. Is there a better way - // to eliminate the cliprect from consideration? - Rect r = { 0, 0, 0x7fff, 0x7fff }; - ClipRect(&r); - - CopyBits(GetPortBitMapForCopyBits(pGWorld), - GetPortBitMapForCopyBits(out_port), - &src_rect, &ddrc_rect, srcCopy, 0); - - if (port_changed) { - QDSwapPort(port_save, nullptr); - } - - DisposeGWorld(pGWorld); -} -#endif // __APPLE__ - -#ifdef __APPLE__ -/** - * Actually paints the rendered image to the browser window. This is the - * newer CoreGraphics implementation on OSX. - */ -void P3DInstance:: -paint_window_osx_cgcontext(CGContextRef context) { - if (!get_framebuffer_osx_cgcontext()) { - // No Panda3D window is showing. - return; - } - - int x_size = min(_wparams.get_win_width(), _swbuffer->get_x_size()); - int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size()); - - if (_buffer_image != nullptr) { - CGRect region = { { 0, 0 }, { (CGFloat)x_size, (CGFloat)y_size } }; - CGContextDrawImage(context, region, _buffer_image); - } -} -#endif // __APPLE__ - -/** - * Responds to the deprecated Carbon event types in Mac OSX. - */ -bool P3DInstance:: -handle_event_osx_event_record(const P3D_event_data &event) { - bool retval = false; - -#if defined(__APPLE__) && !__LP64__ - assert(event._event_type == P3D_ET_osx_event_record); - EventRecord *er = event._event._osx_event_record._event; - - Point pt = er->where; - - // Need to ensure we have the correct port set, in order to convert the - // mouse coordinates successfully via GlobalToLocal(). - const P3D_window_handle &handle = _wparams.get_parent_window(); - if (handle._window_handle_type == P3D_WHT_osx_port) { - GrafPtr out_port = handle._handle._osx_port._port; - GrafPtr port_save = nullptr; - Boolean port_changed = QDSwapPort(out_port, &port_save); - - GlobalToLocal(&pt); - - if (port_changed) { - QDSwapPort(port_save, nullptr); - } - } else { - // First, convert the coordinates from screen coordinates to browser - // window coordinates. - WindowRef window = handle._handle._osx_cgcontext._window; - CGPoint cgpt = { (CGFloat)pt.h, (CGFloat)pt.v }; - HIPointConvert(&cgpt, kHICoordSpaceScreenPixel, nullptr, - kHICoordSpaceWindow, window); - - // Then convert to plugin coordinates. - pt.h = (short)(cgpt.x - _wparams.get_win_x()); - pt.v = (short)(cgpt.y - _wparams.get_win_y()); - } - - SubprocessWindowBuffer::Event swb_event; - swb_event._source = SubprocessWindowBuffer::ES_none; - swb_event._type = SubprocessWindowBuffer::ET_none; - swb_event._code = 0; - swb_event._flags = 0; - add_carbon_modifier_flags(swb_event._flags, er->modifiers); - - bool trust_mouse_data = true; - - switch (er->what) { - case mouseDown: - swb_event._source = SubprocessWindowBuffer::ES_mouse; - swb_event._type = SubprocessWindowBuffer::ET_button_down; - retval = true; - break; - - case mouseUp: - swb_event._source = SubprocessWindowBuffer::ES_mouse; - swb_event._type = SubprocessWindowBuffer::ET_button_up; - retval = true; - break; - - case keyDown: - case keyUp: - case autoKey: - if (_swbuffer != nullptr) { - swb_event._source = SubprocessWindowBuffer::ES_keyboard; - swb_event._code = er->message; - if (er->what == keyUp) { - swb_event._type = SubprocessWindowBuffer::ET_button_up; - } else if (er->what == keyDown) { - swb_event._type = SubprocessWindowBuffer::ET_button_down; - } else { - swb_event._type = SubprocessWindowBuffer::ET_button_again; - } - retval = true; - } - break; - - case updateEvt: - paint_window(); - retval = true; - break; - - case activateEvt: - _mouse_active = ((er->modifiers & 1) != 0); - break; - - case osEvt: - // The mouse data sent with an "os event" seems to be in an indeterminate - // space. - trust_mouse_data = false; - break; - - default: - break; - } - - if (_mouse_active) { - swb_event._flags |= SubprocessWindowBuffer::EF_has_mouse; - if (trust_mouse_data) { - swb_event._x = pt.h; - swb_event._y = pt.v; - swb_event._flags |= SubprocessWindowBuffer::EF_mouse_position; - } - } - - if (_swbuffer != nullptr) { - _swbuffer->add_event(swb_event); - } -#endif // __APPLE__ - - return retval; -} - -/** - * Responds to the new Cocoa event types in Mac OSX. - */ -bool P3DInstance:: -handle_event_osx_cocoa(const P3D_event_data &event) { - bool retval = false; - -#ifdef __APPLE__ - assert(event._event_type == P3D_ET_osx_cocoa); - const P3DCocoaEvent &ce = event._event._osx_cocoa._event; - - SubprocessWindowBuffer::Event swb_event; - swb_event._source = SubprocessWindowBuffer::ES_none; - swb_event._type = SubprocessWindowBuffer::ET_none; - swb_event._code = 0; - swb_event._flags = 0; - - switch (ce.type) { - case P3DCocoaEventDrawRect: - { - CGContextRef context = ce.data.draw.context; - paint_window_osx_cgcontext(context); - retval = true; - } - break; - - case P3DCocoaEventMouseDown: - swb_event._source = SubprocessWindowBuffer::ES_mouse; - swb_event._type = SubprocessWindowBuffer::ET_button_down; - retval = true; - break; - - case P3DCocoaEventMouseUp: - swb_event._source = SubprocessWindowBuffer::ES_mouse; - swb_event._type = SubprocessWindowBuffer::ET_button_up; - retval = true; - break; - - case P3DCocoaEventKeyDown: - swb_event._source = SubprocessWindowBuffer::ES_keyboard; - swb_event._code = ce.data.key.keyCode << 8; - if (ce.data.key.isARepeat) { - swb_event._type = SubprocessWindowBuffer::ET_button_again; - } else { - swb_event._type = SubprocessWindowBuffer::ET_button_down; - if (ce.data.key.characters[0] > 0 & ce.data.key.characters[0] < 0x100) { - swb_event._code |= ce.data.key.characters[0]; - } - } - _modifiers = ce.data.key.modifierFlags; - retval = true; - break; - - case P3DCocoaEventKeyUp: - swb_event._source = SubprocessWindowBuffer::ES_keyboard; - swb_event._type = SubprocessWindowBuffer::ET_button_up; - swb_event._code = ce.data.key.keyCode << 8; - _modifiers = ce.data.key.modifierFlags; - retval = true; - break; - - case P3DCocoaEventFlagsChanged: - _modifiers = ce.data.key.modifierFlags; - retval = true; - break; - - case P3DCocoaEventFocusChanged: - _mouse_active = (ce.data.focus.hasFocus != 0); - retval = true; - break; - } - - add_cocoa_modifier_flags(swb_event._flags, _modifiers); - - switch (ce.type) { - case P3DCocoaEventMouseDown: - case P3DCocoaEventMouseMoved: - case P3DCocoaEventMouseDragged: - swb_event._x = (int)ce.data.mouse.pluginX; - swb_event._y = (int)ce.data.mouse.pluginY; - swb_event._flags |= SubprocessWindowBuffer::EF_mouse_position; - } - - if (_mouse_active) { - swb_event._flags |= SubprocessWindowBuffer::EF_has_mouse; - } - - if (_swbuffer != nullptr) { - _swbuffer->add_event(swb_event); - } -#endif // __APPLE__ - - return retval; -} - -/** - * OSX only: adds the appropriate bits to the Event flag bitmask to correspond - * to the modifier buttons held in the MacOS-style EventRecord::modifiers - * mask. - */ -void P3DInstance:: -add_carbon_modifier_flags(unsigned int &swb_flags, int modifiers) { -#if defined(__APPLE__) && !__LP64__ - if (modifiers & cmdKey) { - swb_flags |= SubprocessWindowBuffer::EF_meta_held; - } - if (modifiers & shiftKey) { - swb_flags |= SubprocessWindowBuffer::EF_shift_held; - } - if (modifiers & optionKey) { - swb_flags |= SubprocessWindowBuffer::EF_alt_held; - } - if (modifiers & controlKey) { - swb_flags |= SubprocessWindowBuffer::EF_control_held; - } -#endif // __APPLE__ -} - -/** - * OSX only: adds the appropriate bits to the Event flag bitmask to correspond - * to the modifier buttons held in the P3DCocoaEvent modifierFlags mask. - */ -void P3DInstance:: -add_cocoa_modifier_flags(unsigned int &swb_flags, int modifiers) { -#ifdef __APPLE__ - if (modifiers & NSCommandKeyMask) { - swb_flags |= SubprocessWindowBuffer::EF_meta_held; - } - if (modifiers & NSShiftKeyMask) { - swb_flags |= SubprocessWindowBuffer::EF_shift_held; - } - if (modifiers & NSAlternateKeyMask) { - swb_flags |= SubprocessWindowBuffer::EF_alt_held; - } - if (modifiers & NSControlKeyMask) { - swb_flags |= SubprocessWindowBuffer::EF_control_held; - } -#endif // __APPLE__ -} - -/** - * Generates a synthetic notify message here at the C++ level. - * - * Most notify messages are generated from within the Python code, and don't - * use this method; but a few have to be sent before Python has started, and - * those come through this method. - */ -void P3DInstance:: -send_notify(const string &message) { - nout << "send_notify(" << message << ")\n"; - P3D_request *request = new P3D_request; - request->_instance = nullptr; - request->_request_type = P3D_RT_notify; - request->_request._notify._message = strdup(message.c_str()); - add_baked_request(request); -} - -#ifdef __APPLE__ -/** - * OSX only: allocates the _swbuffer and associated support objects. If it - * was already allocated, deallocates the previous one first. - */ -void P3DInstance:: -alloc_swbuffer() { - free_swbuffer(); - - int x_size = _wparams.get_win_width(); - int y_size = _wparams.get_win_height(); - - _swbuffer = SubprocessWindowBuffer::new_buffer - (_shared_fd, _shared_mmap_size, _shared_filename, x_size, y_size); - if (_swbuffer != nullptr) { - _reversed_buffer = new char[_swbuffer->get_framebuffer_size()]; - memset(_reversed_buffer, 0, _swbuffer->get_row_size()); - size_t rowsize = _swbuffer->get_row_size(); - - _buffer_data = CFDataCreateWithBytesNoCopy(nullptr, (const UInt8 *)_reversed_buffer, - y_size * rowsize, kCFAllocatorNull); - - _data_provider = CGDataProviderCreateWithCFData(_buffer_data); - _buffer_color_space = CGColorSpaceCreateDeviceRGB(); - - _buffer_image = CGImageCreate(x_size, y_size, 8, 32, rowsize, _buffer_color_space, - kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, - _data_provider, nullptr, false, kCGRenderingIntentDefault); - - } -} -#endif // __APPLE__ - -#ifdef __APPLE__ -/** - * OSX only: releases the _swbuffer and associated support objects previously - * allocated by alloc_swbuffer(). - */ -void P3DInstance:: -free_swbuffer() { - if (_swbuffer != nullptr) { - SubprocessWindowBuffer::destroy_buffer(_shared_fd, _shared_mmap_size, - _shared_filename, _swbuffer); - _swbuffer = nullptr; - } - if (_reversed_buffer != nullptr) { - delete[] _reversed_buffer; - _reversed_buffer = nullptr; - } - - if (_buffer_image != nullptr) { - CGImageRelease(_buffer_image); - CGColorSpaceRelease(_buffer_color_space); - CGDataProviderRelease(_data_provider); - CFRelease(_buffer_data); - - _buffer_data = nullptr; - _data_provider = nullptr; - _buffer_color_space = nullptr; - _buffer_image = nullptr; - } -} -#endif // __APPLE__ - - -#ifdef __APPLE__ -/** - * OSX only: this callback is associated with a CFRunLoopTimer, to be called - * periodically for updating the frame. - */ -void P3DInstance:: -timer_callback(CFRunLoopTimerRef timer, void *info) { - P3DInstance *self = (P3DInstance *)info; - self->request_refresh(); -} -#endif // __APPLE__ - -/** - * - */ -P3DInstance::ImageDownload:: -ImageDownload(P3DInstance *inst, int index) : - _inst(inst), - _index(index) -{ -} - -/** - * Intended to be overloaded to generate a callback when the download - * finishes, either successfully or otherwise. The bool parameter is true if - * the download was successful. - */ -void P3DInstance::ImageDownload:: -download_finished(bool success) { - P3DFileDownload::download_finished(success); - if (success) { - // We've successfully downloaded the image (directly, not via the package - // interface). - _inst->_image_files[_index]._filename = get_filename(); - - // Put it onscreen if it's supposed to be onscreen now, and our splash - // window still exists. - if (_inst->_splash_window != nullptr && - _inst->_image_files[_index]._image_placement != P3DSplashWindow::IP_none) { - P3DSplashWindow::ImagePlacement image_placement = _inst->_image_files[_index]._image_placement; - _inst->_splash_window->set_image_filename(get_filename(), image_placement); - } - } -} - -/** - * - */ -P3DInstance::InstanceDownload:: -InstanceDownload(P3DInstance *inst) : - _inst(inst) -{ -} - -/** - * Intended to be overloaded to generate an occasional callback as new data - * comes in. - */ -void P3DInstance::InstanceDownload:: -download_progress() { - P3DFileDownload::download_progress(); - _inst->report_instance_progress(get_download_progress(), is_download_progress_known(), get_total_data()); -} - -/** - * Intended to be overloaded to generate a callback when the download - * finishes, either successfully or otherwise. The bool parameter is true if - * the download was successful. - */ -void P3DInstance::InstanceDownload:: -download_finished(bool success) { - P3DFileDownload::download_finished(success); - if (success) { - // We've successfully downloaded the instance data. - _inst->report_instance_progress(1.0, true, 0); - _inst->priv_set_p3d_filename(get_filename()); - } else { - // Oops, no joy on the instance data. - _inst->send_notify("ondownloadfail"); - _inst->set_failed(); - } -} diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h deleted file mode 100644 index 7972ef7fbe..0000000000 --- a/direct/src/plugin/p3dInstance.h +++ /dev/null @@ -1,385 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dInstance.h - * @author drose - * @date 2009-05-29 - */ - -#ifndef P3DINSTANCE_H -#define P3DINSTANCE_H - -#include "p3d_plugin_common.h" -#include "p3dFileDownload.h" -#include "p3dFileParams.h" -#include "p3dWindowParams.h" -#include "p3dReferenceCount.h" -#include "p3dSplashWindow.h" -#include "p3dTemporaryFile.h" -#include "p3dMultifileReader.h" -#include "get_tinyxml.h" - -#ifdef __APPLE__ -#include "subprocessWindowBuffer.h" -#include -#endif - -#ifndef _WIN32 -#include -#endif - -#include -#include - -class P3DSession; -class P3DSplashWindow; -class P3DDownload; -class P3DPackage; -class P3DObject; -class P3DMainObject; -class P3DTemporaryFile; - -/** - * This is an instance of a Panda3D window, as seen in the parent-level - * process. - */ -class P3DInstance : public P3D_instance, public P3DReferenceCount { -public: - P3DInstance(P3D_request_ready_func *func, - const P3D_token tokens[], size_t num_tokens, - int argc, const char *argv[], void *user_data); - ~P3DInstance(); - void cleanup(); - - void set_p3d_url(const std::string &p3d_url); - void set_p3d_filename(const std::string &p3d_filename, const int &p3d_offset = 0); - int make_p3d_stream(const std::string &p3d_url); - inline const P3DFileParams &get_fparams() const; - - void set_wparams(const P3DWindowParams &wparams); - inline const P3DWindowParams &get_wparams() const; - - P3D_object *get_panda_script_object() const; - void set_browser_script_object(P3D_object *object); - - bool has_request(); - P3D_request *get_request(); - void bake_requests(); - void add_raw_request(TiXmlDocument *doc); - void add_baked_request(P3D_request *request); - static void finish_request(P3D_request *request, bool handled); - - bool feed_url_stream(int unique_id, - P3D_result_code result_code, - int http_status_code, - size_t total_expected_data, - const unsigned char *this_data, - size_t this_data_size); - - bool handle_event(const P3D_event_data &event); - - inline int get_instance_id() const; - inline const std::string &get_session_key() const; - const std::string &get_log_pathname() const; - inline const std::string &get_session_platform() const; - - inline P3DSession *get_session() const; - - inline P3D_request_ready_func *get_request_ready_func() const; - - void add_package(const std::string &name, const std::string &version, - const std::string &seq, P3DHost *host); - void add_package(P3DPackage *package); - void remove_package(P3DPackage *package); - bool get_packages_info_ready() const; - bool get_packages_ready() const; - bool get_packages_failed() const; - - inline bool is_trusted() const; - inline bool get_matches_script_origin() const; - int start_download(P3DDownload *download, bool add_request = true); - inline bool is_started() const; - inline bool is_failed() const; - void request_stop_sub_thread(); - void request_stop_main_thread(); - void request_refresh(); - void request_callback(P3D_callback_func *func, void *data); - - TiXmlElement *make_xml(); - void splash_button_clicked_sub_thread(); - void splash_button_clicked_main_thread(); - void auth_button_clicked(); - void play_button_clicked(); - - void auth_finished_sub_thread(); - void auth_finished_main_thread(); - - bool uninstall_packages(); - bool uninstall_host(); - -private: - class ImageDownload : public P3DFileDownload { - public: - ImageDownload(P3DInstance *inst, int index); - protected: - virtual void download_finished(bool success); - private: - P3DInstance *_inst; - int _index; - }; - class InstanceDownload : public P3DFileDownload { - public: - InstanceDownload(P3DInstance *inst); - protected: - virtual void download_progress(); - virtual void download_finished(bool success); - private: - P3DInstance *_inst; - }; - - // The different kinds of image files we download for the splash window. - enum ImageType { - // Also update _image_type_names when you update this list. - IT_download, - IT_unauth, - IT_ready, - IT_failed, - IT_launch, - IT_active, - IT_auth_ready, - IT_auth_rollover, - IT_auth_click, - IT_play_ready, - IT_play_rollover, - IT_play_click, - IT_none, // Must be the last value - IT_num_image_types, // Not a real value - }; - - void priv_set_p3d_filename(const std::string &p3d_filename, const int &p3d_offset = -1); - void determine_p3d_basename(const std::string &p3d_url); - - bool check_matches_origin(const std::string &origin_match); - bool check_matches_origin_one(const std::string &origin_match); - bool check_matches_hostname(const std::string &orig, const std::string &match); - void separate_components(std::vector &components, const std::string &str); - bool check_matches_component(const std::string &orig, const std::string &match); - - void check_p3d_signature(); - void mark_p3d_untrusted(); - void mark_p3d_trusted(); - void scan_app_desc_file(TiXmlDocument *doc); - void add_panda3d_package(); - void add_packages(); - std::string find_alt_host_url(const std::string &host_url, const std::string &alt_host); - void get_host_info(P3DHost *host); - std::string get_start_dir_suffix() const; - - void send_browser_script_object(); - P3D_request *make_p3d_request(TiXmlElement *xrequest); - void handle_notify_request(const std::string &message); - void handle_script_request(const std::string &operation, P3D_object *object, - const std::string &property_name, P3D_object *value, - bool needs_response, int unique_id); - - void set_failed(); - void make_splash_window(); - void set_background_image(ImageType image_type); - void set_button_image(ImageType image_type); - void report_package_info_ready(P3DPackage *package); - void consider_start_download(); - void ready_to_install(); - void start_next_download(); - void mark_download_complete(); - void ready_to_start(); - void report_instance_progress(double progress, bool is_progress_known, - size_t received_data); - void report_package_progress(P3DPackage *package, double progress); - void report_package_done(P3DPackage *package, bool success); - void set_install_label(const std::string &install_label); - - void paint_window(); - -#ifdef __APPLE__ - bool get_framebuffer_osx_port(); - bool get_framebuffer_osx_cgcontext(); - void paint_window_osx_port(); - void paint_window_osx_cgcontext(CGContextRef context); -#endif // __APPLE__ - - bool handle_event_osx_event_record(const P3D_event_data &event); - bool handle_event_osx_cocoa(const P3D_event_data &event); - void add_carbon_modifier_flags(unsigned int &swb_flags, int modifiers); - void add_cocoa_modifier_flags(unsigned int &swb_flags, int modifiers); - - void send_notify(const std::string &message); - -#ifdef __APPLE__ - void alloc_swbuffer(); - void free_swbuffer(); - static void timer_callback(CFRunLoopTimerRef timer, void *info); -#endif // __APPLE__ - - P3D_request_ready_func *_func; - P3D_object *_dom_object; - P3DMainObject *_main_object; - std::string _p3d_basename; - std::string _origin_protocol; - std::string _origin_hostname; - std::string _origin_port; - - // We need a list of previous time reports so we can average the predicted - // download time over the past few seconds. - class TimeReport { - public: - double _total; - double _report_time; - }; - typedef std::deque TimeReports; - TimeReports _time_reports; - double _total_time_reports; - - P3DTemporaryFile *_temp_p3d_filename; - - // For downloading the various images used by the splash window. - P3DPackage *_image_package; - static const char *_image_type_names[IT_num_image_types]; - - class ImageFile { - public: - inline ImageFile(); - inline ~ImageFile(); - inline void cleanup(); - - bool _use_standard_image; - P3DTemporaryFile *_temp_filename; - std::string _filename; - P3DSplashWindow::ImagePlacement _image_placement; - }; - ImageFile _image_files[IT_num_image_types]; - ImageType _current_background_image; - ImageType _current_button_image; - - bool _got_fparams; - P3DFileParams _fparams; - P3DMultifileReader _mf_reader; - - bool _got_wparams; - P3DWindowParams _wparams; - - bool _p3d_trusted; - TiXmlElement *_xpackage; - - // Holds the list of certificates that are pre-approved by the plugin - // vendor. - P3DPackage *_certlist_package; - - // For downloading the p3dcert authorization program. - P3DPackage *_p3dcert_package; - - int _instance_id; - std::string _session_key; - std::string _log_basename; - std::string _session_platform; - std::string _prc_name; - std::string _start_dir; - bool _hidden; - bool _matches_run_origin; - bool _matches_script_origin; - bool _allow_python_dev; - bool _keep_user_env; - bool _auto_install; - bool _auto_start; - bool _stop_on_ready; - bool _auth_button_clicked; - bool _failed; - - P3DSession *_session; - P3DAuthSession *_auth_session; - std::string _log_pathname; - -#ifdef __APPLE__ - // On OSX, we have to get a copy of the framebuffer data back from the child - // process, and draw it to the window, here in the parent process. Crazy! - int _shared_fd; - size_t _shared_mmap_size; - std::string _shared_filename; - SubprocessWindowBuffer *_swbuffer; - char *_reversed_buffer; - CFDataRef _buffer_data; - CGDataProviderRef _data_provider; - CGColorSpaceRef _buffer_color_space; - CGImageRef _buffer_image; - - bool _mouse_active; - unsigned int _modifiers; - - CFRunLoopTimerRef _frame_timer; -#endif // __APPLE__ - - P3DSplashWindow *_splash_window; - std::string _install_label; - bool _instance_window_opened; - bool _instance_window_attached; - bool _stuff_to_download; - - // Keep track of when the download was started, for reporting purposes. - // These members are used both for the instance download, and for the later - // package download. -#ifdef _WIN32 - int _start_dl_tick; -#else - struct timeval _start_dl_timeval; -#endif - - // This is set false initially, but true if the instance download continues - // for more than a couple of seconds. - bool _show_dl_instance_progress; - - typedef std::vector Packages; - Packages _packages; - Packages _downloading_packages; - int _download_package_index; - size_t _prev_downloaded; - size_t _total_download_size; - size_t _total_downloaded; - bool _packages_specified; - bool _download_started; - bool _download_complete; - bool _instance_started; - - // We keep the _panda3d pointer separately because it's so important, but - // it's in the above vector also. - P3DPackage *_panda3d_package; - - typedef std::map Downloads; - Downloads _downloads; - - // The _raw_requests queue might be filled up by the read thread, so we - // protect it in a lock. - LOCK _request_lock; - typedef std::deque RawRequests; - RawRequests _raw_requests; - bool _requested_stop; - - // The _baked_requests queue is only touched in the main thread; no lock - // needed. - typedef std::deque BakedRequests; - BakedRequests _baked_requests; - - friend class P3DSession; - friend class P3DAuthSession; - friend class ImageDownload; - friend class InstanceDownload; - friend class P3DWindowParams; - friend class P3DPackage; -}; - -#include "p3dInstance.I" - -#endif diff --git a/direct/src/plugin/p3dInstanceManager.I b/direct/src/plugin/p3dInstanceManager.I deleted file mode 100644 index 3241fea0cf..0000000000 --- a/direct/src/plugin/p3dInstanceManager.I +++ /dev/null @@ -1,322 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dInstanceManager.I - * @author drose - * @date 2009-05-29 - */ - -/** - * Returns true if the instance manager is successfully initialized, false - * otherwise. - */ -inline bool P3DInstanceManager:: -is_initialized() const { - return _is_initialized; -} - -/** - * Recreates the runtime environment if a previous call to uninstall_all() - * removed it. Does nothing if the runtime environment is already correctly - * set up. - */ -inline void P3DInstanceManager:: -reconsider_runtime_environment() { - assert(_is_initialized); - if (!_created_runtime_environment) { - create_runtime_environment(); - } -} - -/** - * Returns the verify_contents setting. When this is set to P3D_VC_none, it - * indicates that we don't need to contact the server to verify that a - * contents.xml file is fresh before using it; we should just use it as it is. - */ -inline P3D_verify_contents P3DInstanceManager:: -get_verify_contents() const { - return _verify_contents; -} - -/** - * Resets the verify_contents flag to P3D_VC_normal, if it is P3D_VC_none. - * This should be done whenever we discover anything needs to be downloaded. - * At this point, we might as well verify everything. - */ -inline void P3DInstanceManager:: -reset_verify_contents() { - if (_verify_contents == P3D_VC_none) { - _verify_contents = P3D_VC_normal; - } -} - - -/** - * Returns the api_version number which was passed to P3D_initialize(). - * Client code may use this to determine how to interpret parameters to - * various functions whose interface may have changed over different versions. - */ -inline int P3DInstanceManager:: -get_api_version() const { - return _api_version; -} - -/** - * Returns the standard host_url which the instances should attempt to contact - * to download auxiliary packages associated with the core API, such as the - * p3dcert and images packages. This is normally the compiled-in - * PANDA_PACKAGE_HOST_URL, but it might be set to something different by the - * -u parameter on the panda3d executable. - */ -inline const std::string &P3DInstanceManager:: -get_host_url() const { - return _host_url; -} - -/** - * Returns the root directory into which all the P3D runtime files are - * downloaded and installed. This must be a writable directory or nothing - * will work. - */ -inline const std::string &P3DInstanceManager:: -get_root_dir() const { - return _root_dir; -} - -/** - * Returns the directory that the .p3d file should be mounted to and run from. - * This is usually the "start" subdirectory of the root_dir. - */ -inline const std::string &P3DInstanceManager:: -get_start_dir() const { - return _start_dir; -} - -/** - * Returns the string that corresponds to the platform on which we are - * running. This string will be used to determine the appropriate packages to - * download. - */ -inline const std::string &P3DInstanceManager:: -get_platform() const { - return _platform; -} - -/** - * Returns the pathname of the directory into which temporary files should be - * written. This filename will end with a slash, so that full pathnames may - * be made by concatenting directly with this string. - */ -inline const std::string &P3DInstanceManager:: -get_temp_directory() const { - return _temp_directory; -} - -/** - * Returns the pathname of the directory into which all log files should be - * written. This filename will end with a slash, so that full pathnames may - * be made by concatenting directly with this string. - */ -inline const std::string &P3DInstanceManager:: -get_log_directory() const { - return _log_directory; -} - -/** - * Returns the filename of the system log file; this file is responsible for - * downloading and installing updates, and launching applications. This is - * different from the session log file(s), which represent the output from a - * particular Python session. - */ -inline const std::string &P3DInstanceManager:: -get_log_pathname() const { - return _log_pathname; -} - -/** - * Returns the value of the trusted_environment flag passed to the - * constructor. If this is true, it means the environment we are running in - * is trusted and the p3d file is already vetted. This means the current - * working directory will remain unchanged, and the p3d file will be run - * without checking its signature. - * - * This should generally be true only when run by panda3d.exe or panda3dw.exe, - * and not when run by the web plugin. - */ -inline bool P3DInstanceManager:: -get_trusted_environment() const { - return _trusted_environment; -} - -/** - * Returns the value of the console_environment flag passed to the - * constructor. If this is true, it means we are running from a text-based - * console window, and not from a desktop environment. - * - * This should generally be true only when run by panda3d.exe, and not when - * run by the web plugin or by panda3dw.exe. - */ -inline bool P3DInstanceManager:: -get_console_environment() const { - return _console_environment; -} - -/** - * Returns the number of different supported platforms available in - * get_supported_platform(). - */ -inline int P3DInstanceManager:: -get_num_supported_platforms() const { - return (int)_supported_platforms.size(); -} - -/** - * Returns the nth supported platform, where 0 <= n < - * get_num_supported_platforms(). - * - * A given runtime environment may support multiple different platforms, e.g. - * win32 or win64, with the restriction that all platform-specific packages - * (beginning from panda3d), must be the same platform. - * - * This function enumerates the different platforms that the current runtime - * environment will support, in order of preference--preferred platforms - * appear first in the list. - */ -inline const std::string &P3DInstanceManager:: -get_supported_platform(int n) const { - return _supported_platforms.at(n); -} - -/** - * Returns the plugin's reported major version number. - */ -inline int P3DInstanceManager:: -get_plugin_major_version() const { - return _plugin_major_version; -} - -/** - * Returns the plugin's reported minor version number. - */ -inline int P3DInstanceManager:: -get_plugin_minor_version() const { - return _plugin_minor_version; -} - -/** - * Returns the plugin's reported sequence version number. - */ -inline int P3DInstanceManager:: -get_plugin_sequence_version() const { - return _plugin_sequence_version; -} - -/** - * Returns true if the plugin claims to be from an "official" build, and the - * its version number is authoritative; or false if it makes no such claim - * (for instance, it was built by someone checking out from cvs). - */ -inline bool P3DInstanceManager:: -get_plugin_official_version() const { - return _plugin_official_version; -} - -/** - * Returns the "distributor" reported by the plugin. This should represent - * the entity that built and hosted the plugin. - */ -inline const std::string &P3DInstanceManager:: -get_plugin_distributor() const { - return _plugin_distributor; -} - -/** - * Returns the host URL from which this Core API was downloaded (according to - * the plugin). This is for reporting purposes only; see get_host_url() for - * the URL to contact to actually download content. - */ -inline const std::string &P3DInstanceManager:: -get_coreapi_host_url() const { - return _coreapi_host_url; -} - -/** - * Returns the timestamp associated with this Core API DLL (according to the - * plugin). This is the timestamp shown in the contents.xml for this host, - * and is usually the time at which the plugin was built. - */ -inline time_t P3DInstanceManager:: -get_coreapi_timestamp() const { - return _coreapi_timestamp; -} - -/** - * Returns the version number associated with the Core API, if provided. Some - * early versions of the Core API, and some early versions of the plugin, did - * not provide a number here. If provided, this will be a string of dot- - * separated integers. - */ -inline const std::string &P3DInstanceManager:: -get_coreapi_set_ver() const { - return _coreapi_set_ver; -} - -/** - * Returns the "super mirror" URL. See p3d_plugin.h. - */ -inline const std::string &P3DInstanceManager:: -get_super_mirror() const { - return _super_mirror_url; -} - -/** - * Returns the number of instances currently running within the world. - */ -inline int P3DInstanceManager:: -get_num_instances() const { - return _instances.size(); -} - -/** - * Returns the singleton "undefined" object, as a new reference. - */ -inline P3D_object *P3DInstanceManager:: -new_undefined_object() { - P3D_OBJECT_INCREF(_undefined_object); - return _undefined_object; -} - -/** - * Returns the singleton "none" object, as a new reference. - */ -inline P3D_object *P3DInstanceManager:: -new_none_object() { - P3D_OBJECT_INCREF(_none_object); - return _none_object; -} - -/** - * Returns the singleton "true" or "false" object, as a new reference. - */ -inline P3D_object *P3DInstanceManager:: -new_bool_object(bool value) { - P3D_object *obj = (value) ? _true_object : _false_object; - P3D_OBJECT_INCREF(obj); - return obj; -} - -/** - * Returns the hex digit corresponding to the indicated integer value. - */ -inline char P3DInstanceManager:: -encode_hexdigit(int c) { - if (c >= 10) { - return c - 10 + 'a'; - } - return c + '0'; -} diff --git a/direct/src/plugin/p3dInstanceManager.cxx b/direct/src/plugin/p3dInstanceManager.cxx deleted file mode 100644 index 93e2ffcfc2..0000000000 --- a/direct/src/plugin/p3dInstanceManager.cxx +++ /dev/null @@ -1,1519 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dInstanceManager.cxx - * @author drose - * @date 2009-05-29 - */ - -#include "p3dInstanceManager.h" -#include "p3dInstance.h" -#include "p3dSession.h" -#include "p3dAuthSession.h" -#include "p3dHost.h" -#include "p3d_plugin_config.h" -#include "p3dWinSplashWindow.h" -#include "p3dUndefinedObject.h" -#include "p3dNoneObject.h" -#include "p3dBoolObject.h" -#include "p3dPackage.h" -#include "find_root_dir.h" -#include "fileSpec.h" -#include "get_tinyxml.h" -#include "binaryXml.h" -#include "mkdir_complete.h" -#include "wstring_encode.h" - -// We can include this header file to get the DTOOL_PLATFORM definition, even -// though we don't link with dtool. -#include "dtool_platform.h" - -#ifdef _WIN32 -#include -#include // chmod() -#include // rmdir() -#include // GetModuleHandle() etc. -#else -#include -#include -#include -#endif - -#ifdef __APPLE__ -#include -#endif - -#include - -using std::string; -using std::vector; -using std::wstring; - -static ofstream logfile; -std::ostream *nout_stream = &logfile; - -P3DInstanceManager *P3DInstanceManager::_global_ptr; - -/** - * - */ -P3DInstanceManager:: -P3DInstanceManager() { - init_xml(); - - _is_initialized = false; - _created_runtime_environment = false; - _api_version = 0; - _next_temp_filename_counter = 1; - _unique_id = 0; - _trusted_environment = false; - _console_environment = false; - - _plugin_major_version = 0; - _plugin_minor_version = 0; - _plugin_sequence_version = 0; - _plugin_official_version = false; - _coreapi_timestamp = 0; - - _notify_thread_continue = false; - _started_notify_thread = false; - INIT_THREAD(_notify_thread); - - // Initialize the singleton objects. - _undefined_object = new P3DUndefinedObject(); - _none_object = new P3DNoneObject(); - _true_object = new P3DBoolObject(true); - _false_object = new P3DBoolObject(false); - - _auth_session = nullptr; - - // Seed the lame random number generator in rand(); we use it to select a - // mirror for downloading. - srand((unsigned int)time(nullptr)); - -#ifdef _WIN32 - // Ensure the appropriate Windows common controls are available to this - // application. - INITCOMMONCONTROLSEX icc; - icc.dwSize = sizeof(icc); - icc.dwICC = ICC_PROGRESS_CLASS; - InitCommonControlsEx(&icc); -#endif - -#ifndef _WIN32 - // On Mac or Linux, we'd better ignore SIGPIPE, or this signal will shut - // down the browser if the plugin exits unexpectedly. - struct sigaction ignore; - memset(&ignore, 0, sizeof(ignore)); - ignore.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &ignore, &_old_sigpipe); -#endif // _WIN32 -} - -/** - * - */ -P3DInstanceManager:: -~P3DInstanceManager() { - if (_started_notify_thread) { - _notify_ready.acquire(); - _notify_thread_continue = false; - _notify_ready.notify(); - _notify_ready.release(); - JOIN_THREAD(_notify_thread); - _started_notify_thread = false; - } - -#ifndef _WIN32 - // Restore the original SIGPIPE handler. - sigaction(SIGPIPE, &_old_sigpipe, nullptr); -#endif // _WIN32 - - // force-finish any remaining instances. - while (!_instances.empty()) { - P3DInstance *inst = *(_instances.begin()); - finish_instance(inst); - } - - assert(_sessions.empty()); - assert(_instances.empty()); - - if (_auth_session != nullptr) { - p3d_unref_delete(_auth_session); - _auth_session = nullptr; - } - - Hosts::iterator hi; - for (hi = _hosts.begin(); hi != _hosts.end(); ++hi) { - delete (*hi).second; - } - _hosts.clear(); - - // Delete any remaining temporary files. - TempFilenames::iterator ti; - for (ti = _temp_filenames.begin(); ti != _temp_filenames.end(); ++ti) { - const string &filename = (*ti); - nout << "Removing delinquent temp file " << filename << "\n"; - unlink(filename.c_str()); - } - _temp_filenames.clear(); - - nout << "counts: " << _undefined_object->_ref_count - << " " << _none_object->_ref_count - << " " << _true_object->_ref_count - << " " << _false_object->_ref_count - << "\n"; - - /* - assert(_undefined_object->_ref_count == 1); - assert(_none_object->_ref_count == 1); - assert(_true_object->_ref_count == 1); - assert(_false_object->_ref_count == 1); - */ - - P3D_OBJECT_DECREF(_undefined_object); - P3D_OBJECT_DECREF(_none_object); - P3D_OBJECT_DECREF(_true_object); - P3D_OBJECT_DECREF(_false_object); - -#ifdef _WIN32 - P3DWinSplashWindow::unregister_window_class(); -#endif -} - -/** - * Called by the plugin host at application startup. It returns true if the - * DLL is successfully initialized, false if it should be immediately shut - * down and redownloaded. - */ -bool P3DInstanceManager:: -initialize(int api_version, const string &contents_filename, - const string &host_url, P3D_verify_contents verify_contents, - const string &platform, const string &log_directory, - const string &log_basename, bool trusted_environment, - bool console_environment, - const string &root_dir, const string &host_dir, - const string &start_dir) { - _api_version = api_version; - _host_url = host_url; - _verify_contents = verify_contents; - _platform = platform; - _log_directory = log_directory; - _log_basename = log_basename; - _trusted_environment = trusted_environment; - _console_environment = console_environment; - - if (_host_url.empty()) { - _host_url = PANDA_PACKAGE_HOST_URL; - } - - if (_platform.empty()) { - // If the platform is compiled in (as opposed to passed in by the caller), - // we might in fact support multiple platforms. - _platform = DTOOL_PLATFORM; -#ifdef _WIN32 - if (_platform == "win_amd64") { - _supported_platforms.push_back("win_amd64"); - _supported_platforms.push_back("win_i386"); - _supported_platforms.push_back("win32"); - - } else if (_platform == "win_i386" || _platform == "win32") { - // This is a WIN32 process, but determine if the underlying OS actually - // supports WIN64. - if (supports_win64()) { - _supported_platforms.push_back("win_amd64"); - } - _supported_platforms.push_back("win_i386"); - _supported_platforms.push_back("win32"); - } -#elif defined(__APPLE__) - if (_platform == "osx_amd64") { - _supported_platforms.push_back("osx_amd64"); - _supported_platforms.push_back("osx_i386"); - - } else if (_platform == "osx_i386") { - // This is a 32-bit process, but determine if the underlying OS supports - // 64-bit. - - int mib[2] = { CTL_HW, HW_MACHINE }; - char machine[512]; - size_t len = 511; - if (sysctl(mib, 2, (void *)machine, &len, nullptr, 0) == 0) { - if (strcmp(machine, "x86_64") == 0) { - _supported_platforms.push_back("osx_amd64"); - } - } - - _supported_platforms.push_back("osx_i386"); - } -#endif // _WIN32 - - // TODO: Linux multiplatform support. Just add the appropriate platform - // strings to _supported_platforms. - } else { - nout << "Platform string was set by plugin to " << _platform << "\n"; - } - - if (_supported_platforms.empty()) { - // Hack for older plug-ins, which should still remain compatible with - // newer versions of the runtime distribution. - if (_platform == "win32") { - _supported_platforms.push_back("win_i386"); - } - - // We always support at least the specific platform on which we're - // running. - _supported_platforms.push_back(_platform); - } - -#ifdef P3D_PLUGIN_LOG_DIRECTORY - if (_log_directory.empty()) { - _log_directory = P3D_PLUGIN_LOG_DIRECTORY; - } -#endif - -#ifdef P3D_PLUGIN_LOG_BASENAME2 - if (_log_basename.empty()) { - _log_basename = P3D_PLUGIN_LOG_BASENAME2; - } -#endif - if (_log_basename.empty()) { - _log_basename = "p3dcore"; - } - - if (root_dir.empty()) { - _root_dir = find_root_dir(); - if (_root_dir.empty()) { - std::cerr << "Could not find root directory.\n"; - return false; - } - } else { - _root_dir = root_dir; - } - - _host_dir = host_dir; - - if (start_dir.empty()) { - _start_dir = _root_dir + "/start"; - } else { - _start_dir = start_dir; - } - - // Allow the caller (e.g. panda3d.exe) to specify a log directory. Or, - // allow the developer to compile one in. Failing that, we write logfiles - // to Panda3Dlog. - if (_log_directory.empty()) { - _log_directory = _root_dir + "/log"; - } - - // Ensure that the log directory ends with a slash. - if (!_log_directory.empty() && _log_directory[_log_directory.size() - 1] != '/') { -#ifdef _WIN32 - if (_log_directory[_log_directory.size() - 1] != '\\') -#endif - _log_directory += "/"; - } - - // Construct the logfile pathname. - _log_pathname = _log_directory; - _log_pathname += _log_basename; - _log_pathname += ".log"; - - create_runtime_environment(); - _is_initialized = true; - - if (!host_url.empty() && !contents_filename.empty()) { - // Attempt to pre-read the supplied contents.xml file, to avoid an - // unnecessary download later. - P3DHost *host = get_host(host_url); - if (!host->read_contents_file(contents_filename, false)) { - nout << "Couldn't read " << contents_filename << "\n"; - } - } - - nout << "Supported platforms:"; - for (size_t pi = 0; pi < _supported_platforms.size(); ++pi) { - nout << " " << _supported_platforms[pi]; - } - nout << "\n"; - - return true; -} - -/** - * Specifies the version of the calling plugin, for reporting to JavaScript - * and the like. - */ -void P3DInstanceManager:: -set_plugin_version(int major, int minor, int sequence, - bool official, const string &distributor, - const string &coreapi_host_url, - time_t coreapi_timestamp, - const string &coreapi_set_ver) { - reconsider_runtime_environment(); - _plugin_major_version = major; - _plugin_minor_version = minor; - _plugin_sequence_version = sequence; - _plugin_official_version = official; - _plugin_distributor = distributor; - - // The Core API "host URL" is both compiled in, and comes in externally; we - // trust the external source in the case of a conflict. - string internal_host_url = PANDA_PACKAGE_HOST_URL; - if (coreapi_host_url != internal_host_url) { - nout << "Warning! Downloaded Core API from " << coreapi_host_url - << ", but its internal URL was " << internal_host_url << "\n"; - } - _coreapi_host_url = coreapi_host_url; - if (_coreapi_host_url.empty()) { - _coreapi_host_url = internal_host_url; - } - - // The Core API timestamp is only available externally. - _coreapi_timestamp = coreapi_timestamp; - - // The Core API "set ver", or version, is both compiled in and comes in - // externally; for this one we trust the internal version in the case of a - // conflict. - string internal_set_ver = P3D_COREAPI_VERSION_STR; - if (coreapi_set_ver != internal_set_ver && !coreapi_set_ver.empty() && !internal_set_ver.empty()) { - nout << "Warning! contents.xml reports Core API version number " - << coreapi_set_ver << ", but its actual version number is " - << internal_set_ver << "\n"; - } - _coreapi_set_ver = internal_set_ver; - if (_coreapi_set_ver.empty()) { - _coreapi_set_ver = coreapi_set_ver; - } - - nout << "Plugin version: " - << _plugin_major_version << "." - << _plugin_minor_version << "." - << _plugin_sequence_version; - if (!_plugin_official_version) { - nout << "c"; - } - nout << "\n"; - nout << "Plugin distributor: " << _plugin_distributor << "\n"; - nout << "Core API host URL: " << _coreapi_host_url << "\n"; - nout << "Core API version: " << _coreapi_set_ver << "\n"; - - const char *timestamp_string = ctime(&_coreapi_timestamp); - if (timestamp_string == nullptr) { - timestamp_string = ""; - } - nout << "Core API date: " << timestamp_string << "\n"; -} - -/** - * Specifies the "super mirror" URL. See p3d_plugin.h. - */ -void P3DInstanceManager:: -set_super_mirror(const string &super_mirror_url) { - reconsider_runtime_environment(); - if (!super_mirror_url.empty()) { - nout << "super_mirror = " << super_mirror_url << "\n"; - } - _super_mirror_url = super_mirror_url; - - // Make sure it ends with a slash. - if (!_super_mirror_url.empty() && _super_mirror_url[_super_mirror_url.size() - 1] != '/') { - _super_mirror_url += '/'; - } -} - -/** - * Returns a newly-allocated P3DInstance with the indicated startup - * information. - */ -P3DInstance *P3DInstanceManager:: -create_instance(P3D_request_ready_func *func, - const P3D_token tokens[], size_t num_tokens, - int argc, const char *argv[], void *user_data) { - reconsider_runtime_environment(); - P3DInstance *inst = new P3DInstance(func, tokens, num_tokens, argc, argv, - user_data); - inst->ref(); - _instances.insert(inst); - - return inst; -} - -/** - * Sets the p3d_filename (or p3d_url) on a particular instance. - */ -bool P3DInstanceManager:: -set_p3d_filename(P3DInstance *inst, bool is_local, - const string &p3d_filename, const int &p3d_offset) { - if (inst->is_started()) { - nout << "Instance started twice: " << inst << "\n"; - return false; - } - if (is_local) { - inst->set_p3d_filename(p3d_filename, p3d_offset); - } else { - inst->set_p3d_url(p3d_filename); - } - - return true; -} - -/** - * Indicates an intention to transmit the p3d data as a stream. Should return - * a new unique stream ID to receive it. - */ -int P3DInstanceManager:: -make_p3d_stream(P3DInstance *inst, const string &p3d_url) { - if (inst->is_started()) { - nout << "Instance started twice: " << inst << "\n"; - return -1; - } - return inst->make_p3d_stream(p3d_url); -} - - -/** - * Actually starts the instance running on a particular session. This is - * called by the P3DInstance when it successfully loads its instance file. - */ -bool P3DInstanceManager:: -start_instance(P3DInstance *inst) { - if (inst->is_failed()) { - // Don't bother trying again. - return false; - } - - if (inst->is_started()) { - // Already started. - return true; - } - - P3DSession *session; - Sessions::iterator si = _sessions.find(inst->get_session_key()); - if (si == _sessions.end()) { - session = new P3DSession(inst); - session->ref(); - bool inserted = _sessions.insert(Sessions::value_type(session->get_session_key(), session)).second; - assert(inserted); - } else { - session = (*si).second; - } - - session->start_instance(inst); - - return inst->is_started(); -} - -/** - * Terminates and removes a previously-returned instance. - */ -void P3DInstanceManager:: -finish_instance(P3DInstance *inst) { - nout << "finish_instance: " << inst << "\n"; - Instances::iterator ii; - ii = _instances.find(inst); - if (ii != _instances.end()) { - _instances.erase(ii); - } - - Sessions::iterator si = _sessions.find(inst->get_session_key()); - if (si != _sessions.end()) { - P3DSession *session = (*si).second; - session->terminate_instance(inst); - - // If that was the last instance in this session, terminate the session. - if (session->get_num_instances() == 0) { - _sessions.erase(session->get_session_key()); - session->shutdown(); - p3d_unref_delete(session); - } - } - - inst->cleanup(); - p3d_unref_delete(inst); -} - -/** - * Creates a new P3DAuthSession object, to pop up a window for the user to - * authorize the certificate on this instance. Automatically terminates any - * previously-created P3DAuthSession. - */ -P3DAuthSession *P3DInstanceManager:: -authorize_instance(P3DInstance *inst) { - if (_auth_session != nullptr) { - // We only want one auth_session window open at a time, to minimize user - // confusion, so close any previous window. - _auth_session->shutdown(true); - p3d_unref_delete(_auth_session); - _auth_session = nullptr; - } - - _auth_session = new P3DAuthSession(inst); - _auth_session->ref(); - return _auth_session; -} - -/** - * Returns the P3DInstance pointer corresponding to the indicated P3D_instance - * if it is valid, or NULL if it is not. - */ -P3DInstance *P3DInstanceManager:: -validate_instance(P3D_instance *instance) { - Instances::iterator ii; - ii = _instances.find((P3DInstance *)instance); - if (ii != _instances.end()) { - return (*ii); - } - - return nullptr; -} - -/** - * If a request is currently pending on any instance, returns its pointer. - * Otherwise, returns NULL. - */ -P3DInstance *P3DInstanceManager:: -check_request() { - Instances::iterator ii; - for (ii = _instances.begin(); ii != _instances.end(); ++ii) { - P3DInstance *inst = (*ii); - if (inst->has_request()) { - return inst; - } - } - - return nullptr; -} - -/** - * Does not return until a request is pending on some instance, or until no - * instances remain, or until the indicated time in seconds has elapsed. Use - * check_request to retrieve the pending request. Due to the possibility of - * race conditions, it is possible for this function to return when there is - * in fact no request pending (another thread may have extracted the request - * first). - */ -void P3DInstanceManager:: -wait_request(double timeout) { -#ifdef _WIN32 - int stop_tick = int(GetTickCount() + timeout * 1000.0); -#else - struct timeval stop_time; - gettimeofday(&stop_time, nullptr); - - int seconds = (int)floor(timeout); - stop_time.tv_sec += seconds; - stop_time.tv_usec += (int)((timeout - seconds) * 1000.0); - if (stop_time.tv_usec > 1000) { - stop_time.tv_usec -= 1000; - ++stop_time.tv_sec; - } -#endif - - _request_ready.acquire(); - if (check_request() != nullptr) { - _request_ready.release(); - return; - } - if (_instances.empty()) { - _request_ready.release(); - return; - } - - // No pending requests; go to sleep. - _request_ready.wait(timeout); - - while (true) { -#ifdef _WIN32 - int remaining_ticks = stop_tick - GetTickCount(); - if (remaining_ticks <= 0) { - break; - } - timeout = remaining_ticks * 0.001; -#else - struct timeval now; - gettimeofday(&now, nullptr); - - struct timeval remaining; - remaining.tv_sec = stop_time.tv_sec - now.tv_sec; - remaining.tv_usec = stop_time.tv_usec - now.tv_usec; - - if (remaining.tv_usec < 0) { - remaining.tv_usec += 1000; - --remaining.tv_sec; - } - if (remaining.tv_sec < 0) { - break; - } - timeout = remaining.tv_sec + remaining.tv_usec * 0.001; -#endif - - if (check_request() != nullptr) { - _request_ready.release(); - return; - } - if (_instances.empty()) { - _request_ready.release(); - return; - } - - // No pending requests; go to sleep. - _request_ready.wait(timeout); - } - _request_ready.release(); -} - -/** - * Returns a (possibly shared) pointer to the indicated download host. - */ -P3DHost *P3DInstanceManager:: -get_host(const string &host_url) { - Hosts::iterator pi = _hosts.find(host_url); - if (pi != _hosts.end()) { - return (*pi).second; - } - - P3DHost *host = new P3DHost(host_url, _host_dir); - bool inserted = _hosts.insert(Hosts::value_type(host_url, host)).second; - assert(inserted); - - return host; -} - -/** - * Removes the indicated host from the cache. - */ -void P3DInstanceManager:: -forget_host(P3DHost *host) { - const string &host_url = host->get_host_url(); - - nout << "Forgetting host " << host_url << "\n"; - - // Hmm, this is a memory leak. But we allow it to remain, since it's an - // unusual circumstance (uninstalling), and it's safer to leak than to risk - // a floating pointer. - _hosts.erase(host_url); -} - -/** - * Returns a number used to uniquify different instances. This number is - * guaranteed to be different at each call, at least until the int space rolls - * over. - */ -int P3DInstanceManager:: -get_unique_id() { - ++_unique_id; - return _unique_id; -} - -/** - * May be called in any thread to indicate that a new P3D_request is available - * in the indicated instance. - */ -void P3DInstanceManager:: -signal_request_ready(P3DInstance *inst) { - if (inst->get_request_ready_func() != nullptr) { - // This instance requires asynchronous notifications of requests. Thus, - // we should tell the notify thread to wake up and make the callback. - _notify_ready.acquire(); - _notify_instances.push_back(inst); - _notify_ready.notify(); - _notify_ready.release(); - - // Oh, and we should spawn the thread if we haven't already. - if (!_started_notify_thread) { - _notify_thread_continue = true; - SPAWN_THREAD(_notify_thread, nt_thread_run, this); - _started_notify_thread = true; - } - } - - // Then, wake up the main thread, in case it's sleeping on wait_request(). - _request_ready.acquire(); - _request_ready.notify(); - _request_ready.release(); -} - -/** - * - */ -P3D_class_definition *P3DInstanceManager:: -make_class_definition() const { - P3D_class_definition *new_class = new P3D_class_definition(P3DObject::_generic_class); - // TODO: save this pointer so we can delete it on destruction. - return new_class; -} - -/** - * Constructs a new, unique temporary filename with the indicated extension. - * You should use the P3DTemporaryFilename interface instead of calling this - * method directly. - */ -string P3DInstanceManager:: -make_temp_filename(const string &extension) { - string result; - bool exists; - - do { - int tid; -#ifdef _WIN32 - tid = GetCurrentProcessId(); -#else - tid = getpid(); -#endif - if (tid == 0) { - tid = 1; - } - int hash = ((clock() + _next_temp_filename_counter) * ((time(nullptr) * tid) >> 8)) & 0xffffff; - ++_next_temp_filename_counter; - char hex_code[10]; - sprintf(hex_code, "%06x", hash); - - result = _temp_directory; - result += "p3d_"; - result += hex_code; - result += extension; - - exists = false; - if (_temp_filenames.find(result) != _temp_filenames.end()) { - // We've previously allocated this file. - exists = true; - - } else { - - // Check if the file exists on disk. -#ifdef _WIN32 - DWORD results = GetFileAttributes(result.c_str()); - if (results != -1) { - exists = true; - } - -#else // _WIN32 - struct stat this_buf; - if (stat(result.c_str(), &this_buf) == 0) { - exists = true; - } -#endif - } - - } while (exists); - - nout << "make_temp_filename: " << result << "\n"; - return result; -} - -/** - * Releases a temporary filename assigned earlier via make_temp_filename(). - * If the file exists, it will be removed. You should use the - * P3DTemporaryFilename interface instead of calling this method directly. - */ -void P3DInstanceManager:: -release_temp_filename(const string &filename) { - nout << "release_temp_filename: " << filename << "\n"; - _temp_filenames.erase(filename); - unlink(filename.c_str()); -} - -/** - * Looks for the particular certificate in the cache of recognized - * certificates. Returns true if it is found, false if not. - */ -bool P3DInstanceManager:: -find_cert(X509 *cert) { - // First, we need the DER representation. - string der = cert_to_der(cert); - - // If we've previously found this certificate, we don't have to hit disk - // again. - ApprovedCerts::iterator ci = _approved_certs.find(der); - if (ci != _approved_certs.end()) { - return true; - } - - // Well, we haven't found it already. Look for it on disk. For this, we - // hash the cert into a hex string. This is similar to OpenSSL's - // get_by_subject() approach, except we hash the whole cert, not just the - // subject. (Since we also store self-signed certs in this list, we can't - // trust the subject name alone.) - string this_cert_dir = get_cert_dir(cert); - nout << "looking in " << this_cert_dir << "\n"; - - vector contents; - scan_directory(this_cert_dir, contents); - - // Now look at each of the files in this directory and see if any of them - // matches the certificate. - vector::iterator si; - for (si = contents.begin(); si != contents.end(); ++si) { - string filename = this_cert_dir + "/" + (*si); - X509 *x509 = nullptr; - FILE *fp = nullptr; -#ifdef _WIN32 - wstring filename_w; - if (string_to_wstring(filename_w, filename)) { - fp = _wfopen(filename_w.c_str(), L"r"); - } -#else // _WIN32 - fp = fopen(filename.c_str(), "r"); -#endif // _WIN32 - if (fp != nullptr) { - x509 = PEM_read_X509(fp, nullptr, nullptr, (void *)""); - fclose(fp); - } - - if (x509 != nullptr) { - string der2 = cert_to_der(x509); - // We might as well save this cert in the table for next time, even if - // it's not the one we're looking for right now. - _approved_certs.insert(der2); - - if (der == der2) { - return true; - } - } - } - - // Nothing matched. - return false; -} - -/** - * Reads the pre-approved certificates in the certlist package and adds them - * to the in-memory cache. - */ -void P3DInstanceManager:: -read_certlist(P3DPackage *package) { - nout << "reading certlist in " << package->get_package_dir() << "\n"; - - vector contents; - scan_directory(package->get_package_dir(), contents); - - vector::iterator si; - for (si = contents.begin(); si != contents.end(); ++si) { - const string &basename = (*si); - if (basename.length() > 4) { - string suffix = basename.substr(basename.length() - 4); - if (suffix == ".pem" || suffix == ".crt") { - string filename = package->get_package_dir() + "/" + basename; - X509 *x509 = nullptr; - FILE *fp = nullptr; -#ifdef _WIN32 - wstring filename_w; - if (string_to_wstring(filename_w, filename)) { - fp = _wfopen(filename_w.c_str(), L"r"); - } -#else // _WIN32 - fp = fopen(filename.c_str(), "r"); -#endif // _WIN32 - if (fp != nullptr) { - x509 = PEM_read_X509(fp, nullptr, nullptr, (void *)""); - fclose(fp); - } - - if (x509 != nullptr) { - string der2 = cert_to_der(x509); - _approved_certs.insert(der2); - } - } - } - } -} - -/** - * Returns the directory searched for this particular certificate. - */ -string P3DInstanceManager:: -get_cert_dir(X509 *cert) { - string der = cert_to_der(cert); - - static const size_t hash_size = 16; - unsigned char md[hash_size]; - - MD5_CTX ctx; - MD5_Init(&ctx); - MD5_Update(&ctx, der.data(), der.size()); - MD5_Final(md, &ctx); - - string basename; - static const size_t keep_hash = 6; - for (size_t i = 0; i < keep_hash; ++i) { - int high = (md[i] >> 4) & 0xf; - int low = md[i] & 0xf; - basename += P3DInstanceManager::encode_hexdigit(high); - basename += P3DInstanceManager::encode_hexdigit(low); - } - - return _certs_dir + "/" + basename; -} - -/** - * Converts the indicated certificate to its binary DER representation. - */ -string P3DInstanceManager:: -cert_to_der(X509 *cert) { - int buffer_size = i2d_X509(cert, nullptr); - unsigned char *buffer = new unsigned char[buffer_size]; - unsigned char *p = buffer; - i2d_X509(cert, &p); - - string result((char *)buffer, buffer_size); - delete[] buffer; - - return result; -} - -/** - * Stops all active instances and removes *all* downloaded files from all - * hosts, and empties the current user's Panda3D directory as much as - * possible. - * - * This cannot remove the coreapi dll or directory on Windows. - */ -void P3DInstanceManager:: -uninstall_all() { - Instances::iterator ii; - for (ii = _instances.begin(); ii != _instances.end(); ++ii) { - P3DInstance *inst = (*ii); - inst->uninstall_host(); - } - - Hosts::iterator hi; - for (hi = _hosts.begin(); hi != _hosts.end(); ++hi) { - P3DHost *host = (*hi).second; - host->uninstall(); - } - - // Close the logfile so we can remove that too. - logfile.close(); - - if (!_root_dir.empty()) { - // This won't be able to delete the coreapi directory on Windows, because - // we're running that DLL right now. But it will delete everything else. - delete_directory_recursively(_root_dir); - } - - _created_runtime_environment = false; -} - -/** - * - */ -P3DInstanceManager *P3DInstanceManager:: -get_global_ptr() { - if (_global_ptr == nullptr) { - _global_ptr = new P3DInstanceManager; - } - return _global_ptr; -} - -/** - * This is called only at plugin shutdown time; it deletes the global instance - * manager pointer and clears it to NULL. - */ -void P3DInstanceManager:: -delete_global_ptr() { - if (_global_ptr != nullptr) { - delete _global_ptr; - _global_ptr = nullptr; - } -} - -/** - * Attempts to open the named filename as if it were a directory and looks for - * the non-hidden files within the directory. Fills the given vector up with - * the sorted list of filenames that are local to this directory. - * - * It is the user's responsibility to ensure that the contents vector is empty - * before making this call; otherwise, the new files will be appended to it. - * - * Returns true on success, false if the directory could not be read for some - * reason. - */ -bool P3DInstanceManager:: -scan_directory(const string &dirname, vector &contents) { -#ifdef _WIN32 - // Use Windows' FindFirstFile() FindNextFile() to walk through the list of - // files in a directory. - size_t orig_size = contents.size(); - - string match = dirname + "\\*.*"; - WIN32_FIND_DATAW find_data; - - wstring match_w; - string_to_wstring(match_w, match); - - HANDLE handle = FindFirstFileW(match_w.c_str(), &find_data); - if (handle == INVALID_HANDLE_VALUE) { - if (GetLastError() == ERROR_NO_MORE_FILES) { - // No matching files is not an error. - return true; - } - return false; - } - - do { - string filename; - wstring_to_string(filename, find_data.cFileName); - if (filename != "." && filename != "..") { - contents.push_back(filename); - } - } while (FindNextFileW(handle, &find_data)); - - bool scan_ok = (GetLastError() == ERROR_NO_MORE_FILES); - FindClose(handle); - - sort(contents.begin() + orig_size, contents.end()); - return scan_ok; - -#else // _WIN32 - // Use Posix's opendir() readdir() to walk through the list of files in a - // directory. - size_t orig_size = contents.size(); - - DIR *root = opendir(dirname.c_str()); - if (root == nullptr) { - return false; - } - - struct dirent *d; - d = readdir(root); - while (d != nullptr) { - if (d->d_name[0] != '.') { - contents.push_back(d->d_name); - } - d = readdir(root); - } - - closedir(root); - - sort(contents.begin() + orig_size, contents.end()); - return true; - -#endif // _WIN32 -} - -/** - * Fills up filename_contents with the list of all files (but not - * directories), and dirname_contents with the list of all directories, rooted - * at the indicated dirname and below. The filenames generated are relative - * to the root of the dirname, with slashes (not backslashes) as the directory - * separator character. - * - * Returns true on success, false if the original dirname wasn't a directory - * or something like that. - */ -bool P3DInstanceManager:: -scan_directory_recursively(const string &dirname, - vector &filename_contents, - vector &dirname_contents, - const string &prefix) { - vector dir_contents; - if (!scan_directory(dirname, dir_contents)) { - // Apparently dirname wasn't a directory. - return false; - } - - // Walk through the contents of dirname. - vector::const_iterator si; - for (si = dir_contents.begin(); si != dir_contents.end(); ++si) { - // Here's a particular file within dirname. Is it another directory, or - // is it a regular file? - string pathname = dirname + "/" + (*si); - string rel_filename = prefix + (*si); - if (scan_directory_recursively(pathname, filename_contents, - dirname_contents, rel_filename + "/")) { - // It's a directory, and it's just added its results to the contents. - dirname_contents.push_back(rel_filename); - - } else { - // It's not a directory, so assume it's an ordinary file, and add it to - // the contents. - filename_contents.push_back(rel_filename); - } - } - - return true; -} - -/** - * Deletes all of the files and directories in the named directory and below, - * like rm -rf. Use with extreme caution. - */ -void P3DInstanceManager:: -delete_directory_recursively(const string &root_dir) { - vector contents, dirname_contents; - if (!scan_directory_recursively(root_dir, contents, dirname_contents)) { - // Maybe it's just a single file, not a directory. Delete it. -#ifdef _WIN32 - wstring root_dir_w; - string_to_wstring(root_dir_w, root_dir); - // Windows can't delete a file if it's read-only. - _wchmod(root_dir_w.c_str(), 0644); - int result = _wunlink(root_dir_w.c_str()); -#else // _WIN32 - int result = unlink(root_dir.c_str()); -#endif // _WIN32 - if (result == 0) { - nout << "Deleted " << root_dir << "\n"; - } else { -#ifdef _WIN32 - result = _waccess(root_dir_w.c_str(), 0); -#else // _WIN32 - result = access(root_dir.c_str(), 0); -#endif // _WIN32 - if (result == 0) { - nout << "Could not delete " << root_dir << "\n"; - } - } - return; - } - - vector::iterator ci; - for (ci = contents.begin(); ci != contents.end(); ++ci) { - string filename = (*ci); - string pathname = root_dir + "/" + filename; - -#ifdef _WIN32 - wstring pathname_w; - string_to_wstring(pathname_w, pathname); - // Windows can't delete a file if it's read-only. - _wchmod(pathname_w.c_str(), 0644); - int result = _wunlink(pathname_w.c_str()); -#else // _WIN32 - int result = unlink(pathname.c_str()); -#endif // _WIN32 - if (result == 0) { - nout << " Deleted " << filename << "\n"; - } else { - nout << " Could not delete " << filename << "\n"; - } - } - - // Now delete all of the directories too. They're already in reverse order, - // so we remove deeper directories first. - for (ci = dirname_contents.begin(); ci != dirname_contents.end(); ++ci) { - string filename = (*ci); - string pathname = root_dir + "/" + filename; - -#ifdef _WIN32 - wstring pathname_w; - string_to_wstring(pathname_w, pathname); - _wchmod(pathname_w.c_str(), 0755); - int result = _wrmdir(pathname_w.c_str()); -#else // _WIN32 - int result = rmdir(pathname.c_str()); -#endif // _WIN32 - if (result == 0) { - nout << " Removed directory " << filename << "\n"; - } else { - nout << " Could not remove directory " << filename << "\n"; - } - } - - // Finally, delete the root directory itself. - string pathname = root_dir; -#ifdef _WIN32 - wstring pathname_w; - string_to_wstring(pathname_w, pathname); - _wchmod(pathname_w.c_str(), 0755); - int result = _wrmdir(pathname_w.c_str()); -#else // _WIN32 - int result = rmdir(pathname.c_str()); -#endif // _WIN32 - if (result == 0) { - nout << "Removed directory " << root_dir << "\n"; - } else { -#ifdef _WIN32 - result = _waccess(pathname_w.c_str(), 0); -#else // _WIN32 - result = access(pathname.c_str(), 0); -#endif // _WIN32 - if (result == 0) { - nout << "Could not remove directory " << root_dir << "\n"; - } - } -} - -/** - * Removes the first instance of the indicated file from the given list. - * Returns true if removed, false if it was not found. - * - * On Windows, the directory separator characters are changed from backslash - * to forward slash before searching in the list; so it is assumed that the - * list contains filenames with a forward slash used as a separator. - */ -bool P3DInstanceManager:: -remove_file_from_list(vector &contents, const string &filename) { -#ifdef _WIN32 - // Convert backslashes to slashes. - string clean_filename; - for (string::const_iterator pi = filename.begin(); - pi != filename.end(); - ++pi) { - if ((*pi) == '\\') { - clean_filename += '/'; - } else { - clean_filename += (*pi); - } - } -#else - const string &clean_filename = filename; -#endif // _WIN32 - - vector::iterator ci; - for (ci = contents.begin(); ci != contents.end(); ++ci) { - if ((*ci) == clean_filename) { - contents.erase(ci); - return true; - } - } - - return false; -} - -/** - * Appends the indicated basename to the root directory name, which is - * modified in-place. The basename is allowed to contain nested slashes, but - * no directory component of the basename may begin with a ".", thus - * precluding ".." and hidden files. - */ -void P3DInstanceManager:: -append_safe_dir(string &root, const string &basename) { - if (basename.empty()) { - return; - } - - size_t p = 0; - while (p < basename.length()) { - size_t q = basename.find('/', p); - if (q == string::npos) { - if (q != p) { - append_safe_dir_component(root, basename.substr(p)); - } - return; - } - if (q != p) { - append_safe_dir_component(root, basename.substr(p, q - p)); - } - p = q + 1; - } -} - -/** - * Called during initialize, or after a previous call to uninstall_all(), to - * make sure all needed directories exist and the logfile is open. - */ -void P3DInstanceManager:: -create_runtime_environment() { - mkdir_complete(_log_directory, std::cerr); - - logfile.close(); - logfile.clear(); -#ifdef _WIN32 - wstring log_pathname_w; - string_to_wstring(log_pathname_w, _log_pathname); - logfile.open(log_pathname_w.c_str(), std::ios::out | std::ios::trunc); -#else - logfile.open(_log_pathname.c_str(), std::ios::out | std::ios::trunc); -#endif // _WIN32 - if (logfile) { - logfile.setf(std::ios::unitbuf); - nout_stream = &logfile; - } - - // Determine the temporary directory. -#ifdef _WIN32 - wchar_t buffer_1[MAX_PATH]; - wstring temp_directory_w; - - // Figuring out the correct path for temporary files is a real mess on - // Windows. We should be able to use GetTempPath(), but that relies on $TMP - // or $TEMP being defined, and it appears that Mozilla clears these - // environment variables for the plugin, which forces GetTempPath() into - // $USERPROFILE instead. This is really an inappropriate place for - // temporary files, so, GetTempPath() isn't a great choice. - - // We could use SHGetSpecialFolderPath() instead to get us the path to - // "Temporary Internet Files", which is acceptable. The trouble is, if we - // happen to be running in "Protected Mode" on Vista, this folder isn't - // actually writable by us! On Vista, we're supposed to use - // IEGetWriteableFolderPath() instead, but *this* function doesn't exist on - // XP and below. Good Lord. - - // We could go through a bunch of LoadLibrary() calls to try to find the - // right path, like we do in find_root_dir(), but I'm just tired of doing - // all that nonsense. We'll use a two-stage trick instead. We'll check for - // $TEMP or $TMP being defined specifically, and if they are, we'll use - // GetTempPath(); otherwise, we'll fall back to SHGetSpecialFolderPath(). - - if (getenv("TEMP") != nullptr || getenv("TMP") != nullptr) { - if (GetTempPathW(MAX_PATH, buffer_1) != 0) { - temp_directory_w = buffer_1; - } - } - if (temp_directory_w.empty()) { - if (SHGetSpecialFolderPathW(nullptr, buffer_1, CSIDL_INTERNET_CACHE, true)) { - temp_directory_w = buffer_1; - - // That just *might* return a non-writable folder, if we're in Protected - // Mode. We'll test this with GetTempFileName(). - wchar_t temp_buffer[MAX_PATH]; - if (!GetTempFileNameW(temp_directory_w.c_str(), L"p3d", 0, temp_buffer)) { - nout << "GetTempFileName failed on " << temp_directory_w - << ", switching to GetTempPath\n"; - temp_directory_w.clear(); - } else { - DeleteFileW(temp_buffer); - } - } - } - - // If both of the above failed, we'll fall back to GetTempPath() once again - // as a last resort, which is supposed to return *something* that works, - // even if $TEMP and $TMP are undefined. - if (temp_directory_w.empty()) { - if (GetTempPathW(MAX_PATH, buffer_1) != 0) { - temp_directory_w = buffer_1; - } - } - - // Also insist that the temp directory is fully specified. - size_t needs_size_2 = GetFullPathNameW(temp_directory_w.c_str(), 0, nullptr, nullptr); - wchar_t *buffer_2 = new wchar_t[needs_size_2]; - if (GetFullPathNameW(temp_directory_w.c_str(), needs_size_2, buffer_2, nullptr) != 0) { - temp_directory_w = buffer_2; - } - delete[] buffer_2; - - // And make sure the directory actually exists. - mkdir_complete_w(temp_directory_w, nout); - wstring_to_string(_temp_directory, temp_directory_w); - -#else - _temp_directory = "/tmp/"; -#endif // _WIN32 - - // Ensure that the temp directory ends with a slash. - if (!_temp_directory.empty() && _temp_directory[_temp_directory.size() - 1] != '/') { -#ifdef _WIN32 - if (_temp_directory[_temp_directory.size() - 1] != '\\') -#endif - _temp_directory += "/"; - } - - nout << "\n_root_dir = " << _root_dir - << ", _temp_directory = " << _temp_directory - << ", platform = " << _platform - << ", host_url = " << _host_url - << ", verify_contents = " << _verify_contents - << "\n"; - if (!_host_dir.empty()) { - nout << "_host_dir = " << _host_dir << "\n"; - } - nout << "api_version = " << _api_version << "\n"; - - // Make the certificate directory. - _certs_dir = _root_dir + "/certs"; - if (!get_trusted_environment()) { - if (!mkdir_complete(_certs_dir, nout)) { - nout << "Couldn't mkdir " << _certs_dir << "\n"; - } - } - -#ifndef _WIN32 - // If we're running from the console, make sure that terminating the parent - // process will cause the child process to terminate as well. - if (_console_environment) { - struct sigaction ignore; - memset(&ignore, 0, sizeof(ignore)); - ignore.sa_handler = SIG_IGN; - sigaction(SIGINT, &ignore, nullptr); - } -#endif - - _created_runtime_environment = true; -} - -/** - * Appends a single directory component, implementing append_safe_dir(), - * above. - */ -void P3DInstanceManager:: -append_safe_dir_component(string &root, const string &component) { - if (component.empty()) { - return; - } - root += '/'; - if (component[0] == '.') { - root += 'x'; - } - root += component; -} - -/** - * The main function for the notify thread. - */ -void P3DInstanceManager:: -nt_thread_run() { -/* - * The notify thread exists because we need to be able to send asynchronous - * notifications of request events. These request events were detected in the - * various read threads associated with each session, but we can't call back - * into the plugin host space from the read thread, since if the host - * immediately responds to a callback by calling back into the p3d_plugin - * space, we will have our read thread doing stuff in here that's not related - * to the read thread. Even worse, some of the things it might need to do - * might require a separate read thread to be running! - */ - - _notify_ready.acquire(); - while (_notify_thread_continue) { - NotifyInstances instances; - while (!_notify_instances.empty()) { - instances.clear(); - instances.swap(_notify_instances); - - // Go ahead and drop the lock while we make the callback, to reduce the - // risk of deadlock. We don't want to be holding any locks when we call - // into client code. - _notify_ready.release(); - NotifyInstances::iterator ni; - for (ni = instances.begin(); ni != instances.end(); ++ni) { - // TODO: a race condition here when instances are deleted. - P3DInstance *inst = (*ni); - assert(inst != nullptr); - P3D_request_ready_func *func = inst->get_request_ready_func(); - if (func != nullptr) { - (*func)(inst); - } - } - _notify_ready.acquire(); - } - - _notify_ready.wait(); - } - _notify_ready.release(); -} - -#ifdef _WIN32 -bool P3DInstanceManager:: -supports_win64() { - BOOL is_win64 = false; - - typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); - LPFN_ISWOW64PROCESS _IsWow64Process; - _IsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process"); - - if (_IsWow64Process != nullptr) { - if (!_IsWow64Process(GetCurrentProcess(), &is_win64)) { - is_win64 = false; - } - } - return (is_win64 != 0); -} -#endif // _WIN32 diff --git a/direct/src/plugin/p3dInstanceManager.h b/direct/src/plugin/p3dInstanceManager.h deleted file mode 100644 index 08591e14cc..0000000000 --- a/direct/src/plugin/p3dInstanceManager.h +++ /dev/null @@ -1,244 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dInstanceManager.h - * @author drose - * @date 2009-05-29 - */ - -#ifndef P3DINSTANCEMANAGER_H -#define P3DINSTANCEMANAGER_H - -#include "p3d_plugin_common.h" -#include "p3dConditionVar.h" - -#include -#include -#include - -#ifndef _WIN32 -#include -#endif - -#define OPENSSL_NO_KRB5 -#include -#include -#include - -class P3DInstance; -class P3DSession; -class P3DAuthSession; -class P3DHost; -class P3DPackage; -class FileSpec; -class TiXmlElement; - -/** - * This global class manages the set of instances in the universe. - */ -class P3DInstanceManager { -private: - P3DInstanceManager(); - ~P3DInstanceManager(); - -public: - bool initialize(int api_version, const std::string &contents_filename, - const std::string &host_url, - P3D_verify_contents verify_contents, - const std::string &platform, - const std::string &log_directory, - const std::string &log_basename, - bool trusted_environment, - bool console_environment, - const std::string &root_dir = "", - const std::string &host_dir = "", - const std::string &start_dir = ""); - - inline bool is_initialized() const; - inline void reconsider_runtime_environment(); - inline P3D_verify_contents get_verify_contents() const; - inline void reset_verify_contents(); - - inline int get_api_version() const; - inline const std::string &get_host_url() const; - inline const std::string &get_root_dir() const; - inline const std::string &get_start_dir() const; - inline const std::string &get_platform() const; - inline const std::string &get_temp_directory() const; - inline const std::string &get_log_directory() const; - inline const std::string &get_log_pathname() const; - inline bool get_trusted_environment() const; - inline bool get_console_environment() const; - - inline int get_num_supported_platforms() const; - inline const std::string &get_supported_platform(int n) const; - - void set_plugin_version(int major, int minor, int sequence, - bool official, const std::string &distributor, - const std::string &coreapi_host_url, - time_t coreapi_timestamp, - const std::string &coreapi_set_ver); - inline int get_plugin_major_version() const; - inline int get_plugin_minor_version() const; - inline int get_plugin_sequence_version() const; - inline bool get_plugin_official_version() const; - inline const std::string &get_plugin_distributor() const; - inline const std::string &get_coreapi_host_url() const; - inline time_t get_coreapi_timestamp() const; - inline const std::string &get_coreapi_set_ver() const; - - void set_super_mirror(const std::string &super_mirror_url); - inline const std::string &get_super_mirror() const; - - P3DInstance * - create_instance(P3D_request_ready_func *func, - const P3D_token tokens[], size_t num_tokens, - int argc, const char *argv[], void *user_data); - - bool set_p3d_filename(P3DInstance *inst, bool is_local, - const std::string &p3d_filename, const int &p3d_offset); - int make_p3d_stream(P3DInstance *inst, const std::string &p3d_url); - bool start_instance(P3DInstance *inst); - void finish_instance(P3DInstance *inst); - P3DAuthSession *authorize_instance(P3DInstance *inst); - - P3DInstance *validate_instance(P3D_instance *instance); - - P3DInstance *check_request(); - void wait_request(double timeout); - - P3DHost *get_host(const std::string &host_url); - void forget_host(P3DHost *host); - - inline int get_num_instances() const; - - int get_unique_id(); - void signal_request_ready(P3DInstance *inst); - - P3D_class_definition *make_class_definition() const; - - inline P3D_object *new_undefined_object(); - inline P3D_object *new_none_object(); - inline P3D_object *new_bool_object(bool value); - - std::string make_temp_filename(const std::string &extension); - void release_temp_filename(const std::string &filename); - - bool find_cert(X509 *cert); - void read_certlist(P3DPackage *package); - std::string get_cert_dir(X509 *cert); - static std::string cert_to_der(X509 *cert); - - void uninstall_all(); - - static P3DInstanceManager *get_global_ptr(); - static void delete_global_ptr(); - - static inline char encode_hexdigit(int c); - static bool scan_directory(const std::string &dirname, std::vector &contents); - static bool scan_directory_recursively(const std::string &dirname, - std::vector &filename_contents, - std::vector &dirname_contents, - const std::string &prefix = ""); - static void delete_directory_recursively(const std::string &root_dir); - static bool remove_file_from_list(std::vector &contents, const std::string &filename); - - static void append_safe_dir(std::string &root, const std::string &basename); - -private: - void create_runtime_environment(); - static void append_safe_dir_component(std::string &root, const std::string &component); - -private: - // The notify thread. This thread runs only for the purpose of generating - // asynchronous notifications of requests, to callers who ask for it. - THREAD_CALLBACK_DECLARATION(P3DInstanceManager, nt_thread_run); - void nt_thread_run(); - -#ifdef _WIN32 - static bool supports_win64(); -#endif // _WIN32 - -private: - bool _is_initialized; - bool _created_runtime_environment; - int _api_version; - std::string _host_url; - std::string _root_dir; - std::string _host_dir; - std::string _start_dir; - std::string _certs_dir; - P3D_verify_contents _verify_contents; - std::string _platform; - std::string _log_directory; - std::string _log_basename; - std::string _log_pathname; - std::string _temp_directory; - bool _trusted_environment; - bool _console_environment; - int _plugin_major_version; - int _plugin_minor_version; - int _plugin_sequence_version; - bool _plugin_official_version; - std::string _plugin_distributor; - std::string _coreapi_host_url; - time_t _coreapi_timestamp; - std::string _coreapi_set_ver; - std::string _super_mirror_url; - - typedef std::vector SupportedPlatforms; - SupportedPlatforms _supported_platforms; - - P3D_object *_undefined_object; - P3D_object *_none_object; - P3D_object *_true_object; - P3D_object *_false_object; - - typedef std::set ApprovedCerts; - ApprovedCerts _approved_certs; - - typedef std::set Instances; - Instances _instances; - P3DAuthSession *_auth_session; - - typedef std::map Sessions; - Sessions _sessions; - - typedef std::map Hosts; - Hosts _hosts; - - typedef std::set TempFilenames; - TempFilenames _temp_filenames; - int _next_temp_filename_counter; - - int _unique_id; - - // This condition var is waited on the main thread and signaled in a sub- - // thread when new request notices arrive. - P3DConditionVar _request_ready; - - // We may need a thread to send async request notices to callers. - bool _notify_thread_continue; - bool _started_notify_thread; - THREAD _notify_thread; - // This queue of instances that need to send notifications is protected by - // _notify_ready's mutex. - typedef std::vector NotifyInstances; - NotifyInstances _notify_instances; - P3DConditionVar _notify_ready; - -#ifndef _WIN32 - struct sigaction _old_sigpipe; -#endif - - static P3DInstanceManager *_global_ptr; -}; - -#include "p3dInstanceManager.I" - -#endif diff --git a/direct/src/plugin/p3dIntObject.cxx b/direct/src/plugin/p3dIntObject.cxx deleted file mode 100644 index 05ad970128..0000000000 --- a/direct/src/plugin/p3dIntObject.cxx +++ /dev/null @@ -1,66 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dIntObject.cxx - * @author drose - * @date 2009-06-30 - */ - -#include "p3dIntObject.h" - -/** - * - */ -P3DIntObject:: -P3DIntObject(int value) : _value(value) { -} - -/** - * - */ -P3DIntObject:: -P3DIntObject(const P3DIntObject ©) : - P3DObject(copy), - _value(copy._value) -{ -} - -/** - * Returns the fundamental type of this kind of object. - */ -P3D_object_type P3DIntObject:: -get_type() { - return P3D_OT_int; -} - -/** - * Returns the object value coerced to a boolean, if possible. - */ -bool P3DIntObject:: -get_bool() { - return (_value != 0); -} - -/** - * Returns the object value coerced to an integer, if possible. - */ -int P3DIntObject:: -get_int() { - return _value; -} - -/** - * Fills the indicated C++ string object with the value of this object coerced - * to a string. - */ -void P3DIntObject:: -make_string(std::string &value) { - std::ostringstream strm; - strm << _value; - value = strm.str(); -} diff --git a/direct/src/plugin/p3dIntObject.h b/direct/src/plugin/p3dIntObject.h deleted file mode 100644 index d77c0d689a..0000000000 --- a/direct/src/plugin/p3dIntObject.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dIntObject.h - * @author drose - * @date 2009-06-30 - */ - -#ifndef P3DINTOBJECT_H -#define P3DINTOBJECT_H - -#include "p3d_plugin_common.h" -#include "p3dObject.h" - -/** - * An object type that contains an integer value. - */ -class P3DIntObject : public P3DObject { -public: - P3DIntObject(int value); - P3DIntObject(const P3DIntObject ©); - -public: - virtual P3D_object_type get_type(); - virtual bool get_bool(); - virtual int get_int(); - virtual void make_string(std::string &value); - -private: - int _value; -}; - -#endif diff --git a/direct/src/plugin/p3dMainObject.cxx b/direct/src/plugin/p3dMainObject.cxx deleted file mode 100644 index 2fd8dba33d..0000000000 --- a/direct/src/plugin/p3dMainObject.cxx +++ /dev/null @@ -1,707 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dMainObject.cxx - * @author drose - * @date 2009-07-10 - */ - -#include "p3dMainObject.h" -#include "p3dPythonObject.h" -#include "p3dInstance.h" -#include "p3dSession.h" -#include "p3dStringObject.h" -#include "p3dInstanceManager.h" - -using std::ios; -using std::max; -using std::streamsize; -using std::string; - -/** - * - */ -P3DMainObject:: -P3DMainObject() : - _pyobj(nullptr), - _inst(nullptr), - _unauth_play(false) -{ -} - -/** - * - */ -P3DMainObject:: -~P3DMainObject() { - set_pyobj(nullptr); - - // Clear the local properties. - Properties::const_iterator pi; - for (pi = _properties.begin(); pi != _properties.end(); ++pi) { - P3D_object *value = (*pi).second; - P3D_OBJECT_DECREF(value); - } - _properties.clear(); -} - -/** - * Returns the fundamental type of this kind of object. - */ -P3D_object_type P3DMainObject:: -get_type() { - return P3D_OT_object; -} - -/** - * Returns the object value coerced to a boolean, if possible. - */ -bool P3DMainObject:: -get_bool() { - return true; -} - -/** - * Returns the object value coerced to an integer, if possible. - */ -int P3DMainObject:: -get_int() { - return 0; -} - -/** - * Returns the object value coerced to a floating-point value, if possible. - */ -double P3DMainObject:: -get_float() { - return 0.0; -} - -/** - * Fills the indicated C++ string object with the value of this object coerced - * to a string. - */ -void P3DMainObject:: -make_string(string &value) { - if (_pyobj == nullptr) { - value = "P3DMainObject"; - } else { - int size = P3D_OBJECT_GET_STRING(_pyobj, nullptr, 0); - char *buffer = new char[size]; - P3D_OBJECT_GET_STRING(_pyobj, buffer, size); - value = string(buffer, size); - delete[] buffer; - } -} - -/** - * Returns the named property element in the object. The return value is a - * new-reference P3D_object, or NULL on error. - */ -P3D_object *P3DMainObject:: -get_property(const string &property) { - if (_pyobj == nullptr) { - // Without a pyobj, we just report whatever's been stored locally. - Properties::const_iterator pi; - pi = _properties.find(property); - if (pi != _properties.end()) { - P3D_object *result = (*pi).second; - P3D_OBJECT_INCREF(result); - return result; - } - return nullptr; - } - - // With a pyobj, we pass the query down to it. - return P3D_OBJECT_GET_PROPERTY(_pyobj, property.c_str()); -} - -/** - * Modifies (or deletes, if value is NULL) the named property element in the - * object. Returns true on success, false on failure. - */ -bool P3DMainObject:: -set_property(const string &property, bool needs_response, P3D_object *value) { - // First, we set the property locally. - if (value != nullptr) { - Properties::iterator pi; - pi = _properties.insert(Properties::value_type(property, nullptr)).first; - assert(pi != _properties.end()); - P3D_object *orig_value = (*pi).second; - if (orig_value != value) { - P3D_OBJECT_XDECREF(orig_value); - (*pi).second = value; - P3D_OBJECT_INCREF(value); - } - } else { - // (Or delete the property locally.) - Properties::iterator pi; - pi = _properties.find(property); - if (pi != _properties.end()) { - P3D_object *orig_value = (*pi).second; - P3D_OBJECT_DECREF(orig_value); - _properties.erase(pi); - } - } - - if (_pyobj == nullptr) { - // Without a pyobj, that's all we do. - return true; - } - - // With a pyobj, we also pass this request down. - return P3D_OBJECT_SET_PROPERTY(_pyobj, property.c_str(), needs_response, value); -} - -/** - * Returns true if the named method exists on this object, false otherwise. - */ -bool P3DMainObject:: -has_method(const string &method_name) { - // Some special-case methods implemented in-place. - if (method_name == "play") { - return true; - } else if (method_name == "read_game_log") { - return true; - } else if (method_name == "read_system_log") { - return true; - } else if (method_name == "read_log") { - return true; - } else if (method_name == "uninstall") { - return true; - } - - if (_pyobj == nullptr) { - // No methods until we get our pyobj. - return false; - } - - return P3D_OBJECT_HAS_METHOD(_pyobj, method_name.c_str()); -} - -/** - * Invokes the named method on the object, passing the indicated parameters. - * If the method name is empty, invokes the object itself. - * - * If needs_response is true, the return value is a new-reference P3D_object - * on success, or NULL on failure. If needs_response is false, the return - * value is always NULL, and there is no way to determine success or failure. - */ -P3D_object *P3DMainObject:: -call(const string &method_name, bool needs_response, - P3D_object *params[], int num_params) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - nout << "main." << method_name << "("; - for (int i = 0; i < num_params; ++i) { - if (i != 0) { - nout << ", "; - } - int buffer_size = P3D_OBJECT_GET_REPR(params[i], nullptr, 0); - char *buffer = new char[buffer_size]; - P3D_OBJECT_GET_REPR(params[i], buffer, buffer_size); - nout.write(buffer, buffer_size); - delete[] buffer; - } - nout << ")\n"; - - if (method_name == "play") { - return call_play(params, num_params); - } else if (method_name == "read_game_log") { - return call_read_game_log(params, num_params); - } else if (method_name == "read_system_log") { - return call_read_system_log(params, num_params); - } else if (method_name == "read_log") { - return call_read_log(params, num_params); - } else if (method_name == "uninstall") { - return call_uninstall(params, num_params); - } - - if (_pyobj == nullptr) { - // No methods until we get our pyobj. - return nullptr; - } - - return P3D_OBJECT_CALL(_pyobj, method_name.c_str(), needs_response, - params, num_params); -} - -/** - * Writes a formatted representation of the value to the indicated string. - * This is intended for developer assistance. - */ -void P3DMainObject:: -output(std::ostream &out) { - out << "P3DMainObject"; -} - -/** - * Changes the internal pyobj pointer. This is the P3D_object that references - * the actual PyObject held within the child process, corresponding to the - * true main object there. The new object's reference count is incremented, - * and the previous object's is decremented. - */ -void P3DMainObject:: -set_pyobj(P3D_object *pyobj) { - if (pyobj == this) { - // We are setting a reference directly to ourselves. This happens when - // the application has accepted the main object we gave it in - // set_instance_info(). This means the application is directly - // manipulating this object as its appRunner.main. In this case, we don't - // actually need to set the reference; instead, we clear anything we had - // set. - nout << "application shares main object\n"; - pyobj = nullptr; - - } else if (pyobj != nullptr) { - // In the alternate case, the application has its own, separate - // appRunner.main object. Thus, we do need to set the pointer. - nout << "application has its own main object\n"; - } - - if (_pyobj != pyobj) { - P3D_OBJECT_XDECREF(_pyobj); - _pyobj = pyobj; - if (_pyobj != nullptr) { - P3D_OBJECT_INCREF(_pyobj); - - // Now that we have a pyobj, we have to transfer down all of the - // properties we'd set locally. - apply_properties(_pyobj); - } - } -} - -/** - * Returns the internal pyobj pointer, or NULL if it has not yet been set. - */ -P3D_object *P3DMainObject:: -get_pyobj() const { - return _pyobj; -} - -/** - * Applies the locally-set properties onto the indicated Python object, but - * does not store the object. This is a one-time copy of the locally-set - * properties (like "coreapiHostUrl" and the like) onto the indicated Python - * object. - */ -void P3DMainObject:: -apply_properties(P3D_object *pyobj) { - P3DPythonObject *p3dpyobj = nullptr; - if (pyobj->_class == &P3DObject::_object_class) { - p3dpyobj = ((P3DObject *)pyobj)->as_python_object(); - } - - Properties::const_iterator pi; - for (pi = _properties.begin(); pi != _properties.end(); ++pi) { - const string &property_name = (*pi).first; - P3D_object *value = (*pi).second; - if (p3dpyobj != nullptr && P3D_OBJECT_GET_TYPE(value) != P3D_OT_object) { - // If we know we have an actual P3DPythonObject (we really expect this), - // then we can call set_property_insecure() directly, because we want to - // allow setting the initial properties even if Javascript has no - // permissions to write into Python. But we don't allow setting objects - // this way in any event. - p3dpyobj->set_property_insecure(property_name, false, value); - } else { - // Otherwise, we go through the generic interface. - P3D_OBJECT_SET_PROPERTY(pyobj, property_name.c_str(), false, value); - } - } -} - -/** - * Sets a callback pointer to the instance that owns this object. When this - * instance destructs, it clears this pointer to NULL. - */ -void P3DMainObject:: -set_instance(P3DInstance *inst) { - if (_inst != nullptr) { - // Save the game log filename of the instance just before it goes away, in - // case JavaScript asks for it later. - _game_log_pathname = _inst->get_log_pathname(); - } - - _inst = inst; -} - -/** - * Starts the process remotely, as if the play button had been clicked. If - * the application has not yet been validated, this pops up the validation - * dialog. - * - * Only applicable if the application was in the ready state, or the unauth - * state. Returns true if the application is now started, false otherwise. - * - * This may be invoked from the unauth state only once. If the user chooses - * not to authorize the plugin at that time, it may not be invoked - * automatically again. - */ -P3D_object *P3DMainObject:: -call_play(P3D_object *params[], int num_params) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (_inst == nullptr) { - return inst_mgr->new_bool_object(false); - } - - // I guess there's no harm in allowing JavaScript to call play(), with or - // without explicit scripting authorization. - nout << "play() called from JavaScript\n"; - - if (!_inst->is_trusted()) { - // Requires authorization. We allow this only once; beyond that, and - // you're only annoying the user. - if (!_unauth_play) { - _unauth_play = true; - _inst->splash_button_clicked_main_thread(); - } - - } else if (!_inst->is_started()) { - // We allow calling play() from a ready state without limit, but probably - // only once will be necessary. - _inst->splash_button_clicked_main_thread(); - } - - return inst_mgr->new_bool_object(_inst->is_started()); -} - -/** - * Reads the entire logfile as a string, and returns it to the calling - * JavaScript process. - */ -P3D_object *P3DMainObject:: -call_read_game_log(P3D_object *params[], int num_params) { - if (_inst != nullptr) { - string log_pathname = _inst->get_log_pathname(); - return read_log(log_pathname, params, num_params); - } - - if (!_game_log_pathname.empty()) { - // The instance has already finished, but we saved its log filename. - return read_log(_game_log_pathname, params, num_params); - } - - // No log available for us. - nout << "read_game_log: error: game log name unknown" << "\n"; - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - return inst_mgr->new_undefined_object(); -} - -/** - * As above, but reads the system log, the logfile for the installation - * process. - */ -P3D_object *P3DMainObject:: -call_read_system_log(P3D_object *params[], int num_params) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - string log_pathname = inst_mgr->get_log_pathname(); - return read_log(log_pathname, params, num_params); -} - -/** - * Reads a named logfile. The filename must end in ".log" and must not - * contain any slashes or colons (it must be found within the log directory). - */ -P3D_object *P3DMainObject:: -call_read_log(P3D_object *params[], int num_params) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - if (num_params < 1) { - nout << "read_log: error: not enough parameters" << "\n"; - return inst_mgr->new_undefined_object(); - } - - int size = P3D_OBJECT_GET_STRING(params[0], nullptr, 0); - char *buffer = new char[size]; - P3D_OBJECT_GET_STRING(params[0], buffer, size); - string log_filename = string(buffer, size); - delete[] buffer; - - if (log_filename.size() < 4 || log_filename.substr(log_filename.size() - 4) != string(".log")) { - // Wrong filename extension. - nout << "read_log: error: invalid filename" << "\n"; - return inst_mgr->new_undefined_object(); - } - - size_t slash = log_filename.find('/'); - if (slash != string::npos) { - // No slashes allowed. - nout << "read_log: error: invalid filename" << "\n"; - return inst_mgr->new_undefined_object(); - } - - slash = log_filename.find('\\'); - if (slash != string::npos) { - // Nor backslashes. - nout << "read_log: error: invalid filename" << "\n"; - return inst_mgr->new_undefined_object(); - } - - size_t colon = log_filename.find(':'); - if (colon != string::npos) { - // Nor colons, for that matter. - nout << "read_log: error: invalid filename" << "\n"; - return inst_mgr->new_undefined_object(); - } - - string log_pathname = inst_mgr->get_log_directory() + log_filename; - P3D_object *result = read_log(log_pathname, params + 1, num_params - 1); - return result; -} - -/** - * log-reader meta function that handles reading previous log files in - * addition to the present one - */ -P3D_object *P3DMainObject:: -read_log(const string &log_pathname, P3D_object *params[], int num_params) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - string log_directory = inst_mgr->get_log_directory(); - std::ostringstream log_data; - - // Check the first parameter, if any--if given, it specifies the last n - // bytes to retrieve. - size_t tail_bytes = 0; - if (num_params > 0) { - tail_bytes = (size_t)max(P3D_OBJECT_GET_INT(params[0]), 0); - } - // Check the second parameter, if any--if given, it specifies the first n - // bytes to retrieve. - size_t head_bytes = 0; - if (num_params > 1) { - head_bytes = (size_t)max(P3D_OBJECT_GET_INT(params[1]), 0); - } - // Check the third parameter, if any--if given, it specifies the last n - // bytes to retrieve from previous copies of this file. - size_t tail_bytes_prev = 0; - if (num_params > 2) { - tail_bytes_prev = (size_t)max(P3D_OBJECT_GET_INT(params[2]), 0); - } - // Check the fourth parameter, if any--if given, it specifies the first n - // bytes to retrieve from previous copies of this file. - size_t head_bytes_prev = 0; - if (num_params > 3) { - head_bytes_prev = (size_t)max(P3D_OBJECT_GET_INT(params[3]), 0); - } - - // Determine the base of the log file names - nout << "log_pathname: " << log_pathname << "\n"; - string log_basename = log_pathname; - size_t slash = log_basename.rfind('/'); - if (slash != string::npos) { - log_basename = log_basename.substr(slash + 1); - } -#ifdef _WIN32 - slash = log_basename.rfind('\\'); - if (slash != string::npos) { - log_basename = log_basename.substr(slash + 1); - } -#endif // _WIN32 - string log_leafname_primary = log_basename; - int dash = log_basename.rfind("-"); - if (dash != string::npos) { - log_basename = log_basename.substr(0, dash+1); - } else { - int dotLog = log_basename.rfind(".log"); - if (dotLog != string::npos) { - log_basename = log_basename.substr(0, dotLog); - log_basename += "-"; - } - } - - // Read matching files - std::vector all_logs; - int log_matches_found = 0; - string log_matching_pathname; - inst_mgr->scan_directory(log_directory, all_logs); - for (int i = (int)all_logs.size() - 1; i >= 0; --i) { - if (all_logs[i] == log_leafname_primary || - (all_logs[i].find(log_basename) == 0 && - all_logs[i].size() > 4 && - all_logs[i].substr(all_logs[i].size() - 4) == string(".log"))) { - log_matches_found++; - log_matching_pathname = (log_directory + all_logs[i]); - read_log_file(log_matching_pathname, tail_bytes, head_bytes, log_data); - tail_bytes = tail_bytes_prev; - head_bytes = head_bytes_prev; - } - } - - if (log_matches_found == 0) { - nout << "read_log: warning: no matching file(s) on disk." << "\n"; - } - - string log_data_str = log_data.str(); - P3D_object *result = new P3DStringObject(log_data_str); - return result; -} - -/** - * The generic log-reader function. - */ -void P3DMainObject:: -read_log_file(const string &log_pathname, - size_t tail_bytes, size_t head_bytes, - std::ostringstream &log_data) { - - // Get leaf name - string log_leafname = log_pathname; - size_t slash = log_leafname.rfind('/'); - if (slash != string::npos) { - log_leafname = log_leafname.substr(slash + 1); - } -#ifdef _WIN32 - slash = log_leafname.rfind('\\'); - if (slash != string::npos) { - log_leafname = log_leafname.substr(slash + 1); - } -#endif // _WIN32 - - // Render log file header to log_data - log_data << "======================================="; - log_data << "=======================================" << "\n"; - log_data << "== PandaLog-" << log_pathname << "\n"; - - // load file - ifstream log(log_pathname.c_str(), ios::in); - if (!log) { - log_data << "== PandaLog-" << "Error opening file"; - log_data << " " << "(" << log_leafname << ")" << "\n"; - return; - } - - // Get the size of the file. - log.seekg(0, ios::end); - size_t file_size = (size_t)log.tellg(); - nout << "read_log: " << log_pathname << " is " << file_size - << " bytes, tail_bytes = " << tail_bytes << ", head_bytes = " - << head_bytes << "\n"; - - // Early out if the file is empty - if (file_size == (size_t)0) { - log_data << "== PandaLog-" << "Empty File"; - log_data << " " << "(" << log_leafname << ")" << "\n"; - return; - } - - // Check if we are getting the full file - size_t full_bytes = 0; - if (file_size <= head_bytes + tail_bytes) { - // We will return the entire log. - full_bytes = file_size; - head_bytes = 0; - tail_bytes = 0; - } - - // Allocate a temp buffer to hold file data - size_t buffer_bytes = max(max(full_bytes, head_bytes), tail_bytes) + 1; - nout << "allocating " << buffer_bytes << " bytes to read at a time from file of size " << file_size << ".\n"; - char *buffer = new char[buffer_bytes]; - if (buffer == nullptr) { - log_data << "== PandaLog-" << "Error allocating buffer"; - log_data << " " << "(" << log_leafname << ")" << "\n"; - return; - } - - // Render log data if full file is to be fetched - if (full_bytes > 0) { - log.seekg(0, ios::beg); - log.read(buffer, full_bytes); - streamsize read_bytes = log.gcount(); - assert(read_bytes < (streamsize)buffer_bytes); - buffer[read_bytes] = '\0'; - log_data << "== PandaLog-" << "Full Start"; - log_data << " " << "(" << log_leafname << ")" << "\n"; - log_data << buffer; - log_data << "== PandaLog-" << "Full End"; - log_data << " " << "(" << log_leafname << ")" << "\n"; - } - - // Render log data if head bytes are to be fetched - if (head_bytes > 0) { - log.seekg(0, ios::beg); - log.read(buffer, head_bytes); - streamsize read_bytes = log.gcount(); - assert(read_bytes < (streamsize)buffer_bytes); - buffer[read_bytes] = '\0'; - log_data << "== PandaLog-" << "Head Start"; - log_data << " " << "(" << log_leafname << ")" << "\n"; - log_data << buffer << "\n"; - log_data << "== PandaLog-" << "Head End"; - log_data << " " << "(" << log_leafname << ")" << "\n"; - } - - // Render separator if head & tail bytes are to be fetched - if ((head_bytes > 0) && (tail_bytes > 0)) { - log_data << "== PandaLog-" << "truncated"; - log_data << " " << "(" << log_leafname << ")" << "\n"; - } - - // Render log data if tail bytes are to be fetched - if (tail_bytes > 0) { - log.seekg(file_size - tail_bytes, ios::beg); - log.read(buffer, tail_bytes); - streamsize read_bytes = log.gcount(); - assert(read_bytes < (streamsize)buffer_bytes); - buffer[read_bytes] = '\0'; - log_data << "== PandaLog-" << "Tail Start"; - log_data << " " << "(" << log_leafname << ")" << "\n"; - log_data << buffer; - log_data << "== PandaLog-" << "Tail End"; - log_data << " " << "(" << log_leafname << ")" << "\n"; - } - - // Render log file footer to log_data log_data << - // "======================================="; log_data << - // "=======================================" << "\n"; - - // cleanup - delete[] buffer; - buffer = nullptr; -} - -/** - * Implements the uninstall() plugin method, which removes all Panda installed - * files for a particular host, or referenced by a particular p3d file. - */ -P3D_object *P3DMainObject:: -call_uninstall(P3D_object *params[], int num_params) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - // Get the first parameter, the uninstall mode. - string mode; - if (num_params > 0) { - int size = P3D_OBJECT_GET_STRING(params[0], nullptr, 0); - char *buffer = new char[size]; - P3D_OBJECT_GET_STRING(params[0], buffer, size); - mode = string(buffer, size); - delete[] buffer; - } - - if (mode == "all") { - nout << "uninstall all\n"; - inst_mgr->uninstall_all(); - return inst_mgr->new_bool_object(true); - } - - if (_inst != nullptr) { - nout << "uninstall " << mode << " for " << _inst << "\n"; - bool success = false; - if (mode == "host") { - success = _inst->uninstall_host(); - } else { - success = _inst->uninstall_packages(); - } - return inst_mgr->new_bool_object(success); - } - - nout << "couldn't uninstall; no instance.\n"; - return inst_mgr->new_bool_object(false); -} diff --git a/direct/src/plugin/p3dMainObject.h b/direct/src/plugin/p3dMainObject.h deleted file mode 100644 index fafad0ff16..0000000000 --- a/direct/src/plugin/p3dMainObject.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dMainObject.h - * @author drose - * @date 2009-07-10 - */ - -#ifndef P3DMAINOBJECT_H -#define P3DMAINOBJECT_H - -#include "p3d_plugin_common.h" -#include "p3dObject.h" -#include -#include - -class P3DSession; -class P3DInstance; - -/** - * This corresponds to the "main" object exposed by a particular instance, as - * returned by P3DInstance::get_panda_script_object(). This object - * corresponds to the appRunner.main object in Python, and the - * document.pluginobject.main object in JavaScript. - * - * This is mostly a wrapper around a P3DPythonObject pointer, and therefore - * functions like any other P3DPythonObject; but it also handles the special - * case of being available before Python has been started; and it furthermore - * reports properties that are generated directly by the core API (like - * downloadProgress and such). - */ -class P3DMainObject : public P3DObject { -public: - P3DMainObject(); - virtual ~P3DMainObject(); - -public: - virtual P3D_object_type get_type(); - virtual bool get_bool(); - virtual int get_int(); - virtual double get_float(); - - virtual void make_string(std::string &value); - - virtual P3D_object *get_property(const std::string &property); - virtual bool set_property(const std::string &property, bool needs_response, - P3D_object *value); - - virtual bool has_method(const std::string &method_name); - virtual P3D_object *call(const std::string &method_name, bool needs_response, - P3D_object *params[], int num_params); - - virtual void output(std::ostream &out); - - void set_pyobj(P3D_object *pyobj); - P3D_object *get_pyobj() const; - void apply_properties(P3D_object *pyobj); - - void set_instance(P3DInstance *inst); - -private: - P3D_object *call_play(P3D_object *params[], int num_params); - P3D_object *call_read_game_log(P3D_object *params[], int num_params); - P3D_object *call_read_system_log(P3D_object *params[], int num_params); - P3D_object *call_read_log(P3D_object *params[], int num_params); - P3D_object *read_log(const std::string &log_pathname, - P3D_object *params[], int num_params); - void read_log_file(const std::string &log_pathname, - size_t tail_bytes, size_t head_bytes, - std::ostringstream &log_data); - P3D_object *call_uninstall(P3D_object *params[], int num_params); - -private: - P3D_object *_pyobj; - P3DInstance *_inst; - - bool _unauth_play; - std::string _game_log_pathname; - - // This map is used to store properties and retrieve until set_pyobj() is - // called for the firs ttime. At that point, the properties stored here are - // transferred down to the internal PyObject. - typedef std::map Properties; - Properties _properties; -}; - -#endif diff --git a/direct/src/plugin/p3dMultifileReader.I b/direct/src/plugin/p3dMultifileReader.I deleted file mode 100644 index ff3daf31b8..0000000000 --- a/direct/src/plugin/p3dMultifileReader.I +++ /dev/null @@ -1,79 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dMultifileReader.I - * @author drose - * @date 2009-06-15 - */ - -/** - * Returns true if the reader is open, false otherwise. - */ -inline bool P3DMultifileReader:: -is_open() const { - return _is_open; -} - -/** - * Extracts an unsigned short from the file. - */ -inline unsigned int P3DMultifileReader:: -read_uint16() { - unsigned int a = _in.get(); - unsigned int b = _in.get(); - return (b << 8) | a; -} - -/** - * Extracts an unsigned long from the file. - */ -inline unsigned int P3DMultifileReader:: -read_uint32() { - unsigned int a = _in.get(); - unsigned int b = _in.get(); - unsigned int c = _in.get(); - unsigned int d = _in.get(); - return (d << 24) | (c << 16) | (b << 8) | a; -} - -/** - * Returns the byte position within the Multifile of the last byte that - * contributes to this Subfile, either in the index record or in the subfile - * data. - */ -inline size_t P3DMultifileReader::Subfile:: -get_last_byte_pos() const { - return std::max(_index_start + _index_length, _data_start + _data_length) - 1; -} - -/** - * Ownership of the X509 object is passed into the CertRecord; it will be - * freed when the CertRecord destructs. - */ -inline P3DMultifileReader::CertRecord:: -CertRecord(X509 *cert) : - _cert(cert) -{ -} - -/** - * - */ -inline P3DMultifileReader::CertRecord:: -CertRecord(const P3DMultifileReader::CertRecord ©) : - _cert(X509_dup(copy._cert)) -{ -} - -/** - * - */ -inline P3DMultifileReader::CertRecord:: -~CertRecord() { - X509_free(_cert); -} diff --git a/direct/src/plugin/p3dMultifileReader.cxx b/direct/src/plugin/p3dMultifileReader.cxx deleted file mode 100644 index e47400bdb6..0000000000 --- a/direct/src/plugin/p3dMultifileReader.cxx +++ /dev/null @@ -1,487 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dMultifileReader.cxx - * @author drose - * @date 2009-06-15 - */ - -#include "p3dMultifileReader.h" -#include "p3dPackage.h" -#include "mkdir_complete.h" -#include "wstring_encode.h" - -#include - -#ifdef _WIN32 -#include -#include -#endif - -using std::ios; -using std::max; -using std::min; -using std::streampos; -using std::streamsize; -using std::string; - -// This sequence of bytes begins each Multifile to identify it as a Multifile. -const char P3DMultifileReader::_header[] = "pmf\0\n\r"; -const size_t P3DMultifileReader::_header_size = 6; - -const int P3DMultifileReader::_current_major_ver = 1; -const int P3DMultifileReader::_current_minor_ver = 1; - -/** - * - */ -P3DMultifileReader:: -P3DMultifileReader() { - _is_open = false; - _read_offset = 0; -} - -/** - * Opens the indicated file for reading. Returns true on success, false on - * failure. - */ -bool P3DMultifileReader:: -open_read(const string &pathname, const int &offset) { - if (_is_open) { - close(); - } - - _read_offset = offset; - if (!read_header(pathname)) { - return false; - } - - _is_open = true; - return true; -} - -/** - * Closes the previously-opened file. - */ -void P3DMultifileReader:: -close() { - _in.close(); - _is_open = false; -} - -/** - * Reads the multifile, and extracts all the expected extractable components - * within it to the indicated directory. Returns true on success, false on - * failure. - * - * Upates the "step" object with the progress through this operation. - */ -bool P3DMultifileReader:: -extract_all(const string &to_dir, P3DPackage *package, - P3DPackage::InstallStepThreaded *step) { - assert(_is_open); - if (_in.fail()) { - return false; - } - - // Now walk through all of the files, and extract only the ones we expect to - // encounter. - Subfiles::iterator si; - for (si = _subfiles.begin(); si != _subfiles.end(); ++si) { - const Subfile &s = (*si); - FileSpec file; - if (package != nullptr && !package->is_extractable(file, s._filename)) { - continue; - } - - string output_pathname = to_dir + "/" + s._filename; - if (!mkfile_complete(output_pathname, nout)) { - return false; - } - - ofstream out; -#ifdef _WIN32 - std::wstring output_pathname_w; - if (string_to_wstring(output_pathname_w, output_pathname)) { - out.open(output_pathname_w.c_str(), ios::out | ios::binary); - } -#else // _WIN32 - out.open(output_pathname.c_str(), ios::out | ios::binary); -#endif // _WIN32 - if (!out) { - nout << "Unable to write to " << output_pathname << "\n"; - return false; - } - - if (!extract_subfile(out, s)) { - return false; - } - out.close(); - - // Check that the file was extracted correctly (and also set the correct - // timestamp). - if (!file.full_verify(to_dir)) { - nout << "After extracting, " << s._filename << " is still incorrect.\n"; - return false; - } - - // Be sure to set execute permissions on the file, in case it's a program - // or something. - chmod(output_pathname.c_str(), 0555); - - if (step != nullptr && package != nullptr) { - step->thread_add_bytes_done(s._data_length); - } - } - - return true; -} - -/** - * Reads the multifile, and extracts only the named component to the indicated - * stream. Returns true on success, false on failure. - */ -bool P3DMultifileReader:: -extract_one(std::ostream &out, const string &filename) { - assert(_is_open); - if (_in.fail()) { - return false; - } - - // Look for the named component. - Subfiles::iterator si; - for (si = _subfiles.begin(); si != _subfiles.end(); ++si) { - const Subfile &s = (*si); - if (s._filename == filename) { - return extract_subfile(out, s); - } - } - - nout << "Could not extract " << filename << ": not found.\n"; - return false; -} - -/** - * Returns the number of matching signatures found on the Multifile. These - * signatures may be iterated via get_signature() and related methods. - * - * A signature on this list is guaranteed to match the Multifile contents, - * proving that the Multifile has been unmodified since the signature was - * applied. However, this does not guarantee that the certificate itself is - * actually from who it says it is from; only that it matches the Multifile - * contents. See validate_signature_certificate() to authenticate a - * particular certificate. - */ -int P3DMultifileReader:: -get_num_signatures() const { - if (_is_open) { - ((P3DMultifileReader *)this)->check_signatures(); - } - - return _signatures.size(); -} - -/** - * Returns the nth signature found on the Multifile. See the comments in - * get_num_signatures(). - */ -const P3DMultifileReader::CertChain &P3DMultifileReader:: -get_signature(int n) const { - static CertChain error_chain; - assert(n >= 0 && n < (int)_signatures.size()); - return _signatures[n]; -} - -/** - * Opens the named multifile and reads the header information and index, - * returning true on success, false on failure. - */ -bool P3DMultifileReader:: -read_header(const string &pathname) { - assert(!_is_open); - _subfiles.clear(); - _cert_special.clear(); - _signatures.clear(); - -#ifdef _WIN32 - std::wstring pathname_w; - if (string_to_wstring(pathname_w, pathname)) { - _in.open(pathname_w.c_str(), ios::in | ios::binary); - } -#else // _WIN32 - _in.open(pathname.c_str(), ios::in | ios::binary); -#endif // _WIN32 - if (!_in) { - nout << "Couldn't open " << pathname << "\n"; - return false; - } - - char this_header[_header_size]; - _in.seekg(_read_offset); - - // Here's a special case: if the multifile begins with a hash character, - // then we continue reading and discarding lines of ASCII text, until we - // come across a nonempty line that does not begin with a hash character. - // This allows a P3D application (which is a multifile) to be run directly - // on the command line on Unix-based systems. - int ch = _in.get(); - - if (ch == '#') { - while (ch != EOF && ch == '#') { - // Skip to the end of the line. - while (ch != EOF && ch != '\n') { - ch = _in.get(); - } - // Skip to the first non-whitespace character of the line. - while (ch != EOF && (isspace(ch) || ch == '\r')) { - ch = _in.get(); - } - } - } - - // Now read the actual Multifile header. - this_header[0] = ch; - _in.read(this_header + 1, _header_size - 1); - if (_in.fail() || _in.gcount() != (unsigned)(_header_size - 1)) { - nout << "Unable to read Multifile header: " << pathname << "\n"; - return false; - } - - if (memcmp(this_header, _header, _header_size) != 0) { - nout << "Failed header check: " << pathname << "\n"; - return false; - } - - unsigned int major = read_uint16(); - unsigned int minor = read_uint16(); - if (major != _current_major_ver || minor != _current_minor_ver) { - nout << "Incompatible multifile version: " << pathname << "\n"; - return false; - } - - unsigned int scale = read_uint32(); - if (scale != 1) { - nout << "Unsupported scale factor in " << pathname << "\n"; - return false; - } - - // We don't care about the overall timestamp. - read_uint32(); - - if (!read_index()) { - nout << "Error reading multifile index\n"; - return false; - } - - return true; -} - -/** - * Assuming the file stream is positioned at the first record, reads all of - * the records into the _subfiles list. Returns true on success, false on - * failure. - */ -bool P3DMultifileReader:: -read_index() { - _last_data_byte = 0; - unsigned int next_entry = read_uint32(); - if (!_in) { - return false; - } - while (next_entry != 0) { - Subfile s; - s._index_start = (size_t)_in.tellg() - _read_offset; - s._index_length = 0; - s._data_start = read_uint32(); - s._data_length = read_uint32(); - unsigned int flags = read_uint16(); - if ((flags & (SF_compressed | SF_encrypted)) != 0) { - // Skip over the uncompressed length. - read_uint32(); - } - - s._timestamp = read_uint32(); - size_t name_length = read_uint16(); - char *buffer = new char[name_length]; - _in.read(buffer, name_length); - - // The filenames are xored with 0xff just for fun. - for (size_t ni = 0; ni < name_length; ++ni) { - buffer[ni] ^= 0xff; - } - - s._filename = string(buffer, name_length); - delete[] buffer; - - s._index_length = (size_t)_in.tellg() - s._index_start - _read_offset; - - if (flags & SF_signature) { - // A subfile with this bit set is a signature. - _cert_special.push_back(s); - } else { - // Otherwise, it's a regular file. - _last_data_byte = max(_last_data_byte, s.get_last_byte_pos()); - - if ((flags & SF_ignore) == 0) { - // We can only support subfiles with none of SF_ignore set. - _subfiles.push_back(s); - } - } - - _in.seekg(next_entry + _read_offset); - next_entry = read_uint32(); - if (!_in) { - return false; - } - } - - return true; -} - -/** - * Extracts the indicated subfile and writes it to the indicated stream. - * Returns true on success, false on failure. - */ -bool P3DMultifileReader:: -extract_subfile(std::ostream &out, const Subfile &s) { - _in.seekg(s._data_start + _read_offset); - - static const streamsize buffer_size = 4096; - char buffer[buffer_size]; - - streamsize remaining_data = s._data_length; - _in.read(buffer, min(buffer_size, remaining_data)); - streamsize count = _in.gcount(); - while (count != 0) { - remaining_data -= count; - out.write(buffer, count); - _in.read(buffer, min(buffer_size, remaining_data)); - count = _in.gcount(); - } - - if (remaining_data != 0) { - nout << "Unable to extract " << s._filename << "\n"; - return false; - } - - return true; -} - - -/** - * Walks through the list of _cert_special entries in the Multifile, moving - * any valid signatures found to _signatures. After this call, _cert_special - * will be empty. - * - * This does not check the validity of the certificates themselves. It only - * checks that they correctly sign the Multifile contents. - */ -void P3DMultifileReader:: -check_signatures() { - Subfiles::iterator pi; - - for (pi = _cert_special.begin(); pi != _cert_special.end(); ++pi) { - Subfile *subfile = &(*pi); - - // Extract the signature data and certificate separately. - _in.seekg(subfile->_data_start + _read_offset); - size_t sig_size = read_uint32(); - char *sig = new char[sig_size]; - _in.read(sig, sig_size); - if (_in.gcount() != sig_size) { - nout << "read failure\n"; - delete[] sig; - return; - } - - size_t num_certs = read_uint32(); - - // Read the remaining buffer of certificate data. - size_t bytes_read = (size_t)_in.tellg() - subfile->_data_start - _read_offset; - size_t buffer_size = subfile->_data_length - bytes_read; - char *buffer = new char[buffer_size]; - _in.read(buffer, buffer_size); - if (_in.gcount() != buffer_size) { - nout << "read failure\n"; - delete[] sig; - delete[] buffer; - return; - } - - // Now convert each of the certificates to an X509 object, and store it in - // our CertChain. - CertChain chain; - EVP_PKEY *pkey = nullptr; - if (buffer_size > 0) { -#if OPENSSL_VERSION_NUMBER >= 0x00908000L - // Beginning in 0.9.8, d2i_X509() accepted a const unsigned char **. - const unsigned char *bp, *bp_end; -#else - // Prior to 0.9.8, d2i_X509() accepted an unsigned char **. - unsigned char *bp, *bp_end; -#endif - bp = (unsigned char *)&buffer[0]; - bp_end = bp + buffer_size; - X509 *x509 = d2i_X509(nullptr, &bp, bp_end - bp); - while (num_certs > 0 && x509 != nullptr) { - chain.push_back(CertRecord(x509)); - --num_certs; - x509 = d2i_X509(nullptr, &bp, bp_end - bp); - } - if (num_certs != 0 || x509 != nullptr) { - nout << "Extra data in signature record.\n"; - } - } - - delete[] buffer; - - if (!chain.empty()) { - pkey = X509_get_pubkey(chain[0]._cert); - } - - if (pkey != nullptr) { - EVP_MD_CTX *md_ctx; -#if OPENSSL_VERSION_NUMBER >= 0x00907000L - md_ctx = EVP_MD_CTX_create(); -#else - md_ctx = new EVP_MD_CTX; -#endif - EVP_VerifyInit(md_ctx, EVP_sha1()); - - // Read and hash the multifile contents, but only up till - // _last_data_byte. - _in.seekg(_read_offset); - streampos bytes_remaining = (streampos)_last_data_byte; - static const streamsize buffer_size = 4096; - char buffer[buffer_size]; - _in.read(buffer, min((streampos)buffer_size, bytes_remaining)); - streamsize count = _in.gcount(); - while (count != 0) { - assert(count <= buffer_size); - EVP_VerifyUpdate(md_ctx, buffer, (size_t)count); - bytes_remaining -= count; - _in.read(buffer, min((streampos)buffer_size, bytes_remaining)); - count = _in.gcount(); - } - assert(bytes_remaining == (streampos)0); - - // Now check that the signature matches the hash. - int verify_result = - EVP_VerifyFinal(md_ctx, (unsigned char *)sig, - sig_size, pkey); - if (verify_result == 1) { - // The signature matches; save the certificate and its chain. - _signatures.push_back(chain); - } - } - - delete[] sig; - } - - _cert_special.clear(); -} diff --git a/direct/src/plugin/p3dMultifileReader.h b/direct/src/plugin/p3dMultifileReader.h deleted file mode 100644 index 8f80a1f67c..0000000000 --- a/direct/src/plugin/p3dMultifileReader.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dMultifileReader.h - * @author drose - * @date 2009-06-15 - */ - -#ifndef P3DMULTIFILEREADER_H -#define P3DMULTIFILEREADER_H - -#include "p3d_plugin_common.h" -#include "p3dInstanceManager.h" // for openssl -#include "p3dPackage.h" - -/** - * A way-simple implementation of Panda's multifile reader. See - * panda/src/express/multifile.cxx for a full description of the binary - * format. This implementation doesn't support per-subfile compression or - * encryption. - */ -class P3DMultifileReader { -public: - P3DMultifileReader(); - bool open_read(const std::string &pathname, const int &offset = 0); - inline bool is_open() const; - void close(); - - bool extract_all(const std::string &to_dir, P3DPackage *package, - P3DPackage::InstallStepThreaded *step); - - bool extract_one(std::ostream &out, const std::string &filename); - - class CertRecord { - public: - inline CertRecord(X509 *cert); - inline CertRecord(const CertRecord ©); - inline ~CertRecord(); - X509 *_cert; - }; - typedef std::vector CertChain; - int get_num_signatures() const; - const CertChain &get_signature(int n) const; - -private: - class Subfile; - - bool read_header(const std::string &pathname); - bool read_index(); - bool extract_subfile(std::ostream &out, const Subfile &s); - - void check_signatures(); - - inline unsigned int read_uint16(); - inline unsigned int read_uint32(); - - enum SubfileFlags { - // If any of these bits are set, we can't read the subfile. - SF_ignore = 0x003f, - - // These bits are also relevant, and specifically so. - SF_compressed = 0x0008, - SF_encrypted = 0x0010, - SF_signature = 0x0020, - }; - - class Subfile { - public: - inline size_t get_last_byte_pos() const; - - std::string _filename; - size_t _index_start; - size_t _index_length; - size_t _data_start; - size_t _data_length; - size_t _timestamp; - }; - - ifstream _in; - bool _is_open; - int _read_offset; - - typedef std::vector Subfiles; - Subfiles _subfiles; - Subfiles _cert_special; - size_t _last_data_byte; - - typedef std::vector Certificates; - Certificates _signatures; - - static const char _header[]; - static const size_t _header_size; - static const int _current_major_ver; - static const int _current_minor_ver; -}; - -#include "p3dMultifileReader.I" - -#endif diff --git a/direct/src/plugin/p3dNoneObject.cxx b/direct/src/plugin/p3dNoneObject.cxx deleted file mode 100644 index 2a1653a2b8..0000000000 --- a/direct/src/plugin/p3dNoneObject.cxx +++ /dev/null @@ -1,46 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dNoneObject.cxx - * @author drose - * @date 2009-06-30 - */ - -#include "p3dNoneObject.h" - -/** - * - */ -P3DNoneObject:: -P3DNoneObject() { -} - -/** - * Returns the fundamental type of this kind of object. - */ -P3D_object_type P3DNoneObject:: -get_type() { - return P3D_OT_none; -} - -/** - * Returns the object value coerced to a boolean, if possible. - */ -bool P3DNoneObject:: -get_bool() { - return false; -} - -/** - * Fills the indicated C++ string object with the value of this object coerced - * to a string. - */ -void P3DNoneObject:: -make_string(std::string &value) { - value = "None"; -} diff --git a/direct/src/plugin/p3dNoneObject.h b/direct/src/plugin/p3dNoneObject.h deleted file mode 100644 index 06fffe89f9..0000000000 --- a/direct/src/plugin/p3dNoneObject.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dNoneObject.h - * @author drose - * @date 2009-06-30 - */ - -#ifndef P3DNONEOBJECT_H -#define P3DNONEOBJECT_H - -#include "p3d_plugin_common.h" -#include "p3dObject.h" - -/** - * An object type that contains no value, similar to Python's None type, or - * JavaScript's null type. - */ -class P3DNoneObject : public P3DObject { -public: - P3DNoneObject(); - -public: - virtual P3D_object_type get_type(); - virtual bool get_bool(); - virtual void make_string(std::string &value); -}; - -#endif diff --git a/direct/src/plugin/p3dObject.I b/direct/src/plugin/p3dObject.I deleted file mode 100644 index 2911760832..0000000000 --- a/direct/src/plugin/p3dObject.I +++ /dev/null @@ -1,33 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dObject.I - * @author drose - * @date 2009-06-30 - */ - -/** - * - */ -inline P3DObject:: -P3DObject() { - _class = &_object_class; - - // The reference count is initially 1. - _ref_count = 1; -} - -/** - * - */ -inline P3DObject:: -P3DObject(const P3DObject ©) { - assert(copy._class == &_object_class); - _class = copy._class; - _ref_count = 1; -} diff --git a/direct/src/plugin/p3dObject.cxx b/direct/src/plugin/p3dObject.cxx deleted file mode 100644 index f34f2d6d2d..0000000000 --- a/direct/src/plugin/p3dObject.cxx +++ /dev/null @@ -1,459 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dObject.cxx - * @author drose - * @date 2009-06-30 - */ - -#include "p3dObject.h" -#include "p3dBoolObject.h" -#include "p3dIntObject.h" -#include "p3dFloatObject.h" -#include "p3dStringObject.h" -#include "p3dInstanceManager.h" -#include // strncpy - -using std::string; - -// The following functions are C-style wrappers around the below P3DObject -// virtual methods; they are defined to allow us to create the C-style -// P3D_class_definition method table to store in the P3D_object structure. -static void -object_finish(P3D_object *object) { - delete (P3DObject *)object; -} - -static P3D_object_type -object_get_type(P3D_object *object) { - return ((P3DObject *)object)->get_type(); -} - -static bool -object_get_bool(P3D_object *object) { - return ((P3DObject *)object)->get_bool(); -} - -static int -object_get_int(P3D_object *object) { - return ((P3DObject *)object)->get_int(); -} - -static double -object_get_float(P3D_object *object) { - return ((P3DObject *)object)->get_float(); -} - -static int -object_get_string(P3D_object *object, char *buffer, int buffer_length) { - return ((P3DObject *)object)->get_string(buffer, buffer_length); -} - -static int -object_get_repr(P3D_object *object, char *buffer, int buffer_length) { - return ((P3DObject *)object)->get_repr(buffer, buffer_length); -} - -static P3D_object * -object_get_property(P3D_object *object, const char *property) { - return ((P3DObject *)object)->get_property(property); -} - -static bool -object_set_property(P3D_object *object, const char *property, - bool needs_response, P3D_object *value) { - return ((P3DObject *)object)->set_property(property, needs_response, value); -} - -static bool -object_has_method(P3D_object *object, const char *method_name) { - return ((P3DObject *)object)->has_method(method_name); -} - -static P3D_object * -object_call(P3D_object *object, const char *method_name, - bool needs_response, - P3D_object *params[], int num_params) { - if (method_name == nullptr) { - method_name = ""; - } - return ((P3DObject *)object)->call(method_name, needs_response, params, num_params); -} - -static P3D_object * -object_eval(P3D_object *object, const char *expression) { - return ((P3DObject *)object)->eval(expression); -} - -P3D_class_definition P3DObject::_object_class = { - &object_finish, - &object_get_type, - &object_get_bool, - &object_get_int, - &object_get_float, - &object_get_string, - &object_get_repr, - &object_get_property, - &object_set_property, - &object_has_method, - &object_call, - &object_eval, -}; - -// The next functions are used to construct the generic P3D_class_definition -// class returned by P3D_make_class_definition(). These are pointers to no-op -// functions, which the host may or may not choose to override. -static void -generic_finish(P3D_object *object) { - // You must override finish(), though, otherwise it's a leak. The core API - // has no idea how to delete your object. - nout << "Warning! default object_finish() method does nothing; object will leak.\n"; -} - -static P3D_object_type -generic_get_type(P3D_object *object) { - // We assume anyone going through the trouble of subclassing this will want - // to return an object, not one of the other fundamental types. - return P3D_OT_object; -} - -static bool -generic_get_bool(P3D_object *object) { - return false; -} - -static int -generic_get_int(P3D_object *object) { - return 0; -} - -static double -generic_get_float(P3D_object *object) { - return 0.0; -} - -static int -generic_get_string(P3D_object *object, char *buffer, int buffer_length) { - return 0; -} - -static int -generic_get_repr(P3D_object *object, char *buffer, int buffer_length) { - return 0; -} - -static P3D_object * -generic_get_property(P3D_object *object, const char *property) { - return nullptr; -} - -static bool -generic_set_property(P3D_object *object, const char *property, - bool needs_response, P3D_object *value) { - return false; -} - -static bool -generic_has_method(P3D_object *object, const char *method_name) { - return false; -} - -static P3D_object * -generic_call(P3D_object *object, const char *method_name, - bool needs_response, P3D_object *params[], int num_params) { - return nullptr; -} - -static P3D_object * -generic_eval(P3D_object *object, const char *expression) { - return nullptr; -} - -P3D_class_definition P3DObject::_generic_class = { - &generic_finish, - &generic_get_type, - &generic_get_bool, - &generic_get_int, - &generic_get_float, - &generic_get_string, - &generic_get_repr, - &generic_get_property, - &generic_set_property, - &generic_has_method, - &generic_call, - &generic_eval, -}; - -/** - * - */ -P3DObject:: -~P3DObject() { - assert(_ref_count == 0); -} - -/** - * Returns the object value coerced to an integer, if possible. - */ -int P3DObject:: -get_int() { - return 0; -} - -/** - * Returns the object value coerced to a floating-point value, if possible. - */ -double P3DObject:: -get_float() { - return get_int(); -} - -/** - * Stores a string that represents the object value in the indicated buffer; a - * null character is included if there is space. Returns the number of - * characters needed in the output (which might be more than the actual number - * of characters stored if buffer_length was too small). - */ -int P3DObject:: -get_string(char *buffer, int buffer_length) { - string result; - make_string(result); - strncpy(buffer, result.c_str(), buffer_length); - return (int)result.size(); -} - -/** - * Returns a user-friendly representation of the object, similar to - * get_string(), above. - */ -int P3DObject:: -get_repr(char *buffer, int buffer_length) { - std::ostringstream strm; - output(strm); - string result = strm.str(); - strncpy(buffer, result.c_str(), buffer_length); - return (int)result.size(); -} - -/** - * Returns the named property element in the object. The return value is a - * new-reference P3D_object, or NULL on error. - */ -P3D_object *P3DObject:: -get_property(const string &property) { - return nullptr; -} - -/** - * Modifies (or deletes, if value is NULL) the named property element in the - * object. Returns true on success, false on failure. - */ -bool P3DObject:: -set_property(const string &property, bool needs_response, P3D_object *value) { - return false; -} - -/** - * Returns true if the named method exists on this object, false otherwise. - */ -bool P3DObject:: -has_method(const string &method_name) { - return false; -} - -/** - * Invokes the named method on the object, passing the indicated parameters. - * If the method name is empty, invokes the object itself. - * - * If needs_response is true, the return value is a new-reference P3D_object - * on success, or NULL on failure. If needs_response is false, the return - * value is always NULL, and there is no way to determine success or failure. - */ -P3D_object *P3DObject:: -call(const string &method_name, bool needs_response, - P3D_object *params[], int num_params) { - return nullptr; -} - -/** - * Evaluates an arbitrary JavaScript expression. None of the P3DObject - * classes implement this. - */ -P3D_object *P3DObject:: -eval(const string &expression) { - return nullptr; -} - -/** - * Writes a formatted representation of the value to the indicated string. - * This is intended for developer assistance. - */ -void P3DObject:: -output(std::ostream &out) { - string value; - make_string(value); - out << value; -} - -/** - * If this object has a valid XML representation for the indicated session - * (that hasn't already been implemented by the generic code in P3DSession), - * this method will apply it to the indicated "value" element and return true. - * Otherwise, this method will leave the element unchanged and return false. - */ -bool P3DObject:: -fill_xml(TiXmlElement *xvalue, P3DSession *session) { - return false; -} - -/** - * Returns a pointer to the array of objects represented by this object, if - * any, or NULL if the object does not represent an array of objects. This - * may also return NULL for a zero-length array; use get_object_array_size() - * to differentiate. - */ -P3D_object **P3DObject:: -get_object_array() { - return nullptr; -} - -/** - * Returns the number of elements in the array returned by get_object_array(), - * or -1 if this object does not representan array of objects. - */ -int P3DObject:: -get_object_array_size() { - return -1; -} - -/** - * Returns this object, downcast to a P3DPythonObject, if it is in fact an - * object of that type; or NULL if it is not. - */ -P3DPythonObject *P3DObject:: -as_python_object() { - return nullptr; -} - -/** - * Returns the value of the named property, as a boolean. Returns 0 if the - * property does not exist. - */ -bool P3DObject:: -get_bool_property(const string &property) { - P3D_object *result = get_property(property); - if (result == nullptr) { - return 0; - } - bool bresult = P3D_OBJECT_GET_BOOL(result); - P3D_OBJECT_DECREF(result); - return bresult; -} - -/** - * Changes the value of the named property to the indicated boolean value. - */ -void P3DObject:: -set_bool_property(const string &property, bool value) { - P3D_object *bvalue = new P3DBoolObject(value); - set_property(property, false, bvalue); - P3D_OBJECT_DECREF(bvalue); -} - -/** - * Returns the value of the named property, as an integer. Returns 0 if the - * property does not exist. - */ -int P3DObject:: -get_int_property(const string &property) { - P3D_object *result = get_property(property); - if (result == nullptr) { - return 0; - } - int iresult = P3D_OBJECT_GET_INT(result); - P3D_OBJECT_DECREF(result); - return iresult; -} - -/** - * Changes the value of the named property to the indicated integer value. - */ -void P3DObject:: -set_int_property(const string &property, int value) { - P3D_object *ivalue = new P3DIntObject(value); - set_property(property, false, ivalue); - P3D_OBJECT_DECREF(ivalue); -} - -/** - * Returns the value of the named property, as a floating-point number. - * Returns 0.0 if the property does not exist. - */ -double P3DObject:: -get_float_property(const string &property) { - P3D_object *result = get_property(property); - if (result == nullptr) { - return 0.0; - } - double fresult = P3D_OBJECT_GET_FLOAT(result); - P3D_OBJECT_DECREF(result); - return fresult; -} - -/** - * Changes the value of the named property to the indicated floating-point - * value. - */ -void P3DObject:: -set_float_property(const string &property, double value) { - P3D_object *fvalue = new P3DFloatObject(value); - set_property(property, false, fvalue); - P3D_OBJECT_DECREF(fvalue); -} - -/** - * Returns the value of the named property, as a string. Returns empty string - * if the property does not exist. - */ -string P3DObject:: -get_string_property(const string &property) { - P3D_object *result = get_property(property); - if (result == nullptr) { - return string(); - } - - int size = P3D_OBJECT_GET_STRING(result, nullptr, 0); - char *buffer = new char[size]; - P3D_OBJECT_GET_STRING(result, buffer, size); - string sresult(buffer, size); - delete[] buffer; - - P3D_OBJECT_DECREF(result); - return sresult; -} - -/** - * Changes the value of the named property to the indicated string value. - */ -void P3DObject:: -set_string_property(const string &property, const string &value) { - P3D_object *svalue = new P3DStringObject(value); - set_property(property, false, svalue); - P3D_OBJECT_DECREF(svalue); -} - -/** - * Changes the value of the named property to the undefined value. - */ -void P3DObject:: -set_undefined_property(const string &property) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3D_object *uvalue = inst_mgr->new_undefined_object(); - set_property(property, false, uvalue); - P3D_OBJECT_DECREF(uvalue); -} diff --git a/direct/src/plugin/p3dObject.h b/direct/src/plugin/p3dObject.h deleted file mode 100644 index 8e69e5bec7..0000000000 --- a/direct/src/plugin/p3dObject.h +++ /dev/null @@ -1,91 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dObject.h - * @author drose - * @date 2009-06-30 - */ - -#ifndef P3DOBJECT_H -#define P3DOBJECT_H - -#include "p3d_plugin_common.h" - -class P3DPythonObject; - -/** - * The C++ implementation of P3D_value, corresponding to a single atomic value - * that is passed around between scripting languages. This is an abstract - * base class; the actual implementations are provided by the various - * specialized classes. - */ -class P3DObject : public P3D_object { -protected: - inline P3DObject(); - inline P3DObject(const P3DObject ©); - -public: - virtual ~P3DObject(); - - virtual P3D_object_type get_type()=0; - virtual bool get_bool()=0; - virtual int get_int(); - virtual double get_float(); - - int get_string(char *buffer, int buffer_length); - int get_repr(char *buffer, int buffer_length); - virtual void make_string(std::string &value)=0; - - virtual P3D_object *get_property(const std::string &property); - virtual bool set_property(const std::string &property, bool needs_response, - P3D_object *value); - - virtual bool has_method(const std::string &method_name); - virtual P3D_object *call(const std::string &method_name, bool needs_response, - P3D_object *params[], int num_params); - virtual P3D_object *eval(const std::string &expression); - - virtual void output(std::ostream &out); - virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session); - virtual P3D_object **get_object_array(); - virtual int get_object_array_size(); - - virtual P3DPythonObject *as_python_object(); - - // Convenience functions. - bool get_bool_property(const std::string &property); - void set_bool_property(const std::string &property, bool value); - - int get_int_property(const std::string &property); - void set_int_property(const std::string &property, int value); - - double get_float_property(const std::string &property); - void set_float_property(const std::string &property, double value); - - std::string get_string_property(const std::string &property); - void set_string_property(const std::string &property, const std::string &value); - - void set_undefined_property(const std::string &property); - -public: - static P3D_class_definition _object_class; - static P3D_class_definition _generic_class; -}; - -#include "p3dObject.I" - -// For classes that inherit from P3DObject, above, we can use the virtual -// method to write the output simply. (For classes that inherit only from -// P3D_object, we have to use the generic C method defined in -// p3d_plugin_common.h, a little clumsier.) -inline std::ostream &operator << (std::ostream &out, P3DObject &value) { - value.output(out); - return out; -} - -#endif diff --git a/direct/src/plugin/p3dOsxSplashWindow.I b/direct/src/plugin/p3dOsxSplashWindow.I deleted file mode 100644 index a9573dafa4..0000000000 --- a/direct/src/plugin/p3dOsxSplashWindow.I +++ /dev/null @@ -1,32 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dOsxSplashWindow.I - * @author drose - * @date 2009-07-16 - */ - -/** - * - */ -inline P3DOsxSplashWindow::OsxImageData:: -OsxImageData() { - _raw_data = nullptr; - _image = nullptr; - _color_space = nullptr; - _provider = nullptr; - _data = nullptr; -} - -/** - * - */ -inline P3DOsxSplashWindow::OsxImageData:: -~OsxImageData() { - dump_image(); -} diff --git a/direct/src/plugin/p3dOsxSplashWindow.cxx b/direct/src/plugin/p3dOsxSplashWindow.cxx deleted file mode 100644 index 4139a9629d..0000000000 --- a/direct/src/plugin/p3dOsxSplashWindow.cxx +++ /dev/null @@ -1,827 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dOsxSplashWindow.cxx - * @author drose - * @date 2009-07-16 - */ - -#include "p3dOsxSplashWindow.h" - -#if defined(__APPLE__) && !__LP64__ - -#include -#include - -#ifndef CGFLOAT_DEFINED - #if defined(__LP64__) && __LP64__ - typedef double CGFloat; - #else - typedef float CGFloat; - #endif -#endif - -using std::string; - -/** - * - */ -P3DOsxSplashWindow:: -P3DOsxSplashWindow(P3DInstance *inst, bool make_visible) : - P3DSplashWindow(inst, make_visible) -{ - _font_attribs = nullptr; - _install_progress = 0; - _progress_known = true; - _received_data = 0; - _got_wparams = false; - // We have to start with _mouse_active true; firefox doesn't send activate - // events. - _mouse_active = true; - _toplevel_window = nullptr; -} - -/** - * - */ -P3DOsxSplashWindow:: -~P3DOsxSplashWindow() { - if (_toplevel_window != nullptr) { - SetWRefCon(_toplevel_window, 0); - HideWindow(_toplevel_window); - DisposeWindow(_toplevel_window); - _toplevel_window = nullptr; - } - if (_font_attribs != nullptr) { - CFRelease(_font_attribs); - } -} - -/** - * Changes the window parameters, e.g. to resize or reposition the window; or - * sets the parameters for the first time, creating the initial window. - */ -void P3DOsxSplashWindow:: -set_wparams(const P3DWindowParams &wparams) { - P3DSplashWindow::set_wparams(wparams); - _got_wparams = true; - - if (_wparams.get_window_type() == P3D_WT_toplevel || - _wparams.get_window_type() == P3D_WT_fullscreen) { - // Creating a toplevel splash window. - if (_toplevel_window == nullptr) { - Rect r; - r.top = _wparams.get_win_y(); - r.left = _wparams.get_win_x(); - - // These are the same defaults used by Panda's osxGraphicsWindow. - if (r.left == -1) r.left = 10; - if (r.top == -1) r.top = 50; - // A coordinate of -2 means to center the window on the screen. - CGRect display_bounds = CGDisplayBounds(kCGDirectMainDisplay); - if (r.left == -2) { - r.left = (short)(0.5 * (CGRectGetWidth(display_bounds) - _win_width)); - } - if (r.top == -2) { - r.top = (short)(0.5 * (CGRectGetHeight(display_bounds) - _win_height)); - } - - r.right = r.left + _win_width; - r.bottom = r.top + _win_height; - WindowAttributes attrib = - kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute; - CreateNewWindow(kDocumentWindowClass, attrib, &r, &_toplevel_window); - - EventHandlerRef application_event_ref_ref1; - EventTypeSpec list1[] = { - { kEventClassWindow, kEventWindowDrawContent }, - { kEventClassWindow, kEventWindowBoundsChanged }, - { kEventClassWindow, kEventWindowClose }, - { kEventClassMouse, kEventMouseUp }, - { kEventClassMouse, kEventMouseDown }, - { kEventClassMouse, kEventMouseMoved }, - { kEventClassMouse, kEventMouseDragged }, - }; - - EventHandlerUPP gEvtHandler = NewEventHandlerUPP(st_event_callback); - InstallWindowEventHandler(_toplevel_window, gEvtHandler, - GetEventTypeCount(list1), list1, this, &application_event_ref_ref1); - - ProcessSerialNumber psn = { 0, kCurrentProcess }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - SetFrontProcess(&psn); - - if (_visible) { - ShowWindow(_toplevel_window); - } - } - } - - // Determine the attributes of the font we're going to use. - int symbolic = 0; - if (_font_style != FS_normal) { - symbolic |= kCTFontItalicTrait; - } - if (_font_weight > 500) { - symbolic |= kCTFontBoldTrait; - } - CFNumberRef symbolic_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &symbolic); - - CFStringRef traits_keys[1] = { kCTFontSymbolicTrait }; - CFTypeRef traits_values[1] = { symbolic_ref }; - CFDictionaryRef traits = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&traits_keys, (const void **)&traits_values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - CFStringRef family = CFStringCreateWithCString(nullptr, _font_family.c_str(), kCFStringEncodingUTF8); - - CFStringRef attribs_keys[2] = { kCTFontFamilyNameAttribute, kCTFontTraitsAttribute }; - CFTypeRef attribs_values[2] = { family, traits }; - CFDictionaryRef attribs = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&attribs_keys, (const void **)&attribs_values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - // Create the font object. - CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes(attribs); - CTFontRef font = CTFontCreateWithFontDescriptor(font_desc, _font_size, nullptr); - - CFStringRef keys[1] = { kCTFontAttributeName }; - CFTypeRef values[1] = { font }; - _font_attribs = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&keys, (const void **)&values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - CFRelease(font); - CFRelease(font_desc); - CFRelease(attribs); - CFRelease(traits); - CFRelease(family); - CFRelease(symbolic_ref); -} - -/** - * Makes the splash window visible or invisible, so as not to compete with the - * embedded Panda window in the same space. - */ -void P3DOsxSplashWindow:: -set_visible(bool visible) { - P3DSplashWindow::set_visible(visible); - - if (_toplevel_window != nullptr) { - if (_visible) { - ShowWindow(_toplevel_window); - } else { - HideWindow(_toplevel_window); - } - } - refresh(); -} - -/** - * Specifies the name of a JPEG image file that is displayed in the center of - * the splash window. - */ -void P3DOsxSplashWindow:: -set_image_filename(const string &image_filename, ImagePlacement image_placement) { - switch (image_placement) { - case IP_background: - load_image(_background_image, image_filename); - break; - - case IP_button_ready: - load_image(_button_ready_image, image_filename); - set_button_range(_button_ready_image); - break; - - case IP_button_rollover: - load_image(_button_rollover_image, image_filename); - break; - - case IP_button_click: - load_image(_button_click_image, image_filename); - break; - - default: - return; - } - - refresh(); -} - -/** - * Specifies the text that is displayed above the install progress bar. - */ -void P3DOsxSplashWindow:: -set_install_label(const string &install_label) { - _install_label = install_label; - refresh(); -} - -/** - * Moves the install progress bar from 0.0 to 1.0. - */ -void P3DOsxSplashWindow:: -set_install_progress(double install_progress, - bool is_progress_known, size_t received_data) { - // Only request a refresh if we're changing substantially. - if (is_progress_known != _progress_known) { - refresh(); - } else if (_progress_known) { - if ((int)(install_progress * 500.0) != (int)(_install_progress * 500.0)) { - refresh(); - } - } else { - if ((int)(received_data * _unknown_progress_rate) != - (int)(_received_data * _unknown_progress_rate)) { - refresh(); - } - } - - _install_progress = install_progress; - _progress_known = is_progress_known; - _received_data = received_data; -} - -/** - * Deals with the event callback from the OS window system. Returns true if - * the event is handled, false if ignored. - */ -bool P3DOsxSplashWindow:: -handle_event(const P3D_event_data &event) { - bool retval = false; - - if (event._event_type == P3D_ET_osx_event_record) { - retval = handle_event_osx_event_record(event); - } else if (event._event_type == P3D_ET_osx_cocoa) { - retval = handle_event_osx_cocoa(event); - } else { - assert(false); - } - - return retval; -} - -/** - * Requests that the window will be repainted. - */ -void P3DOsxSplashWindow:: -refresh() { - if (!_visible) { - return; - } - if (_toplevel_window != nullptr) { - Rect r = { 0, 0, (short)_win_height, (short)_win_width }; - InvalWindowRect(_toplevel_window, &r); - - } else { - _inst->request_refresh(); - } -} - -/** - * Redraws the current splash window. - */ -void P3DOsxSplashWindow:: -paint_window() { - if (!_visible) { - return; - } - - if (_toplevel_window != nullptr || - _wparams.get_parent_window()._window_handle_type == P3D_WHT_osx_port) { - - // The old QuickDraw-style window handle. We use CreateCGContextForPort() - // to map this to the new CoreGraphics-style. - GrafPtr out_port = nullptr; - if (_toplevel_window != nullptr) { - GetPort(&out_port); - - } else { - const P3D_window_handle &handle = _wparams.get_parent_window(); - assert(handle._window_handle_type == P3D_WHT_osx_port); - out_port = handle._handle._osx_port._port; - } - - CGContextRef context; - OSStatus err = CreateCGContextForPort(out_port, &context); - if (err != noErr) { - nout << "Couldn't create CG context\n"; - return; - } - - // Adjust for any SetOrigin calls on out_port - SyncCGContextOriginWithPort(context, out_port); - - // Move the CG origin to the upper left of the port - Rect port_rect; - GetPortBounds(out_port, &port_rect); - CGContextTranslateCTM(context, 0, (float)(port_rect.bottom - port_rect.top)); - - // Flip the y axis so that positive Y points down - CGContextScaleCTM(context, 1.0, -1.0); - - paint_window_osx_cgcontext(context); - - // We need to synchronize, or we don't see the update every frame. - CGContextSynchronize(context); - CGContextRelease(context); - - } else { - // The new CoreGraphics-style window handle. We can draw to this - // directly. - - const P3D_window_handle &handle = _wparams.get_parent_window(); - assert(handle._window_handle_type == P3D_WHT_osx_cgcontext); - CGContextRef context = handle._handle._osx_cgcontext._context; - - paint_window_osx_cgcontext(context); - } -} - -/** - * Redraws the current splash window, using the new CoreGraphics interface. - */ -void P3DOsxSplashWindow:: -paint_window_osx_cgcontext(CGContextRef context) { - // Clear the whole region to the background color before beginning. - CGFloat bg_components[] = { _bgcolor_r / 255.0f, _bgcolor_g / 255.0f, _bgcolor_b / 255.0f, 1 }; - CGColorSpaceRef rgb_space = CGColorSpaceCreateDeviceRGB(); - CGColorRef bg = CGColorCreate(rgb_space, bg_components); - - CGRect region = { { 0, 0 }, { (CGFloat)_win_width, (CGFloat)_win_height } }; - CGContextSetFillColorWithColor(context, bg); - CGContextFillRect(context, region); - - CGColorRelease(bg); - CGColorSpaceRelease(rgb_space); - - // Now paint in the image(s). - paint_image(context, _background_image); - switch (_bstate) { - case BS_hidden: - break; - case BS_ready: - paint_image(context, _button_ready_image); - break; - case BS_rollover: - if (!paint_image(context, _button_rollover_image)) { - paint_image(context, _button_ready_image); - } - break; - case BS_click: - if (!paint_image(context, _button_click_image)) { - paint_image(context, _button_ready_image); - } - break; - } - - // Draw the progress bar. We don't draw this bar at all unless we have - // nonzero progress. - if (!_progress_known || _install_progress != 0.0) { - paint_progress_bar(context); - } -} - -/** - * Responds to the deprecated Carbon event types in Mac OSX. - */ -bool P3DOsxSplashWindow:: -handle_event_osx_event_record(const P3D_event_data &event) { - assert(event._event_type == P3D_ET_osx_event_record); - EventRecord *er = event._event._osx_event_record._event; - - Point pt = er->where; - - // Need to ensure we have the correct port set, in order to convert the - // mouse coordinates successfully via GlobalToLocal(). - const P3D_window_handle &handle = _wparams.get_parent_window(); - if (handle._window_handle_type == P3D_WHT_osx_port) { - GrafPtr out_port = handle._handle._osx_port._port; - GrafPtr port_save = nullptr; - Boolean port_changed = QDSwapPort(out_port, &port_save); - - GlobalToLocal(&pt); - - if (port_changed) { - QDSwapPort(port_save, nullptr); - } - - } else if (handle._window_handle_type == P3D_WHT_osx_cgcontext) { - // First, convert the coordinates from screen coordinates to browser - // window coordinates. - WindowRef window = handle._handle._osx_cgcontext._window; - CGPoint cgpt = { (CGFloat)pt.h, (CGFloat)pt.v }; - HIPointConvert(&cgpt, kHICoordSpaceScreenPixel, nullptr, - kHICoordSpaceWindow, window); - - // Then convert to plugin coordinates. - pt.h = (short)(cgpt.x - _wparams.get_win_x()); - pt.v = (short)(cgpt.y - _wparams.get_win_y()); - } - - switch (er->what) { - case updateEvt: - paint_window(); - break; - - case mouseDown: - set_mouse_data(_mouse_x, _mouse_y, true); - break; - - case mouseUp: - set_mouse_data(_mouse_x, _mouse_y, false); - break; - - case activateEvt: - _mouse_active = ((er->modifiers & 1) != 0); - break; - - default: - break; - } - - if (_mouse_active) { - set_mouse_data(pt.h, pt.v, _mouse_down); - } - - return false; -} - -/** - * Responds to the new Cocoa event types in Mac OSX. - */ -bool P3DOsxSplashWindow:: -handle_event_osx_cocoa(const P3D_event_data &event) { - assert(event._event_type == P3D_ET_osx_cocoa); - const P3DCocoaEvent &ce = event._event._osx_cocoa._event; - - switch (ce.type) { - case P3DCocoaEventDrawRect: - if (_visible) { - CGContextRef context = ce.data.draw.context; - paint_window_osx_cgcontext(context); - return true; - } else { - return false; - } - - case P3DCocoaEventMouseDown: - set_mouse_data((int)ce.data.mouse.pluginX, (int)ce.data.mouse.pluginY, true); - return true; - - case P3DCocoaEventMouseUp: - set_mouse_data((int)ce.data.mouse.pluginX, (int)ce.data.mouse.pluginY, false); - return true; - - case P3DCocoaEventMouseMoved: - case P3DCocoaEventMouseDragged: - set_mouse_data((int)ce.data.mouse.pluginX, (int)ce.data.mouse.pluginY, _mouse_down); - return true; - - case P3DCocoaEventFocusChanged: - _mouse_active = (ce.data.focus.hasFocus != 0); - return true; - - default: - return false; - } -} - -/** - * Loads the named image file into an OsxImageData object. - */ -void P3DOsxSplashWindow:: -load_image(OsxImageData &image, const string &image_filename) { - image.dump_image(); - string string_data; - if (!read_image_data(image, string_data, image_filename)) { - return; - } - - // Now we need to copy from the RGB (or RGBA) source image into the BGRA - // target image. We also flip the image upside-down here to compensate for - // the upside-down vertical scale on CGContextScaleCTM. - int row_stride = image._width * image._num_channels; - int new_row_stride = image._width * 4; - image._raw_data = new char[new_row_stride * image._height]; - for (int yi = 0; yi < image._height; ++yi) { - char *dest = image._raw_data + yi * new_row_stride; - const char *source = string_data.data() + (image._height - 1 - yi) * row_stride; - if (image._num_channels == 3) { - // Source is RGB. - for (int xi = 0; xi < image._width; ++xi) { - char r = source[0]; - char g = source[1]; - char b = source[2]; - dest[0] = b; - dest[1] = g; - dest[2] = r; - dest[3] = 0xff; - source += 3; - dest += 4; - } - } else if (image._num_channels == 4) { - // Source is RGBA. - for (int xi = 0; xi < image._width; ++xi) { - char r = source[0]; - char g = source[1]; - char b = source[2]; - char a = source[3]; - // Little-endian. - dest[0] = b; - dest[1] = g; - dest[2] = r; - dest[3] = a; - source += 4; - dest += 4; - } - } - } - - image._data = - CFDataCreateWithBytesNoCopy(nullptr, (const UInt8 *)image._raw_data, - image._height * new_row_stride, kCFAllocatorNull); - image._provider = CGDataProviderCreateWithCFData(image._data); - image._color_space = CGColorSpaceCreateDeviceRGB(); - - image._image = - CGImageCreate(image._width, image._height, 8, 32, - new_row_stride, image._color_space, - kCGImageAlphaFirst | kCGBitmapByteOrder32Little, - image._provider, nullptr, false, kCGRenderingIntentDefault); -} - -/** - * Draws the indicated image, centered within the window. Returns true on - * success, false if the image is not defined. - */ -bool P3DOsxSplashWindow:: -paint_image(CGContextRef context, const OsxImageData &image) { - if (image._image == nullptr) { - return false; - } - - // Determine the relative size of image and window. - int win_cx = _win_width / 2; - int win_cy = _win_height / 2; - - CGRect rect = { { 0, 0 }, { 0, 0 } }; - - if (image._width <= _win_width && image._height <= _win_height) { - // The bitmap fits within the window; center it. - - // This is the top-left corner of the bitmap in window coordinates. - int p_x = win_cx - image._width / 2; - int p_y = win_cy - image._height / 2; - - rect.origin.x += p_x; - rect.origin.y += p_y; - rect.size.width = image._width; - rect.size.height = image._height; - - } else { - // The bitmap is larger than the window; scale it down. - double x_scale = (double)_win_width / (double)image._width; - double y_scale = (double)_win_height / (double)image._height; - double scale = std::min(x_scale, y_scale); - int sc_width = (int)(image._width * scale); - int sc_height = (int)(image._height * scale); - - int p_x = win_cx - sc_width / 2; - int p_y = win_cy - sc_height / 2; - - rect.origin.x += p_x; - rect.origin.y += p_y; - rect.size.width = sc_width; - rect.size.height = sc_height; - } - - CGContextDrawImage(context, rect, image._image); - - return true; -} - -/** - * Draws the progress bar and the label within the window. - */ -void P3DOsxSplashWindow:: -paint_progress_bar(CGContextRef context) { - CGFloat fg_components[] = { _fgcolor_r / 255.0f, _fgcolor_g / 255.0f, _fgcolor_b / 255.0f, 1 }; - CGFloat bg_components[] = { _bgcolor_r / 255.0f, _bgcolor_g / 255.0f, _bgcolor_b / 255.0f, 1 }; - CGFloat bar_components[] = { _barcolor_r / 255.0f, _barcolor_g / 255.0f, _barcolor_b / 255.0f, 1 }; - CGFloat bar_bg_components[] = { _bar_bgcolor_r / 255.0f, _bar_bgcolor_g / 255.0f, _bar_bgcolor_b / 255.0f, 1 }; - CGColorSpaceRef rgb_space = CGColorSpaceCreateDeviceRGB(); - CGColorRef fg = CGColorCreate(rgb_space, fg_components); - CGColorRef bg = CGColorCreate(rgb_space, bg_components); - CGColorRef bar = CGColorCreate(rgb_space, bar_components); - CGColorRef bar_bg = CGColorCreate(rgb_space, bar_bg_components); - - // Get the proper placement for the progress bar. - int bar_x, bar_y, bar_width, bar_height; - get_bar_placement(bar_x, bar_y, bar_width, bar_height); - - CGRect bar_rect = { { (CGFloat)bar_x, (CGFloat)bar_y }, { (CGFloat)bar_width, (CGFloat)bar_height } }; - - // Clear the entire progress bar to white (or the background color). - CGContextSetFillColorWithColor(context, bar_bg); - CGContextFillRect(context, bar_rect); - - // Draw the interior of the progress bar in blue (or the bar color). - if (_progress_known) { - int progress_width = (int)(bar_width * _install_progress + 0.5); - if (progress_width != 0) { - CGRect prog = { { (CGFloat)bar_x, (CGFloat)bar_y }, { (CGFloat)progress_width, (CGFloat)bar_height } }; - CGContextSetFillColorWithColor(context, bar); - CGContextFillRect(context, prog); - } - } else { - // Progress is unknown. Draw a moving block, not a progress bar filling - // up. - int block_width = (int)(bar_width * 0.1 + 0.5); - int block_travel = bar_width - block_width; - int progress = (int)(_received_data * _unknown_progress_rate); - progress = progress % (block_travel * 2); - if (progress > block_travel) { - progress = block_travel * 2 - progress; - } - - CGRect prog = { { (CGFloat)(bar_x + progress), (CGFloat)bar_y }, { (CGFloat)block_width, (CGFloat)bar_height } }; - CGContextSetFillColorWithColor(context, bar); - CGContextFillRect(context, prog); - } - - // Draw the black stroke around the progress bar. - if (_bar_border > 0) { - // We offset the border by half a pixel, so we'll be drawing the one-pixel - // line through the middle of a pixel, and it won't try to antialias - // itself into a half-black two-pixel line. - CGRect border_rect = { { (CGFloat)(bar_x - 0.5), (CGFloat)(bar_y - 0.5) }, - { (CGFloat)(bar_width + 1), (CGFloat)(bar_height + 1) } }; - - CGContextBeginPath(context); - CGContextSetLineWidth(context, 1); - CGContextSetStrokeColorWithColor(context, fg); - - for (int i = 0; i < _bar_border; ++i) { - CGContextAddRect(context, border_rect); - border_rect.origin.x -= 1; - border_rect.origin.y -= 1; - border_rect.size.width += 2; - border_rect.size.height += 2; - } - CGContextStrokePath(context); - } - - if (!_install_label.empty()) { - // Need to invert the text so it won't be upside-down. - CGAffineTransform text_xform = CGAffineTransformMakeScale(1, -1); - CGContextSetTextMatrix(context, text_xform); - - // Now draw the install_label right above it. - CFStringRef string = CFStringCreateWithCString(nullptr, _install_label.c_str(), kCFStringEncodingUTF8); - CFAttributedStringRef attr_string = CFAttributedStringCreate(nullptr, string, _font_attribs); - CTLineRef line = CTLineCreateWithAttributedString(attr_string); - - // Determine the placement based on the size of the text. - CGRect bounds = CTLineGetImageBounds(line, context); - float text_x = (_win_width - bounds.size.width) / 2.0f - bounds.origin.x; - float text_y = bar_y + bounds.origin.y - 4 - _bar_border; - - // And finally, draw the text. - CGContextSetTextPosition(context, text_x, text_y); - CTLineDraw(line, context); - - CFRelease(line); - CFRelease(attr_string); - CFRelease(string); - } - - CGColorRelease(bar_bg); - CGColorRelease(bar); - CGColorRelease(bg); - CGColorRelease(fg); - CGColorSpaceRelease(rgb_space); -} - -/** - * The event callback on the toplevel window. - */ -pascal OSStatus P3DOsxSplashWindow:: -st_event_callback(EventHandlerCallRef my_handler, EventRef event, - void *user_data) { - return ((P3DOsxSplashWindow *)user_data)->event_callback(my_handler, event); -} - -/** - * The event callback on the toplevel window. - */ -OSStatus P3DOsxSplashWindow:: -event_callback(EventHandlerCallRef my_handler, EventRef event) { - OSStatus result = eventNotHandledErr; - - WindowRef window = nullptr; - UInt32 the_class = GetEventClass(event); - UInt32 kind = GetEventKind(event); - GetEventParameter(event, kEventParamWindowRef, typeWindowRef, nullptr, - sizeof(WindowRef), nullptr, (void*) &window); - switch (the_class) { - case kEventClassWindow: - switch (kind) { - case kEventWindowBoundsChanged: - // If the window changes size, we have to repaint it. - refresh(); - - // Also determine the new size. - { - Rect port_rect; - GrafPtr out_port = GetWindowPort(_toplevel_window); - GetPortBounds(out_port, &port_rect); - int width = port_rect.right - port_rect.left; - int height = port_rect.bottom - port_rect.top; - if (width != _win_width || height != _win_height) { - _win_width = width; - _win_height = height; - set_button_range(_button_ready_image); - } - } - - // We seem to get the mouse-down, but lose the mouse-up, event in this - // case, so infer it. - set_mouse_data(_mouse_x, _mouse_y, false); - result = noErr; - break; - - case kEventWindowDrawContent: - paint_window(); - break; - - case kEventWindowClose: - // When the user closes the splash window, stop the instance. - _inst->request_stop_sub_thread(); - break; - }; - break; - - case kEventClassMouse: - switch (kind) { - case kEventMouseUp: - set_mouse_data(_mouse_x, _mouse_y, false); - break; - - case kEventMouseDown: - set_mouse_data(_mouse_x, _mouse_y, true); - break; - - case kEventMouseMoved: - case kEventMouseDragged: - { - Point point; - GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, nullptr, - sizeof(Point), nullptr, (void *)&point); - - GrafPtr port; - assert(_toplevel_window != nullptr); - port = GetWindowPort(_toplevel_window); - GrafPtr port_save = nullptr; - Boolean port_changed = QDSwapPort(port, &port_save); - GlobalToLocal(&point); - if (port_changed) { - QDSwapPort(port_save, nullptr); - } - - set_mouse_data(point.h, point.v, _mouse_down); - } - break; - } - } - - return result; -} - -/** - * Frees the previous image data. - */ -void P3DOsxSplashWindow::OsxImageData:: -dump_image() { - if (_image != nullptr) { - CGImageRelease(_image); - _image = nullptr; - } - if (_color_space != nullptr) { - CGColorSpaceRelease(_color_space); - _color_space = nullptr; - } - if (_provider != nullptr) { - CGDataProviderRelease(_provider); - _provider = nullptr; - } - if (_data != nullptr) { - CFRelease(_data); - _data = nullptr; - } - if (_raw_data != nullptr) { - delete[] _raw_data; - _raw_data = nullptr; - } -} - -#endif // __APPLE__ diff --git a/direct/src/plugin/p3dOsxSplashWindow.h b/direct/src/plugin/p3dOsxSplashWindow.h deleted file mode 100644 index 9a2a6225bc..0000000000 --- a/direct/src/plugin/p3dOsxSplashWindow.h +++ /dev/null @@ -1,101 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dOsxSplashWindow.h - * @author drose - * @date 2009-07-16 - */ - -#ifndef P3DOSXSPLASHWINDOW_H -#define P3DOSXSPLASHWINDOW_H - -#include "p3d_plugin_common.h" - -#if defined(__APPLE__) && !__LP64__ - -#include "p3dSplashWindow.h" - -#include - -/** - * This is the OSX implementation of the initial-download window. - */ -class P3DOsxSplashWindow : public P3DSplashWindow { -public: - P3DOsxSplashWindow(P3DInstance *inst, bool make_visible); - virtual ~P3DOsxSplashWindow(); - - virtual void set_wparams(const P3DWindowParams &wparams); - virtual void set_visible(bool visible); - virtual void set_image_filename(const std::string &image_filename, - ImagePlacement image_placement); - virtual void set_install_label(const std::string &install_label); - virtual void set_install_progress(double install_progress, - bool is_progress_known, size_t received_data); - - virtual bool handle_event(const P3D_event_data &event); - -protected: - virtual void refresh(); - -private: - void paint_window(); - void paint_window_osx_cgcontext(CGContextRef context); - bool handle_event_osx_event_record(const P3D_event_data &event); - bool handle_event_osx_cocoa(const P3D_event_data &event); - class OsxImageData; - - void load_image(OsxImageData &image, const std::string &image_filename); - bool paint_image(CGContextRef context, const OsxImageData &image); - void paint_progress_bar(CGContextRef context); - - static pascal OSStatus - st_event_callback(EventHandlerCallRef my_handler, EventRef event, - void *user_data); - OSStatus event_callback(EventHandlerCallRef my_handler, EventRef event); - -private: - bool _got_wparams; - - class OsxImageData : public ImageData { - public: - inline OsxImageData(); - inline ~OsxImageData(); - void dump_image(); - - char *_raw_data; - CFDataRef _data; - CGDataProviderRef _provider; - CGColorSpaceRef _color_space; - CGImageRef _image; - }; - - OsxImageData _background_image; - OsxImageData _button_ready_image; - OsxImageData _button_rollover_image; - OsxImageData _button_click_image; - - CFDictionaryRef _font_attribs; - - std::string _install_label; - double _install_progress; - bool _progress_known; - size_t _received_data; - - // Used to track the mouse within the window in the embedded case. - bool _mouse_active; - - // Filled only in the non-embedded case. - WindowRef _toplevel_window; -}; - -#include "p3dOsxSplashWindow.I" - -#endif // __APPLE__ - -#endif diff --git a/direct/src/plugin/p3dPackage.I b/direct/src/plugin/p3dPackage.I deleted file mode 100644 index 371491f5c2..0000000000 --- a/direct/src/plugin/p3dPackage.I +++ /dev/null @@ -1,179 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPackage.I - * @author drose - * @date 2009-06-12 - */ - -/** - * Returns true if the package file information has been downloaded and - * verified and is ready to be consulted, false if it is not yet available. - */ -inline bool P3DPackage:: -get_info_ready() const { - return _info_ready; -} - -/** - * Returns the number of bytes that will need to be downloaded, when this - * package is downloaded. This is intended to be used to estimate the - * download time for this package relative to other packages, for instance to - * update a progress bar sensibly. - */ -inline size_t P3DPackage:: -get_download_size() const { - return _compressed_archive.get_size(); -} - -/** - * Returns true if the package has been downloaded and verified and is ready - * to be used, false if it has not. - */ -inline bool P3DPackage:: -get_ready() const { - return _ready; -} - -/** - * Returns true if the package cannot be made ready, for instance because the - * download server is down. - */ -inline bool P3DPackage:: -get_failed() const { - return _failed; -} - -/** - * Returns the host server which offers this package for download. - */ -inline P3DHost *P3DPackage:: -get_host() const { - return _host; -} - -/** - * Returns the directory into which this package is installed. - */ -inline const std::string &P3DPackage:: -get_package_dir() const { - return _package_dir; -} - -/** - * Returns the name of this package. This is an internal name, used to - * generate filenames and the like; it will generally be all-lowercase and - * will not contain spaces. See also get_package_display_name() for a name - * suitable for displaying to the user. - */ -inline const std::string &P3DPackage:: -get_package_name() const { - return _package_name; -} - -/** - * Returns the version string of this package. - */ -inline const std::string &P3DPackage:: -get_package_version() const { - return _package_version; -} - -/** - * Returns the platform string of this package. - */ -inline const std::string &P3DPackage:: -get_package_platform() const { - return _package_platform; -} - -/** - * Returns the display_name name of this package, as set in the desc file. - */ -inline const std::string &P3DPackage:: -get_package_display_name() const { - return _package_display_name; -} - -/** - * Returns the entry of the package desc file, if any, or NULL if it - * was not present. - */ -inline const TiXmlElement *P3DPackage:: -get_xconfig() const { - return _xconfig; -} - -/** - * Returns the full path to the package's desc file. If this is a "solo" type - * package, the desc file itself represents the entire contents of the - * package. - */ -inline const std::string &P3DPackage:: -get_desc_file_pathname() const { - return _desc_file_pathname; -} - -/** - * Returns the relative path, on the host, of the directory that contains the - * desc file (and to which all of the paths in the desc file are relative). - */ -inline const std::string &P3DPackage:: -get_desc_file_dirname() const { - return _desc_file_dirname; -} - -/** - * Returns the full path to the package's uncompressed archive file. This is - * only valid if get_ready() is true and the package is not a "solo" package. - */ -inline std::string P3DPackage:: -get_archive_file_pathname() const { - return _uncompressed_archive.get_pathname(_package_dir); -} - - -/** - * Returns the relative amount of effort of this step. - */ -inline double P3DPackage::InstallStep:: -get_effort() const { - return _bytes_needed * _bytes_factor; -} - -/** - * Returns the progress of this step, in the range 0..1. - */ -inline double P3DPackage::InstallStep:: -get_progress() const { - if (_bytes_needed == 0) { - return 1.0; - } - return std::min((double)_bytes_done / (double)_bytes_needed, 1.0); -} - -/** - * Notifies the Package that progress has been made on this particular step. - */ -inline void P3DPackage::InstallStep:: -report_step_progress() { - _package->report_progress(this); -} - -/** - * - */ -inline P3DPackage::RequiredPackage:: -RequiredPackage(const std::string &package_name, const std::string &package_version, - const std::string &package_seq, P3DHost *host) : - _package_name(package_name), - _package_version(package_version), - _package_seq(package_seq), - _host(host) -{ -} diff --git a/direct/src/plugin/p3dPackage.cxx b/direct/src/plugin/p3dPackage.cxx deleted file mode 100644 index 4ea29f339d..0000000000 --- a/direct/src/plugin/p3dPackage.cxx +++ /dev/null @@ -1,2015 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPackage.cxx - * @author drose - * @date 2009-06-12 - */ - -#include "p3dPackage.h" -#include "p3dInstanceManager.h" -#include "p3dInstance.h" -#include "p3dMultifileReader.h" -#include "p3dTemporaryFile.h" -#include "p3dPatchFinder.h" -#include "mkdir_complete.h" -#include "wstring_encode.h" - -#include - -#include -#include - -#ifdef _WIN32 -#include // chmod() -#endif - -using std::ios; -using std::ostream; -using std::ostringstream; -using std::string; -using std::vector; - -// Weight factors for computing download progress. This attempts to reflect -// the relative time-per-byte of each of these operations. -const double P3DPackage::_download_factor = 1.0; -const double P3DPackage::_uncompress_factor = 0.01; -const double P3DPackage::_unpack_factor = 0.01; -const double P3DPackage::_patch_factor = 0.01; - -/** - * - */ -P3DPackage:: -P3DPackage(P3DHost *host, const string &package_name, - const string &package_version, const string &package_platform, - const string &alt_host) : - _host(host), - _package_name(package_name), - _package_version(package_version), - _package_platform(package_platform), - _alt_host(alt_host) -{ - set_fullname(); - _per_platform = false; - _patch_version = 0; - - // This is set true if the package is a "solo", i.e. a single file, instead - // of an xml file and a multifile to unpack. - _package_solo = false; - - _host_contents_iseq = 0; - - _xconfig = nullptr; - _temp_contents_file = nullptr; - - _computed_plan_size = false; - _info_ready = false; - _allow_data_download = false; - _ready = false; - _failed = false; - _active_download = nullptr; - _saved_download = nullptr; - _updated = false; -} - -/** - * - */ -P3DPackage:: -~P3DPackage() { - // Tell any pending callbacks that we're no good any more. - if (!_ready && !_failed) { - report_done(false); - } - - // Ditto the outstanding instances. - Instances::iterator ii; - for (ii = _instances.begin(); ii != _instances.end(); ++ii) { - (*ii)->remove_package(this); - } - _instances.clear(); - - if (_xconfig != nullptr) { - delete _xconfig; - _xconfig = nullptr; - } - - // Cancel any pending download. - if (_active_download != nullptr) { - _active_download->cancel(); - set_active_download(nullptr); - } - if (_saved_download != nullptr) { - _saved_download->cancel(); - set_saved_download(nullptr); - } - - if (_temp_contents_file != nullptr) { - delete _temp_contents_file; - _temp_contents_file = nullptr; - } -} - -/** - * Authorizes the package to begin downloading and unpacking the meat of its - * data. Until this is called, the package will download its file information - * only, and then wait. - */ -void P3DPackage:: -activate_download() { - _allow_data_download = true; - - if (_ready) { - // If we've already been downloaded, we can report that now. - Instances::iterator ii; - for (ii = _instances.begin(); ii != _instances.end(); ++ii) { - (*ii)->report_package_done(this, true); - } - - } else { - // Otherwise, if we've already got the desc file, then start the download. - if (_info_ready) { - follow_install_plans(true, false); - } - } -} - -/** - * Returns the name of this package, for output to the user. This will be the - * "public" name of the package, as formatted for user consumption; it will - * include capital letters and spaces where appropriate. - */ -string P3DPackage:: -get_formatted_name() const { - ostringstream strm; - - if (!_package_display_name.empty()) { - strm << _package_display_name; - } else { - strm << _package_name; - if (!_package_version.empty()) { - strm << " " << _package_version; - } - } - - if (_patch_version != 0) { - strm << " rev " << _patch_version; - } - - return strm.str(); -} - -/** - * Specifies an instance that that will be using this package, and may be - * responsible for downloading it. - */ -void P3DPackage:: -add_instance(P3DInstance *inst) { - _instances.push_back(inst); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (!_host->has_current_contents_file(inst_mgr)) { - // If the host needs to update its contents file, we're no longer sure - // that we're current. - _info_ready = false; - _ready = false; - _failed = false; - _allow_data_download = false; - nout << "No longer current: " << get_package_name() << "\n"; - } - - begin_info_download(); -} - -/** - * Indicates that the given instance is no longer interested in this package - * and will not be responsible for downloading it. - */ -void P3DPackage:: -remove_instance(P3DInstance *inst) { - assert(!_instances.empty()); - - if (inst == _instances[0]) { - // This was the primary instance. Cancel any pending download and move to - // the next instance. - if (_active_download != nullptr) { - _active_download->cancel(); - set_active_download(nullptr); - } - } - - Instances::iterator ii = find(_instances.begin(), _instances.end(), inst); - assert(ii != _instances.end()); - _instances.erase(ii); - - begin_info_download(); -} - -/** - * Marks this package as having been "used", for accounting purposes. - */ -void P3DPackage:: -mark_used() { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (inst_mgr->get_verify_contents() == P3D_VC_never) { - // We're not allowed to create any files in the package directory. - return; - } - - // Unlike the Python variant of this function, we don't mess around with - // updating the disk space or anything. - string filename = get_package_dir() + "/usage.xml"; - TiXmlDocument doc(filename); - if (!doc.LoadFile()) { - TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", ""); - doc.LinkEndChild(decl); - } - - TiXmlElement *xusage = doc.FirstChildElement("usage"); - if (xusage == nullptr) { - xusage = new TiXmlElement("usage"); - doc.LinkEndChild(xusage); - } - - time_t now = time(nullptr); - int count = 0; - xusage->Attribute("count_runtime", &count); - if (count == 0) { - xusage->SetAttribute("first_use", (int)now); - } - - ++count; - xusage->SetAttribute("count_runtime", count); - xusage->SetAttribute("last_use", (int)now); - - if (_updated) { - // If we've updated the package, we're no longer sure what its disk space - // is. Remove that from the XML file, so that the Python code can - // recompute it later. - xusage->RemoveAttribute("disk_space"); - xusage->SetAttribute("last_update", (int)now); - } - - // Write the file to a temporary filename, then atomically move it to its - // actual filename, to avoid race conditions. - ostringstream strm; - strm << get_package_dir() << "/usage_"; -#ifdef _WIN32 - strm << GetCurrentProcessId(); -#else - strm << getpid(); -#endif - strm << ".xml"; - string tfile = strm.str(); - - unlink(tfile.c_str()); - if (doc.SaveFile(tfile)) { - if (rename(tfile.c_str(), filename.c_str()) != 0) { - // If rename failed, remove the original file first. - unlink(filename.c_str()); - rename(tfile.c_str(), filename.c_str()); - } - } -} - -/** - * Removes the package directory and all its contents from the user's hard - * disk. - */ -void P3DPackage:: -uninstall() { - if (_package_dir.empty()) { - nout << "Cannot uninstall " << _package_name << ": package directory not yet known.\n"; - return; - } - - nout << "Uninstalling package " << _package_name << " from " << _package_dir << "\n"; - - // First, make sure that all instances that are sharing this package are - // stopped, so there will be no access conflicts preventing us from removing - // the files. This is particularly important on Windows. - Instances::iterator ii; - for (ii = _instances.begin(); ii != _instances.end(); ++ii) { - P3DInstance *inst = (*ii); - P3DSession *session = inst->get_session(); - if (session != nullptr) { - nout << "Stopping session " << session << "\n"; - session->shutdown(); - } - inst->set_failed(); - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - inst_mgr->delete_directory_recursively(_package_dir); - - _info_ready = false; - _ready = false; - _failed = false; - _allow_data_download = false; - - // Make sure the host forgets us too. - _host->forget_package(this); -} - -/** - * Returns a newly-allocated XML structure that corresponds to the package - * data within this instance. - */ -TiXmlElement *P3DPackage:: -make_xml() { - TiXmlElement *xpackage = new TiXmlElement("package"); - - xpackage->SetAttribute("name", _package_name); - if (!_package_platform.empty()) { - xpackage->SetAttribute("platform", _package_platform); - } - if (!_package_version.empty()) { - xpackage->SetAttribute("version", _package_version); - } - xpackage->SetAttribute("host", _host->get_host_url()); - xpackage->SetAttribute("host_dir", _host->get_host_dir()); - - return xpackage; -} - -/** - * Begins downloading and installing the information about the package, - * including its file size and download source and such, if needed. This is - * generally a very small download. - */ -void P3DPackage:: -begin_info_download() { - if (_instances.empty()) { - // Can't download without any instances. - return; - } - - if (_info_ready) { - // Already downloaded. - report_info_ready(); - return; - } - - if (_active_download != nullptr) { - // In the middle of downloading. - return; - } - - download_contents_file(); -} - -/** - * Starts downloading the root-level contents.xml file. This is only done for - * the first package downloaded from a particular host, and only if the host - * doesn't have the file already. - */ -void P3DPackage:: -download_contents_file() { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (!_host->has_contents_file() && inst_mgr->get_verify_contents() != P3D_VC_force) { - // First, read whatever contents file is already on disk. Maybe it's - // current enough. - _host->read_contents_file(); - } - - if (_host->has_current_contents_file(inst_mgr)) { - // We've already got a contents.xml file; go straight to the package desc - // file. - host_got_contents_file(); - return; - } - - // Don't download it if we're not allowed to. - if (inst_mgr->get_verify_contents() == P3D_VC_never) { - contents_file_download_finished(false); - return; - } - - // Download contents.xml to a temporary filename first, in case multiple - // packages are downloading it simultaneously. - if (_temp_contents_file != nullptr) { - delete _temp_contents_file; - _temp_contents_file = nullptr; - } - _temp_contents_file = new P3DTemporaryFile(".xml"); - - start_download(DT_contents_file, "contents.xml", - _temp_contents_file->get_filename(), FileSpec()); -} - -/** - * Called when the contents.xml file has been fully downloaded. - */ -void P3DPackage:: -contents_file_download_finished(bool success) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (!_host->has_current_contents_file(inst_mgr)) { - if (!success || _temp_contents_file == nullptr || - !_host->read_contents_file(_temp_contents_file->get_filename(), true)) { - - if (_temp_contents_file) { - nout << "Couldn't read " << *_temp_contents_file << "\n"; - } - - // Maybe we can read an already-downloaded contents.xml file. - bool success = false; - if (_host->has_host_dir()) { - string standard_filename = _host->get_host_dir() + "/contents.xml"; - if (_host->read_contents_file(standard_filename, false)) { - success = true; - } else { - nout << "Couldn't read " << standard_filename << "\n"; - } - } else { - nout << "No host_dir available for " << _host->get_host_url() - << "\n"; - } - if (!success) { - // Couldn't read an already-downloaded file either. Fail. - report_done(false); - if (_temp_contents_file) { - delete _temp_contents_file; - _temp_contents_file = nullptr; - } - return; - } - } - } - - // The file is correctly installed by now; we can remove the temporary file. - if (_temp_contents_file) { - delete _temp_contents_file; - _temp_contents_file = nullptr; - } - - host_got_contents_file(); -} - -/** - * Starts a new download attempt of contents.xml, to check to see whether our - * local copy is stale. This is called only from download_desc_file(), or - * from Download::download_finished(). If the former, the download pointer - * will be NULL. - * - * If it turns out a new version can be downloaded, the indicated Download - * object (and the current install plan) is discarded, and the package - * download is restarted from the beginning. - * - * If there is no new version available, calls resume_download_finished() on - * the indicated Download object, to carry on as if nothing had happened. - */ -void P3DPackage:: -redownload_contents_file(P3DPackage::Download *download) { - assert(_active_download == nullptr); - assert(_saved_download == nullptr); - - if (_host->get_contents_iseq() != _host_contents_iseq) { - // If the contents_iseq number has changed, we don't even need to download - // anything--just go restart the download. - host_got_contents_file(); - return; - } - - // Don't download it if we're not allowed to. - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (inst_mgr->get_verify_contents() == P3D_VC_never) { - return; - } - - set_saved_download(download); - - // Download contents.xml to a temporary filename first. - if (_temp_contents_file != nullptr) { - delete _temp_contents_file; - _temp_contents_file = nullptr; - } - _temp_contents_file = new P3DTemporaryFile(".xml"); - - start_download(DT_redownload_contents_file, "contents.xml", - _temp_contents_file->get_filename(), FileSpec()); -} - -/** - * Called when the redownload attempt on contents.xml has finished. - */ -void P3DPackage:: -contents_file_redownload_finished(bool success) { - bool contents_changed = false; - - if (_host->get_contents_iseq() != _host_contents_iseq) { - // If the contents_iseq number has changed, we don't even need to bother - // reading what we just downloaded. - contents_changed = true; - } - - if (!contents_changed && success) { - // If we successfully downloaded something, see if it's different from - // what we had before. - if (!_host->check_contents_hash(_temp_contents_file->get_filename())) { - // It changed! Now see if we can read the new contents. - if (!_host->read_contents_file(_temp_contents_file->get_filename(), true)) { - // Huh, appears to have changed to something bad. Never mind. - nout << "Couldn't read " << *_temp_contents_file << "\n"; - - } else { - // The new contents file is read and in place. - contents_changed = true; - } - } - } - - // We no longer need the temporary file. - if (_temp_contents_file) { - delete _temp_contents_file; - _temp_contents_file = nullptr; - } - - if (contents_changed) { - // OK, the contents.xml has changed; this means we have to restart the - // whole download process from the beginning. - nout << "Redownloading contents.xml made a difference.\n"; - set_saved_download(nullptr); - host_got_contents_file(); - - } else { - // Nothing's changed. This was just a useless diversion. We now return - // you to our regularly scheduled download. - nout << "Redownloading contents.xml didn't help.\n"; - Download *download = _saved_download; - _saved_download = nullptr; - if (download == nullptr) { - // But, if _saved_download was NULL (meaning NULL was passed to - // redownload_contents_file(), above), it means that we were called from - // download_desc_file(), and there's nothing more to do. We're just - // hosed. - report_done(false); - - } else { - download->resume_download_finished(false); - p3d_unref_delete(download); - } - } -} - -/** - * We come here when we've successfully downloaded and read the host's - * contents.xml file. This begins the rest of the download process. - */ -void P3DPackage:: -host_got_contents_file() { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - if (!_alt_host.empty()) { - // If we have an alt host specification, maybe we need to change the host - // now. - P3DHost *new_host = _host->get_alt_host(_alt_host); - nout << "Migrating " << get_package_name() << " to alt_host " - << _alt_host << ": " << new_host->get_host_url() << "\n"; - if (new_host != _host) { - _host->migrate_package_host(this, _alt_host, new_host); - _host = new_host; - } - - // Clear the alt_host string now that we're migrated to our final host. - _alt_host.clear(); - - if (!_host->has_current_contents_file(inst_mgr)) { - // Now go back and get the contents.xml file for the new host. - download_contents_file(); - return; - } - } - - // Record this now, so we'll know later whether the host has been reloaded - // (e.g. due to some other package, from some other instance, reloading - // it). - _host_contents_iseq = _host->get_contents_iseq(); - - // Now adjust the platform based on the available platforms provided. - assert(_alt_host.empty()); - string new_platform; - if (_host->choose_suitable_platform(new_platform, _per_platform, - _package_name, _package_version, _package_platform)) { - if (new_platform != _package_platform) { - nout << "Migrating " << get_package_name() << " from platform \"" - << _package_platform << "\" to platform \"" - << new_platform << "\"\n"; - _package_platform = new_platform; - set_fullname(); - } - } else { - nout << "Couldn't find a platform for " << get_package_name() - << " matching \"" << inst_mgr->get_platform() << "\".\n"; - } - - nout << "_per_platform for " << get_package_name() << " = " << _per_platform << "\n"; - - // Now that we have a valid host and platform, we can define the - // _package_dir. - _package_dir = _host->get_host_dir() + string("/") + _package_name; - if (!_package_version.empty()) { - _package_dir += string("/") + _package_version; - } - if (_per_platform && !_package_platform.empty()) { - _package_dir += string("/") + _package_platform; - } - - // Ensure the package directory exists; create it if it does not. - if (inst_mgr->get_verify_contents() != P3D_VC_never) { - mkdir_complete(_package_dir, nout); - } - download_desc_file(); -} - -/** - * Starts downloading the desc file for the package, if it's needed; or read - * the local version if it's fresh enough. - */ -void P3DPackage:: -download_desc_file() { - assert(!_package_dir.empty()); - - // Attempt to check the desc file for freshness. If it already exists, and - // is consistent with the server contents file, we don't need to re-download - // it. - string package_seq; - if (!_host->get_package_desc_file(_desc_file, package_seq, _package_solo, - _package_name, _package_version, - _package_platform)) { - nout << "Couldn't find package " << _package_fullname - << ", platform \"" << _package_platform - << "\" in contents file.\n"; - redownload_contents_file(nullptr); - return; - } - - string url_filename = _desc_file.get_filename(); - - _desc_file_dirname = ""; - _desc_file_basename = url_filename; - size_t slash = _desc_file_basename.rfind('/'); - if (slash != string::npos) { - _desc_file_dirname = _desc_file_basename.substr(0, slash); - _desc_file_basename = _desc_file_basename.substr(slash + 1); - } - - // The desc file might have a different path on the host server than it has - // locally, because we might strip out the platform directory locally - // (according to _per_platform). - FileSpec local_desc_file = _desc_file; - local_desc_file.set_filename(_desc_file_basename); - _desc_file_pathname = local_desc_file.get_pathname(_package_dir); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (!local_desc_file.full_verify(_package_dir) && inst_mgr->get_verify_contents() != P3D_VC_never) { - nout << _desc_file_pathname << " is stale.\n"; - - } else { - // The desc file is current. Attempt to read it. - if (_package_solo) { - // No need to load it: the desc file *is* the package. - report_done(true); - return; - } else { - TiXmlDocument doc(_desc_file_pathname.c_str()); - if (doc.LoadFile()) { - got_desc_file(&doc, false); - return; - } - } - } - - // Don't download it if we're not allowed to. - if (inst_mgr->get_verify_contents() == P3D_VC_never) { - nout << "Couldn't read " << _desc_file_pathname << "\n"; - report_done(false); - return; - } - - // The desc file is not current. Go download it. - start_download(DT_desc_file, _desc_file.get_filename(), - _desc_file_pathname, local_desc_file); -} - -/** - * Called when the desc file has been fully downloaded. - */ -void P3DPackage:: -desc_file_download_finished(bool success) { - if (!success) { - report_done(false); - return; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (inst_mgr->get_verify_contents() != P3D_VC_never) { - // Now that we've downloaded the desc file, make it read-only. - chmod(_desc_file_pathname.c_str(), 0444); - } - - if (_package_solo) { - // No need to load it: the desc file *is* the package. - report_done(true); - return; - - } else { - TiXmlDocument doc(_desc_file_pathname.c_str()); - if (!doc.LoadFile()) { - nout << "Couldn't read " << _desc_file_pathname << "\n"; - report_done(false); - return; - } - - got_desc_file(&doc, true); - } -} - -/** - * Reads the desc file and begins verifying the files. - */ -void P3DPackage:: -got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) { - TiXmlElement *xpackage = doc->FirstChildElement("package"); - if (xpackage == nullptr) { - nout << _package_name << " desc file contains no \n"; - if (!freshly_downloaded) { - download_desc_file(); - return; - } - report_done(false); - return; - } - - bool per_platform = parse_bool_attrib(xpackage, "per_platform", false); - if (per_platform != _per_platform) { - nout << "Warning! per_platform disagreement for " << get_package_name() - << "!\n"; - // We don't do anything with this warning--the original value for - // _per_platform we got from the contents.xml file has to apply, because - // we're already committed to the _package_dir we're using. - } - - xpackage->Attribute("patch_version", &_patch_version); - - TiXmlElement *xconfig = xpackage->FirstChildElement("config"); - if (xconfig != nullptr) { - const char *display_name_cstr = xconfig->Attribute("display_name"); - if (display_name_cstr != nullptr) { - _package_display_name = display_name_cstr; - } - - // Save the config entry within this class for others to query. - _xconfig = (TiXmlElement *)xconfig->Clone(); - } - - TiXmlElement *xuncompressed_archive = - xpackage->FirstChildElement("uncompressed_archive"); - TiXmlElement *xcompressed_archive = - xpackage->FirstChildElement("compressed_archive"); - - if (xuncompressed_archive == nullptr || xcompressed_archive == nullptr) { - // The desc file didn't include the archive file itself, weird. - if (!freshly_downloaded) { - download_desc_file(); - return; - } - report_done(false); - return; - } - - _uncompressed_archive.load_xml(xuncompressed_archive); - _compressed_archive.load_xml(xcompressed_archive); - - // Now get all the extractable components. - _unpack_size = 0; - _extracts.clear(); - TiXmlElement *xextract = xpackage->FirstChildElement("extract"); - while (xextract != nullptr) { - FileSpec file; - file.load_xml(xextract); - _extracts.push_back(file); - _unpack_size += file.get_size(); - xextract = xextract->NextSiblingElement("extract"); - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - // Get the required packages. - _requires.clear(); - TiXmlElement *xrequires = xpackage->FirstChildElement("requires"); - while (xrequires != nullptr) { - const char *package_name = xrequires->Attribute("name"); - const char *host_url = xrequires->Attribute("host"); - if (package_name != nullptr && host_url != nullptr) { - const char *version = xrequires->Attribute("version"); - if (version == nullptr) { - version = ""; - } - const char *seq = xrequires->Attribute("seq"); - if (seq == nullptr) { - seq = ""; - } - P3DHost *host = inst_mgr->get_host(host_url); - _requires.push_back(RequiredPackage(package_name, version, seq, host)); - } - - xrequires = xrequires->NextSiblingElement("requires"); - } - - if (inst_mgr->get_verify_contents() == P3D_VC_never) { - // This means we'll just leave it at this and assume that we're finished. - report_done(true); - return; - } - - // Get a list of all of the files in the directory, so we can remove files - // that don't belong. - vector contents, dirname_contents; - inst_mgr->scan_directory_recursively(_package_dir, contents, dirname_contents); - - inst_mgr->remove_file_from_list(contents, _desc_file_basename); - inst_mgr->remove_file_from_list(contents, _uncompressed_archive.get_filename()); - inst_mgr->remove_file_from_list(contents, "usage.xml"); - Extracts::iterator ei; - for (ei = _extracts.begin(); ei != _extracts.end(); ++ei) { - inst_mgr->remove_file_from_list(contents, (*ei).get_filename()); - } - - // Now, any files that are still in the contents list don't belong. It's - // important to remove these files before we start verifying the files that - // we expect to find here, in case there is a problem with ambiguous - // filenames or something (e.g. case insensitivity). - vector::iterator ci; - for (ci = contents.begin(); ci != contents.end(); ++ci) { - string filename = (*ci); - nout << "Removing " << filename << "\n"; - string pathname = _package_dir + "/" + filename; - -#ifdef _WIN32 - // Windows can't delete a file if it's read-only. - chmod(pathname.c_str(), 0644); -#endif - unlink(pathname.c_str()); - _updated = true; - } - - // Verify the uncompressed archive. - bool all_extracts_ok = true; - if (!_uncompressed_archive.quick_verify(_package_dir)) { - nout << "File is incorrect: " << _uncompressed_archive.get_filename() << "\n"; - all_extracts_ok = false; - } - - // Verify all of the extracts. - for (ei = _extracts.begin(); ei != _extracts.end() && all_extracts_ok; ++ei) { - if (!(*ei).quick_verify(_package_dir)) { - nout << "File is incorrect: " << (*ei).get_filename() << "\n"; - all_extracts_ok = false; - } - - // Make sure all extracts are still marked executable. - chmod((*ei).get_pathname(_package_dir).c_str(), 0555); - } - - if (all_extracts_ok) { - // Great, we're ready to begin. - nout << "All " << _extracts.size() << " extracts of " << _package_name - << " seem good.\n"; - - report_done(true); - - } else { - // We need to get the file data still, but at least we know all about it - // by this point. - build_install_plans(doc); - - if (!_allow_data_download) { - // Not authorized to start downloading yet; just report that we're - // ready. - report_info_ready(); - } else { - // We've already been authorized to start downloading, so do it. - follow_install_plans(true, false); - } - } -} - -/** - * Empties _install_plans cleanly. - */ -void P3DPackage:: -clear_install_plans() { - InstallPlans::iterator pi; - for (pi = _install_plans.begin(); pi != _install_plans.end(); ++pi) { - InstallPlan &plan = (*pi); - InstallPlan::iterator si; - for (si = plan.begin(); si != plan.end(); ++si) { - InstallStep *step = (*si); - delete step; - } - } - - _install_plans.clear(); - _computed_plan_size = false; -} - -/** - * Sets up _install_plans, a list of one or more "plans" to download and - * install the package. - */ -void P3DPackage:: -build_install_plans(TiXmlDocument *doc) { - clear_install_plans(); - - if (_instances.empty()) { - // Can't download without any instances. - return; - } - - if (_ready) { - // Already downloaded. - return; - } - - _install_plans.push_front(InstallPlan()); - InstallPlan &plan = _install_plans.front(); - _computed_plan_size = false; - - bool needs_redownload = false; - - InstallStep *step; - if (!_uncompressed_archive.quick_verify(_package_dir)) { - // The uncompressed archive is no good. - - if (!_compressed_archive.quick_verify(_package_dir)) { - // The compressed archive is no good either. Download a new compressed - // archive. - needs_redownload = true; - step = new InstallStepDownloadFile(this, _compressed_archive); - plan.push_back(step); - } - - // Uncompress the compressed archive to generate the uncompressed archive. - step = new InstallStepUncompressFile - (this, _compressed_archive, _uncompressed_archive, true); - plan.push_back(step); - } - - // Unpack the uncompressed archive. - step = new InstallStepUnpackArchive(this, _unpack_size); - plan.push_back(step); - - if (needs_redownload) { - // Since we need to do some downloading, try to build a plan that involves - // downloading patches instead of downloading the whole file. This will - // be our first choice, plan A, if we can do it. - - // We'll need the md5 hash of the uncompressed archive currently on disk. - - // Maybe we've already read the md5 hash and we have it stored here. - const FileSpec *on_disk_ptr = _uncompressed_archive.get_actual_file(); - FileSpec on_disk; - if (on_disk_ptr == nullptr) { - // If not, we have to go read it now. - if (on_disk.read_hash(_uncompressed_archive.get_pathname(_package_dir))) { - on_disk_ptr = &on_disk; - } - } - - if (on_disk_ptr != nullptr) { - P3DPatchFinder patch_finder; - P3DPatchFinder::Patchfiles chain; - if (patch_finder.get_patch_chain_to_current(chain, doc, *on_disk_ptr)) { - nout << "Got patch chain of length " << chain.size() << "\n"; - - // OK, we can create a plan to download and apply the patches. - _install_plans.push_front(InstallPlan()); - InstallPlan &plan = _install_plans.front(); - - P3DPatchFinder::Patchfiles::iterator pi; - for (pi = chain.begin(); pi != chain.end(); ++pi) { - P3DPatchFinder::Patchfile *patchfile = (*pi); - - // Download the patchfile - step = new InstallStepDownloadFile(this, patchfile->_file); - plan.push_back(step); - - // Uncompress it - FileSpec new_file = patchfile->_file; - string new_filename = new_file.get_filename(); - size_t dot = new_filename.rfind('.'); - string extension = new_filename.substr(dot); - assert(extension == ".pz" || extension == ".gz"); - new_filename = new_filename.substr(0, dot); - new_file.set_filename(new_filename); - step = new InstallStepUncompressFile - (this, patchfile->_file, new_file, false); - plan.push_back(step); - - // And apply it - FileSpec source_file = patchfile->_source_file; - FileSpec target_file = patchfile->_target_file; - source_file.set_filename(_uncompressed_archive.get_filename()); - target_file.set_filename(_uncompressed_archive.get_filename()); - step = new InstallStepApplyPatch - (this, new_file, source_file, target_file); - plan.push_back(step); - } - - // Unpack the uncompressed archive. - step = new InstallStepUnpackArchive(this, _unpack_size); - plan.push_back(step); - } - } - } -} - -/** - * Performs the next step in the current install plan. - * - * If download_finished is false, there is a pending download that has not - * fully completed yet; otherwise, download_finished should be set true. - * - * If plan_failed is false, it means that the top-of-stack plan is still good; - * if true, the top-of-stack plan has failed and should be removed. - */ -void P3DPackage:: -follow_install_plans(bool download_finished, bool plan_failed) { - if (!_allow_data_download || _failed) { - // Not authorized yet, or something went wrong. - return; - } - - while (!_install_plans.empty()) { - // Pull the next step off the current plan. - - InstallPlan &plan = _install_plans.front(); - - if (!_computed_plan_size) { - _total_plan_size = 0.0; - _total_plan_completed = 0.0; - InstallPlan::iterator si; - for (si = plan.begin(); si != plan.end(); ++si) { - double step_effort = (*si)->get_effort(); - _total_plan_size += step_effort; - _total_plan_completed += (*si)->get_progress() * step_effort; - } - - _download_progress = 0.0; - if (_total_plan_size > 0.0) { - _download_progress = _total_plan_completed / _total_plan_size; - } - _computed_plan_size = true; - nout << "Selected install plan for " << get_package_name() - << ": " << _total_plan_completed << " of " - << _total_plan_size << "\n"; - } - - while (!plan.empty() && !plan_failed) { - InstallStep *step = plan.front(); - _current_step_effort = step->get_effort(); - - InstallToken token = step->do_step(download_finished); - switch (token) { - case IT_step_failed: - // This plan has failed. - plan_failed = true; - break; - - case IT_terminate: - // All plans have failed. - _install_plans.clear(); - report_done(false); - return; - - case IT_continue: - // A callback hook has been attached; we'll come back later. - return; - - case IT_needs_callback: - // We need to install a callback hook and come back later. - request_callback(); - return; - - case IT_step_complete: - // So far, so good. Go on to the next step. - _total_plan_completed += _current_step_effort; - delete step; - plan.pop_front(); - break; - } - } - - if (!plan_failed) { - // We've finished the plan successfully. - clear_install_plans(); - report_done(true); - return; - } - - // That plan failed. Go on to the next plan. - nout << "Plan failed.\n"; - _install_plans.pop_front(); - _computed_plan_size = false; - - // The next plan is (so far as we know) still good. - plan_failed = false; - } - - // All plans failed. Too bad for us. - report_done(false); -} - -/** - * This function is registered as the callback hook when a package is in the - * middle of processing in a sub-thread. - */ -void P3DPackage:: -st_callback(void *self) { - ((P3DPackage *)self)->follow_install_plans(false, false); -} - -/** - * Requests that follow_install_plans() will be called again in the future. - */ -void P3DPackage:: -request_callback() { - Instances::iterator ii; - for (ii = _instances.begin(); ii != _instances.end(); ++ii) { - (*ii)->request_callback(&st_callback, this); - } -} - -/** - * Reports the current install progress to all interested instances. - */ -void P3DPackage:: -report_progress(P3DPackage::InstallStep *step) { - if (_computed_plan_size) { - double size = _total_plan_completed + _current_step_effort * step->get_progress(); - _download_progress = std::min(size / _total_plan_size, 1.0); - - Instances::iterator ii; - for (ii = _instances.begin(); ii != _instances.end(); ++ii) { - (*ii)->report_package_progress(this, _download_progress); - } - } -} - -/** - * Called when the package information has been successfully downloaded but - * activate_download() has not yet been called, and the package is now idle, - * waiting for activate_download() to be called. - */ -void P3DPackage:: -report_info_ready() { - _info_ready = true; - - Instances::iterator ii; - for (ii = _instances.begin(); ii != _instances.end(); ++ii) { - (*ii)->report_package_info_ready(this); - } -} - -/** - * Transitions the package to "ready" or "failure" state, and reports this - * change to all the interested instances. - */ -void P3DPackage:: -report_done(bool success) { - // Don't call report_done() twice. - if (_ready || _failed) { - nout << get_package_name() << ": report_done() called twice\n"; - _failed = true; - return; - } - - if (success) { - _info_ready = true; - _ready = true; - _failed = false; - } else { - _ready = false; - _failed = true; - } - - if (!_allow_data_download && success) { - // If we haven't been authorized to start downloading yet, just report - // that we're ready to start. - Instances::iterator ii; - for (ii = _instances.begin(); ii != _instances.end(); ++ii) { - (*ii)->report_package_info_ready(this); - } - - } else { - // Otherwise, we can report that we're fully downloaded. - Instances::iterator ii; - for (ii = _instances.begin(); ii != _instances.end(); ++ii) { - (*ii)->report_package_done(this, success); - } - } -} - -/** - * Initiates a download of the indicated file. Returns the new Download - * object. - */ -P3DPackage::Download *P3DPackage:: -start_download(P3DPackage::DownloadType dtype, const string &urlbase, - const string &pathname, const FileSpec &file_spec) { - // Only one download should be active at a time - assert(_active_download == nullptr); - // This can't happen! If verify_contents is set to P3D_VC_never, we're not - // allowed to download anything, so we shouldn't get here - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - assert(inst_mgr->get_verify_contents() != P3D_VC_never); - - // We can't explicitly support partial downloads here, because Mozilla - // provides no interface to ask for one. We have to trust that Mozilla's - // use of the browser cache handles partial downloads for us automatically. - - // Delete the target file before we begin. -#ifdef _WIN32 - // Windows can't delete a file if it's read-only. - chmod(pathname.c_str(), 0644); -#endif - unlink(pathname.c_str()); - - Download *download = new Download(this, dtype, file_spec); - - // Fill up the _try_urls vector for URL's to try getting this file from, in - // reverse order. - bool is_contents_file = (dtype == DT_contents_file || dtype == DT_redownload_contents_file); - - // The last thing we try is the actual authoritative host, with a cache- - // busting query string. - ostringstream strm; - if (is_contents_file) { - strm << _host->get_host_url_prefix(); - } else { - strm << _host->get_download_url_prefix(); - } - strm << urlbase << "?" << time(nullptr); - string url = strm.str(); - download->_try_urls.push_back(url); - - if (!is_contents_file) { - // Before we try the cache-buster out of desperation, we try the - // authoritative host, allowing caches. - url = _host->get_download_url_prefix() + urlbase; - download->_try_urls.push_back(url); - - // Before *that*, we try a couple of mirrors, chosen at random. - vector mirrors; - _host->choose_random_mirrors(mirrors, 2); - for (vector::iterator si = mirrors.begin(); - si != mirrors.end(); - ++si) { - url = (*si) + urlbase; - download->_try_urls.push_back(url); - } - } - - if (dtype == DT_redownload_contents_file) { - // When we're redownloading the contents file after a download error, we - // always go straight to the authoritative host, not even to the super- - // mirror. - - } else { - // In other cases, if the "super mirror" is enabled, we try that first. - if (!inst_mgr->get_super_mirror().empty()) { - string url = inst_mgr->get_super_mirror() + urlbase; - download->_try_urls.push_back(url); - } - } - - if (download->_try_urls.size() == 1) { - // If we only ended up with only one URL on the try list, then try it - // twice, for a bit of redundancy in case there's a random network hiccup - // or something. - - // Save a copy into its own string object first to avoid self- - // dereferencing errors in the push_back() method. - string url = download->_try_urls[0]; - download->_try_urls.push_back(url); - } - - // OK, start the download. - assert(!download->_try_urls.empty()); - url = download->_try_urls.back(); - download->_try_urls.pop_back(); - download->set_url(url); - download->set_filename(pathname); - - set_active_download(download); - assert(!_instances.empty()); - - _instances[0]->start_download(download); - return download; -} - -/** - * Changes _active_download to point to the indicated object, respecting - * reference counts. - */ -void P3DPackage:: -set_active_download(Download *download) { - if (_active_download != download) { - if (_active_download != nullptr) { - p3d_unref_delete(_active_download); - } - _active_download = download; - if (_active_download != nullptr) { - _active_download->ref(); - } - } -} - -/** - * Changes _saved_download to point to the indicated object, respecting - * reference counts. - */ -void P3DPackage:: -set_saved_download(Download *download) { - if (_saved_download != download) { - if (_saved_download != nullptr) { - p3d_unref_delete(_saved_download); - } - _saved_download = download; - if (_saved_download != nullptr) { - _saved_download->ref(); - } - } -} - -/** - * Returns true if the name file is on the extract list, false otherwise. If - * true, fills in the FileSpec with the file's information. - */ -bool P3DPackage:: -is_extractable(FileSpec &file, const string &filename) const { - Extracts::const_iterator ei; - for (ei = _extracts.begin(); ei != _extracts.end(); ++ei) { - if ((*ei).get_filename() == filename) { - file = (*ei); - return true; - } - } - - return false; -} - - -/** - * Called when P3D_RC_shutdown is received by any Download object, which - * indicates that the instance owning this download object is terminating and - * we should either find a new instance or abort the download. - * - * The return value is true if a new instance is available, or false if not. - */ -bool P3DPackage:: -instance_terminating(P3DInstance *instance) { - if (_instances.empty() || - (_instances.size() == 1 && instance == _instances[0])) { - // No other instances. - return false; - } - - // There are more instances available to continue this download; pick one of - // them. Move this one to the end of the list. - Instances::iterator ii = find(_instances.begin(), _instances.end(), instance); - if (ii != _instances.end()) { - _instances.erase(ii); - _instances.push_back(instance); - } - return true; -} - -/** - * Assigns _package_fullname to the appropriate combination of name, version, - * and platform. - */ -void P3DPackage:: -set_fullname() { - _package_fullname = _package_name; - if (!_package_version.empty()) { - _package_fullname += string(".") + _package_version; - } - if (!_package_platform.empty()) { - _package_fullname += string(".") + _package_platform; - } -} - -/** - * - */ -P3DPackage::Download:: -Download(P3DPackage *package, DownloadType dtype, const FileSpec &file_spec) : - _package(package), - _dtype(dtype), - _file_spec(file_spec) -{ -} - -/** - * - */ -P3DPackage::Download:: -Download(const P3DPackage::Download ©) : - P3DFileDownload(copy), - _try_urls(copy._try_urls), - _package(copy._package), - _dtype(copy._dtype), - _file_spec(copy._file_spec) -{ -} - -/** - * - */ -void P3DPackage::Download:: -download_progress() { - P3DFileDownload::download_progress(); - assert(_package->_active_download == this); - - switch (_dtype) { - case DT_contents_file: - case DT_redownload_contents_file: - break; - - case DT_desc_file: - break; - - case DT_install_step: - _package->follow_install_plans(false, false); - break; - } -} - -/** - * - */ -void P3DPackage::Download:: -download_finished(bool success) { - P3DFileDownload::download_finished(success); - assert(_package->_active_download == this); - if (get_ref_count() == 1) { - // No one cares anymore. - nout << "No one cares about " << get_url() << "\n"; - _package->set_active_download(nullptr); - return; - } - - _package->set_active_download(nullptr); - assert(get_ref_count() > 0); - - if (success && !_file_spec.get_filename().empty()) { - // We think we downloaded it correctly. Check the hash to be sure. - if (!_file_spec.full_verify(_package->_package_dir)) { - nout << "After downloading " << get_url() - << ", failed hash check\n"; - nout << "expected: "; - _file_spec.output_hash(nout); - nout << "\n"; - if (_file_spec.get_actual_file() != nullptr) { - nout << " got: "; - _file_spec.get_actual_file()->output_hash(nout); - nout << "\n"; - } - - success = false; - } - } - - close_file(); - - if (!success) { - if (get_download_terminated()) { - // Short-circuit the exit. - _try_urls.clear(); - resume_download_finished(false); - return; - } - - // Maybe it failed because our contents.xml file is out-of-date. Go try - // to freshen it. - bool is_contents_file = (_dtype == DT_contents_file || _dtype == DT_redownload_contents_file); - if (!is_contents_file) { - _package->redownload_contents_file(this); - return; - } - } - - // Carry on. - resume_download_finished(success); -} - -/** - * Continuing the work begun in download_finished(). This is a separate entry - * point so that it can be called again after determining that the host's - * contents.xml file is *not* stale. - */ -void P3DPackage::Download:: -resume_download_finished(bool success) { - if (!success && !_try_urls.empty()) { - // Try the next mirror. - string url = _try_urls.back(); - _try_urls.pop_back(); - - clear(); - set_url(url); - set_filename(get_filename()); - _package->set_active_download(this); - - assert(!_package->_instances.empty()); - _package->_instances[0]->start_download(this); - return; - } - - switch (_dtype) { - case DT_contents_file: - _package->contents_file_download_finished(success); - break; - - case DT_redownload_contents_file: - _package->contents_file_redownload_finished(success); - break; - - case DT_desc_file: - _package->desc_file_download_finished(success); - break; - - case DT_install_step: - _package->follow_install_plans(true, !success); - break; - } -} - -/** - * - */ -P3DPackage::InstallStep:: -InstallStep(P3DPackage *package, size_t bytes, double factor) : - _package(package), - _bytes_needed(bytes), - _bytes_done(0), - _bytes_factor(factor) -{ -} - -/** - * - */ -P3DPackage::InstallStep:: -~InstallStep() { -} - -/** - * - */ -P3DPackage::InstallStepDownloadFile:: -InstallStepDownloadFile(P3DPackage *package, const FileSpec &file) : - InstallStep(package, file.get_size(), _download_factor), - _file(file) -{ - _urlbase = _package->get_desc_file_dirname(); - _urlbase += "/"; - _urlbase += _file.get_filename(); - - _pathname = _package->get_package_dir() + "/" + _file.get_filename(); - - _download = nullptr; -} - -/** - * - */ -P3DPackage::InstallStepDownloadFile:: -~InstallStepDownloadFile() { - if (_download != nullptr) { - p3d_unref_delete(_download); - } -} - -/** - * - */ -P3DPackage::InstallToken P3DPackage::InstallStepDownloadFile:: -do_step(bool download_finished) { - if (_download == nullptr) { - // First, we have to start the download going. - assert(_package->_active_download == nullptr); - - _download = _package->start_download(DT_install_step, _urlbase, - _pathname, _file); - assert(_download != nullptr); - _download->ref(); - } - - _bytes_done = _download->get_total_data(); - report_step_progress(); - - if (!_download->get_download_finished() || !download_finished) { - // Wait for it. - return IT_continue; - } - - if (_download->get_download_success()) { - // The Download object has already validated the hash. - _package->_updated = true; - return IT_step_complete; - - } else if (_download->get_download_terminated()) { - // The download was interrupted because its instance is shutting down. - // Don't try any other plans, unless we have some more instances. - P3DInstance *instance = _download->get_instance(); - if (!_package->instance_terminating(instance)) { - // That was the only instance referencing this package, so stop the - // download. - nout << "Terminating download of " << _urlbase << "\n"; - return IT_terminate; - } - nout << "Restarting download of " << _urlbase << " on new instance\n"; - - p3d_unref_delete(_download); - _download = nullptr; - return IT_continue; - - } else { - // The Download object has already tried all of the mirrors, and they all - // failed. - return IT_step_failed; - } -} - -/** - * - */ -void P3DPackage::InstallStepDownloadFile:: -output(ostream &out) { - out << "InstallStepDownloadFile(" << _package->get_package_name() - << ", " << _file.get_filename() << ")"; -} - - -/** - * - */ -P3DPackage::InstallStepThreaded:: -InstallStepThreaded(P3DPackage *package, size_t bytes, double factor) : - InstallStep(package, bytes, factor) -{ - INIT_THREAD(_thread); - INIT_LOCK(_thread_lock); - _thread_started = false; - _thread_token = IT_needs_callback; - _thread_bytes_done = 0; -} - -/** - * - */ -P3DPackage::InstallStepThreaded:: -~InstallStepThreaded() { - if (_thread_started) { - JOIN_THREAD(_thread); - _thread_started = false; - } - DESTROY_LOCK(_thread_lock); -} - -/** - * - */ -P3DPackage::InstallToken P3DPackage::InstallStepThreaded:: -do_step(bool download_finished) { - // This method is called within the main thread. It simply checks the - // thread status, and returns. - - // Spawn a thread and wait for it to finish. - if (!_thread_started) { - nout << "Spawning thread to handle " << _package->get_package_name() << "\n"; - _thread_started = true; - SPAWN_THREAD(_thread, thread_main, this); - } - - InstallToken token; - bool made_progress = false; - - ACQUIRE_LOCK(_thread_lock); - token = _thread_token; - if (_bytes_done != _thread_bytes_done) { - _bytes_done = _thread_bytes_done; - made_progress = true; - } - RELEASE_LOCK(_thread_lock); - - if (made_progress) { - report_step_progress(); - } - - return token; -} - -/** - * - */ -void P3DPackage::InstallStepThreaded:: -thread_main() { - // This method is called within the sub-thread. It calls thread_step() to - // do its work. - - InstallToken token = IT_needs_callback; - do { - // Perform the nested step, then update the token. - token = thread_step(); - - ACQUIRE_LOCK(_thread_lock); - _thread_token = token; - RELEASE_LOCK(_thread_lock); - - // Do it again if needed. - } while (token == IT_needs_callback); - - // All done. -} - -/** - * Should be called from time to time within the sub-thread to update the - * number of bytes processed. - */ -void P3DPackage::InstallStepThreaded:: -thread_set_bytes_done(size_t bytes_done) { - ACQUIRE_LOCK(_thread_lock); - _thread_bytes_done = bytes_done; - RELEASE_LOCK(_thread_lock); -} - -/** - * Should be called from time to time within the sub-thread to update the - * number of bytes processed. - */ -void P3DPackage::InstallStepThreaded:: -thread_add_bytes_done(size_t bytes_done) { - ACQUIRE_LOCK(_thread_lock); - _thread_bytes_done += bytes_done; - RELEASE_LOCK(_thread_lock); -} - -/** - * - */ -P3DPackage::InstallStepUncompressFile:: -InstallStepUncompressFile(P3DPackage *package, const FileSpec &source, - const FileSpec &target, bool verify_target) : - InstallStepThreaded(package, target.get_size(), _uncompress_factor), - _source(source), - _target(target), - _verify_target(verify_target) -{ -} - - -/** - * - */ -P3DPackage::InstallToken P3DPackage::InstallStepUncompressFile:: -thread_step() { - string source_pathname = _package->get_package_dir() + "/" + _source.get_filename(); - string target_pathname = _package->get_package_dir() + "/" + _target.get_filename(); - - ifstream source; -#ifdef _WIN32 - std::wstring source_pathname_w; - if (string_to_wstring(source_pathname_w, source_pathname)) { - source.open(source_pathname_w.c_str(), ios::in | ios::binary); - } -#else // _WIN32 - source.open(source_pathname.c_str(), ios::in | ios::binary); -#endif // _WIN32 - if (!source) { - nout << "Couldn't open " << source_pathname << "\n"; - return IT_step_failed; - } - - if (!mkfile_complete(target_pathname, nout)) { - return IT_step_failed; - } - - ofstream target; -#ifdef _WIN32 - std::wstring target_pathname_w; - if (string_to_wstring(target_pathname_w, target_pathname)) { - target.open(target_pathname_w.c_str(), ios::out | ios::binary); - } -#else // _WIN32 - target.open(target_pathname.c_str(), ios::out | ios::binary); -#endif // _WIN32 - if (!target) { - nout << "Couldn't write to " << target_pathname << "\n"; - return IT_step_failed; - } - - static const int decompress_buffer_size = 81920; - char decompress_buffer[decompress_buffer_size]; - static const int write_buffer_size = 81920; - char write_buffer[write_buffer_size]; - - z_stream z; - z.next_in = Z_NULL; - z.avail_in = 0; - z.next_out = Z_NULL; - z.avail_out = 0; - z.zalloc = Z_NULL; - z.zfree = Z_NULL; - z.opaque = Z_NULL; - z.msg = (char *)"no error message"; - - bool eof = false; - int flush = 0; - - source.read(decompress_buffer, decompress_buffer_size); - std::streamsize read_count = source.gcount(); - eof = (read_count == 0 || source.eof() || source.fail()); - - z.next_in = (Bytef *)decompress_buffer; - z.avail_in = (size_t)read_count; - - int result = inflateInit2(&z, 32 + 15); - if (result < 0) { - nout << z.msg << "\n"; - return IT_step_failed; - } - - while (true) { - if (z.avail_in == 0 && !eof) { - source.read(decompress_buffer, decompress_buffer_size); - std::streamsize read_count = source.gcount(); - eof = (read_count == 0 || source.eof() || source.fail()); - - z.next_in = (Bytef *)decompress_buffer; - z.avail_in = (size_t)read_count; - } - - z.next_out = (Bytef *)write_buffer; - z.avail_out = write_buffer_size; - int result = inflate(&z, flush); - if (z.avail_out < write_buffer_size) { - target.write(write_buffer, write_buffer_size - z.avail_out); - if (!target) { - nout << "Couldn't write entire file to " << target_pathname << "\n"; - return IT_step_failed; - } - - thread_add_bytes_done(write_buffer_size - z.avail_out); - } - - if (result == Z_STREAM_END) { - // Here's the end of the file. - break; - - } else if (result == Z_BUF_ERROR && flush == 0) { - // We might get this if no progress is possible, for instance if the - // input stream is truncated. In this case, tell zlib to dump - // everything it's got. - flush = Z_FINISH; - - } else if (result < 0) { - nout << z.msg << "\n"; - inflateEnd(&z); - return IT_step_failed; - } - } - - result = inflateEnd(&z); - if (result < 0) { - nout << z.msg << "\n"; - return IT_step_failed; - } - - source.close(); - target.close(); - - if (_verify_target && !_target.full_verify(_package->get_package_dir())) { - nout << "after uncompressing " << target_pathname - << ", failed hash check\n"; - return IT_step_failed; - } - - // Now that we've verified the target, make it read-only. - chmod(target_pathname.c_str(), 0444); - - // Now we can safely remove the source. -#ifdef _WIN32 - chmod(source_pathname.c_str(), 0644); -#endif - unlink(source_pathname.c_str()); - - // All done uncompressing. - _package->_updated = true; - return IT_step_complete; -} - -/** - * - */ -void P3DPackage::InstallStepUncompressFile:: -output(ostream &out) { - out << "InstallStepUncompressFile(" << _package->get_package_name() - << ", " << _source.get_filename() << ", " << _target.get_filename() - << ", " << _verify_target << ")"; -} - -/** - * - */ -P3DPackage::InstallStepUnpackArchive:: -InstallStepUnpackArchive(P3DPackage *package, size_t unpack_size) : - InstallStepThreaded(package, unpack_size, _unpack_factor) -{ -} - -/** - * - */ -P3DPackage::InstallToken P3DPackage::InstallStepUnpackArchive:: -thread_step() { - string source_pathname = _package->get_archive_file_pathname(); - P3DMultifileReader reader; - - if (!reader.open_read(source_pathname)) { - nout << "Couldn't read " << source_pathname << "\n"; - return IT_step_failed; - } - - if (!reader.extract_all(_package->get_package_dir(), _package, this)) { - nout << "Failure extracting " << source_pathname << "\n"; - return IT_step_failed; - } - - _package->_updated = true; - return IT_step_complete; -} - -/** - * - */ -void P3DPackage::InstallStepUnpackArchive:: -output(ostream &out) { - out << "InstallStepUnpackArchive(" << _package->get_package_name() << ")"; -} - - -/** - * - */ -P3DPackage::InstallStepApplyPatch:: -InstallStepApplyPatch(P3DPackage *package, const FileSpec &patchfile, - const FileSpec &source, const FileSpec &target) : - InstallStepThreaded(package, target.get_size(), _patch_factor), - _reader(package->get_package_dir(), patchfile, source, target) -{ -} - -/** - * - */ -P3DPackage::InstallToken P3DPackage::InstallStepApplyPatch:: -thread_step() { - // Open the patchfile - if (!_reader.open_read()) { - _reader.close(); - return IT_step_failed; - } - - // Apply the patch. - while (_reader.step()) { - size_t bytes_written = _reader.get_bytes_written(); - thread_set_bytes_done(bytes_written); - } - - // Close and verify. - _reader.close(); - - if (!_reader.get_success()) { - nout << "Patching failed\n"; - return IT_step_failed; - } - - _package->_updated = true; - return IT_step_complete; -} - -/** - * - */ -void P3DPackage::InstallStepApplyPatch:: -output(ostream &out) { - out << "InstallStepApplyPatch(" << _package->get_package_name() << ")"; -} diff --git a/direct/src/plugin/p3dPackage.h b/direct/src/plugin/p3dPackage.h deleted file mode 100644 index 4d44282c67..0000000000 --- a/direct/src/plugin/p3dPackage.h +++ /dev/null @@ -1,310 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPackage.h - * @author drose - * @date 2009-06-12 - */ - -#ifndef P3DPACKAGE_H -#define P3DPACKAGE_H - -#include "p3d_plugin_common.h" -#include "p3dFileDownload.h" -#include "p3dPatchfileReader.h" -#include "fileSpec.h" -#include "get_tinyxml.h" -#include - -class P3DHost; -class P3DInstance; -class P3DTemporaryFile; - -/** - * This corresponds to a downloadable, patchable package, and all its - * constituent files. For instance, a particular version of the Panda3D - * runtime, which consists of a bunch of dll's downloaded in a single tar - * file, is a package. - * - * The core API is responsible for managing these packages on disk, - * downloading new versions when needed, and removing stale versions to limit - * disk space waste. - */ -class P3DPackage { -private: - P3DPackage(P3DHost *host, - const std::string &package_name, - const std::string &package_version, - const std::string &package_platform, - const std::string &alt_host); - ~P3DPackage(); - -public: - inline bool get_info_ready() const; - inline size_t get_download_size() const; - - void activate_download(); - inline bool get_ready() const; - inline bool get_failed() const; - inline P3DHost *get_host() const; - inline const std::string &get_package_dir() const; - inline const std::string &get_package_name() const; - inline const std::string &get_package_version() const; - inline const std::string &get_package_platform() const; - inline const std::string &get_package_display_name() const; - std::string get_formatted_name() const; - inline const TiXmlElement *get_xconfig() const; - - inline const std::string &get_desc_file_pathname() const; - inline const std::string &get_desc_file_dirname() const; - inline std::string get_archive_file_pathname() const; - - void add_instance(P3DInstance *inst); - void remove_instance(P3DInstance *inst); - - void mark_used(); - void uninstall(); - - TiXmlElement *make_xml(); - -private: - typedef std::vector Extracts; - - enum DownloadType { - DT_contents_file, - DT_redownload_contents_file, - DT_desc_file, - DT_install_step, - }; - - typedef std::vector TryUrls; - - class Download : public P3DFileDownload { - public: - Download(P3DPackage *package, DownloadType dtype, - const FileSpec &file_spec); - Download(const Download ©); - - protected: - virtual void download_progress(); - virtual void download_finished(bool success); - - public: - void resume_download_finished(bool success); - - public: - // URL's to try downloading from, in reverse order. - TryUrls _try_urls; - - private: - P3DPackage *_package; - DownloadType _dtype; - - // FileSpec to validate the download against. - FileSpec _file_spec; - }; - - enum InstallToken { - IT_step_complete, - IT_step_failed, - IT_continue, - IT_needs_callback, - IT_terminate, - }; - - class InstallStep { - public: - InstallStep(P3DPackage *package, size_t bytes, double factor); - virtual ~InstallStep(); - - virtual InstallToken do_step(bool download_finished) = 0; - virtual void output(std::ostream &out) = 0; - - inline double get_effort() const; - inline double get_progress() const; - inline void report_step_progress(); - - P3DPackage *_package; - size_t _bytes_needed; - size_t _bytes_done; - double _bytes_factor; - }; - - class InstallStepDownloadFile : public InstallStep { - public: - InstallStepDownloadFile(P3DPackage *package, const FileSpec &file); - virtual ~InstallStepDownloadFile(); - - virtual InstallToken do_step(bool download_finished); - virtual void output(std::ostream &out); - - std::string _urlbase; - std::string _pathname; - FileSpec _file; - Download *_download; - }; - - class InstallStepThreaded : public InstallStep { - public: - InstallStepThreaded(P3DPackage *package, size_t bytes, double factor); - virtual ~InstallStepThreaded(); - - virtual InstallToken do_step(bool download_finished); - - THREAD_CALLBACK_DECLARATION(InstallStepThreaded, thread_main); - void thread_main(); - virtual InstallToken thread_step()=0; - void thread_set_bytes_done(size_t bytes_done); - void thread_add_bytes_done(size_t bytes_done); - - THREAD _thread; - LOCK _thread_lock; - bool _thread_started; - InstallToken _thread_token; - size_t _thread_bytes_done; - }; - - class InstallStepUncompressFile : public InstallStepThreaded { - public: - InstallStepUncompressFile(P3DPackage *package, const FileSpec &source, - const FileSpec &target, bool verify_target); - virtual InstallToken thread_step(); - virtual void output(std::ostream &out); - - FileSpec _source; - FileSpec _target; - bool _verify_target; - }; - - class InstallStepUnpackArchive : public InstallStepThreaded { - public: - InstallStepUnpackArchive(P3DPackage *package, size_t unpack_size); - virtual InstallToken thread_step(); - virtual void output(std::ostream &out); - }; - - class InstallStepApplyPatch : public InstallStepThreaded { - public: - InstallStepApplyPatch(P3DPackage *package, - const FileSpec &patchfile, - const FileSpec &source, - const FileSpec &target); - virtual InstallToken thread_step(); - virtual void output(std::ostream &out); - - P3DPatchfileReader _reader; - }; - - typedef std::deque InstallPlan; - typedef std::deque InstallPlans; - InstallPlans _install_plans; - - bool _computed_plan_size; - double _total_plan_size; - double _total_plan_completed; - double _download_progress; - double _current_step_effort; - - void begin_info_download(); - void download_contents_file(); - void contents_file_download_finished(bool success); - void redownload_contents_file(Download *download); - void contents_file_redownload_finished(bool success); - void host_got_contents_file(); - - void download_desc_file(); - void desc_file_download_finished(bool success); - void got_desc_file(TiXmlDocument *doc, bool freshly_downloaded); - - void clear_install_plans(); - void build_install_plans(TiXmlDocument *doc); - void follow_install_plans(bool download_finished, bool plan_failed); - static void st_callback(void *self); - void request_callback(); - - void report_progress(InstallStep *step); - void report_info_ready(); - void report_done(bool success); - Download *start_download(DownloadType dtype, const std::string &urlbase, - const std::string &pathname, const FileSpec &file_spec); - void set_active_download(Download *download); - void set_saved_download(Download *download); - - bool is_extractable(FileSpec &file, const std::string &filename) const; - bool instance_terminating(P3DInstance *instance); - void set_fullname(); - -public: - class RequiredPackage { - public: - inline RequiredPackage(const std::string &package_name, - const std::string &package_version, - const std::string &package_seq, - P3DHost *host); - std::string _package_name; - std::string _package_version; - std::string _package_seq; - P3DHost *_host; - }; - typedef std::vector Requires; - Requires _requires; - -private: - P3DHost *_host; - int _host_contents_iseq; - - std::string _package_name; - std::string _package_version; - std::string _package_platform; - bool _per_platform; - int _patch_version; - std::string _alt_host; - bool _package_solo; - std::string _package_display_name; - std::string _package_fullname; - std::string _package_dir; - TiXmlElement *_xconfig; - - P3DTemporaryFile *_temp_contents_file; - - FileSpec _desc_file; - std::string _desc_file_dirname; - std::string _desc_file_basename; - std::string _desc_file_pathname; - - bool _info_ready; - bool _allow_data_download; - bool _ready; - bool _failed; - Download *_active_download; - Download *_saved_download; - - typedef std::vector Instances; - Instances _instances; - - FileSpec _compressed_archive; - FileSpec _uncompressed_archive; - - size_t _unpack_size; - Extracts _extracts; - bool _updated; - - static const double _download_factor; - static const double _uncompress_factor; - static const double _unpack_factor; - static const double _patch_factor; - - friend class Download; - friend class InstallStep; - friend class P3DMultifileReader; - friend class P3DHost; -}; - -#include "p3dPackage.I" - -#endif diff --git a/direct/src/plugin/p3dPatchFinder.I b/direct/src/plugin/p3dPatchFinder.I deleted file mode 100644 index af066807ca..0000000000 --- a/direct/src/plugin/p3dPatchFinder.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPatchFinder.I - * @author drose - * @date 2009-09-27 - */ diff --git a/direct/src/plugin/p3dPatchFinder.cxx b/direct/src/plugin/p3dPatchFinder.cxx deleted file mode 100644 index fbc109fe8c..0000000000 --- a/direct/src/plugin/p3dPatchFinder.cxx +++ /dev/null @@ -1,399 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPatchFinder.cxx - * @author drose - * @date 2009-09-27 - */ - -#include "p3dPatchFinder.h" - -using std::string; - -/** - * - */ -P3DPatchFinder::PackageVersion:: -PackageVersion(const PackageVersionKey &key) : - _package_name(key._package_name), - _platform(key._platform), - _version(key._version), - _host_url(key._host_url), - _file(key._file) -{ - _package_current = nullptr; - _package_base = nullptr; -} - -/** - * Fills chain with the list of patches that, when applied in sequence to the - * indicated PackageVersion object, produces this PackageVersion object. - * Returns false if no chain can be found. - */ -bool P3DPatchFinder::PackageVersion:: -get_patch_chain(Patchfiles &chain, PackageVersion *start_pv, - const PackageVersionsList &already_visited_in) { - chain.clear(); - if (this == start_pv) { - // We're already here. A zero-length patch chain is therefore the answer. - return true; - } - if (::find(already_visited_in.begin(), already_visited_in.end(), this) != already_visited_in.end()) { - // We've already been here; this is a loop. Avoid infinite recursion. - return false; - } - - // Yeah, we make a new copy of this vector at each stage of the recursion. - // This could be made much faster with a linked list instead, but I'm - // working on the assumption that there will be no more than a few dozen - // patchfiles, in which case this naive approach should be fast enough. - PackageVersionsList already_visited = already_visited_in; - already_visited.push_back(this); - - bool found_any = false; - Patchfiles::iterator pi; - for (pi = _from_patches.begin(); pi != _from_patches.end(); ++pi) { - Patchfile *patchfile = (*pi); - PackageVersion *from_pv = patchfile->_from_pv; - assert(from_pv != nullptr); - Patchfiles this_chain; - if (from_pv->get_patch_chain(this_chain, start_pv, already_visited)) { - // There's a path through this patchfile. - this_chain.push_back(patchfile); - if (!found_any || this_chain.size() < chain.size()) { - found_any = true; - chain.swap(this_chain); - } - } - } - - // If found_any is true, we've already filled chain with the shortest path - // found. - return found_any; -} - -/** - * - */ -P3DPatchFinder::PackageVersionKey:: -PackageVersionKey(const string &package_name, - const string &platform, - const string &version, - const string &host_url, - const FileSpec &file) : - _package_name(package_name), - _platform(platform), - _version(version), - _host_url(host_url), - _file(file) -{ -} - -/** - * - */ -bool P3DPatchFinder::PackageVersionKey:: -operator < (const PackageVersionKey &other) const { - if (_package_name != other._package_name) { - return _package_name < other._package_name; - } - if (_platform != other._platform) { - return _platform < other._platform; - } - if (_version != other._version) { - return _version < other._version; - } - if (_host_url != other._host_url) { - return _host_url < other._host_url; - } - return _file.compare_hash(other._file) < 0; -} - -/** - * - */ -void P3DPatchFinder::PackageVersionKey:: -output(std::ostream &out) const { - out << "(" << _package_name << ", " << _platform << ", " << _version - << ", " << _host_url << ", "; - _file.output_hash(out); - out << ")"; -} - -/** - * - */ -P3DPatchFinder::Patchfile:: -Patchfile(Package *package) : - _package(package), - _from_pv(nullptr), - _to_pv(nullptr) -{ - _package_name = package->_package_name; - _platform = package->_platform; - _version = package->_version; - _host_url = package->_host_url; -} - -/** - * Returns the key for locating the package that this patchfile can be applied - * to. - */ -P3DPatchFinder::PackageVersionKey P3DPatchFinder::Patchfile:: -get_source_key() const { - return PackageVersionKey(_package_name, _platform, _version, _host_url, _source_file); -} - -/** - * Returns the key for locating the package that this patchfile will generate. - */ -P3DPatchFinder::PackageVersionKey P3DPatchFinder::Patchfile:: -get_target_key() const { - return PackageVersionKey(_package_name, _platform, _version, _host_url, _target_file); -} - -/** - * Reads the data structures from an xml file. - */ -void P3DPatchFinder::Patchfile:: -load_xml(TiXmlElement *xpatch) { - const char *package_name_cstr = xpatch->Attribute("name"); - if (package_name_cstr != nullptr && *package_name_cstr) { - _package_name = package_name_cstr; - } - const char *platform_cstr = xpatch->Attribute("platform"); - if (platform_cstr != nullptr && *platform_cstr) { - _platform = platform_cstr; - } - const char *version_cstr = xpatch->Attribute("version"); - if (version_cstr != nullptr && *version_cstr) { - _version = version_cstr; - } - const char *host_url_cstr = xpatch->Attribute("host"); - if (host_url_cstr != nullptr && *host_url_cstr) { - _host_url = host_url_cstr; - } - - _file.load_xml(xpatch); - - TiXmlElement *xsource = xpatch->FirstChildElement("source"); - if (xsource != nullptr) { - _source_file.load_xml(xsource); - } - TiXmlElement *xtarget = xpatch->FirstChildElement("target"); - if (xtarget != nullptr) { - _target_file.load_xml(xtarget); - } -} - -/** - * - */ -P3DPatchFinder::Package:: -Package() { - _current_pv = nullptr; - _base_pv = nullptr; - _got_base_file = false; -} - -/** - * Returns the key to locate the current version of this package. - */ -P3DPatchFinder::PackageVersionKey P3DPatchFinder::Package:: -get_current_key() const { - return PackageVersionKey(_package_name, _platform, _version, _host_url, _current_file); -} - -/** - * Returns the key to locate the "base" or oldest version of this package. - */ -P3DPatchFinder::PackageVersionKey P3DPatchFinder::Package:: -get_base_key() const { - return PackageVersionKey(_package_name, _platform, _version, _host_url, _base_file); -} - -/** - * Returns the key that has the indicated hash. - */ -P3DPatchFinder::PackageVersionKey P3DPatchFinder::Package:: -get_generic_key(const FileSpec &file) const { - return PackageVersionKey(_package_name, _platform, _version, _host_url, file); -} - -/** - * Reads the package's desc file for the package information. Returns true on - * success, false on failure. - */ -bool P3DPatchFinder::Package:: -read_desc_file(TiXmlDocument *doc) { - TiXmlElement *xpackage = doc->FirstChildElement("package"); - if (xpackage == nullptr) { - return false; - } - - const char *package_name_cstr = xpackage->Attribute("name"); - if (package_name_cstr != nullptr && *package_name_cstr) { - _package_name = package_name_cstr; - } - const char *platform_cstr = xpackage->Attribute("platform"); - if (platform_cstr != nullptr && *platform_cstr) { - _platform = platform_cstr; - } - const char *version_cstr = xpackage->Attribute("version"); - if (version_cstr != nullptr && *version_cstr) { - _version = version_cstr; - } - const char *host_url_cstr = xpackage->Attribute("host"); - if (host_url_cstr != nullptr && *host_url_cstr) { - _host_url = host_url_cstr; - } - - // Get the current version. - TiXmlElement *xarchive = xpackage->FirstChildElement("uncompressed_archive"); - if (xarchive != nullptr) { - _current_file.load_xml(xarchive); - } - - // Get the base_version--the bottom (oldest) of the patch chain. - xarchive = xpackage->FirstChildElement("base_version"); - if (xarchive != nullptr) { - _base_file.load_xml(xarchive); - _got_base_file = true; - } - - _patches.clear(); - TiXmlElement *xpatch = xpackage->FirstChildElement("patch"); - while (xpatch != nullptr) { - Patchfile *patchfile = new Patchfile(this); - patchfile->load_xml(xpatch); - _patches.push_back(patchfile); - xpatch = xpatch->NextSiblingElement("patch"); - } - - return true; -} - -/** - * - */ -P3DPatchFinder:: -P3DPatchFinder() { -} - -/** - * - */ -P3DPatchFinder:: -~P3DPatchFinder() { - // TODO. Cleanup nicely. -} - -/** - * Loads the package defined in the indicated desc file, and constructs a - * patch chain from the version represented by file to the current version of - * this package, if possible. Returns true if successful, false otherwise. - */ -bool P3DPatchFinder:: -get_patch_chain_to_current(Patchfiles &chain, TiXmlDocument *doc, - const FileSpec &file) { - chain.clear(); - Package *package = read_package_desc_file(doc); - if (package == nullptr) { - return false; - } - - build_patch_chains(); - PackageVersion *from_pv = get_package_version(package->get_generic_key(file)); - PackageVersion *to_pv = package->_current_pv; - - if (to_pv != nullptr && from_pv != nullptr) { - return to_pv->get_patch_chain(chain, from_pv, PackageVersionsList()); - } - - return false; -} - - -/** - * Reads a desc file associated with a particular package, and adds the - * package to _packages. Returns the Package object, or NULL on failure. - */ -P3DPatchFinder::Package *P3DPatchFinder:: -read_package_desc_file(TiXmlDocument *doc) { - Package *package = new Package; - if (!package->read_desc_file(doc)) { - delete package; - return nullptr; - } - - _packages.push_back(package); - return package; -} - -/** - * Builds up the chains of PackageVersions and the patchfiles that connect - * them. - */ -void P3DPatchFinder:: -build_patch_chains() { - Packages::iterator pi; - for (pi = _packages.begin(); pi != _packages.end(); ++pi) { - Package *package = (*pi); - if (!package->_got_base_file) { - // This package doesn't have any versions yet. - continue; - } - - PackageVersion *current_pv = get_package_version(package->get_current_key()); - package->_current_pv = current_pv; - current_pv->_package_current = package; - current_pv->_print_name = package->_current_file.get_filename(); - - PackageVersion *base_pv = get_package_version(package->get_base_key()); - package->_base_pv = base_pv; - base_pv->_package_base = package; - base_pv->_print_name = package->_base_file.get_filename(); - - Patchfiles::iterator fi; - for (fi = package->_patches.begin(); fi != package->_patches.end(); ++fi) { - record_patchfile(*fi); - } - } -} - -/** - * Returns a shared PackageVersion object for the indicated key. - */ -P3DPatchFinder::PackageVersion *P3DPatchFinder:: -get_package_version(const PackageVersionKey &key) { - assert(!key._package_name.empty()); - PackageVersions::const_iterator vi = _package_versions.find(key); - if (vi != _package_versions.end()) { - return (*vi).second; - } - - PackageVersion *pv = new PackageVersion(key); - bool inserted = _package_versions.insert(PackageVersions::value_type(key, pv)).second; - assert(inserted); - return pv; -} - -/** - * Adds the indicated patchfile to the patch chains. - */ -void P3DPatchFinder:: -record_patchfile(Patchfile *patchfile) { - PackageVersion *from_pv = get_package_version(patchfile->get_source_key()); - patchfile->_from_pv = from_pv; - from_pv->_to_patches.push_back(patchfile); - - PackageVersion *to_pv = get_package_version(patchfile->get_target_key()); - patchfile->_to_pv = to_pv; - to_pv->_from_patches.push_back(patchfile); - to_pv->_print_name = patchfile->_file.get_filename(); -} diff --git a/direct/src/plugin/p3dPatchFinder.h b/direct/src/plugin/p3dPatchFinder.h deleted file mode 100644 index f25145a8e3..0000000000 --- a/direct/src/plugin/p3dPatchFinder.h +++ /dev/null @@ -1,179 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPatchFinder.h - * @author drose - * @date 2009-09-27 - */ - -#ifndef P3DPATCHFINDER_H -#define P3DPATCHFINDER_H - -#include "p3d_plugin_common.h" -#include "fileSpec.h" -#include "get_tinyxml.h" -#include -#include - -/** - * This class is used to reconstruct the patch chain--the chain of patch files - * needed to generate a file--for downloading a package via patches, rather - * than downloading the entire file. - * - * It is similar to PatchMaker.py, except it only reads patches, it does not - * generate them. - */ -class P3DPatchFinder { -public: - class Package; - class Patchfile; - class PackageVersion; - - typedef std::vector Patchfiles; - typedef std::vector PackageVersionsList; - - // This class is used to index into a map to locate PackageVersion objects, - // below. - class PackageVersionKey { - public: - PackageVersionKey(const std::string &package_name, - const std::string &platform, - const std::string &version, - const std::string &host_url, - const FileSpec &file); - bool operator < (const PackageVersionKey &other) const; - void output(std::ostream &out) const; - - public: - std::string _package_name; - std::string _platform; - std::string _version; - std::string _host_url; - FileSpec _file; - }; - - // A specific version of a package. This is not just a package's "version" - // string; it also corresponds to the particular patch version, which - // increments independently of the "version". - class PackageVersion { - public: - PackageVersion(const PackageVersionKey &key); - - bool get_patch_chain(Patchfiles &chain, PackageVersion *start_pv, - const PackageVersionsList &already_visited_in); - - public: - std::string _package_name; - std::string _platform; - std::string _version; - std::string _host_url; - FileSpec _file; - std::string _print_name; - - // The Package object that produces this version if this is the current - // form or the base form, respectively. - Package *_package_current; - Package *_package_base; - - // A list of patchfiles that can produce this version. - Patchfiles _from_patches; - - // A list of patchfiles that can start from this version. - Patchfiles _to_patches; - }; - - // A single patchfile for a package. - class Patchfile { - public: - Patchfile(Package *package); - - PackageVersionKey get_source_key() const; - PackageVersionKey get_target_key() const; - void load_xml(TiXmlElement *xpatch); - - public: - Package *_package; - std::string _package_name; - std::string _platform; - std::string _version; - std::string _host_url; - - // The patchfile itself - FileSpec _file; - - // The package file that the patch is applied to - FileSpec _source_file; - - // The package file that the patch generates - FileSpec _target_file; - - // The PackageVersion corresponding to our source_file - PackageVersion *_from_pv; - - // The PackageVersion corresponding to our target_file - PackageVersion *_to_pv; - }; - - // This is a particular package. This contains all of the information - // extracted from the package's desc file. - class Package { - public: - Package(); - - PackageVersionKey get_current_key() const; - PackageVersionKey get_base_key() const; - PackageVersionKey get_generic_key(const FileSpec &file) const; - - bool read_desc_file(TiXmlDocument *doc); - - public: - std::string _package_name; - std::string _platform; - std::string _version; - std::string _host_url; - - PackageVersion *_current_pv; - PackageVersion *_base_pv; - - FileSpec _current_file; - FileSpec _base_file; - bool _got_base_file; - - Patchfiles _patches; - }; - -public: - P3DPatchFinder(); - ~P3DPatchFinder(); - - bool get_patch_chain_to_current(Patchfiles &chain, TiXmlDocument *doc, - const FileSpec &file); - - Package *read_package_desc_file(TiXmlDocument *doc); - void build_patch_chains(); - PackageVersion *get_package_version(const PackageVersionKey &key); - -private: - void record_patchfile(Patchfile *patchfile); - -private: - typedef std::map PackageVersions; - PackageVersions _package_versions; - - typedef std::vector Packages; - Packages _packages; -}; - -#include "p3dPatchFinder.I" - -inline std::ostream &operator << (std::ostream &out, const P3DPatchFinder::PackageVersionKey &key) { - key.output(out); - return out; -} - -#endif diff --git a/direct/src/plugin/p3dPatchfileReader.I b/direct/src/plugin/p3dPatchfileReader.I deleted file mode 100644 index 9e290b43c8..0000000000 --- a/direct/src/plugin/p3dPatchfileReader.I +++ /dev/null @@ -1,72 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPatchfileReader.I - * @author drose - * @date 2009-09-28 - */ - -/** - * Returns true if the patchfile is currently open, false otherwise. - */ -inline bool P3DPatchfileReader:: -is_open() const { - return _is_open; -} - -/** - * Returns the number of bytes written to the output file so far during the - * patching process. - */ -inline size_t P3DPatchfileReader:: -get_bytes_written() const { - return _bytes_written; -} - -/** - * Returns true if the patching process has completed successfully, false if - * it has failed or has not yet completed. - */ -inline bool P3DPatchfileReader:: -get_success() const { - return _success; -} - -/** - * Extracts an unsigned short from the patchfile. - */ -inline unsigned int P3DPatchfileReader:: -read_uint16() { - unsigned int a = _patch_in.get(); - unsigned int b = _patch_in.get(); - return (b << 8) | a; -} - -/** - * Extracts an unsigned long from the patchfile. - */ -inline unsigned int P3DPatchfileReader:: -read_uint32() { - unsigned int a = _patch_in.get(); - unsigned int b = _patch_in.get(); - unsigned int c = _patch_in.get(); - unsigned int d = _patch_in.get(); - return (d << 24) | (c << 16) | (b << 8) | a; -} - -/** - * Extracts a signed long from the patchfile. - */ -inline int P3DPatchfileReader:: -read_int32() { - unsigned int a = _patch_in.get(); - unsigned int b = _patch_in.get(); - unsigned int c = _patch_in.get(); - int d = _patch_in.get(); - return (d << 24) | (c << 16) | (b << 8) | a; -} diff --git a/direct/src/plugin/p3dPatchfileReader.cxx b/direct/src/plugin/p3dPatchfileReader.cxx deleted file mode 100644 index 14ede5e9f9..0000000000 --- a/direct/src/plugin/p3dPatchfileReader.cxx +++ /dev/null @@ -1,280 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPatchfileReader.cxx - * @author drose - * @date 2009-09-28 - */ - -#include "p3dPatchfileReader.h" -#include "wstring_encode.h" - -using std::ios; -using std::string; - -/** - * - */ -P3DPatchfileReader:: -P3DPatchfileReader(const string &package_dir, const FileSpec &patchfile, - const FileSpec &source, const FileSpec &target) : - _package_dir(package_dir), - _patchfile(patchfile), - _source(source), - _target(target) -{ - _is_open = false; - _bytes_written = 0; - _success = false; -} - -/** - * - */ -P3DPatchfileReader:: -~P3DPatchfileReader() { - close(); -} - -/** - * Opens the named patchfile for reading, reads the header, and validates the - * inputs. Returns true on success, false otherwise. If this returns false, - * you should immediately call close(), or let this object destruct. - */ -bool P3DPatchfileReader:: -open_read() { - close(); - - // Synthesize an output filename, in case the source and the target refer to - // the same filename. - _output_pathname = _target.get_pathname(_package_dir); - _output_pathname += ".tmp"; - - string patch_pathname = _patchfile.get_pathname(_package_dir); - _patch_in.clear(); -#ifdef _WIN32 - std::wstring patch_pathname_w; - if (string_to_wstring(patch_pathname_w, patch_pathname)) { - _patch_in.open(patch_pathname_w.c_str(), ios::in | ios::binary); - } -#else // _WIN32 - _patch_in.open(patch_pathname.c_str(), ios::in | ios::binary); -#endif // _WIN32 - - string source_pathname = _source.get_pathname(_package_dir); - _source_in.clear(); -#ifdef _WIN32 - std::wstring source_pathname_w; - if (string_to_wstring(source_pathname_w, source_pathname)) { - _source_in.open(source_pathname_w.c_str(), ios::in | ios::binary); - } -#else // _WIN32 - _source_in.open(source_pathname.c_str(), ios::in | ios::binary); -#endif // _WIN32 - - mkfile_complete(_output_pathname, nout); - _target_out.clear(); -#ifdef _WIN32 - std::wstring output_pathname_w; - if (string_to_wstring(output_pathname_w, _output_pathname)) { - _target_out.open(output_pathname_w.c_str(), ios::in | ios::binary); - } -#else // _WIN32 - _target_out.open(_output_pathname.c_str(), ios::in | ios::binary); -#endif // _WIN32 - - _is_open = true; - - // If any of those failed to open, we fail. - if (_patch_in.fail() || _source_in.fail() || _target_out.fail()) { - nout << "Couldn't open patchfile source, input, and/or target.\n"; - return false; - } - - // Read the patchfile header and validate it against the hashes we were - // given. - unsigned int magic_number = read_uint32(); - if (magic_number != 0xfeebfaac) { - nout << "Not a valid patchfile: " << patch_pathname << "\n"; - return false; - } - - unsigned int version = read_uint16(); - if (version != 2) { - // This code only knows about patchfile version 2. If the patchfile code - // is updated, we have to update this code accordingly. - nout << "Unsupported patchfile version: " << version << "\n"; - return false; - } - - size_t source_length = read_uint32(); - if (source_length != _source.get_size()) { - nout << "Patchfile " << patch_pathname - << " doesn't match source size.\n"; - return false; - } - FileSpec validate; - validate.read_hash_stream(_patch_in); - if (_source.compare_hash(validate) != 0) { - nout << "Patchfile " << patch_pathname - << " doesn't match source hash.\n"; - return false; - } - - _target_length = read_uint32(); - if (_target_length != _target.get_size()) { - nout << "Patchfile " << patch_pathname - << " doesn't match target size.\n"; - return false; - } - validate.read_hash_stream(_patch_in); - if (_target.compare_hash(validate) != 0) { - nout << "Patchfile " << patch_pathname - << " doesn't match target hash.\n"; - return false; - } - - return true; -} - -/** - * Performs one incremental step of the patching operation. Returns true if - * the operation should continue and step() should be called again, false if - * the patching is done (either successfully, or due to failure). - */ -bool P3DPatchfileReader:: -step() { - assert(_is_open); - - size_t add_length = read_uint16(); - if (add_length != 0) { - // Add a number of bytes from the patchfile. - if (!copy_bytes(_patch_in, add_length)) { - nout << "Truncated patchfile.\n"; - return false; - } - } - - size_t copy_length = read_uint16(); - if (copy_length != 0) { - // Copy a number of bytes from the original source. - int offset = read_int32(); - _source_in.seekg(offset, ios::cur); - if (!copy_bytes(_source_in, copy_length)) { - nout << "Garbage in patchfile.\n"; - return false; - } - } - - assert(_bytes_written <= _target_length); - - // When both counts reach 0, the patchfile is done. - if (add_length != 0 || copy_length != 0) { - // So, we've still got more to do. - return true; - } - - if (_bytes_written != _target_length) { - nout << "Patchfile wrote truncated file.\n"; - return false; - } - - // Set the _success flag true, so close() will move the finished file into - // place. - _success = true; - close(); - - // Now validate the hash. - if (!_target.full_verify(_package_dir)) { - nout << "After patching, " << _target.get_filename() - << " is still incorrect.\n"; - _success = false; - return false; - } - - // Successfully patched! Return false to indicate completion. - return false; -} - -/** - * Closes the previously-opened files, and moves the output file into place. - * This also deletes the patchfile, assuming it will not be needed after it - * has been used. - */ -void P3DPatchfileReader:: -close() { - if (!_is_open) { - return; - } - - _is_open = false; - - _patch_in.close(); - _source_in.close(); - _target_out.close(); - - // Delete the patchfile. - string patch_pathname = _patchfile.get_pathname(_package_dir); -#ifdef _WIN32 - // Windows can't delete a file if it's read-only. - chmod(patch_pathname.c_str(), 0644); -#endif - unlink(patch_pathname.c_str()); - - if (_success) { - // Move the output file onto the target file. - string target_pathname = _target.get_pathname(_package_dir); -#ifdef _WIN32 - chmod(target_pathname.c_str(), 0644); -#endif - unlink(target_pathname.c_str()); - rename(_output_pathname.c_str(), target_pathname.c_str()); - - } else { - // Failure; remove the output file. -#ifdef _WIN32 - chmod(_output_pathname.c_str(), 0644); -#endif - unlink(_output_pathname.c_str()); - } -} - -/** - * Copies the indicated number of bytes from the indicated stream onto the - * output stream. Returns true on success, false if the input stream didn't - * have enough bytes. - */ -bool P3DPatchfileReader:: -copy_bytes(std::istream &in, size_t copy_byte_count) { - static const size_t buffer_size = 8192; - char buffer[buffer_size]; - - std::streamsize read_size = std::min(copy_byte_count, buffer_size); - in.read(buffer, read_size); - std::streamsize count = in.gcount(); - while (count != 0) { - _target_out.write(buffer, count); - _bytes_written += (size_t)count; - if (_bytes_written > _target_length) { - nout << "Runaway patchfile.\n"; - return false; - } - if (count != read_size) { - return false; - } - copy_byte_count -= (size_t)count; - count = 0; - if (copy_byte_count != 0) { - read_size = std::min(copy_byte_count, buffer_size); - in.read(buffer, read_size); - count = in.gcount(); - } - } - - return (copy_byte_count == 0); -} diff --git a/direct/src/plugin/p3dPatchfileReader.h b/direct/src/plugin/p3dPatchfileReader.h deleted file mode 100644 index 1c9501f5d1..0000000000 --- a/direct/src/plugin/p3dPatchfileReader.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPatchfileReader.h - * @author drose - * @date 2009-09-27 - */ - -#ifndef P3DPATCHFILEREADER_H -#define P3DPATCHFILEREADER_H - -#include "p3d_plugin_common.h" -#include "p3dInstanceManager.h" // for openssl -#include "fileSpec.h" - -/** - * A read-only implementation of Panda's patchfile format, for applying - * patches. - * - * This object assumes that the sourcefile has been already validated against - * its md5 hash, and does not validate it again. It *does* verify that the - * md5 hash in source and target match those read in the patchfile header; and - * it verifies the md5 hash on the target after completion. - */ -class P3DPatchfileReader { -public: - P3DPatchfileReader(const std::string &package_dir, - const FileSpec &patchfile, - const FileSpec &source, - const FileSpec &target); - ~P3DPatchfileReader(); - - bool open_read(); - inline bool is_open() const; - - bool step(); - inline size_t get_bytes_written() const; - inline bool get_success() const; - - void close(); - -private: - bool copy_bytes(std::istream &in, size_t copy_byte_count); - inline unsigned int read_uint16(); - inline unsigned int read_uint32(); - inline int read_int32(); - -private: - std::string _package_dir; - FileSpec _patchfile; - FileSpec _source; - FileSpec _target; - - std::string _output_pathname; - ifstream _patch_in; - ifstream _source_in; - ofstream _target_out; - - bool _is_open; - size_t _target_length; - size_t _bytes_written; - bool _success; -}; - -#include "p3dPatchfileReader.I" - -#endif diff --git a/direct/src/plugin/p3dPythonMain.cxx b/direct/src/plugin/p3dPythonMain.cxx deleted file mode 100644 index b5d11e6052..0000000000 --- a/direct/src/plugin/p3dPythonMain.cxx +++ /dev/null @@ -1,153 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPythonMain.cxx - * @author drose - * @date 2009-08-29 - */ - -#include "run_p3dpython.h" - -#include -#include -#include -#include -#include // strrchr - -#if defined(_WIN32) && defined(NON_CONSOLE) -// On Windows, we may need to build p3dpythonw.exe, a non-console version of -// this program. - -// We'll wrap the main() function with our own startup WinMain(). -#define main local_main -int main(int argc, char *argv[]); - -// Returns a newly-allocated string representing the quoted argument beginning -// at p. Advances p to the first character following the close quote. -static char * -parse_quoted_arg(char *&p) { - char quote = *p; - ++p; - std::string result; - - while (*p != '\0' && *p != quote) { - // TODO: handle escape characters? Not sure if we need to. - result += *p; - ++p; - } - if (*p == quote) { - ++p; - } - return strdup(result.c_str()); -} - -// Returns a newly-allocated string representing the unquoted argument -// beginning at p. Advances p to the first whitespace following the argument. -static char * -parse_unquoted_arg(char *&p) { - std::string result; - while (*p != '\0' && !isspace(*p)) { - result += *p; - ++p; - } - return strdup(result.c_str()); -} - -int WINAPI -WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { - char *command_line = GetCommandLine(); - - std::vector argv; - - char *p = command_line; - while (*p != '\0') { - if (*p == '"') { - char *arg = parse_quoted_arg(p); - argv.push_back(arg); - } else { - char *arg = parse_unquoted_arg(p); - argv.push_back(arg); - } - - // Skip whitespace. - while (*p != '\0' && isspace(*p)) { - ++p; - } - } - - assert(!argv.empty()); - return main(argv.size(), &argv[0]); -} -#endif // NON_CONSOLE - -/** - * This is a trivial main() function that invokes P3DPythonRun. It's used to - * build p3dpython.exe, which is the preferred way to run Python in a child - * process, as a separate executable. - */ -int -main(int argc, char *argv[]) { - const char *program_name = argv[0]; - const char *archive_file = nullptr; - const char *input_handle_str = nullptr; - const char *output_handle_str = nullptr; - const char *interactive_console_str = nullptr; - - if (argc > 1) { - archive_file = argv[1]; - } - if (argc > 2) { - input_handle_str = argv[2]; - } - if (argc > 3) { - output_handle_str = argv[3]; - } - if (argc > 4) { - interactive_console_str = argv[4]; - } - - if (archive_file == nullptr || *archive_file == '\0') { - std::cerr << "No archive filename specified on command line.\n"; - return 1; - } - - FHandle input_handle = invalid_fhandle; - if (input_handle_str != nullptr && *input_handle_str) { - std::stringstream stream(input_handle_str); - stream >> input_handle; - if (!stream) { - input_handle = invalid_fhandle; - } - } - - FHandle output_handle = invalid_fhandle; - if (output_handle_str != nullptr && *output_handle_str) { - std::stringstream stream(output_handle_str); - stream >> output_handle; - if (!stream) { - output_handle = invalid_fhandle; - } - } - - bool interactive_console = false; - if (interactive_console_str != nullptr && *interactive_console_str) { - std::stringstream stream(interactive_console_str); - int flag; - stream >> flag; - if (!stream.fail()) { - interactive_console = (flag != 0); - } - } - - int status = run_p3dpython(program_name, archive_file, input_handle, - output_handle, nullptr, interactive_console); - if (status != 0) { - std::cerr << "Failure on startup.\n"; - } - return status; -} diff --git a/direct/src/plugin/p3dPythonObject.cxx b/direct/src/plugin/p3dPythonObject.cxx deleted file mode 100644 index 58a2d0348f..0000000000 --- a/direct/src/plugin/p3dPythonObject.cxx +++ /dev/null @@ -1,341 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPythonObject.cxx - * @author drose - * @date 2009-07-03 - */ - -#include "p3dPythonObject.h" - -using std::string; - -/** - * - */ -P3DPythonObject:: -P3DPythonObject(P3DSession *session, int object_id) : - _session(session), - _object_id(object_id) -{ - _session->ref(); -} - -/** - * - */ -P3DPythonObject:: -~P3DPythonObject() { - // When the P3DPythonObject wrapper goes away, we have to inform the child - // process that we no longer need the corresponding PyObject to be kept - // around. - _session->drop_pyobj(_object_id); - p3d_unref_delete(_session); -} - -/** - * Returns the fundamental type of this kind of object. - */ -P3D_object_type P3DPythonObject:: -get_type() { - return P3D_OT_object; -} - -/** - * Returns the object value coerced to a boolean, if possible. - */ -bool P3DPythonObject:: -get_bool() { - bool bresult = 0; - - P3D_object *result = call("__bool__", true, nullptr, 0); - if (result != nullptr) { - bresult = P3D_OBJECT_GET_BOOL(result); - P3D_OBJECT_DECREF(result); - } - - return bresult; -} - -/** - * Returns the object value coerced to an integer, if possible. - */ -int P3DPythonObject:: -get_int() { - int iresult = 0; - - P3D_object *result = call("__int__", true, nullptr, 0); - if (result != nullptr) { - iresult = P3D_OBJECT_GET_INT(result); - P3D_OBJECT_DECREF(result); - } - - return iresult; -} - -/** - * Returns the object value coerced to a floating-point value, if possible. - */ -double P3DPythonObject:: -get_float() { - double fresult = 0.0; - - P3D_object *result = call("__float__", true, nullptr, 0); - if (result != nullptr) { - fresult = P3D_OBJECT_GET_FLOAT(result); - P3D_OBJECT_DECREF(result); - } - - return fresult; -} - -/** - * Fills the indicated C++ string object with the value of this object coerced - * to a string. - */ -void P3DPythonObject:: -make_string(string &value) { - P3D_object *result = call("__str__", true, nullptr, 0); - if (result != nullptr) { - int size = P3D_OBJECT_GET_STRING(result, nullptr, 0); - char *buffer = new char[size]; - P3D_OBJECT_GET_STRING(result, buffer, size); - value = string(buffer, size); - delete[] buffer; - - P3D_OBJECT_DECREF(result); - } -} - -/** - * Returns the named property element in the object. The return value is a - * new-reference P3D_object, or NULL on error. - */ -P3D_object *P3DPythonObject:: -get_property(const string &property) { - P3D_object *params[1]; - params[0] = new P3DStringObject(property); - - P3D_object *result = call("__get_property__", true, params, 1); - P3D_OBJECT_DECREF(params[0]); - return result; -} - -/** - * Modifies (or deletes, if value is NULL) the named property element in the - * object. Returns true on success, false on failure. - */ -bool P3DPythonObject:: -set_property(const string &property, bool needs_response, P3D_object *value) { - if (!_session->get_matches_script_origin()) { - // If you can't be scripting us, you can't be setting properties either. - return false; - } - - return set_property_insecure(property, needs_response, value); -} - -/** - * Works as set_property(), but does not check the matches_script_origin flag. - * Intended to be called internally only, never to be called from Javascript. - */ -bool P3DPythonObject:: -set_property_insecure(const string &property, bool needs_response, - P3D_object *value) { - bool bresult = !needs_response; - - P3D_object *params[2]; - params[0] = new P3DStringObject(property); - - P3D_object *result = nullptr; - - if (value == nullptr) { - // Delete an attribute. - result = call_insecure("__del_property__", needs_response, params, 1); - - } else { - // Set a new attribute. - params[1] = value; - result = call_insecure("__set_property__", needs_response, params, 2); - } - - P3D_OBJECT_DECREF(params[0]); - - if (result != nullptr) { - bresult = P3D_OBJECT_GET_BOOL(result); - P3D_OBJECT_DECREF(result); - } - - return bresult; -} - -/** - * Returns true if the named method exists on this object, false otherwise. - */ -bool P3DPythonObject:: -has_method(const string &method_name) { - // First, check the cache. - std::pair cresult = _has_method.insert(HasMethod::value_type(method_name, false)); - HasMethod::iterator hi = cresult.first; - if (!cresult.second) { - // Already cached. - return (*hi).second; - } - - bool bresult = false; - - P3D_object *params[1]; - params[0] = new P3DStringObject(method_name); - - P3D_object *result = call("__has_method__", true, params, 1); - P3D_OBJECT_DECREF(params[0]); - - if (result != nullptr) { - bresult = P3D_OBJECT_GET_BOOL(result); - P3D_OBJECT_DECREF(result); - } - - // Save the cached result, so we don't have to keep asking this question. - // We assume that the set of methods on an object don't change - // substantially, so we can get away with keeping this cache. - (*hi).second = bresult; - - return bresult; -} - -/** - * Invokes the named method on the object, passing the indicated parameters. - * If the method name is empty, invokes the object itself. - * - * If needs_response is true, the return value is a new-reference P3D_object - * on success, or NULL on failure. If needs_response is false, the return - * value is always NULL, and there is no way to determine success or failure. - */ -P3D_object *P3DPythonObject:: -call(const string &method_name, bool needs_response, - P3D_object *params[], int num_params) { - if (!_session->get_matches_script_origin()) { - // If you can't be scripting us, you can't be calling methods. - return nullptr; - } - - return call_insecure(method_name, needs_response, params, num_params); -} - -/** - * Works as call(), but does not check the matches_script_origin flag. - * Intended to be called internally only, never to be called from Javascript. - */ -P3D_object *P3DPythonObject:: -call_insecure(const string &method_name, bool needs_response, - P3D_object *params[], int num_params) { - TiXmlDocument *doc = new TiXmlDocument; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "pyobj"); - xcommand->SetAttribute("op", "call"); - if (!method_name.empty()) { - xcommand->SetAttribute("method_name", method_name); - } - - TiXmlElement *xobject = _session->p3dobj_to_xml(this); - xobject->SetValue("object"); - xcommand->LinkEndChild(xobject); - - for (int i = 0; i < num_params; ++i) { - TiXmlElement *xparams = _session->p3dobj_to_xml(params[i]); - xcommand->LinkEndChild(xparams); - } - - doc->LinkEndChild(xcommand); - - // If no response is requested, send the command out in a vacuum, and return - // NULL. - if (!needs_response) { - _session->send_command(doc); - return nullptr; - } - - // If a response is requested, we have to send the command and wait for it. - TiXmlDocument *response = _session->command_and_response(doc); - - P3D_object *result = nullptr; - if (response != nullptr) { - TiXmlElement *xresponse = response->FirstChildElement("response"); - if (xresponse != nullptr) { - TiXmlElement *xvalue = xresponse->FirstChildElement("value"); - if (xvalue != nullptr) { - result = _session->xml_to_p3dobj(xvalue); - } - } - delete response; - } - - return result; -} - -/** - * Writes a formatted representation of the value to the indicated string. - * This is intended for developer assistance. - */ -void P3DPythonObject:: -output(std::ostream &out) { - P3D_object *result = call("__repr__", true, nullptr, 0); - out << "Python " << _object_id; - if (result != nullptr) { - out << ": " << *result; - P3D_OBJECT_DECREF(result); - } -} - -/** - * If this object has a valid XML representation for the indicated session - * (that hasn't already been implemented by the generic code in P3DSession), - * this method will apply it to the indicated "value" element and return true. - * Otherwise, this method will leave the element unchanged and return false. - */ -bool P3DPythonObject:: -fill_xml(TiXmlElement *xvalue, P3DSession *session) { - if (session == _session) { - // If it's a P3DPythonObject from the same session, just send the - // object_id down, since the actual implementation of this object exists - // (as a Python object) in the sub-process space. - xvalue->SetAttribute("type", "python"); - xvalue->SetAttribute("object_id", _object_id); - return true; - } - - // Otherwise, we have to send it as a browser object. - return false; -} - -/** - * Returns this object, downcast to a P3DPythonObject, if it is in fact an - * object of that type; or NULL if it is not. - */ -P3DPythonObject *P3DPythonObject:: -as_python_object() { - return this; -} - -/** - * Returns the session that this object is identified with. - */ -P3DSession *P3DPythonObject:: -get_session() { - return _session; -} - - -/** - * Returns the object_id number that is used to uniquely identify this object - * in the XML stream. - */ -int P3DPythonObject:: -get_object_id() { - return _object_id; -} diff --git a/direct/src/plugin/p3dPythonObject.h b/direct/src/plugin/p3dPythonObject.h deleted file mode 100644 index a4f9c5d7d0..0000000000 --- a/direct/src/plugin/p3dPythonObject.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPythonObject.h - * @author drose - * @date 2009-07-02 - */ - -#ifndef P3DPYTHONOBJECT_H -#define P3DPYTHONOBJECT_H - -#include "p3d_plugin_common.h" -#include "p3dObject.h" - -class P3DSession; - -/** - * An object type that references a PyObject in the subordinate process. It - * allows querying and/or modifying the state of the referenced PyObject, via - * clever XML communication in P3DSession::command_and_response(). - */ -class P3DPythonObject : public P3DObject { -public: - P3DPythonObject(P3DSession *session, int object_id); - virtual ~P3DPythonObject(); - -public: - virtual P3D_object_type get_type(); - virtual bool get_bool(); - virtual int get_int(); - virtual double get_float(); - - virtual void make_string(std::string &value); - - virtual P3D_object *get_property(const std::string &property); - virtual bool set_property(const std::string &property, bool needs_response, P3D_object *value); - bool set_property_insecure(const std::string &property, bool needs_response, - P3D_object *value); - - virtual bool has_method(const std::string &method_name); - virtual P3D_object *call(const std::string &method_name, bool needs_response, - P3D_object *params[], int num_params); - P3D_object *call_insecure(const std::string &method_name, bool needs_response, - P3D_object *params[], int num_params); - - virtual void output(std::ostream &out); - virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session); - - virtual P3DPythonObject *as_python_object(); - - P3DSession *get_session(); - int get_object_id(); - -private: - P3DSession *_session; - int _object_id; - - typedef std::map HasMethod; - HasMethod _has_method; -}; - -#endif diff --git a/direct/src/plugin/p3dPythonRun.I b/direct/src/plugin/p3dPythonRun.I deleted file mode 100644 index dabbe7ef8f..0000000000 --- a/direct/src/plugin/p3dPythonRun.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPythonRun.I - * @author drose - * @date 2009-06-05 - */ diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx deleted file mode 100644 index b9e4c5655e..0000000000 --- a/direct/src/plugin/p3dPythonRun.cxx +++ /dev/null @@ -1,1930 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPythonRun.cxx - * @author drose - * @date 2009-06-05 - */ - -#include "p3dPythonRun.h" -#include "asyncTaskManager.h" -#include "binaryXml.h" -#include "multifile.h" -#include "virtualFileSystem.h" -#include "nativeWindowHandle.h" - -#include "py_panda.h" - -using std::string; - -extern "C" { - // This has been compiled-in by the build system, if all is well. - extern struct _frozen _PyImport_FrozenModules[]; -}; - -// There is only one P3DPythonRun object in any given process space. Makes -// the statics easier to deal with, and we don't need multiple instances of -// this thing. -P3DPythonRun *P3DPythonRun::_global_ptr = nullptr; - -TypeHandle P3DPythonRun::P3DWindowHandle::_type_handle; - -/** - * - */ -P3DPythonRun:: -P3DPythonRun(const char *program_name, const char *archive_file, - FHandle input_handle, FHandle output_handle, - const char *log_pathname, bool interactive_console) { - P3DWindowHandle::init_type(); - init_xml(); - - _read_thread_continue = false; - _program_continue = true; - _session_terminated = false; - _taskMgr = nullptr; - - INIT_LOCK(_commands_lock); - INIT_THREAD(_read_thread); - - _session_id = 0; - _next_sent_id = 0; - - _interactive_console = interactive_console; - - if (program_name != nullptr) { -#if PY_MAJOR_VERSION >= 3 - // Python 3 case: we have to convert it to a wstring. - TextEncoder enc; - enc.set_text(program_name); - _program_name = enc.get_wtext(); -#else - // Python 2 is happy with a regular string. - _program_name = program_name; -#endif - } - if (archive_file != nullptr) { - _archive_file = Filename::from_os_specific(archive_file); - } - - _py_argc = 1; -#if PY_MAJOR_VERSION >= 3 - _py_argv[0] = (wchar_t *)_program_name.c_str(); -#else - _py_argv[0] = (char *)_program_name.c_str(); -#endif - _py_argv[1] = nullptr; - -#ifdef NDEBUG - // In OPTIMIZE 4 compilation mode, run Python in optimized mode too. - extern int Py_OptimizeFlag; - Py_OptimizeFlag = 2; -#endif - - // Turn off the automatic load of site.py at startup. - extern int Py_NoSiteFlag; - Py_NoSiteFlag = 1; - Py_NoUserSiteDirectory = 1; - - // Tell Python not to write bytecode files for loaded modules. - Py_DontWriteBytecodeFlag = 1; - - // Prevent Python from complaining about finding the standard modules. - Py_FrozenFlag = 1; - - // This contains the modules we need in order to call Py_Initialize, as well - // as the VFSImporter. - PyImport_FrozenModules = _PyImport_FrozenModules; - - // Initialize Python. It appears to be important to do this before we open - // the pipe streams and spawn the thread, below. -#if PY_MAJOR_VERSION >= 3 - Py_SetProgramName((wchar_t *)_program_name.c_str()); - Py_SetPythonHome((wchar_t *)L""); -#else - Py_SetProgramName((char *)_program_name.c_str()); - Py_SetPythonHome((char *)""); -#endif - Py_Initialize(); - PyEval_InitThreads(); - PySys_SetArgvEx(_py_argc, _py_argv, 0); - - // Open the error output before we do too much more. - if (log_pathname != nullptr && *log_pathname != '\0') { - Filename f = Filename::from_os_specific(log_pathname); - f.set_text(); - if (f.open_write(_error_log)) { - // Set up the indicated error log as the Notify output. - _error_log.setf(std::ios::unitbuf); - Notify::ptr()->set_ostream_ptr(&_error_log, false); - } - } - - // Open the pipe streams with the input and output handles from the parent. - _pipe_read.open_read(input_handle); - _pipe_write.open_write(output_handle); - - if (!_pipe_read) { - nout << "unable to open read pipe\n"; - } - if (!_pipe_write) { - nout << "unable to open write pipe\n"; - } - - spawn_read_thread(); -} - -/** - * - */ -P3DPythonRun:: -~P3DPythonRun() { - terminate_session(); - - // Close the write pipe, so the parent process will terminate us. - _pipe_write.close(); - - // Shut down Python and Panda. - Py_Finalize(); - - join_read_thread(); - DESTROY_LOCK(_commands_lock); - - // Restore the notify stream in case it tries to write to anything else - // after our shutdown. - Notify::ptr()->set_ostream_ptr(&std::cerr, false); -} - -/** - * Runs the embedded Python process. This method does not return until the - * plugin is ready to exit. - * - * Returns the exit status, which will be 0 on success. - */ -int P3DPythonRun:: -run_python() { -#if defined(_WIN32) && defined(USE_DEBUG_PYTHON) - // On Windows, in a debug build, we have to preload sys.dll_suffix = "_d", - // so that the Panda DLL preloader can import the correct filenames. - PyRun_SimpleString("import sys; sys.dll_suffix = '_d'"); -#endif - - Filename dir = _archive_file.get_dirname(); - - // We'll need to synthesize a 'panda3d' module before loading VFSImporter. - // We could simply freeze it, but Python has a bug setting __path__ of - // frozen modules properly. - PyObject *panda3d_module = PyImport_AddModule("panda3d"); - if (panda3d_module == nullptr) { - nout << "Failed to add panda3d module:\n"; - PyErr_Print(); - return 1; - } - - // Set the __path__ such that it can find panda3dcore.pyd, etc. - Filename panda3d_dir(dir, "panda3d"); - string dir_str = panda3d_dir.to_os_specific(); - PyModule_AddObject(panda3d_module, "__path__", Py_BuildValue("[s#]", dir_str.data(), dir_str.length())); - PyModule_AddStringConstant(panda3d_module, "__package__", "panda3d"); - - // Import the VFSImporter module that was frozen in. - PyObject *vfsimporter_module = PyImport_ImportModule("direct.showbase.VFSImporter"); - if (vfsimporter_module == nullptr) { - nout << "Failed to import VFSImporter:\n"; - PyErr_Print(); - return 1; - } - - // Now repair the "direct" and "direct.showbase" trees, which were - // presumably frozen along with the VFSImporter, by setting their __path__ - // such that we can still find the other direct modules. - Filename direct_dir(dir, "direct"); - PyObject *direct_module = PyImport_AddModule("direct"); - if (direct_module != nullptr) { - dir_str = direct_dir.to_os_specific(); - PyModule_AddObject(direct_module, "__path__", Py_BuildValue("[s#]", dir_str.data(), dir_str.length())); - PyModule_AddStringConstant(direct_module, "__package__", "direct"); - } - - PyObject *showbase_module = PyImport_AddModule("direct.showbase"); - if (showbase_module != nullptr) { - Filename showbase_dir(direct_dir, "showbase"); - dir_str = showbase_dir.to_os_specific(); - PyModule_AddObject(showbase_module, "__path__", Py_BuildValue("[s#]", dir_str.data(), dir_str.length())); - PyModule_AddStringConstant(showbase_module, "__package__", "direct.showbase"); - } - - PyObject *stdpy_module = PyImport_AddModule("direct.stdpy"); - if (stdpy_module != nullptr) { - Filename stdpy_dir(direct_dir, "stdpy"); - dir_str = stdpy_dir.to_os_specific(); - PyModule_AddObject(stdpy_module, "__path__", Py_BuildValue("[s#]", dir_str.data(), dir_str.length())); - PyModule_AddStringConstant(stdpy_module, "__package__", "direct.stdpy"); - } - - // And the encodings package as well, since we've presumably picked up a - // bunch of encodings modules as part of the frozen bundle. - Filename encodings_dir(dir, "encodings"); - PyObject *encodings_module = PyImport_AddModule("encodings"); - if (encodings_module != nullptr) { - dir_str = encodings_dir.to_os_specific(); - PyModule_AddObject(encodings_module, "__path__", Py_BuildValue("[s#]", dir_str.data(), dir_str.length())); - PyModule_AddStringConstant(encodings_module, "__package__", "encodings"); - } - - // And register the VFSImporter. - PyObject *result = PyObject_CallMethod(vfsimporter_module, (char *)"register", (char *)""); - if (result == nullptr) { - nout << "Failed to call VFSImporter.register():\n"; - PyErr_Print(); - return 1; - } - Py_DECREF(result); - Py_DECREF(vfsimporter_module); - - // Now, the VFSImporter has been registered, which means we can start - // importing the rest of the Python modules, which are all defined in the - // multifile. First, we need to mount the multifile into the VFS. - PT(Multifile) mf = new Multifile; - if (!mf->open_read(_archive_file)) { - nout << "Could not read " << _archive_file << "\n"; - return 1; - } - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - if (!vfs->mount(mf, dir, VirtualFileSystem::MF_read_only)) { - nout << "Could not mount " << _archive_file << "\n"; - return 1; - } - - // And finally, we can import the startup module. - PyObject *app_runner_module = PyImport_ImportModule("direct.p3d.AppRunner"); - if (app_runner_module == nullptr) { - nout << "Failed to import direct.p3d.AppRunner\n"; - PyErr_Print(); - return 1; - } - - // Get the pointers to the objects needed within the module. - PyObject *app_runner_class = PyObject_GetAttrString(app_runner_module, "AppRunner"); - if (app_runner_class == nullptr) { - nout << "Failed to get AppRunner class\n"; - PyErr_Print(); - return 1; - } - - // Construct an instance of AppRunner. - _runner = PyObject_CallFunction(app_runner_class, (char *)""); - if (_runner == nullptr) { - nout << "Failed to construct AppRunner instance\n"; - PyErr_Print(); - return 1; - } - Py_DECREF(app_runner_class); - - // Import the JavaScript module. - PyObject *javascript_module = PyImport_ImportModule("direct.p3d.JavaScript"); - if (javascript_module == nullptr) { - nout << "Failed to import direct.p3d.JavaScript\n"; - PyErr_Print(); - return false; - } - - // Get the UndefinedObject class. - _undefined_object_class = PyObject_GetAttrString(javascript_module, "UndefinedObject"); - if (_undefined_object_class == nullptr) { - PyErr_Print(); - return 1; - } - - // And the "Undefined" instance. - _undefined = PyObject_GetAttrString(javascript_module, "Undefined"); - if (_undefined == nullptr) { - PyErr_Print(); - return 1; - } - - // Get the ConcreteStruct class. - _concrete_struct_class = PyObject_GetAttrString(javascript_module, "ConcreteStruct"); - if (_concrete_struct_class == nullptr) { - PyErr_Print(); - return 1; - } - - // Get the BrowserObject class. - _browser_object_class = PyObject_GetAttrString(javascript_module, "BrowserObject"); - if (_browser_object_class == nullptr) { - PyErr_Print(); - return 1; - } - - // Get the global TaskManager. - _taskMgr = PyObject_GetAttrString(app_runner_module, "taskMgr"); - if (_taskMgr == nullptr) { - PyErr_Print(); - return 1; - } - - Py_DECREF(app_runner_module); - Py_DECREF(javascript_module); - - // Construct a Python wrapper around our methods we need to expose to - // Python. - static PyMethodDef p3dpython_methods[] = { - { "check_comm", P3DPythonRun::st_check_comm, METH_VARARGS, - "Poll for communications from the parent process" }, - { "request_func", P3DPythonRun::st_request_func, METH_VARARGS, - "Send an asynchronous request to the plugin host" }, - { nullptr, nullptr, 0, nullptr } /* Sentinel */ - }; - -#if PY_MAJOR_VERSION >= 3 - static PyModuleDef p3dpython_module = { - PyModuleDef_HEAD_INIT, - "p3dpython", - nullptr, - -1, - p3dpython_methods, - nullptr, nullptr, nullptr, nullptr - }; - PyObject *p3dpython = PyModule_Create(&p3dpython_module); -#else - PyObject *p3dpython = Py_InitModule("p3dpython", p3dpython_methods); -#endif - if (p3dpython == nullptr) { - PyErr_Print(); - return 1; - } - PyObject *request_func = PyObject_GetAttrString(p3dpython, "request_func"); - if (request_func == nullptr) { - PyErr_Print(); - return 1; - } - - // Now pass that func pointer back to our AppRunner instance, so it can call - // up to us. - result = PyObject_CallMethod(_runner, (char *)"setRequestFunc", (char *)"N", request_func); - if (result == nullptr) { - PyErr_Print(); - return 1; - } - Py_DECREF(result); - - // Now add check_comm() as a task. It can be a threaded task, but this does - // mean that application programmers will have to be alert to asynchronous - // calls coming in from JavaScript. We'll put it on its own task chain so - // the application programmer can decide how it should be. - AsyncTaskManager *task_mgr = AsyncTaskManager::get_global_ptr(); - PT(AsyncTaskChain) chain = task_mgr->make_task_chain("JavaScript"); - - // The default is not threaded (num_threads == 0), but if the app programmer - // decides to enable threads, the default is TP_low priority. - chain->set_thread_priority(TP_low); - - PyObject *check_comm = PyObject_GetAttrString(p3dpython, "check_comm"); - if (check_comm == nullptr) { - PyErr_Print(); - return 1; - } - - // Add it to the task manager. We do this instead of constructing a - // PythonTask because linking p3dpython with core.pyd is problematic. - result = PyObject_CallMethod(_taskMgr, (char *)"add", (char *)"Ns", check_comm, "check_comm"); - if (result == nullptr) { - PyErr_Print(); - return 1; - } - Py_DECREF(result); - - // Finally, get lost in AppRunner.run() (which is really a call to - // taskMgr.run()). - PyObject *done = PyObject_CallMethod(_runner, (char *)"run", (char *)""); - if (done == nullptr) { - int status = 1; - - // An uncaught application exception, and not handled by - // appRunner.exceptionHandler. If it is a SystemExit, extract the exit - // status that we should return. - if (PyErr_ExceptionMatches(PyExc_SystemExit)) { - PyObject *ptype, *ptraceback; - PyObject *value = nullptr; - PyErr_Fetch(&ptype, &value, &ptraceback); - - if (value != nullptr && PyExceptionInstance_Check(value)) { - PyObject *code = PyObject_GetAttrString(value, "code"); - if (code) { - Py_DECREF(value); - value = code; - } - } - - if (value == nullptr || value == Py_None) { - status = 0; -#if PY_MAJOR_VERSION >= 3 - } else if (PyLong_Check(value)) { - status = (int)PyLong_AsLong(value); -#else - } else if (PyInt_Check(value)) { - status = (int)PyInt_AsLong(value); -#endif - } else { - status = 1; - } - } else { - PyErr_Print(); - } - - if (_interactive_console) { - // Give an interactive user a chance to explore the exception. - run_interactive_console(); - return 0; - } - - // We're done. - return status; - } - - // A normal exit from the taskManager. We're presumably done. - Py_DECREF(done); - - if (_interactive_console) { - run_interactive_console(); - } - - return 0; -} - -/** - * Called from low-level Panda (via the P3DWindowHandle object) when a child - * window has been attached or detached from the browser window. This - * triggers the "onwindowattach" and "onwindowdetach" JavaScript - * notifications. - */ -void P3DPythonRun:: -set_window_open(P3DCInstance *inst, bool is_open) { - TiXmlDocument doc; - TiXmlElement *xrequest = new TiXmlElement("request"); - xrequest->SetAttribute("instance_id", inst->get_instance_id()); - xrequest->SetAttribute("rtype", "notify"); - if (is_open) { - xrequest->SetAttribute("message", "onwindowattach"); - } else { - xrequest->SetAttribute("message", "onwindowdetach"); - } - doc.LinkEndChild(xrequest); - - write_xml(_pipe_write, &doc, nout); -} - -/** - * Called from low-level Panda (via the P3DWindowHandle object) when its main - * window requires keyboard focus, but is unable to assign it directly. This - * is particularly necessary under Windows Vista, where a child window of the - * browser is specifically disallowed from being given keyboard focus. - * - * This sends a notify request up to the parent process, to ask the parent to - * manage keyboard events by proxy, and send them back down to Panda, again - * via the P3DWindowHandle. - */ -void P3DPythonRun:: -request_keyboard_focus(P3DCInstance *inst) { - TiXmlDocument doc; - TiXmlElement *xrequest = new TiXmlElement("request"); - xrequest->SetAttribute("instance_id", inst->get_instance_id()); - xrequest->SetAttribute("rtype", "notify"); - xrequest->SetAttribute("message", "keyboardfocus"); - doc.LinkEndChild(xrequest); - - write_xml(_pipe_write, &doc, nout); -} - -/** - * Gives the user a chance to type interactive Python commands, for easy - * development of a p3d application. This method is only called if - * "interactive_console=1" is set as a web token, and "allow_python_dev" is - * set within the application itself. - */ -void P3DPythonRun:: -run_interactive_console() { -#ifdef _WIN32 - // Make sure that control-C support is enabled for the interpreter. - SetConsoleCtrlHandler(nullptr, false); -#endif - - // The "readline" module makes the Python prompt friendlier, with command - // history and everything. Simply importing it is sufficient. - PyObject *readline_module = PyImport_ImportModule("readline"); - if (readline_module == nullptr) { - // But, the module might not exist on certain platforms. If not, no - // sweat. - PyErr_Clear(); - } else { - Py_DECREF(readline_module); - } - - PyRun_InteractiveLoop(stdin, ""); -} - -/** - * Handles a command received from the plugin host, via an XML syntax on the - * wire. Ownership of the XML document object is passed into this method. - * - * It's important *not* to be holding _commands_lock when calling this method. - */ -void P3DPythonRun:: -handle_command(TiXmlDocument *doc) { - TiXmlElement *xcommand = doc->FirstChildElement("command"); - if (xcommand != nullptr) { - bool needs_response = false; - int want_response_id; - if (xcommand->QueryIntAttribute("want_response_id", &want_response_id) == TIXML_SUCCESS) { - // This command will be waiting for a response. - needs_response = true; - } - - const char *cmd = xcommand->Attribute("cmd"); - if (cmd != nullptr) { - if (strcmp(cmd, "init") == 0) { - assert(!needs_response); - - // The only purpose of the "init" command is to send us a unique - // session ID, which in fact we don't do much with. - xcommand->Attribute("session_id", &_session_id); - - // We do use it to initiate our object id sequence with a number at - // least a little bit distinct from other sessions, though. No - // technical requirement that we do this, but it does make debugging - // the logs a bit easier. - _next_sent_id = _session_id * 1000; - - PyObject *obj = PyObject_CallMethod(_runner, (char*)"setSessionId", (char *)"i", _session_id); - Py_XDECREF(obj); - - } else if (strcmp(cmd, "start_instance") == 0) { - assert(!needs_response); - TiXmlElement *xinstance = xcommand->FirstChildElement("instance"); - if (xinstance != nullptr) { - P3DCInstance *inst = new P3DCInstance(xinstance); - start_instance(inst, xinstance); - } - - } else if (strcmp(cmd, "terminate_instance") == 0) { - assert(!needs_response); - int instance_id; - if (xcommand->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS) { - terminate_instance(instance_id); - } - - } else if (strcmp(cmd, "setup_window") == 0) { - assert(!needs_response); - int instance_id; - TiXmlElement *xwparams = xcommand->FirstChildElement("wparams"); - if (xwparams != nullptr && - xcommand->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS) { - setup_window(instance_id, xwparams); - } - - } else if (strcmp(cmd, "windows_message") == 0) { - assert(!needs_response); - // This is a special message that we use to proxy keyboard events from - // the parent process down into Panda, a necessary hack on Vista. - int instance_id = 0, msg = 0, wparam = 0, lparam = 0; - xcommand->Attribute("instance_id", &instance_id); - xcommand->Attribute("msg", &msg); - xcommand->Attribute("wparam", &wparam); - xcommand->Attribute("lparam", &lparam); - send_windows_message(instance_id, msg, wparam, lparam); - - } else if (strcmp(cmd, "exit") == 0) { - assert(!needs_response); - terminate_session(); - - } else if (strcmp(cmd, "pyobj") == 0) { - // Manipulate or query a python object. - handle_pyobj_command(xcommand, needs_response, want_response_id); - - } else if (strcmp(cmd, "script_response") == 0) { - // Response from a script request. In this case, we just store it - // away instead of processing it immediately. - - MutexHolder holder(_responses_lock); - _responses.push_back(doc); - - // And now we must return out, instead of deleting the document at the - // bottom of this method. - return; - - } else if (strcmp(cmd, "drop_pyobj") == 0) { - int object_id; - if (xcommand->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) { - SentObjects::iterator si = _sent_objects.find(object_id); - if (si != _sent_objects.end()) { - PyObject *obj = (*si).second; - Py_DECREF(obj); - _sent_objects.erase(si); - } - } - - } else { - nout << "Unhandled command " << cmd << "\n"; - if (needs_response) { - // Better send a response. - TiXmlDocument doc; - TiXmlElement *xresponse = new TiXmlElement("response"); - xresponse->SetAttribute("response_id", want_response_id); - doc.LinkEndChild(xresponse); - write_xml(_pipe_write, &doc, nout); - } - } - } - } - - delete doc; -} - -/** - * Handles the pyobj command, which queries or modifies a Python object from - * the browser scripts. - */ -void P3DPythonRun:: -handle_pyobj_command(TiXmlElement *xcommand, bool needs_response, - int want_response_id) { - TiXmlDocument doc; - TiXmlElement *xresponse = new TiXmlElement("response"); - xresponse->SetAttribute("response_id", want_response_id); - doc.LinkEndChild(xresponse); - - const char *op = xcommand->Attribute("op"); - if (op != nullptr && !PyErr_Occurred()) { - if (strcmp(op, "get_panda_script_object") == 0) { - // Get Panda's toplevel Python object. - PyObject *obj = PyObject_CallMethod(_runner, (char*)"getPandaScriptObject", (char *)""); - if (obj != nullptr) { - xresponse->LinkEndChild(pyobj_to_xml(obj)); - Py_DECREF(obj); - } - - } else if (strcmp(op, "set_browser_script_object") == 0) { - // Set the Browser's toplevel window object. - PyObject *obj; - TiXmlElement *xvalue = xcommand->FirstChildElement("value"); - if (xvalue != nullptr) { - obj = xml_to_pyobj(xvalue); - } else { - obj = Py_None; - Py_INCREF(obj); - } - - Py_XDECREF(PyObject_CallMethod - (_runner, (char *)"setBrowserScriptObject", (char *)"N", obj)); - - } else if (strcmp(op, "call") == 0) { - // Call the named method on the indicated object, or the object itself - // if method_name isn't given. - TiXmlElement *xobject = xcommand->FirstChildElement("object"); - if (xobject != nullptr) { - PyObject *obj = xml_to_pyobj(xobject); - - const char *method_name = xcommand->Attribute("method_name"); - - // Build up a list of params. - PyObject *list = PyList_New(0); - - TiXmlElement *xchild = xcommand->FirstChildElement("value"); - while (xchild != nullptr) { - PyObject *child = xml_to_pyobj(xchild); - PyList_Append(list, child); - Py_DECREF(child); - xchild = xchild->NextSiblingElement("value"); - } - - // Convert the list to a tuple for the call. - PyObject *params = PyList_AsTuple(list); - Py_DECREF(list); - - // Now call the method. - PyObject *result = nullptr; - if (method_name == nullptr) { - // No method name; call the object directly. - result = PyObject_CallObject(obj, params); - - // Several special-case "method" names. - } else if (strcmp(method_name, "__bool__") == 0) { - result = PyBool_FromLong(PyObject_IsTrue(obj)); - - } else if (strcmp(method_name, "__int__") == 0) { -#if PY_MAJOR_VERSION >= 3 - result = PyNumber_Long(obj); -#else - result = PyNumber_Int(obj); -#endif - } else if (strcmp(method_name, "__float__") == 0) { - result = PyNumber_Float(obj); - - } else if (strcmp(method_name, "__repr__") == 0) { - result = PyObject_Repr(obj); - - } else if (strcmp(method_name, "__str__") == 0 || - strcmp(method_name, "toString") == 0) { - result = PyObject_Str(obj); - - } else if (strcmp(method_name, "__set_property__") == 0) { - // We call these methods __set_property__ et al instead of - // __setattr__ et al, because they do not precisely duplicate the - // Python semantics. - char *property_name; - PyObject *value; - if (PyArg_ParseTuple(params, "sO", &property_name, &value)) { - bool success = false; - - // If it already exists as an attribute, update it there. - if (PyObject_HasAttrString(obj, property_name)) { - if (PyObject_SetAttrString(obj, property_name, value) != -1) { - success = true; - } else { - PyErr_Clear(); - } - } - - // If the object supports the mapping protocol, store it in the - // object's dictionary. - if (!success && PyMapping_Check(obj)) { - if (PyMapping_SetItemString(obj, property_name, value) != -1) { - success = true; - } else { - PyErr_Clear(); - } - } - - // Finally, try to store it on the object. - if (!success) { - if (PyObject_SetAttrString(obj, property_name, value) != -1) { - success = true; - } else { - PyErr_Clear(); - } - } - - if (success) { - result = Py_True; - } else { - result = Py_False; - } - Py_INCREF(result); - } - - } else if (strcmp(method_name, "__del_property__") == 0) { - char *property_name; - if (PyArg_ParseTuple(params, "s", &property_name)) { - bool success = false; - - if (PyObject_HasAttrString(obj, property_name)) { - if (PyObject_DelAttrString(obj, property_name) != -1) { - success = true; - } else { - PyErr_Clear(); - } - } - - if (!success) { - if (PyObject_DelItemString(obj, property_name) != -1) { - success = true; - } else { - PyErr_Clear(); - } - } - - if (success) { - result = Py_True; - } else { - result = Py_False; - } - Py_INCREF(result); - } - - } else if (strcmp(method_name, "__get_property__") == 0) { - char *property_name; - if (PyArg_ParseTuple(params, "s", &property_name)) { - bool success = false; - - if (PyObject_HasAttrString(obj, property_name)) { - result = PyObject_GetAttrString(obj, property_name); - if (result != nullptr) { - success = true; - } else { - PyErr_Clear(); - } - } - - if (!success) { - if (PyMapping_HasKeyString(obj, property_name)) { - result = PyMapping_GetItemString(obj, property_name); - if (result != nullptr) { - success = true; - } else { - PyErr_Clear(); - } - } - } - - if (!success) { - result = nullptr; - } - } - - } else if (strcmp(method_name, "__has_method__") == 0) { - char *property_name; - result = Py_False; - if (PyArg_ParseTuple(params, "s", &property_name)) { - if (*property_name) { - // Check for a callable method - if (PyObject_HasAttrString(obj, property_name)) { - PyObject *prop = PyObject_GetAttrString(obj, property_name); - if (PyCallable_Check(prop)) { - result = Py_True; - } - Py_DECREF(prop); - } - } else { - // Check for the default method - if (PyCallable_Check(obj)) { - result = Py_True; - } - } - } - Py_INCREF(result); - - } else { - // Not a special-case name. Call the named method. - PyObject *method = PyObject_GetAttrString(obj, (char *)method_name); - if (method != nullptr) { - result = PyObject_CallObject(method, params); - Py_DECREF(method); - } - } - Py_DECREF(params); - - // Feed the return value back through the XML pipe to the caller. - if (result != nullptr) { - xresponse->LinkEndChild(pyobj_to_xml(result)); - Py_DECREF(result); - } else { - // Print the Python error message if there was one. - PyErr_Print(); - } - - Py_DECREF(obj); - } - } - } - - if (needs_response) { - write_xml(_pipe_write, &doc, nout); - } -} - -/** - * This method is added to the task manager (via st_check_comm, below) so that - * it gets a call every frame. Its job is to check for commands received from - * the plugin host in the parent process. - */ -void P3DPythonRun:: -check_comm() { - // nout << ":"; - ACQUIRE_LOCK(_commands_lock); - while (!_commands.empty()) { - TiXmlDocument *doc = _commands.front(); - _commands.pop_front(); - RELEASE_LOCK(_commands_lock); - handle_command(doc); - if (_session_terminated) { - return; - } - ACQUIRE_LOCK(_commands_lock); - } - RELEASE_LOCK(_commands_lock); - - if (!_program_continue) { - // The low-level thread detected an error, for instance pipe closed. We - // should exit gracefully. - terminate_session(); - return; - } - - // Sleep to yield the timeslice, but only if we're not running in the main - // thread. - if (Thread::get_current_thread() != Thread::get_main_thread()) { - Thread::sleep(0.001); - } -} - -/** - * This is a static Python wrapper around py_check_comm, needed to add the - * function to a PythonTask. - */ -PyObject *P3DPythonRun:: -st_check_comm(PyObject *, PyObject *args) { - P3DPythonRun::_global_ptr->check_comm(); - return Py_BuildValue("i", AsyncTask::DS_cont); -} - -/** - * This method is similar to check_comm(), above, but instead of handling all - * events, it waits for a specific script_response ID to come back from the - * browser, and leaves all other events in the queue. - */ -TiXmlDocument *P3DPythonRun:: -wait_script_response(int response_id) { - // nout << "waiting script_response " << response_id << "\n"; - while (true) { - Commands::iterator ci; - - // First, walk through the _commands queue to see if there's anything that - // needs immediate processing. - ACQUIRE_LOCK(_commands_lock); - for (ci = _commands.begin(); ci != _commands.end(); ++ci) { - TiXmlDocument *doc = (*ci); - - TiXmlElement *xcommand = doc->FirstChildElement("command"); - if (xcommand != nullptr) { - const char *cmd = xcommand->Attribute("cmd"); - if ((cmd != nullptr && strcmp(cmd, "script_response") == 0) || - xcommand->Attribute("want_response_id") != nullptr) { - - // This is either a response, or it's a command that will want a - // response itself. In either case we should handle it right away. - // ("handling" a response means moving it to the _responses queue.) - _commands.erase(ci); - RELEASE_LOCK(_commands_lock); - handle_command(doc); - if (_session_terminated) { - return nullptr; - } - ACQUIRE_LOCK(_commands_lock); - break; - } - } - } - RELEASE_LOCK(_commands_lock); - - // Now, walk through the _responses queue to look for the particular - // response we're waiting for. - _responses_lock.acquire(); - for (ci = _responses.begin(); ci != _responses.end(); ++ci) { - TiXmlDocument *doc = (*ci); - - TiXmlElement *xcommand = doc->FirstChildElement("command"); - assert(xcommand != nullptr); - const char *cmd = xcommand->Attribute("cmd"); - assert(cmd != nullptr && strcmp(cmd, "script_response") == 0); - - int unique_id; - if (xcommand->QueryIntAttribute("unique_id", &unique_id) == TIXML_SUCCESS) { - if (unique_id == response_id) { - // This is the response we were waiting for. - _responses.erase(ci); - _responses_lock.release(); - // nout << "got script_response " << unique_id << "\n"; - return doc; - } - } - } - _responses_lock.release(); - - if (!_program_continue) { - terminate_session(); - return nullptr; - } - -#ifdef _WIN32 - // Make sure we process the Windows event loop while we're waiting, or - // everything that depends on Windows messages will starve. - - // We appear to be best off with just a single PeekMessage() call here; - // the full message pump seems to cause problems. - MSG msg; - PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE | PM_NOYIELD); -#endif // _WIN32 - - // nout << "."; - - // It hasn't shown up yet. Give the sub-thread a chance to process the - // input and append it to the queue. - Thread::force_yield(); - } - assert(false); -} - -/** - * This method is a special Python function that is added as a callback to the - * AppRunner class, to allow Python to upcall into this object. - */ -PyObject *P3DPythonRun:: -py_request_func(PyObject *args) { - int instance_id; - const char *request_type; - PyObject *extra_args; - if (!PyArg_ParseTuple(args, "isO", &instance_id, &request_type, &extra_args)) { - return nullptr; - } - - if (strcmp(request_type, "wait_script_response") == 0) { - // This is a special case. Instead of generating a new request, this - // means to wait for a particular script_response to come in on the wire. - int response_id; - if (!PyArg_ParseTuple(extra_args, "i", &response_id)) { - return nullptr; - } - - TiXmlDocument *doc = wait_script_response(response_id); - if (_session_terminated) { - return Py_BuildValue(""); - } - assert(doc != nullptr); - TiXmlElement *xcommand = doc->FirstChildElement("command"); - assert(xcommand != nullptr); - TiXmlElement *xvalue = xcommand->FirstChildElement("value"); - - PyObject *value = nullptr; - if (xvalue != nullptr) { - value = xml_to_pyobj(xvalue); - } else { - // An absence of a element is an exception. We will return NULL - // from this function, but first set the error condition. - PyErr_SetString(PyExc_EnvironmentError, "Error on script call"); - } - - delete doc; - return value; - } - - TiXmlDocument doc; - TiXmlElement *xrequest = new TiXmlElement("request"); - xrequest->SetAttribute("instance_id", instance_id); - xrequest->SetAttribute("rtype", request_type); - doc.LinkEndChild(xrequest); - - if (strcmp(request_type, "notify") == 0) { - // A general notification to be sent directly to the instance. - const char *message; - if (!PyArg_ParseTuple(extra_args, "s", &message)) { - return nullptr; - } - - xrequest->SetAttribute("message", message); - write_xml(_pipe_write, &doc, nout); - - } else if (strcmp(request_type, "script") == 0) { - // Meddling with a scripting variable on the browser side. - const char *operation; - PyObject *object; - const char *property_name; - PyObject *value; - int needs_response; - int unique_id; - if (!PyArg_ParseTuple(extra_args, "sOsOii", - &operation, &object, &property_name, &value, - &needs_response, &unique_id)) { - return nullptr; - } - - xrequest->SetAttribute("operation", operation); - xrequest->SetAttribute("property_name", property_name); - xrequest->SetAttribute("needs_response", (int)(needs_response != 0)); - xrequest->SetAttribute("unique_id", unique_id); - TiXmlElement *xobject = pyobj_to_xml(object); - xobject->SetValue("object"); - xrequest->LinkEndChild(xobject); - TiXmlElement *xvalue = pyobj_to_xml(value); - xrequest->LinkEndChild(xvalue); - - write_xml(_pipe_write, &doc, nout); - - } else if (strcmp(request_type, "drop_p3dobj") == 0) { - // Release a particular P3D_object that we were holding a reference to. - int object_id; - if (!PyArg_ParseTuple(extra_args, "i", &object_id)) { - return nullptr; - } - - xrequest->SetAttribute("object_id", object_id); - write_xml(_pipe_write, &doc, nout); - - } else if (strcmp(request_type, "forget_package") == 0) { - // A request to the instance to drop a particular package (or host) from - // the cache. - const char *host_url; - const char *package_name; - const char *package_version; - if (!PyArg_ParseTuple(extra_args, "sss", &host_url, &package_name, &package_version)) { - return nullptr; - } - - xrequest->SetAttribute("host_url", host_url); - if (*package_name) { - xrequest->SetAttribute("package_name", package_name); - if (*package_version) { - xrequest->SetAttribute("package_version", package_version); - } - } - write_xml(_pipe_write, &doc, nout); - - } else { - string message = string("Unsupported request type: ") + string(request_type); - PyErr_SetString(PyExc_ValueError, message.c_str()); - return nullptr; - } - - return Py_BuildValue(""); -} - -/** - * This is the static wrapper around py_request_func. - */ -PyObject *P3DPythonRun:: -st_request_func(PyObject *, PyObject *args) { - return P3DPythonRun::_global_ptr->py_request_func(args); -} - -/** - * Starts the read thread. This thread is responsible for reading the - * standard input socket for XML commands and storing them in the _commands - * queue. - */ -void P3DPythonRun:: -spawn_read_thread() { - assert(!_read_thread_continue); - - // We have to use direct OS calls to create the thread instead of Panda - // constructs, because it has to be an actual thread, not necessarily a - // Panda thread (we can't use Panda's simple threads implementation, because - // we can't get overlapped IO on an anonymous pipe in Windows). - - _read_thread_continue = true; - SPAWN_THREAD(_read_thread, rt_thread_run, this); -} - -/** - * Waits for the read thread to stop. - */ -void P3DPythonRun:: -join_read_thread() { - _read_thread_continue = false; - JOIN_THREAD(_read_thread); -} - -/** - * Starts the indicated instance running within the Python process. - */ -void P3DPythonRun:: -start_instance(P3DCInstance *inst, TiXmlElement *xinstance) { - _instances[inst->get_instance_id()] = inst; - - set_instance_info(inst, xinstance); - - TiXmlElement *xpackage = xinstance->FirstChildElement("package"); - while (xpackage != nullptr) { - add_package_info(inst, xpackage); - xpackage = xpackage->NextSiblingElement("package"); - } - - TiXmlElement *xfparams = xinstance->FirstChildElement("fparams"); - if (xfparams != nullptr) { - set_p3d_filename(inst, xfparams); - } - - TiXmlElement *xwparams = xinstance->FirstChildElement("wparams"); - if (xwparams != nullptr) { - setup_window(inst, xwparams); - } -} - -/** - * Stops the instance with the indicated id. - */ -void P3DPythonRun:: -terminate_instance(int id) { - Instances::iterator ii = _instances.find(id); - if (ii == _instances.end()) { - nout << "Can't stop instance " << id << ": not started.\n"; - return; - } - - P3DCInstance *inst = (*ii).second; - _instances.erase(ii); - delete inst; - - // TODO: we don't currently have any way to stop just one instance of a - // multi-instance session. This will require a different Python interface - // than ShowBase. - terminate_session(); -} - -/** - * Sets some global information about the instance. - */ -void P3DPythonRun:: -set_instance_info(P3DCInstance *inst, TiXmlElement *xinstance) { - const char *root_dir = xinstance->Attribute("root_dir"); - if (root_dir == nullptr) { - root_dir = ""; - } - - const char *log_directory = xinstance->Attribute("log_directory"); - if (log_directory == nullptr) { - log_directory = ""; - } - - int verify_contents = 0; - xinstance->Attribute("verify_contents", &verify_contents); - - const char *super_mirror = xinstance->Attribute("super_mirror"); - if (super_mirror == nullptr) { - super_mirror = ""; - } - - // Get the initial "main" object, if specified. - PyObject *main; - TiXmlElement *xmain = xinstance->FirstChildElement("main"); - if (xmain != nullptr) { - main = xml_to_pyobj(xmain); - } else { - main = Py_None; - Py_INCREF(main); - } - - int respect_per_platform = 0; - xinstance->Attribute("respect_per_platform", &respect_per_platform); - - PyObject *result = PyObject_CallMethod - (_runner, (char *)"setInstanceInfo", (char *)"sssiNi", root_dir, - log_directory, super_mirror, verify_contents, main, respect_per_platform); - - if (result == nullptr) { - PyErr_Print(); - if (_interactive_console) { - run_interactive_console(); - } - exit(1); - } - Py_XDECREF(result); -} - -/** - * Adds some information about a pre-loaded package. - */ -void P3DPythonRun:: -add_package_info(P3DCInstance *inst, TiXmlElement *xpackage) { - const char *name = xpackage->Attribute("name"); - const char *platform = xpackage->Attribute("platform"); - const char *version = xpackage->Attribute("version"); - const char *host = xpackage->Attribute("host"); - const char *host_dir = xpackage->Attribute("host_dir"); - if (name == nullptr || host == nullptr) { - return; - } - if (version == nullptr) { - version = ""; - } - if (platform == nullptr) { - platform = ""; - } - if (host_dir == nullptr) { - host_dir = ""; - } - - PyObject *result = PyObject_CallMethod - (_runner, (char *)"addPackageInfo", (char *)"sssss", - name, platform, version, host, host_dir); - - if (result == nullptr) { - PyErr_Print(); - if (_interactive_console) { - run_interactive_console(); - } - exit(1); - } - - Py_XDECREF(result); -} - -/** - * Sets the startup filename and tokens for the indicated instance. - */ -void P3DPythonRun:: -set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams) { - string p3d_filename; - const char *p3d_filename_c = xfparams->Attribute("p3d_filename"); - if (p3d_filename_c != nullptr) { - p3d_filename = p3d_filename_c; - } - - int p3d_offset = 0; - xfparams->Attribute("p3d_offset", &p3d_offset); - - string p3d_url; - const char *p3d_url_c = xfparams->Attribute("p3d_url"); - if (p3d_url_c != nullptr) { - p3d_url = p3d_url_c; - } - - PyObject *token_list = PyList_New(0); - TiXmlElement *xtoken = xfparams->FirstChildElement("token"); - while (xtoken != nullptr) { - string keyword, value; - const char *keyword_c = xtoken->Attribute("keyword"); - if (keyword_c != nullptr) { - keyword = keyword_c; - } - - const char *value_c = xtoken->Attribute("value"); - if (value_c != nullptr) { - value = value_c; - } - - PyObject *tuple = Py_BuildValue("(ss)", keyword.c_str(), - value.c_str()); - PyList_Append(token_list, tuple); - Py_DECREF(tuple); - - xtoken = xtoken->NextSiblingElement("token"); - } - - PyObject *arg_list = PyList_New(0); - TiXmlElement *xarg = xfparams->FirstChildElement("arg"); - while (xarg != nullptr) { - string value; - const char *value_c = xarg->Attribute("value"); - if (value_c != nullptr) { - value = value_c; - } - - PyObject *str = Py_BuildValue("s", value.c_str()); - PyList_Append(arg_list, str); - Py_DECREF(str); - - xarg = xarg->NextSiblingElement("arg"); - } - - PyObject *result = PyObject_CallMethod - (_runner, (char *)"setP3DFilename", (char *)"sNNiiis", p3d_filename.c_str(), - token_list, arg_list, inst->get_instance_id(), _interactive_console, p3d_offset, - p3d_url.c_str()); - - if (result == nullptr) { - PyErr_Print(); - if (_interactive_console) { - run_interactive_console(); - } - exit(1); - } - Py_XDECREF(result); -} - -/** - * Sets the window parameters for the indicated instance. - */ -void P3DPythonRun:: -setup_window(int id, TiXmlElement *xwparams) { - Instances::iterator ii = _instances.find(id); - if (ii == _instances.end()) { - nout << "Can't setup window for " << id << ": not started.\n"; - return; - } - - P3DCInstance *inst = (*ii).second; - setup_window(inst, xwparams); -} - -/** - * Sets the window parameters for the indicated instance. - */ -void P3DPythonRun:: -setup_window(P3DCInstance *inst, TiXmlElement *xwparams) { - string window_type; - const char *window_type_c = xwparams->Attribute("window_type"); - if (window_type_c != nullptr) { - window_type = window_type_c; - } - - int win_x, win_y, win_width, win_height; - - xwparams->Attribute("win_x", &win_x); - xwparams->Attribute("win_y", &win_y); - xwparams->Attribute("win_width", &win_width); - xwparams->Attribute("win_height", &win_height); - - PT(WindowHandle) parent_window_handle; - -#ifdef _WIN32 - int hwnd; - if (xwparams->Attribute("parent_hwnd", &hwnd)) { - parent_window_handle = NativeWindowHandle::make_win((HWND)hwnd); - } - -#elif __APPLE__ - // On Mac, we don't parent windows directly to the browser; instead, we have - // to go through this subprocess-window nonsense. - - const char *subprocess_window = xwparams->Attribute("subprocess_window"); - if (subprocess_window != nullptr) { - Filename filename = Filename::from_os_specific(subprocess_window); - parent_window_handle = NativeWindowHandle::make_subprocess(filename); - } - -#elif defined(HAVE_X11) - // Use stringstream to decode the "long" attribute. - const char *parent_cstr = xwparams->Attribute("parent_xwindow"); - if (parent_cstr != nullptr) { - long window; - std::istringstream strm(parent_cstr); - strm >> window; - parent_window_handle = NativeWindowHandle::make_x11((X11_Window)window); - } -#endif - - PyObject *py_handle = Py_None; - if (parent_window_handle != nullptr) { - - // We have a valid parent WindowHandle, but replace it with a - // P3DWindowHandle so we can get the callbacks. - parent_window_handle = new P3DWindowHandle(this, inst, *parent_window_handle); - inst->_parent_window_handle = parent_window_handle; - - // Also pass this P3DWindowHandle object down into Panda, via the - // setupWindow() call. For this, we need to create a Python wrapper - // objcet. - parent_window_handle->ref(); - py_handle = DTool_CreatePyInstanceTyped(parent_window_handle.p(), true); - } - Py_INCREF(py_handle); - - // TODO: direct this into the particular instance. This will require a - // specialized ShowBase replacement. - PyObject *result = PyObject_CallMethod - (_runner, (char *)"setupWindow", (char *)"siiiiN", window_type.c_str(), - win_x, win_y, win_width, win_height, py_handle); - - if (result == nullptr) { - PyErr_Print(); - if (_interactive_console) { - run_interactive_console(); - } - exit(1); - } - Py_XDECREF(result); -} - -/** - * This is used to deliver a windows keyboard message to the Panda process - * from the parent process, a necessary hack on Vista. - */ -void P3DPythonRun:: -send_windows_message(int id, unsigned int msg, int wparam, int lparam) { - Instances::iterator ii = _instances.find(id); - if (ii == _instances.end()) { - return; - } - - P3DCInstance *inst = (*ii).second; - if (inst->_parent_window_handle != nullptr) { - inst->_parent_window_handle->send_windows_message(msg, wparam, lparam); - } -} - -/** - * Stops all currently-running instances. - */ -void P3DPythonRun:: -terminate_session() { - Instances::iterator ii; - for (ii = _instances.begin(); ii != _instances.end(); ++ii) { - P3DCInstance *inst = (*ii).second; - delete inst; - } - _instances.clear(); - - if (!_session_terminated) { - if (_taskMgr != nullptr) { - PyObject *result = PyObject_CallMethod(_taskMgr, (char *)"stop", (char *)""); - if (result == nullptr) { - PyErr_Print(); - } else { - Py_DECREF(result); - } - } - - _session_terminated = true; - } -} - -/** - * Converts the indicated PyObject to the appropriate XML representation of a - * P3D_value type, and returns a freshly-allocated TiXmlElement. - */ -TiXmlElement *P3DPythonRun:: -pyobj_to_xml(PyObject *value) { - TiXmlElement *xvalue = new TiXmlElement("value"); - if (value == Py_None) { - // None. - xvalue->SetAttribute("type", "none"); - - } else if (PyBool_Check(value)) { - // A bool value. - xvalue->SetAttribute("type", "bool"); - xvalue->SetAttribute("value", PyObject_IsTrue(value)); - -#if PY_MAJOR_VERSION < 3 - } else if (PyInt_Check(value)) { - // A plain integer value. - xvalue->SetAttribute("type", "int"); - xvalue->SetAttribute("value", PyInt_AsLong(value)); -#endif - - } else if (PyLong_Check(value)) { - // A long integer value. This gets converted either as an integer or as a - // floating-point type, whichever fits. - long lvalue = PyLong_AsLong(value); - if (PyErr_Occurred()) { - // It won't fit as an integer; make it a double. - PyErr_Clear(); - xvalue->SetAttribute("type", "float"); - xvalue->SetDoubleAttribute("value", PyLong_AsDouble(value)); - } else { - // It fits as an integer. - xvalue->SetAttribute("type", "int"); - xvalue->SetAttribute("value", lvalue); - } - - } else if (PyFloat_Check(value)) { - // A floating-point value. - xvalue->SetAttribute("type", "float"); - xvalue->SetDoubleAttribute("value", PyFloat_AsDouble(value)); - - - } else if (PyUnicode_Check(value)) { - // A unicode value. Convert to utf-8 for the XML encoding. - xvalue->SetAttribute("type", "string"); - -#if PY_MAJOR_VERSION >= 3 - // In Python 3, there are only unicode strings, and there is a handy - // function for getting the UTF-8 encoded version. - Py_ssize_t length = 0; - char *buffer = PyUnicode_AsUTF8AndSize(value, &length); - if (buffer != nullptr) { - string str(buffer, length); - xvalue->SetAttribute("value", str); - } -#else - PyObject *as_str = PyUnicode_AsUTF8String(value); - if (as_str != nullptr) { - char *buffer; - Py_ssize_t length; - if (PyString_AsStringAndSize(as_str, &buffer, &length) != -1) { - string str(buffer, length); - xvalue->SetAttribute("value", str); - } - Py_DECREF(as_str); - } - - } else if (PyString_Check(value)) { - // A string value. Insist that it is utf-8 encoded, by decoding it first - // using the standard encoding, then re-encoding it. - xvalue->SetAttribute("type", "string"); - - PyObject *ustr = PyUnicode_FromEncodedObject(value, nullptr, nullptr); - if (ustr == nullptr) { - PyErr_Print(); - return xvalue; - } else { - PyObject *as_str = PyUnicode_AsUTF8String(ustr); - if (as_str != nullptr) { - char *buffer; - Py_ssize_t length; - if (PyString_AsStringAndSize(as_str, &buffer, &length) != -1) { - string str(buffer, length); - xvalue->SetAttribute("value", str); - } - Py_DECREF(as_str); - } - Py_DECREF(ustr); - } -#endif // PY_MAJOR_VERSION - - } else if (PyTuple_Check(value)) { - // An immutable sequence. Pass it as a concrete. - xvalue->SetAttribute("type", "concrete_sequence"); - - Py_ssize_t length = PySequence_Length(value); - for (Py_ssize_t i = 0; i < length; ++i) { - PyObject *item = PySequence_GetItem(value, i); - if (item != nullptr) { - xvalue->LinkEndChild(pyobj_to_xml(item)); - Py_DECREF(item); - } - } - - } else if (PyObject_IsInstance(value, _concrete_struct_class)) { - // This is a ConcreteStruct. - xvalue->SetAttribute("type", "concrete_struct"); - - PyObject *items = PyObject_CallMethod(value, (char *)"getConcreteProperties", (char *)""); - if (items == nullptr) { - PyErr_Print(); - return xvalue; - } - - Py_ssize_t length = PySequence_Length(items); - for (Py_ssize_t i = 0; i < length; ++i) { - PyObject *item = PySequence_GetItem(items, i); - if (item != nullptr) { - PyObject *a = PySequence_GetItem(item, 0); - if (a != nullptr) { - PyObject *b = PySequence_GetItem(item, 1); - if (b != nullptr) { - TiXmlElement *xitem = pyobj_to_xml(b); - Py_DECREF(b); - - PyObject *as_str; - if (PyUnicode_Check(a)) { - // The key is a unicode value. -#if PY_MAJOR_VERSION >= 3 - as_str = a; -#else - as_str = PyUnicode_AsUTF8String(a); -#endif - } else { - // The key is a string value or something else. Make it a - // string. - as_str = PyObject_Str(a); - } - char *buffer; - Py_ssize_t length; -#if PY_MAJOR_VERSION >= 3 - buffer = PyUnicode_AsUTF8AndSize(as_str, &length); - if (buffer != nullptr) { -#else - if (PyString_AsStringAndSize(as_str, &buffer, &length) != -1) { -#endif - string str(buffer, length); - xitem->SetAttribute("key", str); - } - Py_DECREF(as_str); - - xvalue->LinkEndChild(xitem); - } - Py_DECREF(a); - } - Py_DECREF(item); - } - } - - // We've already avoided errors in the above code; clear the error flag. - PyErr_Clear(); - - Py_DECREF(items); - - } else if (PyObject_IsInstance(value, _undefined_object_class)) { - // This is an UndefinedObject. - xvalue->SetAttribute("type", "undefined"); - - } else if (PyObject_IsInstance(value, _browser_object_class)) { - // This is a BrowserObject, a reference to an object that actually exists - // in the host namespace. So, pass up the appropriate object ID. - PyObject *objectId = PyObject_GetAttrString(value, (char *)"_BrowserObject__objectId"); - if (objectId != nullptr) { - int object_id = PyInt_AsLong(objectId); - xvalue->SetAttribute("type", "browser"); - xvalue->SetAttribute("object_id", object_id); - Py_DECREF(objectId); - } - - } else { - // Some other kind of object. Make it a generic Python object. This is - // more expensive for the caller to deal with--it requires a back-and- - // forth across the XML pipe--but it's much more general. - - int object_id = _next_sent_id; - ++_next_sent_id; - bool inserted = _sent_objects.insert(SentObjects::value_type(object_id, value)).second; - while (!inserted) { - // Hmm, we must have cycled around the entire int space? Either that, - // or there's a logic bug somewhere. Assume the former, and keep - // looking for an empty slot. - object_id = _next_sent_id; - ++_next_sent_id; - inserted = _sent_objects.insert(SentObjects::value_type(object_id, value)).second; - } - - // Now that it's stored in the map, increment its reference count. - Py_INCREF(value); - - xvalue->SetAttribute("type", "python"); - xvalue->SetAttribute("object_id", object_id); - } - - return xvalue; -} - -/** - * Converts the XML representation of a P3D_value type into the equivalent - * Python object and returns it. - */ -PyObject *P3DPythonRun:: -xml_to_pyobj(TiXmlElement *xvalue) { - const char *type = xvalue->Attribute("type"); - if (strcmp(type, "none") == 0) { - return Py_BuildValue(""); - - } else if (strcmp(type, "bool") == 0) { - int value; - if (xvalue->QueryIntAttribute("value", &value) == TIXML_SUCCESS) { - return PyBool_FromLong(value); - } - - } else if (strcmp(type, "int") == 0) { - int value; - if (xvalue->QueryIntAttribute("value", &value) == TIXML_SUCCESS) { -#if PY_MAJOR_VERSION >= 3 - return PyLong_FromLong(value); -#else - return PyInt_FromLong(value); -#endif - } - - } else if (strcmp(type, "float") == 0) { - double value; - if (xvalue->QueryDoubleAttribute("value", &value) == TIXML_SUCCESS) { - return PyFloat_FromDouble(value); - } - - } else if (strcmp(type, "string") == 0) { - // Using the string form here instead of the char * form, so we don't get - // tripped up on embedded null characters. - const string *value = xvalue->Attribute(string("value")); - if (value != nullptr) { - return PyUnicode_DecodeUTF8(value->data(), value->length(), nullptr); - } - - } else if (strcmp(type, "undefined") == 0) { - Py_INCREF(_undefined); - return _undefined; - - } else if (strcmp(type, "browser") == 0) { - int object_id; - if (xvalue->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) { - // Construct a new BrowserObject wrapper around this object. - return PyObject_CallFunction(_browser_object_class, (char *)"Oi", - _runner, object_id); - } - - } else if (strcmp(type, "concrete_sequence") == 0) { - // Receive a concrete sequence as a tuple. - PyObject *list = PyList_New(0); - - TiXmlElement *xitem = xvalue->FirstChildElement("value"); - while (xitem != nullptr) { - PyObject *item = xml_to_pyobj(xitem); - if (item != nullptr) { - PyList_Append(list, item); - Py_DECREF(item); - } - xitem = xitem->NextSiblingElement("value"); - } - - PyObject *result = PyList_AsTuple(list); - Py_DECREF(list); - return result; - - } else if (strcmp(type, "concrete_struct") == 0) { - // Receive a concrete struct as a new ConcreteStruct instance. - PyObject *obj = PyObject_CallFunction(_concrete_struct_class, (char *)""); - - if (obj != nullptr) { - TiXmlElement *xitem = xvalue->FirstChildElement("value"); - while (xitem != nullptr) { - const char *key = xitem->Attribute("key"); - if (key != nullptr) { - PyObject *item = xml_to_pyobj(xitem); - if (item != nullptr) { - PyObject_SetAttrString(obj, (char *)key, item); - Py_DECREF(item); - } - } - xitem = xitem->NextSiblingElement("value"); - } - return obj; - } - - } else if (strcmp(type, "python") == 0) { - int object_id; - if (xvalue->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) { - SentObjects::iterator si = _sent_objects.find(object_id); - PyObject *result; - if (si == _sent_objects.end()) { - // Hmm, the parent process gave us a bogus object ID. - result = _undefined; - } else { - result = (*si).second; - } - Py_INCREF(result); - return result; - } - } - - // Something went wrong in decoding. - PyObject *result = _undefined; - Py_INCREF(result); - return result; -} - -/** - * The main function for the read thread. - */ -void P3DPythonRun:: -rt_thread_run() { - while (_read_thread_continue) { - TiXmlDocument *doc = read_xml(_pipe_read, nout); - if (doc == nullptr) { - // Some error on reading. Abort. - _program_continue = false; - return; - } - - // Successfully read an XML document. - - // Check for one special case: the "exit" command means we shut down the - // read thread along with everything else. - TiXmlElement *xcommand = doc->FirstChildElement("command"); - if (xcommand != nullptr) { - const char *cmd = xcommand->Attribute("cmd"); - if (cmd != nullptr) { - if (strcmp(cmd, "exit") == 0) { - _read_thread_continue = false; - } - } - } - - // Feed the command up to the parent. - ACQUIRE_LOCK(_commands_lock); - _commands.push_back(doc); - RELEASE_LOCK(_commands_lock); - } -} - -/** - * - */ -P3DPythonRun::P3DWindowHandle:: -P3DWindowHandle(P3DPythonRun *p3dpython, P3DCInstance *inst, - const WindowHandle ©) : - WindowHandle(copy), - _p3dpython(p3dpython), - _inst(inst), - _child_count(0) -{ -} - -/** - * Called on a parent handle to indicate a child window's intention to attach - * itself. - */ -void P3DPythonRun::P3DWindowHandle:: -attach_child(WindowHandle *child) { - WindowHandle::attach_child(child); - ++_child_count; - - if (_child_count == 1) { - // When the first child window is attached, we tell the plugin. - _p3dpython->set_window_open(_inst, true); - } -} - -/** - * Called on a parent handle to indicate a child window's intention to detach - * itself. - */ -void P3DPythonRun::P3DWindowHandle:: -detach_child(WindowHandle *child) { - WindowHandle::detach_child(child); - --_child_count; - - if (_child_count == 0) { - // When the last child window is detached, we tell the plugin. - _p3dpython->set_window_open(_inst, false); - } -} - -/** - * Called on a parent handle to indicate a child window's wish to receive - * keyboard button events. - */ -void P3DPythonRun::P3DWindowHandle:: -request_keyboard_focus(WindowHandle *child) { - WindowHandle::request_keyboard_focus(child); - _p3dpython->request_keyboard_focus(_inst); -} diff --git a/direct/src/plugin/p3dPythonRun.h b/direct/src/plugin/p3dPythonRun.h deleted file mode 100644 index ee3fbc3d00..0000000000 --- a/direct/src/plugin/p3dPythonRun.h +++ /dev/null @@ -1,207 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dPythonRun.h - * @author drose - * @date 2009-06-05 - */ - -#ifndef P3DPYTHONRUN_H -#define P3DPYTHONRUN_H - -#include "pandabase.h" - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#endif - -#include "run_p3dpython.h" -#include "p3d_lock.h" -#include "handleStream.h" -#include "p3dCInstance.h" -#include "pandaFileStreamBuf.h" -#include "pmap.h" -#include "pdeque.h" -#include "pmutex.h" -#include "get_tinyxml.h" -#include "filename.h" - -#include - -// Python 2.5 adds Py_ssize_t; earlier versions don't have it. -#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) -typedef int Py_ssize_t; -#define PY_SSIZE_T_MAX INT_MAX -#define PY_SSIZE_T_MIN INT_MIN -#endif - -/** - * This class is used to run, and communicate with, embedded Python in a sub- - * process. It is compiled and launched as a separate executable from the - * p3d_plugin dll, because that's the only way Windows can launch a sub- - * process, and also because it makes it possible to compile-time link with - * Panda and Python, instead of having to go through the clumsy dynamic- - * loading interface. - * - * Communication is via XML files exchanged via anonymous pipes from the - * parent process. This isn't terribly eficient, of course, but it's easy; - * and it's a fairly low-bandwidth channel so efficiency is not paramount. - * - * This executable is not designed to stand alone; it is designed to be - * invoked only by p3d_plugin. - */ -class P3DPythonRun { -public: - P3DPythonRun(const char *program_name, const char *archive_file, - FHandle input_handle, FHandle output_handle, - const char *log_pathname, bool interactive_console); - ~P3DPythonRun(); - - int run_python(); - - void set_window_open(P3DCInstance *inst, bool is_open); - void request_keyboard_focus(P3DCInstance *inst); - -private: - void run_interactive_console(); - void handle_command(TiXmlDocument *doc); - void handle_pyobj_command(TiXmlElement *xcommand, bool needs_response, - int want_response_id); - void handle_script_response_command(TiXmlElement *xcommand); - - void check_comm(); - static PyObject *st_check_comm(PyObject *, PyObject *args); - TiXmlDocument *wait_script_response(int response_id); - - PyObject *py_request_func(PyObject *args); - static PyObject *st_request_func(PyObject *, PyObject *args); - - void spawn_read_thread(); - void join_read_thread(); - - void start_instance(P3DCInstance *inst, TiXmlElement *xinstance); - void terminate_instance(int id); - void set_instance_info(P3DCInstance *inst, TiXmlElement *xinstance); - void add_package_info(P3DCInstance *inst, TiXmlElement *xpackage); - void set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams); - void setup_window(int id, TiXmlElement *xwparams); - void setup_window(P3DCInstance *inst, TiXmlElement *xwparams); - - void send_windows_message(int id, unsigned int msg, int wparam, int lparam); - - void terminate_session(); - -private: - TiXmlElement *pyobj_to_xml(PyObject *value); - PyObject *xml_to_pyobj(TiXmlElement *xvalue); - -private: - // This subclass of WindowHandle is associated with the parent window we are - // given by the parent process. We use it to add hooks for communicating - // with the parent window, for instance to ask for the parent window to - // manage keyboard focus when necessary. - class P3DWindowHandle : public WindowHandle { - public: - P3DWindowHandle(P3DPythonRun *p3dpython, P3DCInstance *inst, - const WindowHandle ©); - - public: - virtual void attach_child(WindowHandle *child); - virtual void detach_child(WindowHandle *child); - virtual void request_keyboard_focus(WindowHandle *child); - - private: - P3DPythonRun *_p3dpython; - P3DCInstance *_inst; - int _child_count; - - public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - WindowHandle::init_type(); - register_type(_type_handle, "P3DWindowHandle", - WindowHandle::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - - private: - static TypeHandle _type_handle; - }; - -private: - // This method runs only within the read thread. - THREAD_CALLBACK_DECLARATION(P3DPythonRun, rt_thread_run); - void rt_thread_run(); - -private: - typedef pmap Instances; - Instances _instances; - - int _session_id; - - Filename _archive_file; - int _py_argc; -#if PY_MAJOR_VERSION >= 3 - wchar_t *_py_argv[2]; - std::wstring _program_name; -#else - char *_py_argv[2]; - std::string _program_name; -#endif - bool _interactive_console; - - PyObject *_runner; - PyObject *_undefined_object_class; - PyObject *_undefined; - PyObject *_concrete_struct_class; - PyObject *_browser_object_class; - PyObject *_taskMgr; - - // This map keeps track of the PyObject pointers we have delivered to the - // parent process. We have to hold the reference count on each of these - // until the parent process tells us it's safe to release them. - typedef pmap SentObjects; - SentObjects _sent_objects; - int _next_sent_id; - - typedef pdeque Commands; - - // This is a special queue of responses extracted from the _commands queue, - // below. It's protected by the Panda mutex. - Commands _responses; - Mutex _responses_lock; - - // The remaining members are manipulated by the read thread. - Commands _commands; - - // This has to be an actual OS LOCK instead of Panda's Mutex, because we - // have to use a true thread here, not one of Panda's simple threads. - LOCK _commands_lock; - - HandleStream _pipe_read; - HandleStream _pipe_write; - pofstream _error_log; - - bool _read_thread_continue; - bool _program_continue; - bool _session_terminated; - THREAD _read_thread; - -public: - static P3DPythonRun *_global_ptr; -}; - -#include "p3dPythonRun.I" - -#endif diff --git a/direct/src/plugin/p3dReferenceCount.I b/direct/src/plugin/p3dReferenceCount.I deleted file mode 100644 index b4cac7c05f..0000000000 --- a/direct/src/plugin/p3dReferenceCount.I +++ /dev/null @@ -1,68 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dReferenceCount.I - * @author drose - * @date 2009-07-09 - */ - -/** - * - */ -inline P3DReferenceCount:: -P3DReferenceCount() { - _ref_count = 0; -} - -/** - * - */ -inline P3DReferenceCount:: -~P3DReferenceCount() { - assert(_ref_count == 0); -} - -/** - * Explicitly increments the reference count. - */ -inline void P3DReferenceCount:: -ref() const { - ++((P3DReferenceCount *)this)->_ref_count; -} - -/** - * Explicitly decrements the reference count. Usually, you should call - * p3d_unref_delete() instead. - * - * The return value is true if the new reference count is nonzero, false if it - * is zero. - */ -inline bool P3DReferenceCount:: -unref() const { - return --(((P3DReferenceCount *)this)->_ref_count) != 0; -} - -/** - * Returns the current reference count. - */ -inline int P3DReferenceCount:: -get_ref_count() const { - return _ref_count; -} - -/** - * This global helper function will unref the given P3DReferenceCount object, - * and if the reference count reaches zero, automatically delete it. - */ -template -inline void -p3d_unref_delete(RefCountType *ptr) { - if (!ptr->unref()) { - delete ptr; - } -} diff --git a/direct/src/plugin/p3dReferenceCount.cxx b/direct/src/plugin/p3dReferenceCount.cxx deleted file mode 100644 index 9747476e73..0000000000 --- a/direct/src/plugin/p3dReferenceCount.cxx +++ /dev/null @@ -1,14 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dReferenceCount.cxx - * @author drose - * @date 2009-07-09 - */ - -#include "p3dReferenceCount.h" diff --git a/direct/src/plugin/p3dReferenceCount.h b/direct/src/plugin/p3dReferenceCount.h deleted file mode 100644 index b11eb82931..0000000000 --- a/direct/src/plugin/p3dReferenceCount.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dReferenceCount.h - * @author drose - * @date 2009-07-09 - */ - -#ifndef P3DREFERENCECOUNT_H -#define P3DREFERENCECOUNT_H - -#include "p3d_plugin_common.h" - -/** - * A base class for reference-counted objects in this module. We follow the - * Panda convention, rather than the Python convention: the reference count of - * a new object is initially 0. - */ -class P3DReferenceCount { -public: - inline P3DReferenceCount(); - inline ~P3DReferenceCount(); - - inline void ref() const; - inline bool unref() const; - inline int get_ref_count() const; - -private: - int _ref_count; -}; - -template -inline void p3d_unref_delete(RefCountType *ptr); - -#include "p3dReferenceCount.I" - -#endif diff --git a/direct/src/plugin/p3dSession.I b/direct/src/plugin/p3dSession.I deleted file mode 100644 index 010a81c172..0000000000 --- a/direct/src/plugin/p3dSession.I +++ /dev/null @@ -1,49 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dSession.I - * @author drose - * @date 2009-06-03 - */ - -/** - * Returns a string that uniquely identifies this session. See - * P3dInstance::get_session_key(). - */ -inline const std::string &P3DSession:: -get_session_key() const { - return _session_key; -} - -/** - * Returns the log filename for this particular session, if the session was - * started and if it has a log file. Returns empty string if the session - * never started or if it lacks a log file. - */ -inline const std::string &P3DSession:: -get_log_pathname() const { - return _log_pathname; -} - -/** - * Returns true if the instances of this session are allowed to be scripted by - * its embedding web page, false otherwise. - */ -inline bool P3DSession:: -get_matches_script_origin() const { - return _matches_script_origin; -} - -/** - * Returns the number of instances currently running within the session. When - * this is zero, the session may be safely deleted. - */ -inline int P3DSession:: -get_num_instances() const { - return _instances.size(); -} diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx deleted file mode 100644 index 4a769ec9e1..0000000000 --- a/direct/src/plugin/p3dSession.cxx +++ /dev/null @@ -1,1791 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dSession.cxx - * @author drose - * @date 2009-06-03 - */ - -#include "p3dSession.h" -#include "p3dInstance.h" -#include "p3dInstanceManager.h" -#include "p3d_plugin_config.h" -#include "p3dUndefinedObject.h" -#include "p3dNoneObject.h" -#include "p3dBoolObject.h" -#include "p3dIntObject.h" -#include "p3dFloatObject.h" -#include "p3dPythonObject.h" -#include "p3dConcreteSequence.h" -#include "p3dConcreteStruct.h" -#include "binaryXml.h" -#include "mkdir_complete.h" -#include "wstring_encode.h" -#include "run_p3dpython.h" - -#include -#include - -#ifndef _WIN32 -#include -#include -#include -#include -#include -#endif - -#ifdef __APPLE__ -#include -#endif - -using std::string; -using std::wstring; - -/** - * Creates a new session, corresponding to a new subprocess with its own copy - * of Python. The initial parameters for the session are taken from the - * indicated instance object (but the instance itself is not automatically - * started within the session). - */ -P3DSession:: -P3DSession(P3DInstance *inst) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - _session_id = inst_mgr->get_unique_id(); - _session_key = inst->get_session_key(); - _matches_script_origin = inst->get_matches_script_origin(); - _keep_user_env = false; - _failed = false; - -#ifdef _WIN32 - _p3dpython_handle = INVALID_HANDLE_VALUE; -#else - _p3dpython_pid = -1; -#endif - _p3dpython_one_process = false; - _p3dpython_started = false; - _p3dpython_running = false; - - _started_read_thread = false; - _read_thread_continue = false; - - INIT_LOCK(_instances_lock); - INIT_THREAD(_read_thread); -} - - -/** - * - */ -P3DSession:: -~P3DSession() { - assert(!_p3dpython_running); - DESTROY_LOCK(_instances_lock); -} - -/** - * Terminates the session by shutting down Python and stopping the subprocess. - */ -void P3DSession:: -shutdown() { - set_failed(); - - int exit_code = 0; - - if (_p3dpython_started) { - // Tell the process we're going away. - TiXmlDocument doc; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "exit"); - doc.LinkEndChild(xcommand); - write_xml(_pipe_write, &doc, nout); - - // Also close the pipe, to help underscore the point. - _pipe_write.close(); - - // Closing _pipe_read before the thread has stopped can result in a hang. - // Don't need to close it yet. _pipe_read.close(); - - static const int max_wait_ms = 2000; - - if (_p3dpython_one_process) { - // Since it's running in a thread, we can't reliably force-kill it. So, - // just wait. - nout << "Waiting for Python thread to exit\n"; - JOIN_THREAD(_p3dpython_thread); - nout << "Done waiting.\n"; - _p3dpython_one_process = false; - - } else { - // Python's running in a sub-process, the preferred way. In this case, - // we can wait a brief amount of time before it closes itself; but if it - // doesn't, we can safely force-kill it. - -#ifdef _WIN32 - // Wait for a certain amount of time for the process to stop by itself. - while (WaitForSingleObject(_p3dpython_handle, max_wait_ms) == WAIT_TIMEOUT) { - // It didn't shut down cleanly, so kill it the hard way. - nout << "Force-killing python process.\n"; - TerminateProcess(_p3dpython_handle, 2); - } - - DWORD dw_exit_code = 0; - if (!GetExitCodeProcess(_p3dpython_handle, &dw_exit_code)) { - nout << "GetExitCodeProcess failed, error: " << GetLastError() << "\n"; - } else { - exit_code = (int)dw_exit_code; - } - - CloseHandle(_p3dpython_handle); - _p3dpython_handle = INVALID_HANDLE_VALUE; - -#else // _WIN32 - // Wait for a certain amount of time for the process to stop by itself. - struct timeval start; - gettimeofday(&start, nullptr); - int start_ms = start.tv_sec * 1000 + start.tv_usec / 1000; - - int status; - pid_t result = waitpid(_p3dpython_pid, &status, WNOHANG); - while (result != _p3dpython_pid) { - if (result == -1) { - perror("waitpid"); - break; - } - - struct timeval now; - gettimeofday(&now, nullptr); - int now_ms = now.tv_sec * 1000 + now.tv_usec / 1000; - int elapsed = now_ms - start_ms; - - if (elapsed > max_wait_ms) { - // Tired of waiting. Kill the process. - nout << "Force-killing python process, pid " << _p3dpython_pid - << "\n"; - kill(_p3dpython_pid, SIGKILL); - start_ms = now_ms; - } - - // Yield the timeslice and wait some more. - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 1; - select(0, nullptr, nullptr, nullptr, &tv); - result = waitpid(_p3dpython_pid, &status, WNOHANG); - } - _p3dpython_pid = -1; - - nout << "Python process has successfully stopped.\n"; - if (WIFEXITED(status)) { - exit_code = WEXITSTATUS(status); - nout << " exited normally, status = " << exit_code << "\n"; - - } else if (WIFSIGNALED(status)) { - nout << " signalled by " << WTERMSIG(status) << ", core = " - << WCOREDUMP(status) << "\n"; - - // This seems to be a popular shell convention. - exit_code = 128 + WTERMSIG(status); - - } else if (WIFSTOPPED(status)) { - nout << " stopped by " << WSTOPSIG(status) << "\n"; - } - -#endif // _WIN32 - } - - _p3dpython_running = false; - _p3dpython_started = false; - } - - // If there are any leftover commands in the queue (presumably implying we - // have never started the python process), then delete them now, unsent. - Commands::iterator ci; - for (ci = _commands.begin(); ci != _commands.end(); ++ci) { - delete (*ci); - } - _commands.clear(); - - join_read_thread(); - - // Close the pipe now. - _pipe_read.close(); - - // If we had an exit status, pass it on to the runtime process. - if (exit_code != 0) { - _exit(exit_code); - } -} - -/** - * Adds the indicated instance to the session, and starts it running. It is - * an error if the instance has been started anywhere else. - * - * The instance must have the same session_key as the one that was passed to - * the P3DSession constructor. - */ -void P3DSession:: -start_instance(P3DInstance *inst) { - assert(inst->_session == nullptr); - assert(inst->get_session_key() == _session_key); - if (_failed) { - inst->set_failed(); - return; - } - - inst->ref(); - ACQUIRE_LOCK(_instances_lock); - inst->_session = this; - inst->_instance_started = true; - bool inserted = _instances.insert(Instances::value_type(inst->get_instance_id(), inst)).second; - RELEASE_LOCK(_instances_lock); - assert(inserted); - - TiXmlDocument *doc = new TiXmlDocument; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "start_instance"); - TiXmlElement *xinstance = inst->make_xml(); - - doc->LinkEndChild(xcommand); - xcommand->LinkEndChild(xinstance); - - send_command(doc); - inst->send_browser_script_object(); - - // We shouldn't have gotten here unless the instance is fully downloaded and - // ready to start. - assert(inst->get_packages_ready()); - - start_p3dpython(inst); -} - -/** - * Removes the indicated instance from the session, and stops it. It is an - * error if the instance is not already running on this session. - */ -void P3DSession:: -terminate_instance(P3DInstance *inst) { - TiXmlDocument *doc = new TiXmlDocument; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "terminate_instance"); - xcommand->SetAttribute("instance_id", inst->get_instance_id()); - - doc->LinkEndChild(xcommand); - - send_command(doc); - - ACQUIRE_LOCK(_instances_lock); - if (inst->_session == this) { - nout << "Assigning " << inst << "->log_pathname = " << _log_pathname << "\n"; - inst->_log_pathname = _log_pathname; - inst->_session = nullptr; - _instances.erase(inst->get_instance_id()); - } - RELEASE_LOCK(_instances_lock); - p3d_unref_delete(inst); -} - -/** - * Sends the indicated command to the running Python process. If the process - * has not yet been started, queues it up until it is ready. - * - * The command must be a newly-allocated TiXmlDocument; it will be deleted - * after it has been delivered to the process. - */ -void P3DSession:: -send_command(TiXmlDocument *command) { - if (_p3dpython_started) { - // Python is running. Send the command. - write_xml(_pipe_write, command, nout); - delete command; - } else { - // Python not yet running. Queue up the command instead. - _commands.push_back(command); - } -} - -/** - * Sends the indicated command to the running Python process, and waits for a - * response. Returns the newly-allocated response on success, or NULL on - * failure. - * - * The command must be a newly-allocated TiXmlDocument; it will be deleted - * after it has been delivered to the process. - * - * This will fail if the python process is not running or if it suddenly - * stops. - */ -TiXmlDocument *P3DSession:: -command_and_response(TiXmlDocument *command) { - if (!_p3dpython_started) { - return nullptr; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - int response_id = inst_mgr->get_unique_id(); - - // Add the "want_response_id" attribute to the toplevel command, so the sub- - // process knows we'll be waiting for its response. - TiXmlElement *xcommand = command->FirstChildElement("command"); - assert(xcommand != nullptr); - xcommand->SetAttribute("want_response_id", response_id); - - write_xml(_pipe_write, command, nout); - delete command; - - // Now block, waiting for the response to be delivered. - _response_ready.acquire(); - Responses::iterator ri = _responses.find(response_id); - while (ri == _responses.end()) { - if (!_p3dpython_running) { - // Hmm, looks like Python has gone away. - _response_ready.release(); - return nullptr; - } - - // Make sure we bake requests while we are waiting, to process recursive - // script requests. (The child process might have to wait for us to - // process some of these before it can fulfill the command we're actually - // waiting for.) - - // Release the mutex while we do this, so we can safely call back in - // recursively. - _response_ready.release(); - Instances::iterator ii; - // TODO: should we acquire _instances_lock? Deadlock concerns? - for (ii = _instances.begin(); ii != _instances.end(); ++ii) { - P3DInstance *inst = (*ii).second; - inst->bake_requests(); - } - _response_ready.acquire(); - - ri = _responses.find(response_id); - if (ri != _responses.end()) { - // We got the response we were waiting for while we had the mutex - // unlocked. - break; - } - -#ifdef _WIN32 - // Make sure we process the Windows event loop while we're waiting, or - // everything that depends on Windows messages within the subprocess will - // starve, and we could end up with deadlock. - - // A single call to PeekMessage() appears to be sufficient. This will - // scan the message queue and deliver messages to the appropriate threads, - // so that our subprocess can find them. If we don't do this, the - // messages that come into this parent window will never get delivered to - // the subprocess, even though somehow the subprocess will know they're - // coming and will block waiting for them. - - MSG msg; - PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE | PM_NOYIELD); - - // We wait with a timeout, so we can go back and spin the event loop some - // more. On Windows, the timeout needs to be small, so we continue to - // process windows messages in a timely fashion. - _response_ready.wait(0.01); - -#else - // On other platforms, we shouldn't need a timeout at all--we could just - // block indefinitely--but we go ahead and put one in anyway, just in case - // a notification slips past somehow, and also so we can see evidence that - // we're actively waiting. This timeout doesn't need to be nearly so - // small, since it's only a "just in case" sort of thing. - _response_ready.wait(0.5); -#endif // _WIN32 - - ri = _responses.find(response_id); - } - // When we exit the loop, we've found the desired response. - TiXmlDocument *response = (*ri).second; - _responses.erase(ri); - - _response_ready.release(); - - return response; -} - -/** - * Converts the XML representation of the particular object value into a - * corresponding P3D_object. Returns the object, a new reference. - */ -P3D_object *P3DSession:: -xml_to_p3dobj(const TiXmlElement *xvalue) { - const char *type = xvalue->Attribute("type"); - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - if (strcmp(type, "undefined") == 0) { - return inst_mgr->new_undefined_object(); - - } else if (strcmp(type, "none") == 0) { - return inst_mgr->new_none_object(); - - } else if (strcmp(type, "bool") == 0) { - int value; - if (xvalue->QueryIntAttribute("value", &value) == TIXML_SUCCESS) { - return inst_mgr->new_bool_object(value != 0); - } - - } else if (strcmp(type, "int") == 0) { - int value; - if (xvalue->QueryIntAttribute("value", &value) == TIXML_SUCCESS) { - return new P3DIntObject(value); - } - - } else if (strcmp(type, "float") == 0) { - double value; - if (xvalue->QueryDoubleAttribute("value", &value) == TIXML_SUCCESS) { - return new P3DFloatObject(value); - } - - } else if (strcmp(type, "string") == 0) { - // Using the string form here instead of the char * form, so we don't get - // tripped up on embedded null characters. - const string *value = xvalue->Attribute(string("value")); - if (value != nullptr) { - return new P3DStringObject(*value); - } - - } else if (strcmp(type, "concrete_sequence") == 0) { - P3DConcreteSequence *obj = new P3DConcreteSequence; - const TiXmlElement *xitem = xvalue->FirstChildElement("value"); - while (xitem != nullptr) { - P3D_object *item = xml_to_p3dobj(xitem); - if (item != nullptr) { - obj->append(item); - P3D_OBJECT_DECREF(item); - } - xitem = xitem->NextSiblingElement("value"); - } - return obj; - - } else if (strcmp(type, "concrete_struct") == 0) { - P3DConcreteStruct *obj = new P3DConcreteStruct; - const TiXmlElement *xitem = xvalue->FirstChildElement("value"); - while (xitem != nullptr) { - const char *key = xitem->Attribute("key"); - if (key != nullptr) { - P3D_object *item = xml_to_p3dobj(xitem); - if (item != nullptr) { - obj->set_property(key, item); - P3D_OBJECT_DECREF(item); - } - } - xitem = xitem->NextSiblingElement("value"); - } - return obj; - - } else if (strcmp(type, "browser") == 0) { - int object_id; - if (xvalue->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) { - SentObjects::iterator si = _sent_objects.find(object_id); - if (si == _sent_objects.end()) { - // Hmm, the child process gave us a bogus object ID. - return inst_mgr->new_undefined_object(); - } - - P3D_object *obj = (*si).second; - - P3D_OBJECT_INCREF(obj); - return obj; - } - - } else if (strcmp(type, "python") == 0) { - int object_id; - if (xvalue->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) { - return new P3DPythonObject(this, object_id); - } - } - - // Something went wrong in decoding. - return inst_mgr->new_undefined_object(); -} - -/** - * Allocates and returns a new XML structure corresponding to the indicated - * value. The supplied P3DObject's reference count is not decremented; the - * caller remains responsible for decrementing it later. - */ -TiXmlElement *P3DSession:: -p3dobj_to_xml(P3D_object *obj) { - TiXmlElement *xvalue = new TiXmlElement("value"); - - switch (P3D_OBJECT_GET_TYPE(obj)) { - case P3D_OT_undefined: - xvalue->SetAttribute("type", "undefined"); - break; - - case P3D_OT_none: - xvalue->SetAttribute("type", "none"); - break; - - case P3D_OT_bool: - xvalue->SetAttribute("type", "bool"); - xvalue->SetAttribute("value", (int)P3D_OBJECT_GET_BOOL(obj)); - break; - - case P3D_OT_int: - xvalue->SetAttribute("type", "int"); - xvalue->SetAttribute("value", P3D_OBJECT_GET_INT(obj)); - break; - - case P3D_OT_float: - xvalue->SetAttribute("type", "float"); - xvalue->SetDoubleAttribute("value", P3D_OBJECT_GET_FLOAT(obj)); - break; - - case P3D_OT_string: - { - xvalue->SetAttribute("type", "string"); - int size = P3D_OBJECT_GET_STRING(obj, nullptr, 0); - char *buffer = new char[size]; - P3D_OBJECT_GET_STRING(obj, buffer, size); - xvalue->SetAttribute("value", string(buffer, size)); - delete [] buffer; - } - break; - - case P3D_OT_object: - P3DObject *p3dobj = nullptr; - if (obj->_class == &P3DObject::_object_class) { - p3dobj = (P3DObject *)obj; - } - - if (p3dobj != nullptr && p3dobj->fill_xml(xvalue, this)) { - // This object has a specialized XML representation, valid for this - // particular session. It has already been filled into xvalue. - - } else { - // Otherwise, it must a host-provided object, or a Python object from - // another session; which means we should pass a reference down to this - // particular object, so the Python process knows to call back up to - // here to query it. - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - int object_id = inst_mgr->get_unique_id(); - bool inserted = _sent_objects.insert(SentObjects::value_type(object_id, obj)).second; - while (!inserted) { - // Hmm, we must have cycled around the entire int space? Either that, - // or there's a logic bug somewhere. Assume the former, and keep - // looking for an empty slot. - object_id = inst_mgr->get_unique_id(); - inserted = _sent_objects.insert(SentObjects::value_type(object_id, obj)).second; - } - - // Now that it's stored in the map, increment its reference count. - P3D_OBJECT_INCREF(obj); - - xvalue->SetAttribute("type", "browser"); - xvalue->SetAttribute("object_id", object_id); - } - break; - } - - return xvalue; -} - -/** - * This is called by the splash window to deliver a windows keyboard message - * to the Panda process. It will be called in a sub-thread, but that's OK, - * since write_xml() supports locking. - */ -void P3DSession:: -send_windows_message(P3DInstance *inst, unsigned int msg, int wparam, int lparam) { - if (_p3dpython_started) { - TiXmlDocument doc; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "windows_message"); - xcommand->SetAttribute("instance_id", inst->get_instance_id()); - xcommand->SetAttribute("msg", msg); - xcommand->SetAttribute("wparam", wparam); - xcommand->SetAttribute("lparam", lparam); - doc.LinkEndChild(xcommand); - write_xml(_pipe_write, &doc, nout); - } -} - -/** - * May be called in any thread to indicate that a new P3D_request is available - * in the indicated instance. - */ -void P3DSession:: -signal_request_ready(P3DInstance *inst) { - // Since a new request might require baking, we should wake up a blocked - // command_and_request() process, so the main thread can go back and bake - // the new request. - - // Technically, a response isn't really ready now, but we still need the - // main thread to wake up and look around for a bit. - _response_ready.acquire(); - _response_ready.notify(); - _response_ready.release(); -} - -/** - * If the session is still active, issues the command to the child process to - * release the indicated PyObject from its table. This is intended to be - * called strictly by the P3DPythonObject destructor. - */ -void P3DSession:: -drop_pyobj(int object_id) { - if (_p3dpython_started) { - TiXmlDocument doc; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "drop_pyobj"); - xcommand->SetAttribute("object_id", object_id); - doc.LinkEndChild(xcommand); - write_xml(_pipe_write, &doc, nout); - } -} - -/** - * Responds to a drop_p3dobj message from the child process indicating that a - * particular P3D_object is no longer being used by the child. This removes - * the corresponding P3D_object from our tables and decrements its reference - * count. - */ -void P3DSession:: -drop_p3dobj(int object_id) { - SentObjects::iterator si = _sent_objects.find(object_id); - if (si != _sent_objects.end()) { - P3D_object *obj = (*si).second; - P3D_OBJECT_DECREF(obj); - _sent_objects.erase(si); - } -} - -/** - * Starts Python running in a child process. - */ -void P3DSession:: -start_p3dpython(P3DInstance *inst) { - if (_p3dpython_started) { - // Already started. - return; - } - - if (inst->_panda3d_package == nullptr) { - nout << "Couldn't start Python: no panda3d dependency.\n"; - set_failed(); - return; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - _python_root_dir = inst->_panda3d_package->get_package_dir(); - replace_slashes(_python_root_dir); - - // If we're not to be preserving the user's current directory, then we'll - // need to change to the standard start directory. - _keep_user_env = false; - if (inst_mgr->get_trusted_environment() && - inst_mgr->get_console_environment() && - inst->_keep_user_env) { - _keep_user_env = true; - } - if (!_keep_user_env) { - _start_dir = inst_mgr->get_start_dir() + inst->get_start_dir_suffix(); - mkdir_complete(_start_dir, nout); - } - replace_slashes(_start_dir); - - // Also make sure the prc directory is present. - string prc_root = inst_mgr->get_root_dir() + "/prc"; - replace_slashes(prc_root); - mkdir_complete(prc_root, nout); - -#ifdef _WIN32 - char sep = ';'; -#else - char sep = ':'; -#endif // _WIN32 - - // Build up a search path that includes all of the required packages that - // have already been installed. We build this in reverse order, so that the - // higher-order packages come first in the list; that allows them to shadow - // settings in the lower-order packages. - assert(!inst->_packages.empty()); - string search_path; - size_t pi = inst->_packages.size() - 1; - search_path = inst->_packages[pi]->get_package_dir(); - while (pi > 0) { - --pi; - search_path += sep; - search_path += inst->_packages[pi]->get_package_dir(); - } - - replace_slashes(search_path); - nout << "Search path is " << search_path << "\n"; - - bool keep_pythonpath = false; - if (inst->_allow_python_dev) { - // If "allow_python_dev" is set in the instance's p3d_info.xml, *and* we - // have keep_pythonpath in the tokens, then we set keep_pythonpath true. - keep_pythonpath = (inst->get_fparams().lookup_token_int("keep_pythonpath") != 0); - } - - string sys_path = search_path; - string ld_path = search_path; - string dyld_path = search_path; - string python_path = search_path; - string prc_path = prc_root + sep + search_path; - - string prc_name = inst->get_fparams().lookup_token("prc_name"); - if (prc_name.empty()) { - prc_name = inst->_prc_name; - - if (!prc_name.empty()) { - // If the prc_name is taken from the p3d file (and not from the HTML - // tokens), then we also append the alt_host name to the prc_name, so - // that each alt_host variant will run in a different directory. - string alt_host = inst->get_fparams().lookup_token("alt_host"); - if (!alt_host.empty()) { - prc_name += "_"; - prc_name += alt_host; - } - } - } - if (!prc_name.empty()) { - // Add the prc_name to the path too, even if this directory doesn't - // actually exist. - string this_prc_dir = inst_mgr->get_root_dir() + "/prc"; - inst_mgr->append_safe_dir(this_prc_dir, prc_name); - replace_slashes(this_prc_dir); - prc_path = this_prc_dir + sep + prc_path; - } - - if (keep_pythonpath) { - // With keep_pythonpath true, we preserve the PYTHONPATH setting from the - // caller's environment; in fact, we put it in the front. This allows the - // caller's on-disk Python files to shadow the similar-named files in the - // p3d file, allowing easy iteration on the code in the p3d file. - if (get_env(python_path, "PYTHONPATH")) { - replace_slashes(python_path); - python_path += sep; - python_path += search_path; - } - - // We also preserve PRC_PATH. - if (get_env(prc_path, "PRC_PATH") || get_env(prc_path, "PANDA_PRC_PATH")) { - replace_slashes(prc_path); - prc_path += sep; - prc_path += search_path; - } - - nout << "keep_pythonpath is true\n" - << "PYTHONPATH set to: " << python_path << "\n" - << "PRC_PATH set to: " << prc_path << "\n"; - } - - // Get the name of the executable to run. Ideally, we'll run the executable - // successfully, in a sub-process; this will in turn load and run the - // dynamic library. If that fails for some reason, we can fall back to - // loading and running the library directly. - _p3dpython_exe = P3D_PLUGIN_P3DPYTHON; - string p3dpythonw_exe = _p3dpython_exe + "w"; - if (_p3dpython_exe.empty()) { - // Allow package to override the name of the p3dpython executables. - const char *p3dpython_name_xconfig = nullptr; - const char *p3dpythonw_name_xconfig = nullptr; - const TiXmlElement *panda3d_xconfig = inst->_panda3d_package->get_xconfig(); - if (panda3d_xconfig != nullptr) { - p3dpython_name_xconfig = panda3d_xconfig->Attribute("p3dpython_name"); - p3dpythonw_name_xconfig = panda3d_xconfig->Attribute("p3dpythonw_name"); - } - - string p3dpython_name = "p3dpython"; - if (p3dpython_name_xconfig != nullptr) { - nout << "p3dpython_name from panda3d xconfig: " << p3dpython_name_xconfig << "\n"; - p3dpython_name = p3dpython_name_xconfig; - } - - string p3dpythonw_name = p3dpython_name + "w"; - if (p3dpythonw_name_xconfig != nullptr) { - nout << "p3dpythonw_name from panda3d xconfig: " << p3dpythonw_name_xconfig << "\n"; - p3dpythonw_name = p3dpythonw_name_xconfig; - } - - // Build full executable path. - _p3dpython_exe = _python_root_dir + "/" + p3dpython_name; - p3dpythonw_exe = _python_root_dir + "/" + p3dpythonw_name; -#ifdef __APPLE__ - // On OSX, run from the packaged bundle, if it exists. - string bundle_exe = _python_root_dir + "/P3DPython.app/Contents/MacOS/p3dpython"; - if (access(bundle_exe.c_str(), X_OK) == 0) { - _p3dpython_exe = bundle_exe; - } -#endif - } -#ifdef _WIN32 - if (!inst_mgr->get_console_environment()) { - _p3dpython_exe = p3dpythonw_exe; - } - _p3dpython_exe += ".exe"; -#endif - replace_slashes(_p3dpython_exe); - nout << "_p3dpython_exe: " << _p3dpython_exe << "\n"; - - // Populate the new process' environment. - _env = string(); - - if (!_keep_user_env) { - // Reconstruct an environment just for running the process. Completely - // replace most of the existing environment variables with our own. - - // These are the enviroment variables we forward from the current - // environment, if they are set. - const char *keep[] = { - "HOME", "USER", -#ifdef _WIN32 - "SYSTEMROOT", "USERPROFILE", "COMSPEC", - "SYSTEMDRIVE", "ALLUSERSPROFILE", "APPDATA", "COMMONPROGRAMFILES", - "PROGRAMFILES", "WINDIR", "PROGRAMDATA", "USERDOMAIN", -#else - "LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG", -#endif -#ifdef HAVE_X11 - "DISPLAY", "XAUTHORITY", -#endif - nullptr - }; - for (int ki = 0; keep[ki] != nullptr; ++ki) { - string value; - if (get_env(value, keep[ki])) { - _env += keep[ki]; - _env += "="; - _env += value; - _env += '\0'; - } - } - - } else { - // In a trusted environment, when the application asks us to, we forward - // *all* environment variables, except those defined specifically below. - const char *dont_keep[] = { - "PATH", "LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH", - "PYTHONPATH", "PYTHONHOME", "PRC_PATH", "PANDA_PRC_PATH", - "TEMP", "CTPROJS", - nullptr - }; - -#ifdef _WIN32 - // Windows has a leading underscore in the name, and the word "environ" is - // a keyword. (!) - extern char **_environ; - char **global_environ = _environ; -#elif defined(__APPLE__) - // Apple doesn't guarantee that environ is available for shared libraries, - // but provides _NSGetEnviron(). - char **global_environ = *_NSGetEnviron(); -#else - // Posix is straightforward. - extern char **environ; - char **global_environ = environ; -#endif // _WIN32 - - char **ep; - for (ep = global_environ; *ep != nullptr; ++ep) { - string env = *ep; - size_t equals = env.find('='); - if (equals != string::npos) { - string var = env.substr(0, equals); - const char *varc = var.c_str(); - bool found = false; - for (int i = 0; dont_keep[i] != nullptr && !found; ++i) { -#ifdef _WIN32 - found = (_stricmp(dont_keep[i], varc) == 0); -#else - found = (strcmp(dont_keep[i], varc) == 0); -#endif - } - if (!found) { - // This variable is OK, keep it. - _env += env; - _env += '\0'; - } - } - } - } - - // We also append the original PATH et al to the *end* of the new - // definitions, even if keep_user_env is not set. This is necessary for - // os.system() and such to work as expected within the embedded app. It's - // also necessary for webbrowser on Linux. - string orig_path; - if (get_env(orig_path, "PATH")) { - sys_path += sep; - sys_path += orig_path; - } - string orig_ld_path; - if (get_env(orig_ld_path, "LD_LIBRARY_PATH")) { - ld_path += sep; - ld_path += orig_ld_path; - } - string orig_dyld_path; - if (get_env(orig_dyld_path, "DYLD_LIBRARY_PATH")) { - dyld_path += sep; - dyld_path += orig_dyld_path; - } - - // Define some new environment variables. - _env += "PATH="; - _env += sys_path; - _env += '\0'; - - _env += "LD_LIBRARY_PATH="; - _env += ld_path; - _env += '\0'; - - _env += "DYLD_LIBRARY_PATH="; - _env += dyld_path; - _env += '\0'; - - _env += "PYTHONPATH="; - _env += python_path; - _env += '\0'; - - // Let's leave PYTHONHOME empty. Setting it adds junk to our carefully- - // constructed PYTHONPATH. - _env += "PYTHONHOME="; - _env += '\0'; - - _env += "PRC_PATH="; - _env += prc_path; - _env += '\0'; - - _env += "PANDA_PRC_PATH="; - _env += prc_path; - _env += '\0'; - - _env += "TEMP="; - string temp_dir = inst_mgr->get_temp_directory(); - replace_slashes(temp_dir); - _env += temp_dir; - _env += '\0'; - - // Define each package's root directory in an environment variable named - // after the package, for the convenience of the packages in setting up - // their config files. - for (size_t pi = 0; pi < inst->_packages.size(); ++pi) { - P3DPackage *package = inst->_packages[pi]; - const string package_name = package->get_package_name(); - for (string::const_iterator si = package_name.begin(); - si != package_name.end(); - ++si) { - _env += toupper(*si); - } - _env += string("_ROOT="); - _env += package->get_package_dir(); - _env += '\0'; - - package->mark_used(); - } - - // Check for a few tokens that have special meaning at this level. - bool console_output = (inst->get_fparams().lookup_token_int("console_output") != 0); - bool one_process = (inst->get_fparams().lookup_token_int("one_process") != 0); - _interactive_console = (inst->get_fparams().lookup_token_int("interactive_console") != 0); - - if (!inst->_allow_python_dev) { - // interactive_console is only allowed to be enabled if allow_python_dev - // is also set within the p3d file. - _interactive_console = false; - } - - if (_interactive_console) { - // If we have interactive_console set, it follows we also need - // console_output. - console_output = true; - } - - // Get the log filename from the HTML tokens, or from the p3d_info.xml file. - string log_basename = inst->get_fparams().lookup_token("log_basename"); - if (log_basename.empty()) { - log_basename = inst->_log_basename; - - if (!log_basename.empty()) { - // If the log_basename is taken from the p3d file (and not from the HTML - // tokens), then we also append the alt_host name to the log_basename, - // so that each alt_host variant will run in a different directory. - string alt_host = inst->get_fparams().lookup_token("alt_host"); - if (!alt_host.empty()) { - log_basename += "_"; - log_basename += alt_host; - } - } - } - - if (log_basename.empty()) { -#ifdef P3D_PLUGIN_LOG_BASENAME3 - // No log_basename specified for the app; use the compiled-in default. - log_basename = P3D_PLUGIN_LOG_BASENAME3; -#endif - if (log_basename.empty()) { - log_basename = "p3dsession"; - } - } - - // However, it is always written into the log directory only; the user may - // not override the log file to put it anywhere else. - size_t slash = log_basename.rfind('/'); - if (slash != string::npos) { - log_basename = log_basename.substr(slash + 1); - } -#ifdef _WIN32 - slash = log_basename.rfind('\\'); - if (slash != string::npos) { - log_basename = log_basename.substr(slash + 1); - } -#endif // _WIN32 - - // Get the log history count from the HTML tokens, or from the p3d_info.xml - // file. - int log_history = inst->get_fparams().lookup_token_int("log_history"); - - // Check if we want to keep copies of recent logs on disk. - if (!log_basename.empty()) { - // Get a list of all logs on disk - std::vector all_logs; - string log_directory = inst_mgr->get_log_directory(); - inst_mgr->scan_directory(log_directory, all_logs); - - // If keeping logs, only logs with a -timestamp suffix are valid. - if (log_history > 0) { - // Remove exact match (no suffix) file, if it is on disk - string log_exact_leafname = (log_basename + string(".log")); - for (int i=0; i<(int)all_logs.size(); ++i) { - if (all_logs[i] == log_exact_leafname) { - string log_exact_pathname = (log_directory + log_exact_leafname); - unlink(log_exact_pathname.c_str()); - break; - } - } - } - - // Remove all but the most recent log_history timestamped logs - string log_basename_dash = (log_basename + string("-")); - string log_matching_pathname; - std::vector matching_logs; - for (int i=0; i<(int)all_logs.size(); ++i) { - if ((all_logs[i].size() > 4) && - (all_logs[i].find(log_basename_dash) == 0) && - (all_logs[i].substr(all_logs[i].size() - 4) == string(".log"))) { - log_matching_pathname = (log_directory + all_logs[i]); - matching_logs.push_back(log_matching_pathname); - } - } - for (int i=0; i<(int)matching_logs.size()-log_history; ++i) { - unlink(matching_logs[i].c_str()); - } - } - - // Append a timestamp suffix to the log_basename - if (!log_basename.empty() && (log_history > 0)) { -#ifdef _WIN32 - _tzset(); -#else - tzset(); -#endif - time_t log_time_seconds = time(nullptr); - struct tm *log_time_local_p = localtime(&log_time_seconds); - if (log_time_local_p != nullptr) { - struct tm log_time_local = *log_time_local_p; - static const size_t buffer_size = 16; - char buffer[buffer_size]; - sprintf(buffer, "%02d%02d%02d_%02d%02d%02d", - (int)(log_time_local.tm_year+1900-2000), - (int)(log_time_local.tm_mon+1), - (int)(log_time_local.tm_mday), - (int)(log_time_local.tm_hour), - (int)(log_time_local.tm_min), - (int)(log_time_local.tm_sec)); - log_basename += "-"; - log_basename += buffer; - } - } - - if (!console_output && !log_basename.empty()) { - _log_pathname = inst_mgr->get_log_directory(); - _log_pathname += log_basename; - - // We always tack on the extension ".log", to make it even more difficult - // to overwrite a system file. - _log_pathname += ".log"; - } - - // Create the pipes for communication. -#ifdef _WIN32 - // Create a bi-directional pipe to communicate with the sub-process. - HANDLE r_to, w_to, r_from, w_from; - - // Create the pipe to the process. - if (!CreatePipe(&r_to, &w_to, nullptr, 0)) { - nout << "failed to create pipe\n"; - set_failed(); - } else { - // Make sure the right end of the pipe is inheritable. - SetHandleInformation(r_to, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); - SetHandleInformation(w_to, HANDLE_FLAG_INHERIT, 0); - } - - // Create the pipe from the process. - if (!CreatePipe(&r_from, &w_from, nullptr, 0)) { - nout << "failed to create pipe\n"; - set_failed(); - } else { - // Make sure the right end of the pipe is inheritable. - SetHandleInformation(w_from, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); - SetHandleInformation(r_from, HANDLE_FLAG_INHERIT, 0); - } - - _output_handle = w_from; - _input_handle = r_to; - _pipe_read.open_read(r_from); - _pipe_write.open_write(w_to); - -#else - // Create a bi-directional pipe to communicate with the sub-process. - int to_fd[2]; - if (pipe(to_fd) < 0) { - perror("failed to create pipe"); - set_failed(); - } - int from_fd[2]; - if (pipe(from_fd) < 0) { - perror("failed to create pipe"); - set_failed(); - } - - _input_handle = to_fd[0]; - _output_handle = from_fd[1]; - _pipe_read.open_read(from_fd[0]); - _pipe_write.open_write(to_fd[1]); -#endif // _WIN32 - - if (_failed) { - return; - } - - nout << "Setting environment:\n"; - write_env(); - - // Get the filename of the Panda3D multifile. We need to pass this to - // p3dpython. - _mf_filename = inst->_panda3d_package->get_archive_file_pathname(); - - nout << "Attempting to start python from " << _p3dpython_exe << "\n"; - - bool started_p3dpython; - if (one_process) { - nout << "one_process is set; running Python within parent process.\n"; - started_p3dpython = false; - } else { -#ifdef _WIN32 - _p3dpython_handle = win_create_process(); - started_p3dpython = (_p3dpython_handle != INVALID_HANDLE_VALUE); -#else - _p3dpython_pid = posix_create_process(); - started_p3dpython = (_p3dpython_pid > 0); -#endif - if (!started_p3dpython) { - nout << "Failed to create process.\n"; - } - } - - if (!started_p3dpython) { - // Well, we couldn't run python in a sub-process, for some reason. Fall - // back to running it in a sub-thread within the same process. This isn't - // nearly as good, but I guess it's better than nothing. - - INIT_THREAD(_p3dpython_thread); - SPAWN_THREAD(_p3dpython_thread, p3dpython_thread_run, this); - _p3dpython_one_process = true; - } - _p3dpython_started = true; - _p3dpython_running = true; - - if (!_pipe_read) { - nout << "unable to open read pipe\n"; - } - if (!_pipe_write) { - nout << "unable to open write pipe\n"; - } - - spawn_read_thread(); - - // The very first command we send to the process is its session_id. - TiXmlDocument doc; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "init"); - xcommand->SetAttribute("session_id", _session_id); - doc.LinkEndChild(xcommand); - write_xml(_pipe_write, &doc, nout); - - // Also feed it any commands we may have queued up from before the process - // was started. - Commands::iterator ci; - for (ci = _commands.begin(); ci != _commands.end(); ++ci) { - write_xml(_pipe_write, (*ci), nout); - delete (*ci); - } - _commands.clear(); -} - -/** - * Sets the "failed" indication to display sadness to the user--we're unable - * to launch the instance for some reason. - * - * When this is called on the P3DSession instead of on a particular - * P3DInstance, it means that all instances attached to this session are - * marked failed. - */ -void P3DSession:: -set_failed() { - _failed = true; - - Instances::iterator ii; - ACQUIRE_LOCK(_instances_lock); - for (ii = _instances.begin(); ii != _instances.end(); ++ii) { - P3DInstance *inst = (*ii).second; - inst->set_failed(); - } - RELEASE_LOCK(_instances_lock); -} - -/** - * Starts the read thread. This thread is responsible for reading the - * standard input socket for XML requests and storing them in the _requests - * queue. - */ -void P3DSession:: -spawn_read_thread() { - assert(!_started_read_thread && !_read_thread_continue); - - _read_thread_continue = true; - SPAWN_THREAD(_read_thread, rt_thread_run, this); - _started_read_thread = true; -} - -/** - * Waits for the read thread to stop. - */ -void P3DSession:: -join_read_thread() { - if (!_started_read_thread) { - return; - } - - _read_thread_continue = false; - - JOIN_THREAD(_read_thread); - _started_read_thread = false; -} - -/** - * Changes the forward slashes to backslashes on Windows. Does nothing on the - * other platforms. - */ -void P3DSession:: -replace_slashes(string &str) { -#ifdef _WIN32 - // It turns out that some very low-level Windows functions fail when you - // give them a forward slash instead of a backslash. In particular, Windows - // fails to load the MSVS runtime DLL's (and their associated manifest - // files) correctly in this case. So we have to be sure to replace forward - // slashes in our PATH variable (and other environment variables, for good - // measure) with backslashes. - for (size_t i = 0; i < str.length(); ++i) { - if (str[i] == '/') { - str[i] = '\\'; - } - } -#endif // _WIN32 -} - -/** - * The main function for the read thread. - */ -void P3DSession:: -rt_thread_run() { - while (_read_thread_continue) { - TiXmlDocument *doc = read_xml(_pipe_read, nout); - if (doc == nullptr) { - // Some error on reading. Abort. - rt_terminate(); - _p3dpython_running = false; - _response_ready.acquire(); - _response_ready.notify(); - _response_ready.release(); - return; - } - - // Successfully read an XML document. - rt_handle_request(doc); - } - - nout << "Exiting rt_thread_run in " << this << "\n"; -} - -/** - * Processes a single request or notification received from an instance. This - * method runs in the read thread. - */ -void P3DSession:: -rt_handle_request(TiXmlDocument *doc) { - TiXmlElement *xresponse = doc->FirstChildElement("response"); - if (xresponse != nullptr) { - int response_id; - if (xresponse->QueryIntAttribute("response_id", &response_id) == TIXML_SUCCESS) { - // This is a response to a previous command-and-response. Send it to - // the parent thread. - _response_ready.acquire(); - bool inserted = _responses.insert(Responses::value_type(response_id, doc)).second; - assert(inserted); - _response_ready.notify(); - _response_ready.release(); - return; - } - } - - TiXmlElement *xrequest = doc->FirstChildElement("request"); - if (xrequest != nullptr) { - int instance_id; - if (xrequest->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS) { - // Look up the particular instance this is related to. - ACQUIRE_LOCK(_instances_lock); - Instances::const_iterator ii; - ii = _instances.find(instance_id); - if (ii != _instances.end()) { - P3DInstance *inst = (*ii).second; - inst->add_raw_request(doc); - doc = nullptr; - } - RELEASE_LOCK(_instances_lock); - } - } - - if (doc != nullptr) { - delete doc; - } -} - -/** - * Got a closed pipe from the sub-process. Send a terminate request for all - * instances. - */ -void P3DSession:: -rt_terminate() { - Instances icopy; - ACQUIRE_LOCK(_instances_lock); - icopy = _instances; - RELEASE_LOCK(_instances_lock); - - // TODO: got a race condition here. What happens if someone deletes an - // instance while we're processing this loop? - - for (Instances::iterator ii = icopy.begin(); ii != icopy.end(); ++ii) { - P3DInstance *inst = (*ii).second; - inst->request_stop_main_thread(); - } -} - -#ifdef _WIN32 -/** - * Creates a sub-process to run _p3dpython_exe, with the appropriate command- - * line arguments, and the environment string defined in _env. Standard error - * is logged to _log_pathname, if that string is nonempty. - * - * Opens the two HandleStreams _pipe_read and _pipe_write as the read and - * write pipes to the child process's standard output and standard input, - * respectively. - * - * Returns the handle to the created process on success, or - * INVALID_HANDLE_VALUE on falure. - */ -HANDLE P3DSession:: -win_create_process() { - // Make sure we see an error dialog if there is a missing DLL. - SetErrorMode(0); - - // Open the log file. - HANDLE error_handle = GetStdHandle(STD_ERROR_HANDLE); - bool got_error_handle = false; - if (!_log_pathname.empty()) { - wstring log_pathname_w; - string_to_wstring(log_pathname_w, _log_pathname); - HANDLE handle = CreateFileW - (log_pathname_w.c_str(), GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - nullptr, CREATE_ALWAYS, 0, nullptr); - if (handle != INVALID_HANDLE_VALUE) { - error_handle = handle; - SetHandleInformation(error_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); - got_error_handle = true; - } else { - nout << "Unable to open " << _log_pathname << "\n"; - } - } - - STARTUPINFOW startup_info; - ZeroMemory(&startup_info, sizeof(startup_info)); - startup_info.cb = sizeof(startup_info); - - // Set up the IO handles. We send stderr and stdout to our error_handle. - startup_info.hStdError = error_handle; - startup_info.hStdOutput = error_handle; - startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - startup_info.dwFlags |= STARTF_USESTDHANDLES; - - // We want to show the output window these days. - startup_info.wShowWindow = SW_SHOW; - startup_info.dwFlags |= STARTF_USESHOWWINDOW; - - // If _keep_user_env is true, meaning not to change the current directory, - // then pass NULL in to CreateProcess(). Otherwise pass in _start_dir. - const wchar_t *start_dir_cstr; - wstring start_dir_w; - if (_keep_user_env) { - start_dir_cstr = nullptr; - nout << "Not changing working directory.\n"; - } else { - string_to_wstring(start_dir_w, _start_dir); - start_dir_cstr = start_dir_w.c_str(); - nout << "Setting working directory: " << _start_dir << "\n"; - } - - // Construct the command-line string, containing the quoted command-line - // arguments. - std::ostringstream stream; - stream << "\"" << _p3dpython_exe << "\" \"" << _mf_filename - << "\" \"" << _input_handle << "\" \"" << _output_handle - << "\" \"" << _interactive_console << "\""; - - // I'm not sure why CreateProcess wants a non-const char pointer for its - // command-line string, but I'm not taking chances. It gets a non-const - // char array that it can modify. - wstring command_line_str; - string_to_wstring(command_line_str, stream.str()); - wchar_t *command_line = new wchar_t[command_line_str.size() + 1]; - memcpy(command_line, command_line_str.c_str(), sizeof(wchar_t) * command_line_str.size() + 1); - - nout << "Command line: " << command_line_str << "\n"; - - wstring p3dpython_exe_w; - string_to_wstring(p3dpython_exe_w, _p3dpython_exe); - wstring env_w; - string_to_wstring(env_w, _env); - - PROCESS_INFORMATION process_info; - BOOL result = CreateProcessW - (p3dpython_exe_w.c_str(), command_line, nullptr, nullptr, TRUE, - CREATE_UNICODE_ENVIRONMENT, (void *)env_w.c_str(), - start_dir_cstr, &startup_info, &process_info); - bool started_program = (result != 0); - - if (!started_program) { - nout << "CreateProcess failed, error: " << GetLastError() << "\n"; - } - - delete[] command_line; - - // Close the pipe handles that are now owned by the child. - CloseHandle(_output_handle); - CloseHandle(_input_handle); - if (got_error_handle) { - CloseHandle(error_handle); - } - - if (!started_program) { - _pipe_read.close(); - _pipe_write.close(); - return INVALID_HANDLE_VALUE; - } - - CloseHandle(process_info.hThread); - return process_info.hProcess; -} -#endif // _WIN32 - - -#ifndef _WIN32 -/** - * Creates a sub-process to run _p3dpython_exe, with the appropriate command- - * line arguments, and the environment string defined in _env. Standard error - * is logged to _log_pathname, if that string is nonempty. - * - * Opens the two HandleStreams _pipe_read and _pipe_write as the read and - * write pipes to the child process's standard output and standard input, - * respectively. - * - * Returns the pid of the created process on success, or -1 on falure. - */ -int P3DSession:: -posix_create_process() { - // If the program file doesn't exist or isn't executable, don't even bother - // to try. - if (access(_p3dpython_exe.c_str(), X_OK) != 0) { - return -1; - } - - // Fork and exec. - pid_t child = fork(); - if (child < 0) { - perror("fork"); - return -1; - } - - if (child == 0) { - // Here we are in the child process. - - // Close the parent's ends of the pipes. - _pipe_read.close(); - _pipe_write.close(); - - if (!_log_pathname.empty()) { - // Open a logfile. - int logfile_fd = open(_log_pathname.c_str(), - O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (logfile_fd < 0) { - nout << "Unable to open " << _log_pathname << "\n"; - } else { - // Redirect stderr and stdout onto our logfile. - dup2(logfile_fd, STDERR_FILENO); - dup2(logfile_fd, STDOUT_FILENO); - close(logfile_fd); - } - } - - if (_keep_user_env) { - nout << "Not changing working directory.\n"; - } else { - nout << "Setting working directory: " << _start_dir << "\n"; - if (chdir(_start_dir.c_str()) < 0) { - nout << "Could not chdir to " << _start_dir << "\n"; - // This is a warning, not an error. We don't actually care that much - // about the starting directory. - } - } - - // build up an array of char strings for the environment. - std::vector ptrs; - size_t p = 0; - size_t zero = _env.find('\0', p); - while (zero != string::npos) { - ptrs.push_back(_env.data() + p); - p = zero + 1; - zero = _env.find('\0', p); - } - ptrs.push_back(nullptr); - - std::stringstream input_handle_stream; - input_handle_stream << _input_handle; - string input_handle_str = input_handle_stream.str(); - - std::stringstream output_handle_stream; - output_handle_stream << _output_handle; - string output_handle_str = output_handle_stream.str(); - - execle(_p3dpython_exe.c_str(), _p3dpython_exe.c_str(), - _mf_filename.c_str(), input_handle_str.c_str(), - output_handle_str.c_str(), - _interactive_console ? "1" : "0", (char *)0, &ptrs[0]); - nout << "Failed to exec " << _p3dpython_exe << "\n"; - _exit(1); - } - - // Close the handles that are now owned by the child. - close(_input_handle); - close(_output_handle); - - // Let's wait a few milliseconds and see if the child is going to - // immediately exit with a failure status. This isn't 100% reliable, but - // it's a lot easier than sending back a "yes I successfully started the - // program" message. Maybe I'll put in the more reliable test later. - - struct timeval start; - gettimeofday(&start, nullptr); - int start_ms = start.tv_sec * 1000 + start.tv_usec / 1000; - - int status; - pid_t result = waitpid(child, &status, WNOHANG); - while (result != child) { - if (result == -1) { - perror("waitpid"); - break; - } - - struct timeval now; - gettimeofday(&now, nullptr); - int now_ms = now.tv_sec * 1000 + now.tv_usec / 1000; - int elapsed = now_ms - start_ms; - if (elapsed > 100) { - // OK, we've waited, and the child process is still alive. Assume it - // will stay that way. - nout << "child still alive after " << elapsed << " ms\n"; - return child; - } - - // Yield the timeslice and wait some more. - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 1; - select(0, nullptr, nullptr, nullptr, &tv); - result = waitpid(child, &status, WNOHANG); - } - - // The child process died for some reason; maybe it couldn't exec() its - // process. Report an error condition. - nout << "Python process stopped immediately.\n"; - if (WIFEXITED(status)) { - int code = WEXITSTATUS(status); - - nout << " exited normally, status = " << code << "\n"; - if (code != 0) { - _exit(code); - } - - } else if (WIFSIGNALED(status)) { - nout << " signalled by " << WTERMSIG(status) << ", core = " - << WCOREDUMP(status) << "\n"; - - // This seems to be a popular shell convention. - _exit(128 + WTERMSIG(status)); - - } else if (WIFSTOPPED(status)) { - nout << " stopped by " << WSTOPSIG(status) << "\n"; - } - - return -1; -} -#endif // _WIN32 - -/** - * This method is called in a sub-thread to fire up p3dpython within this same - * process, but only if the above attempt to create a sub-process failed. - */ -void P3DSession:: -p3dpython_thread_run() { - nout << "running p3dpython_thread_run()\n"; - - // Set the environment. Hopefully this won't be too destructive to the - // current process. - - // Note that on OSX at least, changing the DYLD_LIBRARY_PATH after the - // process has started has no effect (and furthermore you can't specify a - // full path to dlopen() calls), so this whole one-process approach is - // fatally flawed on OSX. - size_t p = 0; - size_t zero = _env.find('\0', p); - while (zero != string::npos) { - const char *start = _env.data() + p; -#ifdef _WIN32 - _putenv(start); -#else - const char *equals = strchr(start, '='); - if (equals != nullptr) { - string variable(start, equals - start); - setenv(variable.c_str(), equals + 1, true); - } -#endif // _WIN32 - p = zero + 1; - zero = _env.find('\0', p); - } - - // Now load the library. - string libp3dpython = _python_root_dir + "/libp3dpython"; -#ifdef _WIN32 -#ifdef _DEBUG - libp3dpython += "_d.dll"; -#else - libp3dpython += ".dll"; -#endif - SetErrorMode(0); - HMODULE module = LoadLibrary(libp3dpython.c_str()); - if (module == nullptr) { - // Couldn't load the DLL. - nout << "Couldn't load " << libp3dpython << "\n"; - return; - } - - #define get_func GetProcAddress - -#else // _WIN32 - // Posix case. - #ifdef __APPLE__ - libp3dpython += ".dylib"; - #else - libp3dpython += ".so"; - #endif - void *module = dlopen(libp3dpython.c_str(), RTLD_LAZY | RTLD_LOCAL); - if (module == nullptr) { - // Couldn't load the .so. - nout << "Couldn't load " << libp3dpython << "\n"; - return; - } - - #define get_func dlsym - -#endif // _WIN32 - - run_p3dpython_func *run_p3dpython = (run_p3dpython_func *)get_func(module, "run_p3dpython"); - if (run_p3dpython == nullptr) { - nout << "Couldn't find run_p3dpython\n"; - return; - } - - int status = run_p3dpython(libp3dpython.c_str(), _mf_filename.c_str(), - _input_handle, _output_handle, _log_pathname.c_str(), - _interactive_console); - if (status != 0) { - nout << "Failure on startup.\n"; - _exit(status); - } -} - -/** - * Implements getenv(), respecting Windows' Unicode environment. Returns true - * if the variable is defined, false if it is not. If it is defined, fills - * value with its definition. - */ -bool P3DSession:: -get_env(string &value, const string &varname) { -#ifdef _WIN32 - wstring varname_w; - string_to_wstring(varname_w, varname); - const wchar_t *vc = _wgetenv(varname_w.c_str()); - if (vc == nullptr) { - return false; - } - wstring_to_string(value, vc); - return true; -#else // _WIN32 - const char *vc = getenv(varname.c_str()); - if (vc == nullptr) { - return false; - } - value = vc; - return true; -#endif -} - -/** - * Writes _env, which is formatted as a string containing zero-byte-terminated - * environment defintions, to the nout stream, one definition per line. - */ -void P3DSession:: -write_env() const { - size_t p = 0; - size_t zero = _env.find('\0', p); - while (zero != string::npos) { - nout << " "; - nout.write(_env.data() + p, zero - p); - nout << "\n"; - p = zero + 1; - zero = _env.find('\0', p); - } -} diff --git a/direct/src/plugin/p3dSession.h b/direct/src/plugin/p3dSession.h deleted file mode 100644 index eca5702fd6..0000000000 --- a/direct/src/plugin/p3dSession.h +++ /dev/null @@ -1,166 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dSession.h - * @author drose - * @date 2009-06-03 - */ - -#ifndef P3DSESSION_H -#define P3DSESSION_H - -#include "p3d_plugin_common.h" -#include "handleStream.h" -#include "p3dPackage.h" -#include "p3dConditionVar.h" -#include "p3dReferenceCount.h" -#include "get_tinyxml.h" - -#include -#include - -class P3DInstance; -class P3DProgressWindow; - -/** - * Corresponds to a single session: a subprocess with a unique instance of - * Python running within it, which might include one or more P3DInstance - * objects running in the same memory space with each other. - */ -class P3DSession : public P3DReferenceCount { -public: - P3DSession(P3DInstance *inst); - ~P3DSession(); - - void shutdown(); - - inline const std::string &get_session_key() const; - inline const std::string &get_log_pathname() const; - inline bool get_matches_script_origin() const; - - void start_instance(P3DInstance *inst); - void terminate_instance(P3DInstance *inst); - - inline int get_num_instances() const; - - void send_command(TiXmlDocument *command); - TiXmlDocument *command_and_response(TiXmlDocument *command); - P3D_object *xml_to_p3dobj(const TiXmlElement *xvalue); - TiXmlElement *p3dobj_to_xml(P3D_object *obj); - void send_windows_message(P3DInstance *inst, unsigned int msg, - int wparam, int lparam); - - void signal_request_ready(P3DInstance *inst); - - void drop_pyobj(int object_id); - void drop_p3dobj(int object_id); - -private: - void start_p3dpython(P3DInstance *inst); - void set_failed(); - - void spawn_read_thread(); - void join_read_thread(); - - static void replace_slashes(std::string &str); - -private: - // These methods run only within the read thread. - THREAD_CALLBACK_DECLARATION(P3DSession, rt_thread_run); - void rt_thread_run(); - void rt_terminate(); - void rt_handle_request(TiXmlDocument *doc); - -#ifdef _WIN32 - HANDLE win_create_process(); -#else - int posix_create_process(); -#endif - - // In case we can't get a separate process, we'll run p3dpython in a sub- - // thread. - THREAD_CALLBACK_DECLARATION(P3DSession, p3dpython_thread_run); - void p3dpython_thread_run(); - - static bool get_env(std::string &value, const std::string &varname); - void write_env() const; - -private: - int _session_id; - std::string _session_key; - std::string _log_pathname; - std::string _python_root_dir; - std::string _start_dir; - bool _matches_script_origin; - bool _keep_user_env; - bool _failed; - - // This information is passed to create_process(), or to - // p3dpython_thread_run(). - std::string _p3dpython_exe; - std::string _p3dpython_dll; - std::string _mf_filename; - std::string _env; - FHandle _input_handle, _output_handle; - bool _interactive_console; - - typedef std::map Instances; - Instances _instances; - LOCK _instances_lock; - - // Commands that are queued up to send down the pipe. Normally these only - // accumulate before the python process has been started; after that, - // commands are written to the pipe directly. - typedef std::vector Commands; - Commands _commands; - - // This map keeps track of the P3D_object pointers we have delivered to the - // child process. We have to keep each of these until the child process - // tells us it's safe to delete them. - typedef std::map SentObjects; - SentObjects _sent_objects; - - P3DPackage *_panda3d; - - // If this is true, then CreateProcess() or fork() failed (or we had - // one_process set true in the tokens), and we're forced to run p3dpython in - // a sub-thread within the same process, rather than in a separate process. - // This means we can't have multiple sessions running simultaneously, - // because Python don't play that way. - bool _p3dpython_one_process; - - // Members for communicating with the p3dpython child process (or thread, as - // the case may be). -#ifdef _WIN32 - HANDLE _p3dpython_handle; -#else - int _p3dpython_pid; -#endif - THREAD _p3dpython_thread; - bool _p3dpython_started; - bool _p3dpython_running; - - // The _response_ready mutex protects this structure. - typedef std::map Responses; - Responses _responses; - P3DConditionVar _response_ready; - - // The remaining members are manipulated by or for the read thread. - bool _started_read_thread; - HandleStream _pipe_read; - HandleStream _pipe_write; - - bool _read_thread_continue; - THREAD _read_thread; - - friend class P3DInstance; -}; - -#include "p3dSession.I" - -#endif diff --git a/direct/src/plugin/p3dSplashWindow.I b/direct/src/plugin/p3dSplashWindow.I deleted file mode 100644 index c9562a083a..0000000000 --- a/direct/src/plugin/p3dSplashWindow.I +++ /dev/null @@ -1,47 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dSplashWindow.I - * @author drose - * @date 2009-06-17 - */ - -/** - * Returns the current file parameters. - */ -inline const P3DFileParams &P3DSplashWindow:: -get_fparams() const { - return _fparams; -} - -/** - * Returns the current window parameters. - */ -inline const P3DWindowParams &P3DSplashWindow:: -get_wparams() const { - return _wparams; -} - -/** - * Returns the current setting of the "visible" flag. If false, the splash - * window is hidden. - */ -inline bool P3DSplashWindow:: -get_visible() const { - return _visible; -} - -/** - * - */ -inline P3DSplashWindow::ImageData:: -ImageData() { - _width = 0; - _height = 0; - _num_channels = 0; -} diff --git a/direct/src/plugin/p3dSplashWindow.cxx b/direct/src/plugin/p3dSplashWindow.cxx deleted file mode 100644 index 13bcc09ff2..0000000000 --- a/direct/src/plugin/p3dSplashWindow.cxx +++ /dev/null @@ -1,522 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dSplashWindow.cxx - * @author drose - * @date 2009-06-17 - */ - -#include "p3dSplashWindow.h" -#include "wstring_encode.h" - -// We use the public domain stb_image library for loading images. Define the -// stb_image implementation. We only use it in this unit. -#define STB_IMAGE_STATIC -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" - -using std::max; -using std::min; -using std::string; - -// The number of pixels to move the block per byte downloaded, when we don't -// know the actual file size we're downloading. -const double P3DSplashWindow::_unknown_progress_rate = 1.0 / 4096; - -/** - * By the time the SplashWindow is created, the instance has received both its - * fparams and its wparams. Copy them both into this class for reference. - */ -P3DSplashWindow:: -P3DSplashWindow(P3DInstance *inst, bool make_visible) : - _inst(inst), - _fparams(inst->get_fparams()), - _wparams(inst->get_wparams()) -{ - _visible = make_visible; - _fgcolor_r = 0x00; - _fgcolor_g = 0x00; - _fgcolor_b = 0x00; - _bgcolor_r = 0xff; - _bgcolor_g = 0xff; - _bgcolor_b = 0xff; - _barcolor_r = 0x6c; - _barcolor_g = 0xa5; - _barcolor_b = 0xe0; - _bar_bgcolor_r = _bgcolor_r; - _bar_bgcolor_g = _bgcolor_g; - _bar_bgcolor_b = _bgcolor_b; - _bar_border = 1; - _bar_bottom = 24; - _bar_width = 398; - _bar_height = 22; - _bar_width_ratio = 0.6; - _bar_height_ratio = 0.1; - _font_family = "Helvetica"; - _font_size = 12; - _font_style = FS_normal; - _font_weight = FW_normal; - _button_width = 0; - _button_height = 0; - _button_x = 0; - _button_y = 0; - _button_active = false; - _mouse_x = -1; - _mouse_y = -1; - _mouse_down = false; - _button_depressed = false; - _bstate = BS_hidden; -} - -/** - * - */ -P3DSplashWindow:: -~P3DSplashWindow() { -} - -/** - * Changes the window parameters, e.g. to resize or reposition the window; or - * sets the parameters for the first time, creating the initial window. - */ -void P3DSplashWindow:: -set_wparams(const P3DWindowParams &wparams) { - _wparams = wparams; - _win_width = _wparams.get_win_width(); - _win_height = _wparams.get_win_height(); -} - -/** - * Makes the splash window visible or invisible, so as not to compete with the - * embedded Panda window in the same space. - */ -void P3DSplashWindow:: -set_visible(bool visible) { - nout << "P3DSplashWindow::set_visible(" << visible << ")\n"; - _visible = visible; -} - -/** - * Specifies the name of a JPEG or PNG image file that is displayed in the - * center of the splash window. - * - * image_placement defines the specific context in which this particular image - * is displayed. It is similar to the P3DInstance's image_type, but it is a - * more specific, lower-level usage. - */ -void P3DSplashWindow:: -set_image_filename(const string &image_filename, ImagePlacement image_placement) { -} - -/** - * Specifies the color that is used to display the text above the loading bar. - * - * This may only be set before wparams is set. - */ -void P3DSplashWindow:: -set_fgcolor(int r, int g, int b) { - nout << "fgcolor " << r << ", " << g << ", " << b << "\n"; - _fgcolor_r = r; - _fgcolor_g = g; - _fgcolor_b = b; -} - -/** - * Specifies the solid color that is displayed behind the splash image, if - * any, or before the splash image is loaded. - * - * This may only be set before wparams is set. - */ -void P3DSplashWindow:: -set_bgcolor(int r, int g, int b) { - nout << "bgcolor " << r << ", " << g << ", " << b << "\n"; - _bgcolor_r = r; - _bgcolor_g = g; - _bgcolor_b = b; -} - -/** - * Specifies the color that is used to fill the loading bar. - * - * This may only be set before wparams is set. - */ -void P3DSplashWindow:: -set_barcolor(int r, int g, int b) { - nout << "barcolor " << r << ", " << g << ", " << b << "\n"; - _barcolor_r = r; - _barcolor_g = g; - _barcolor_b = b; -} - -/** - * Specifies the solid color that is displayed behind the loading bar. - * - * This may only be set before wparams is set. - */ -void P3DSplashWindow:: -set_bar_bgcolor(int r, int g, int b) { - nout << "bar_bgcolor " << r << ", " << g << ", " << b << "\n"; - _bar_bgcolor_r = r; - _bar_bgcolor_g = g; - _bar_bgcolor_b = b; -} - -/** - * Sets the width in pixels of the border around the loading bar, or 0 not to - * draw a bar at all. - * - * This may only be set before wparams is set. - */ -void P3DSplashWindow:: -set_bar_border(int border) { - nout << "bar_border " << border << "\n"; - _bar_border = border; -} - -/** - * Sets the amount of background pixels between the bottom edge of the window - * and the bottom edge of the loading bar border. - * - * This may only be set before wparams is set. - */ -void P3DSplashWindow:: -set_bar_bottom(int bottom) { - nout << "bar_bottom " << bottom << "\n"; - _bar_bottom = bottom; -} - -/** - * Sets the width of the loading bar. If percent is true, it is interpreted - * as a percentage of the window width. If false, it is interpreted as an - * absolute width in pixels. - * - * This may only be set before wparams is set. - */ -void P3DSplashWindow:: -set_bar_width(int width, bool percent) { - nout << "bar_width " << width; - if (percent) { - nout << '%'; - } - nout << '\n'; - - if (percent) { - _bar_width = -1; - _bar_width_ratio = abs(width) / 100.0; - } else { - _bar_width = abs(width); - _bar_width_ratio = 1.0; - } -} - -/** - * Sets the height of the loading bar. If percent is true, it is interpreted - * as a percentage of the window height. If false, it is interpreted as an - * absolute height in pixels. - * - * This may only be set before wparams is set. - */ -void P3DSplashWindow:: -set_bar_height(int height, bool percent) { - nout << "bar_height " << height; - if (percent) { - nout << '%'; - } - nout << '\n'; - - if (percent) { - _bar_height = -1; - _bar_height_ratio = abs(height) / 100.0; - } else { - _bar_height = abs(height); - _bar_height_ratio = 1.0; - } -} - -/** - * Sets the font family of the text above the loading bar. - * - * This may only be set before wparams is set. - */ -void P3DSplashWindow:: -set_font_family(const string &family) { - nout << "font_family " << family << "\n"; - _font_family = family; -} - -/** - * Sets the font size in pixels of the text above the loading bar. The - * default value is 12. - * - * This may only be set before wparams is set. - */ -void P3DSplashWindow:: -set_font_size(int size) { - nout << "font_size " << size << "\n"; - _font_size = size; -} - -/** - * Sets the font style of the text above the loading bar. - * - * This may only be set before wparams is set. - */ -void P3DSplashWindow:: -set_font_style(FontStyle style) { - nout << "font_style " << style << "\n"; - _font_style = style; -} - -/** - * Sets the font weight of the text above the loading bar. The default is - * FW_normal. It should be a multiple of 100 in the range 100-900. - * - * This may only be set before wparams is set. - */ -void P3DSplashWindow:: -set_font_weight(int weight) { - nout << "font_weight " << weight << "\n"; - _font_weight = weight; -} - -/** - * Specifies the text that is displayed above the install progress bar. - */ -void P3DSplashWindow:: -set_install_label(const string &install_label) { -} - -/** - * Moves the install progress bar from 0.0 to 1.0. - */ -void P3DSplashWindow:: -set_install_progress(double install_progress, - bool is_progress_known, size_t received_data) { -} - -/** - * Deals with the event callback from the OS window system. Returns true if - * the event is handled, false if ignored. - */ -bool P3DSplashWindow:: -handle_event(const P3D_event_data &event) { - return false; -} - -/** - * Sets whether the button should be visible and active (true) or invisible - * and inactive (false). If active, the button image will be displayed in the - * window, and a click event will be generated when the user clicks the - * button. - */ -void P3DSplashWindow:: -set_button_active(bool flag) { - _button_active = flag; - - // Now light up the button according to the current mouse position. - set_mouse_data(_mouse_x, _mouse_y, _mouse_down); -} - -/** - * The Panda window is asking us to manage keyboard focus in proxy for it. - * This is used on Vista, where the Panda window may be disallowed from - * directly assigning itself keyboard focus. - */ -void P3DSplashWindow:: -request_keyboard_focus() { -} - -/** - * Reads the image filename and sets image parameters width, height, - * num_channels, and data. Returns true on success, false on failure. - */ -bool P3DSplashWindow:: -read_image_data(ImageData &image, string &data, - const string &image_filename) { - image._width = 0; - image._height = 0; - image._num_channels = 0; - data.clear(); - - if (image_filename.empty()) { - return false; - } - - nout << "Reading splash file image: " << image_filename << "\n"; - - unsigned char *imgdata = stbi_load(image_filename.c_str(), &image._width, - &image._height, &image._num_channels, 0); - - if (imgdata == nullptr) { - nout << "Couldn't read splash file image: " << image_filename << "\n"; - const char *reason = stbi_failure_reason(); - if (reason != nullptr) { - nout << "stbi_failure_reason: " << reason << "\n"; - } - return false; - } - - size_t data_size = image._width * image._height * image._num_channels; - data.resize(data_size); - memcpy(&data[0], imgdata, data_size); - - stbi_image_free(imgdata); - return true; -} - -/** - * Given the window width and height, determine the rectangle in which to - * place the progress bar. - */ -void P3DSplashWindow:: -get_bar_placement(int &bar_x, int &bar_y, - int &bar_width, int &bar_height) { - - bar_width = (int)(_win_width * _bar_width_ratio); - bar_height = (int)(_win_height * _bar_height_ratio); - - if (_bar_width >= 0) { - bar_width = min(bar_width, _bar_width); - } - if (_bar_height >= 0) { - bar_height = min(bar_height, _bar_height); - } - - // Horizontally center the bar, and set it at a fixed distance from the - // bottom edge of the splash window. - bar_x = (_win_width - bar_width) / 2; - bar_y = _win_height - _bar_bottom - _bar_border - bar_height; -} - -/** - * Specifies the image that contains the "ready" button image, which in turn - * determines the clickable dimensions of the button within the window. - */ -void P3DSplashWindow:: -set_button_range(const ImageData &image) { - // The clickable area has a certain minimum size, even if it's a very small - // image. - _button_width = max(image._width, 64); - _button_height = max(image._height, 64); - - // But it can't be larger than the window itself. - _button_width = min(_button_width, _win_width); - _button_height = min(_button_height, _win_height); - - // Compute the top-left corner of the button image in window coordinates. - _button_x = (_win_width - _button_width) / 2; - _button_y = (_win_height - _button_height) / 2; -} - -/** - * Intended to be called by the subclasses as the mouse is tracked through the - * window, whether the button is currently active or not. This updates the - * internal state of the mouse pointer, and also (if the button is active) - * updates the button state appropriately, and generates the click event when - * the mouse button transitions from down to up over the button area. - */ -void P3DSplashWindow:: -set_mouse_data(int mouse_x, int mouse_y, bool mouse_down) { - ButtonState orig_bstate = _bstate; - - _mouse_x = mouse_x; - _mouse_y = mouse_y; - _mouse_down = mouse_down; - - ButtonState bstate = BS_hidden; - bool click_detected = false; - - if (!_button_active) { - // The button isn't active, so it's hidden, regardless of the mouse - // position. - bstate = BS_hidden; - } else { - // Is the mouse pointer within the button region? - bool is_within = (_mouse_x >= _button_x && _mouse_x < _button_x + _button_width && - _mouse_y >= _button_y && _mouse_y < _button_y + _button_height); - if (is_within) { - // The mouse is within the button region. This means either click or - // rollover state, according to the mouse button. - if (_mouse_down) { - // We only count it mouse-down if you've clicked down while over the - // button (or you never released the button since the last time you - // clicked down). Clicking down somewhere else and dragging over the - // button doesn't count. - if (orig_bstate == BS_rollover || _button_depressed) { - _button_depressed = true; - bstate = BS_click; - } else { - // Otherwise, we're mousing over the button region with the button - // held down. Hmm, don't think the button should light up. - bstate = BS_ready; - } - } else { - _button_depressed = false; - if (orig_bstate == BS_click) { - // If we just transitioned from mouse down to mouse up, this means a - // click. And the button automatically hides itself after a - // successful click. - bstate = BS_hidden; - _button_active = false; - click_detected = true; - } else { - bstate = BS_rollover; - } - } - } else { - // The mouse is not within the button region. This means ready state. - bstate = BS_ready; - if (!_mouse_down) { - _button_depressed = false; - } - } - } - - set_bstate(bstate); - - // If we detected a click operation in the above, make the callback here, at - // the end of the method, after we have finished updating the button state. - if (click_detected) { - button_click_detected(); - } -} - -/** - * Called when a button click by the user is detected in set_mouse_data(), - * this method simply turns around and notifies the instance. It's a virtual - * method to give subclasses a chance to redirect this message to the main - * thread or process, as necessary. - * - * Note that this method might be called in a sub-thread. - */ -void P3DSplashWindow:: -button_click_detected() { - assert(_inst != nullptr); - nout << "Play button clicked by user\n"; - _inst->splash_button_clicked_sub_thread(); -} - -/** - * Changes the button state as the mouse interacts with it. - */ -void P3DSplashWindow:: -set_bstate(ButtonState bstate) { - if (_bstate != bstate) { - _bstate = bstate; - // If we've changed button states, we need to refresh the window. - refresh(); - } -} - -/** - * Requests that the window will be repainted. This may or may not be - * implemented for a particular specialization of P3DSplashWindow. - */ -void P3DSplashWindow:: -refresh() { -} diff --git a/direct/src/plugin/p3dSplashWindow.h b/direct/src/plugin/p3dSplashWindow.h deleted file mode 100644 index 4f67b5c1c0..0000000000 --- a/direct/src/plugin/p3dSplashWindow.h +++ /dev/null @@ -1,156 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dSplashWindow.h - * @author drose - * @date 2009-06-17 - */ - -#ifndef P3DSPLASHWINDOW_H -#define P3DSPLASHWINDOW_H - -#include "p3d_plugin_common.h" -#include "p3dFileParams.h" -#include "p3dWindowParams.h" - -class P3DInstance; - -/** - * This window is displayed temporarily, in place of an instance's actual - * window, during the initial launch of an applet, and also during the initial - * download of Panda3D code if necessary. - * - * This is the base implementation; it contains no specific code to open a - * window. - */ -class P3DSplashWindow { -public: - P3DSplashWindow(P3DInstance *inst, bool make_visible); - virtual ~P3DSplashWindow(); - - inline const P3DFileParams &get_fparams() const; - - virtual void set_wparams(const P3DWindowParams &wparams); - inline const P3DWindowParams &get_wparams() const; - - virtual void set_visible(bool visible); - inline bool get_visible() const; - - enum ImagePlacement { - IP_background, - IP_button_ready, - IP_button_rollover, - IP_button_click, - IP_none - }; - enum FontStyle { - FS_normal, - FS_italic, - FS_oblique - }; - enum FontWeight { - FW_normal = 400, - FW_medium = 500, - FW_bold = 700, - FW_black = 900 - }; - - virtual void set_image_filename(const std::string &image_filename, - ImagePlacement image_placement); - void set_fgcolor(int r, int g, int b); - void set_bgcolor(int r, int g, int b); - void set_barcolor(int r, int g, int b); - void set_bar_bgcolor(int r, int g, int b); - void set_bar_border(int border); - void set_bar_bottom(int bottom); - void set_bar_width(int width, bool percent=false); - void set_bar_height(int height, bool percent=false); - void set_font_family(const std::string &family); - void set_font_size(int size); - void set_font_style(FontStyle style); - void set_font_weight(int weight); - virtual void set_install_label(const std::string &install_label); - virtual void set_install_progress(double install_progress, - bool is_progress_known, size_t received_data); - - virtual bool handle_event(const P3D_event_data &event); - - virtual void set_button_active(bool flag); - virtual void request_keyboard_focus(); - -protected: - // This ImageData base class provides minimal functionality for storing a - // loaded image. Most of the real meat of this class is provided by the - // various subclasses. - class ImageData { - public: - inline ImageData(); - int _width, _height, _num_channels; - }; - - bool read_image_data(ImageData &image, std::string &data, - const std::string &image_filename); - void get_bar_placement(int &bar_x, int &bar_y, - int &bar_width, int &bar_height); - void set_button_range(const ImageData &image); - void set_mouse_data(int mouse_x, int mouse_y, bool mouse_down); - - // The current visual state of the button. - enum ButtonState { - BS_hidden, - BS_ready, - BS_rollover, - BS_click, - }; - - virtual void button_click_detected(); - virtual void set_bstate(ButtonState bstate); - virtual void refresh(); - -protected: - P3DInstance *_inst; - P3DFileParams _fparams; - P3DWindowParams _wparams; - int _win_width, _win_height; - bool _visible; - - int _fgcolor_r, _fgcolor_g, _fgcolor_b; - int _bgcolor_r, _bgcolor_g, _bgcolor_b; - int _barcolor_r, _barcolor_g, _barcolor_b; - int _bar_bgcolor_r, _bar_bgcolor_g, _bar_bgcolor_b; - int _bar_border; - -private: - int _bar_bottom; - int _bar_width, _bar_height; - double _bar_width_ratio, _bar_height_ratio; - -protected: - std::string _font_family; - int _font_size; - FontStyle _font_style; - int _font_weight; - - // The region of the window for accepting button clicks. - int _button_width, _button_height; - int _button_x, _button_y; - bool _button_active; - - // Tracking the mouse pointer within the window, for the purposes of - // clicking the button. - int _mouse_x, _mouse_y; - bool _mouse_down; - bool _button_depressed; - ButtonState _bstate; - - static const double _unknown_progress_rate; -}; - -#include "p3dSplashWindow.I" - -#endif diff --git a/direct/src/plugin/p3dStringObject.cxx b/direct/src/plugin/p3dStringObject.cxx deleted file mode 100644 index 8d87a2dc8c..0000000000 --- a/direct/src/plugin/p3dStringObject.cxx +++ /dev/null @@ -1,109 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dStringObject.cxx - * @author drose - * @date 2009-06-30 - */ - -#include "p3dStringObject.h" - -/** - * - */ -P3DStringObject:: -P3DStringObject(const std::string &value) : _value(value) { -} - -/** - * - */ -P3DStringObject:: -P3DStringObject(const char *data, size_t size) : _value(data, size) { -} - -/** - * - */ -P3DStringObject:: -P3DStringObject(const P3DStringObject ©) : - P3DObject(copy), - _value(copy._value) -{ -} - -/** - * - */ -P3DStringObject:: -~P3DStringObject() { -} - -/** - * Returns the fundamental type of this kind of object. - */ -P3D_object_type P3DStringObject:: -get_type() { - return P3D_OT_string; -} - -/** - * Returns the object value coerced to a boolean, if possible. - */ -bool P3DStringObject:: -get_bool() { - return !_value.empty(); -} - -/** - * Fills the indicated C++ string object with the value of this object coerced - * to a string. - */ -void P3DStringObject:: -make_string(std::string &value) { - value = _value; -} - -/** - * Writes a formatted representation of the value to the indicated string. - * This is intended for developer assistance. - */ -void P3DStringObject:: -output(std::ostream &out) { - out << '"'; - for (std::string::const_iterator si = _value.begin(); si != _value.end(); ++si) { - if (isprint(*si)) { - switch (*si) { - case '"': - out << "\\\x22"; - break; - - default: - out << *si; - } - } else { - switch (*si) { - case '\n': - out << "\\n"; - break; - - case '\t': - out << "\\t"; - break; - - default: - { - char buffer[128]; - sprintf(buffer, "%02x", (unsigned char)(*si)); - out << "\\x" << buffer; - } - } - } - } - out << '"'; -} diff --git a/direct/src/plugin/p3dStringObject.h b/direct/src/plugin/p3dStringObject.h deleted file mode 100644 index 3e358e56b9..0000000000 --- a/direct/src/plugin/p3dStringObject.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dStringObject.h - * @author drose - * @date 2009-06-30 - */ - -#ifndef P3DSTRINGOBJECT_H -#define P3DSTRINGOBJECT_H - -#include "p3d_plugin_common.h" -#include "p3dObject.h" - -/** - * An object type that contains a string value. - */ -class P3DStringObject : public P3DObject { -public: - P3DStringObject(const std::string &value); - P3DStringObject(const char *data, size_t size); - P3DStringObject(const P3DStringObject ©); - -public: - virtual ~P3DStringObject(); - - virtual P3D_object_type get_type(); - virtual bool get_bool(); - virtual void make_string(std::string &value); - - virtual void output(std::ostream &out); - -private: - std::string _value; -}; - -#endif diff --git a/direct/src/plugin/p3dTemporaryFile.I b/direct/src/plugin/p3dTemporaryFile.I deleted file mode 100644 index c1d8eeb7e2..0000000000 --- a/direct/src/plugin/p3dTemporaryFile.I +++ /dev/null @@ -1,20 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dTemporaryFile.I - * @author drose - * @date 2009-08-19 - */ - -/** - * Returns the temporary filename. - */ -inline const std::string &P3DTemporaryFile:: -get_filename() const { - return _filename; -} diff --git a/direct/src/plugin/p3dTemporaryFile.cxx b/direct/src/plugin/p3dTemporaryFile.cxx deleted file mode 100644 index bb7c8ca7a1..0000000000 --- a/direct/src/plugin/p3dTemporaryFile.cxx +++ /dev/null @@ -1,33 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dTemporaryFile.cxx - * @author drose - * @date 2009-08-19 - */ - -#include "p3dTemporaryFile.h" -#include "p3dInstanceManager.h" - -/** - * Constructs a new, unique temporary filename. - */ -P3DTemporaryFile:: -P3DTemporaryFile(const std::string &extension) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - _filename = inst_mgr->make_temp_filename(extension); -} - -/** - * Deletes the temporary file, if it exists. - */ -P3DTemporaryFile:: -~P3DTemporaryFile() { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - inst_mgr->release_temp_filename(_filename); -} diff --git a/direct/src/plugin/p3dTemporaryFile.h b/direct/src/plugin/p3dTemporaryFile.h deleted file mode 100644 index 57fb6cef5a..0000000000 --- a/direct/src/plugin/p3dTemporaryFile.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dTemporaryFile.h - * @author drose - * @date 2009-08-19 - */ - -#ifndef P3DTEMPORARYFILE_H -#define P3DTEMPORARYFILE_H - -#include "p3d_plugin_common.h" - -/** - * This represents a temporary filename for some transitory purpose. This - * returns a filename which is guaranteed to be unique at the time the - * constructor was called. - * - * The file on disk, if it exists, will automatically be deleted when the - * destructor is called. - */ -class P3DTemporaryFile { -public: - P3DTemporaryFile(const std::string &extension); - ~P3DTemporaryFile(); - - inline const std::string &get_filename() const; - -private: - std::string _filename; -}; - -inline std::ostream &operator << (std::ostream &out, P3DTemporaryFile &tfile) { - return out << tfile.get_filename(); -} - -#include "p3dTemporaryFile.I" - -#endif diff --git a/direct/src/plugin/p3dUndefinedObject.cxx b/direct/src/plugin/p3dUndefinedObject.cxx deleted file mode 100644 index 0ed227573f..0000000000 --- a/direct/src/plugin/p3dUndefinedObject.cxx +++ /dev/null @@ -1,46 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dUndefinedObject.cxx - * @author drose - * @date 2009-07-07 - */ - -#include "p3dUndefinedObject.h" - -/** - * - */ -P3DUndefinedObject:: -P3DUndefinedObject() { -} - -/** - * Returns the fundamental type of this kind of object. - */ -P3D_object_type P3DUndefinedObject:: -get_type() { - return P3D_OT_undefined; -} - -/** - * Returns the object value coerced to a boolean, if possible. - */ -bool P3DUndefinedObject:: -get_bool() { - return false; -} - -/** - * Fills the indicated C++ string object with the value of this object coerced - * to a string. - */ -void P3DUndefinedObject:: -make_string(std::string &value) { - value = "Undefined"; -} diff --git a/direct/src/plugin/p3dUndefinedObject.h b/direct/src/plugin/p3dUndefinedObject.h deleted file mode 100644 index 3cba1d73b4..0000000000 --- a/direct/src/plugin/p3dUndefinedObject.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dUndefinedObject.h - * @author drose - * @date 2009-07-07 - */ - -#ifndef P3DUNDEFINEDOBJECT_H -#define P3DUNDEFINEDOBJECT_H - -#include "p3d_plugin_common.h" -#include "p3dObject.h" - -/** - * An object type that represents an undefined value. Python doesn't have - * such a concept, but JavaScript does, and it is sometimes an important - * return value. - */ -class P3DUndefinedObject : public P3DObject { -public: - P3DUndefinedObject(); - -public: - virtual P3D_object_type get_type(); - virtual bool get_bool(); - virtual void make_string(std::string &value); -}; - -#endif diff --git a/direct/src/plugin/p3dWinSplashWindow.I b/direct/src/plugin/p3dWinSplashWindow.I deleted file mode 100644 index bae5fb0680..0000000000 --- a/direct/src/plugin/p3dWinSplashWindow.I +++ /dev/null @@ -1,29 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dWinSplashWindow.I - * @author drose - * @date 2009-06-17 - */ - -/** - * - */ -inline P3DWinSplashWindow::WinImageData:: -WinImageData() { - _filename_changed = false; - _bitmap = nullptr; -} - -/** - * - */ -inline P3DWinSplashWindow::WinImageData:: -~WinImageData() { - dump_image(); -} diff --git a/direct/src/plugin/p3dWinSplashWindow.cxx b/direct/src/plugin/p3dWinSplashWindow.cxx deleted file mode 100644 index 256acb5eb8..0000000000 --- a/direct/src/plugin/p3dWinSplashWindow.cxx +++ /dev/null @@ -1,926 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dWinSplashWindow.cxx - * @author drose - * @date 2009-06-17 - */ - -#include "p3dWinSplashWindow.h" - -#ifdef _WIN32 - -#ifndef WM_MOUSEWHEEL -#define WM_MOUSEWHEEL 0x20a -#endif - -bool P3DWinSplashWindow::_registered_window_class = false; - -/** - * - */ -P3DWinSplashWindow:: -P3DWinSplashWindow(P3DInstance *inst, bool make_visible) : - P3DSplashWindow(inst, make_visible) -{ - _thread = nullptr; - _thread_id = 0; - _hwnd = nullptr; - _font = nullptr; - _fg_brush = nullptr; - _bg_brush = nullptr; - _bar_brush = nullptr; - _bar_bg_brush = nullptr; - _thread_running = false; - _install_progress = 0.0; - _progress_known = true; - _received_data = 0; - - _drawn_bstate = BS_hidden; - _drawn_progress = 0.0; - _drawn_progress_known = true; - _drawn_received_data = 0; - _focus_seq = 0; - - _request_focus_tick = 0; - - INIT_LOCK(_install_lock); -} - -/** - * - */ -P3DWinSplashWindow:: -~P3DWinSplashWindow() { - stop_thread(); - - DESTROY_LOCK(_install_lock); -} - -/** - * Changes the window parameters, e.g. to resize or reposition the window; or - * sets the parameters for the first time, creating the initial window. - */ -void P3DWinSplashWindow:: -set_wparams(const P3DWindowParams &wparams) { - P3DSplashWindow::set_wparams(wparams); - - if (_thread_id == 0) { - start_thread(); - } -} - -/** - * Makes the splash window visible or invisible, so as not to compete with the - * embedded Panda window in the same space. - */ -void P3DWinSplashWindow:: -set_visible(bool visible) { - P3DSplashWindow::set_visible(visible); - - if (_visible) { - ShowWindow(_hwnd, SW_SHOWNORMAL); - } else { - ShowWindow(_hwnd, SW_HIDE); - } -} - -/** - * Specifies the name of a JPEG image file that is displayed in the center of - * the splash window. - */ -void P3DWinSplashWindow:: -set_image_filename(const std::string &image_filename, ImagePlacement image_placement) { - nout << "image_filename = " << image_filename << ", thread_id = " << _thread_id << "\n"; - WinImageData *image = nullptr; - switch (image_placement) { - case IP_background: - image = &_background_image; - break; - - case IP_button_ready: - image = &_button_ready_image; - set_button_range(_button_ready_image); - break; - - case IP_button_rollover: - image = &_button_rollover_image; - break; - - case IP_button_click: - image = &_button_click_image; - break; - } - if (image != nullptr) { - ACQUIRE_LOCK(_install_lock); - if (image->_filename != image_filename) { - image->_filename = image_filename; - image->_filename_changed = true; - } - RELEASE_LOCK(_install_lock); - } - - if (_thread_id != 0) { - // Post a silly message to spin the message loop. - PostThreadMessage(_thread_id, WM_USER, 0, 0); - - if (!_thread_running && _thread_continue) { - // The user must have closed the window. Let's shut down the instance, - // too. - _inst->request_stop_main_thread(); - } - } -} - -/** - * Specifies the text that is displayed above the install progress bar. - */ -void P3DWinSplashWindow:: -set_install_label(const std::string &install_label) { - ACQUIRE_LOCK(_install_lock); - if (_install_label != install_label) { - _install_label = install_label; - } - RELEASE_LOCK(_install_lock); - - if (_thread_id != 0) { - // Post a silly message to spin the message loop. - PostThreadMessage(_thread_id, WM_USER, 0, 0); - - if (!_thread_running && _thread_continue) { - // The user must have closed the window. Let's shut down the instance, - // too. - _inst->request_stop_main_thread(); - } - } -} - -/** - * Moves the install progress bar from 0.0 to 1.0. - */ -void P3DWinSplashWindow:: -set_install_progress(double install_progress, - bool is_progress_known, size_t received_data) { - ACQUIRE_LOCK(_install_lock); - _install_progress = install_progress; - _progress_known = is_progress_known; - _received_data = received_data; - RELEASE_LOCK(_install_lock); - - if (_thread_id != 0) { - // Post a silly message to spin the message loop. - PostThreadMessage(_thread_id, WM_USER, 0, 0); - - if (!_thread_running && _thread_continue) { - // The user must have closed the window. Let's shut down the instance, - // too. - _inst->request_stop_main_thread(); - } - } -} - -/** - * The Panda window is asking us to manage keyboard focus in proxy for it. - * This is used on Vista, where the Panda window may be disallowed from - * directly assigning itself keyboard focus. - */ -void P3DWinSplashWindow:: -request_keyboard_focus() { - // Store the time at which we last requested focus. - _request_focus_tick = GetTickCount(); - - // Increment the _focus_seq to tell the thread to call SetFocus(). - ACQUIRE_LOCK(_install_lock); - ++_focus_seq; - RELEASE_LOCK(_install_lock); - - if (_thread_id != 0) { - // Post a silly message to spin the message loop. - PostThreadMessage(_thread_id, WM_USER, 0, 0); - } -} - -/** - * Registers the window class for this window, if needed. - */ -void P3DWinSplashWindow:: -register_window_class() { - if (!_registered_window_class) { - HINSTANCE application = GetModuleHandle(nullptr); - - WNDCLASS wc; - ZeroMemory(&wc, sizeof(WNDCLASS)); - wc.lpfnWndProc = (WNDPROC)st_window_proc; - wc.hInstance = application; - wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.lpszClassName = "panda3d_splash"; - - if (!RegisterClass(&wc)) { - nout << "Could not register window class panda3d_splash\n"; - } - _registered_window_class = true; - } -} - -/** - * Unregisters the window class for this window. It is necessary to do this - * before unloading the DLL. - */ -void P3DWinSplashWindow:: -unregister_window_class() { - if (_registered_window_class) { - HINSTANCE application = GetModuleHandle(nullptr); - - if (!UnregisterClass("panda3d_splash", application)) { - nout << "Could not unregister window class panda3d_splash\n"; - } - _registered_window_class = false; - } -} - -/** - * Called when a button click by the user is detected in set_mouse_data(), - * this method simply turns around and notifies the instance. It's a virtual - * method to give subclasses a chance to redirect this message to the main - * thread or process, as necessary. - */ -void P3DWinSplashWindow:: -button_click_detected() { - P3DSplashWindow::button_click_detected(); -} - -/** - * Spawns the sub-thread. - */ -void P3DWinSplashWindow:: -start_thread() { - _thread_continue = true; - _thread_running = true; - _thread = CreateThread(nullptr, 0, &win_thread_run, this, 0, &_thread_id); - if (_thread == nullptr) { - // Thread never got started. - _thread_running = false; - } -} - -/** - * Terminates and joins the sub-thread. - */ -void P3DWinSplashWindow:: -stop_thread() { - _thread_continue = false; - - if (_thread_id != 0) { - // Post a silly message to spin the message loop. - PostThreadMessage(_thread_id, WM_USER, 0, 0); - } - - if (_thread != nullptr){ - // If the thread doesn't close right away, call PeekMessage() to check for - // Windows messages that the thread might be waiting for. - while (WaitForSingleObject(_thread, 200) == WAIT_TIMEOUT) { - MSG msg; - PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE | PM_NOYIELD); - nout << "Waiting for thread\n"; - } - - CloseHandle(_thread); - _thread = nullptr; - - // Now that the thread has exited, we can safely close its window. (We - // couldn't close the window in the thread, because that would cause a - // deadlock situation--the thread can't acknowledge the window closing - // until we spin the event loop in the parent thread.) - close_window(); - } -} - -/** - * The sub-thread's main run method. - */ -void P3DWinSplashWindow:: -thread_run() { - make_window(); - - int last_focus_seq = 0; - MSG msg; - int retval; - retval = GetMessage(&msg, nullptr, 0, 0); - while (retval != 0 && _thread_continue) { - if (retval == -1) { - nout << "Error processing message queue.\n"; - break; - } - TranslateMessage(&msg); - DispatchMessage(&msg); - - ACQUIRE_LOCK(_install_lock); - - update_image(_background_image); - update_image(_button_ready_image); - update_image(_button_rollover_image); - update_image(_button_click_image); - - if (_drawn_label != _install_label) { - // The label has changed. Redraw. - _drawn_label = _install_label; - InvalidateRect(_hwnd, nullptr, TRUE); - } - - // Also redraw when the progress bar changes. - bool needs_update_progress = false; - if (_progress_known != _drawn_progress_known) { - needs_update_progress = true; - } else if (_progress_known) { - if (_install_progress != _drawn_progress) { - needs_update_progress = true; - } - } else { - if (_received_data != _drawn_received_data) { - needs_update_progress = true; - } - } - if (needs_update_progress) { - _drawn_progress = _install_progress; - _drawn_progress_known = _progress_known; - _drawn_received_data = _received_data; - InvalidateRect(_hwnd, nullptr, TRUE); - } - - if (_drawn_bstate != _bstate) { - // The button has changed state. Redraw it. - _drawn_bstate = _bstate; - InvalidateRect(_hwnd, nullptr, TRUE); - } - - if (_focus_seq != last_focus_seq) { - last_focus_seq = _focus_seq; - if (SetFocus(_hwnd) == nullptr && GetLastError() != 0) { - nout << "SetFocus(" << _hwnd << ") failed: " << GetLastError() << "\n"; - } - } - - RELEASE_LOCK(_install_lock); - - retval = GetMessage(&msg, nullptr, 0, 0); - } - - // Tell our parent thread that we're done. - _thread_running = false; - - // Close the instance. - _inst->request_stop_sub_thread(); -} - -/** - * The OS-specific thread callback function. - */ -DWORD P3DWinSplashWindow:: -win_thread_run(LPVOID data) { - ((P3DWinSplashWindow *)data)->thread_run(); - return 0; -} - -/** - * Creates the window for displaying progress. Runs within the sub-thread. - */ -void P3DWinSplashWindow:: -make_window() { - register_window_class(); - HINSTANCE application = GetModuleHandle(nullptr); - - int width = 320; - int height = 240; - if (_wparams.get_win_width() != 0 && _wparams.get_win_height() != 0) { - width = _wparams.get_win_width(); - height = _wparams.get_win_height(); - } - - int x = _wparams.get_win_x(); - int y = _wparams.get_win_y(); - if (x == -1) x = CW_USEDEFAULT; - if (y == -1) y = CW_USEDEFAULT; - if (x == -2) x = (int)(0.5 * (GetSystemMetrics(SM_CXSCREEN) - width)); - if (y == -2) y = (int)(0.5 * (GetSystemMetrics(SM_CYSCREEN) - height)); - - if (_wparams.get_window_type() == P3D_WT_embedded) { - // Create an embedded window. - DWORD window_style = - WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; - - const P3D_window_handle &handle = _wparams.get_parent_window(); - assert(handle._window_handle_type == P3D_WHT_win_hwnd); - HWND parent_hwnd = handle._handle._win_hwnd._hwnd; - - _hwnd = - CreateWindow("panda3d_splash", "Panda3D", window_style, - x, y, width, height, - parent_hwnd, nullptr, application, 0); - - if (!_hwnd) { - nout << "Could not create embedded window!\n"; - return; - } - - } else { - // Create a toplevel window. - DWORD window_style = - WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | - WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | - WS_SIZEBOX | WS_MAXIMIZEBOX; - - RECT win_rect = { 0, 0, width, height }; - // Adjust window size based on desired client area size - AdjustWindowRect(&win_rect, window_style, FALSE); - _hwnd = - CreateWindow("panda3d_splash", "Panda3D", window_style, - x, y, - win_rect.right - win_rect.left, - win_rect.bottom - win_rect.top, - nullptr, nullptr, application, 0); - if (!_hwnd) { - nout << "Could not create toplevel window!\n"; - return; - } - } - SetWindowLongPtr(_hwnd, GWLP_USERDATA, (LONG_PTR)this); - nout << "Created splash window " << _hwnd << "\n"; - - if (_visible) { - ShowWindow(_hwnd, SW_SHOWNORMAL); - } else { - ShowWindow(_hwnd, SW_HIDE); - } - - // Load the requested font. - _font = CreateFontA(-_font_size, 0, 0, 0, _font_weight, - (_font_style != FS_normal), FALSE, FALSE, - ANSI_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, - CLEARTYPE_QUALITY, VARIABLE_PITCH, _font_family.c_str()); - - if (_font == nullptr) { - nout << "CreateFont failed: " << GetLastError() << "\n"; - _font = (HFONT)GetStockObject(ANSI_VAR_FONT); - } - - _fg_brush = CreateSolidBrush(RGB(_fgcolor_r, _fgcolor_g, _fgcolor_b)); - _bg_brush = CreateSolidBrush(RGB(_bgcolor_r, _bgcolor_g, _bgcolor_b)); - _bar_brush = CreateSolidBrush(RGB(_barcolor_r, _barcolor_g, _barcolor_b)); - _bar_bg_brush = CreateSolidBrush(RGB(_bar_bgcolor_r, _bar_bgcolor_g, _bar_bgcolor_b)); -} - -/** - * Loads the image from the named file (if it has changed), converts to to - * BITMAP form, and stores it in _bitmap. Runs only in the sub-thread. - */ -void P3DWinSplashWindow:: -update_image(WinImageData &image) { - if (!image._filename_changed) { - // No changes. - return; - } - image._filename_changed = false; - - // Clear the old image. - image.dump_image(); - - // Since we'll be displaying a new image, we need to refresh the window. - InvalidateRect(_hwnd, nullptr, TRUE); - - // Go read the image. - std::string data; - if (!read_image_data(image, data, image._filename)) { - return; - } - - // Massage the data into Windows' conventions. - int row_stride = image._width * image._num_channels; - int new_row_stride = (image._width * 4); - - int new_data_length = new_row_stride * image._height; - char *new_data = new char[new_data_length]; - - if (image._num_channels == 4) { - // We have to reverse the order of the RGB channels: libjpeg and Windows - // follow an opposite convention. - for (int yi = 0; yi < image._height; ++yi) { - const char *sp = data.data() + yi * row_stride; - char *dp = new_data + yi * new_row_stride; - for (int xi = 0; xi < image._width; ++xi) { - dp[0] = sp[2]; - dp[1] = sp[1]; - dp[2] = sp[0]; - dp[3] = sp[3]; - sp += 4; - dp += 4; - } - } - } else if (image._num_channels == 3) { - // We have to reverse the order of the RGB channels: libjpeg and Windows - // follow an opposite convention. - for (int yi = 0; yi < image._height; ++yi) { - const char *sp = data.data() + yi * row_stride; - char *dp = new_data + yi * new_row_stride; - for (int xi = 0; xi < image._width; ++xi) { - dp[0] = sp[2]; - dp[1] = sp[1]; - dp[2] = sp[0]; - dp[3] = (char)0xff; - sp += 3; - dp += 4; - } - } - } else if (image._num_channels == 1) { - // A grayscale image. Replicate out the channels. - for (int yi = 0; yi < image._height; ++yi) { - const char *sp = data.data() + yi * row_stride; - char *dp = new_data + yi * new_row_stride; - for (int xi = 0; xi < image._width; ++xi) { - dp[0] = sp[0]; - dp[1] = sp[0]; - dp[2] = sp[0]; - dp[3] = (char)0xff; - sp += 1; - dp += 4; - } - } - } - - // Now load the image. - BITMAPINFOHEADER bmih; - bmih.biSize = sizeof(bmih); - bmih.biWidth = image._width; - bmih.biHeight = -image._height; - bmih.biPlanes = 1; - bmih.biBitCount = 32; - bmih.biCompression = BI_RGB; - bmih.biSizeImage = 0; - bmih.biXPelsPerMeter = 0; - bmih.biYPelsPerMeter = 0; - bmih.biClrUsed = 0; - bmih.biClrImportant = 0; - - BITMAPINFO bmi; - memcpy(&bmi, &bmih, sizeof(bmih)); - - HDC dc = GetDC(_hwnd); - image._bitmap = CreateDIBitmap(dc, &bmih, CBM_INIT, new_data, &bmi, 0); - ReleaseDC(_hwnd, dc); - - delete[] new_data; - - nout << "Loaded image: " << image._filename << "\n"; -} - -/** - * Closes the window created above. This call is actually made in the main - * thread. - */ -void P3DWinSplashWindow:: -close_window() { - if (_hwnd != nullptr) { - ShowWindow(_hwnd, SW_HIDE); - CloseWindow(_hwnd); - _hwnd = nullptr; - } - - if (_fg_brush != nullptr) { - DeleteObject(_fg_brush); - _fg_brush = nullptr; - } - if (_bg_brush != nullptr) { - DeleteObject(_bg_brush); - _bg_brush = nullptr; - } - if (_bar_brush != nullptr) { - DeleteObject(_bar_brush); - _bar_brush = nullptr; - } - if (_bar_bg_brush != nullptr) { - DeleteObject(_bar_bg_brush); - _bar_bg_brush = nullptr; - } - - _background_image.dump_image(); - _button_ready_image.dump_image(); - _button_rollover_image.dump_image(); - _button_click_image.dump_image(); -} - -/** - * Paints the contents of the window into the indicated DC. - */ -void P3DWinSplashWindow:: -paint_window(HDC dc) { - RECT rect; - GetClientRect(_hwnd, &rect); - - int width = rect.right - rect.left; - int height = rect.bottom - rect.top; - if (width != _win_width || height != _win_height) { - _win_width = width; - _win_height = height; - set_button_range(_button_ready_image); - } - - // Double-buffer with an offscreen bitmap first. - HDC bdc = CreateCompatibleDC(dc); - HBITMAP buffer = CreateCompatibleBitmap(dc, _win_width, _win_height); - SelectObject(bdc, buffer); - - // Start by painting the background color. - FillRect(bdc, &rect, _bg_brush); - - // Then paint the background image on top of that. - paint_image(bdc, _background_image, false); - - // And then, paint the button, if any, on top of *that*. - switch (_drawn_bstate) { - case BS_hidden: - break; - case BS_ready: - paint_image(bdc, _button_ready_image, true); - break; - case BS_rollover: - if (!paint_image(bdc, _button_rollover_image, true)) { - paint_image(bdc, _button_ready_image, true); - } - break; - case BS_click: - if (!paint_image(bdc, _button_click_image, true)) { - paint_image(bdc, _button_ready_image, true); - } - break; - } - - // Draw the progress bar. We don't draw this bar at all unless we have - // nonzero progress. - if (!_drawn_progress_known || _drawn_progress != 0.0) { - paint_progress_bar(bdc); - } - - // Now blit the buffer to the window. - BitBlt(dc, 0, 0, _win_width, _win_height, bdc, 0, 0, SRCCOPY); - - DeleteObject(bdc); - DeleteObject(buffer); -} - -/** - * Draws the indicated image, centered within the window. Returns true on - * success, false if the image is not defined. - */ -bool P3DWinSplashWindow:: -paint_image(HDC dc, const WinImageData &image, bool use_alpha) { - if (image._bitmap == nullptr) { - return false; - } - - // Paint the background splash image. - HDC mem_dc = CreateCompatibleDC(dc); - SelectObject(mem_dc, image._bitmap); - - // Determine the relative size of bitmap and window. - int win_cx = _win_width / 2; - int win_cy = _win_height / 2; - - BLENDFUNCTION bf; - bf.BlendOp = AC_SRC_OVER; - bf.BlendFlags = 0; - bf.SourceConstantAlpha = 0xff; - bf.AlphaFormat = AC_SRC_ALPHA; - - if (image._width <= _win_width && image._height <= _win_height) { - // The bitmap fits within the window; center it. - - // This is the top-left corner of the bitmap in window coordinates. - int p_x = win_cx - image._width / 2; - int p_y = win_cy - image._height / 2; - - if (!use_alpha) { - BitBlt(dc, p_x, p_y, image._width, image._height, - mem_dc, 0, 0, SRCCOPY); - } else { - AlphaBlend(dc, p_x, p_y, image._width, image._height, - mem_dc, 0, 0, image._width, image._height, - bf); - } - - } else { - // The bitmap is larger than the window; scale it down. - double x_scale = (double)_win_width / (double)image._width; - double y_scale = (double)_win_height / (double)image._height; - double scale = std::min(x_scale, y_scale); - int sc_width = (int)(image._width * scale); - int sc_height = (int)(image._height * scale); - - int p_x = win_cx - sc_width / 2; - int p_y = win_cy - sc_height / 2; - - if (!use_alpha) { - StretchBlt(dc, p_x, p_y, sc_width, sc_height, - mem_dc, 0, 0, image._width, image._height, SRCCOPY); - } else { - // For some reason, AlphaBlend has issues when scaling a black-and-white - // image to draw onto the window: it draws the image in the last fill - // color used on the dc, instead of black. This only happens when the - // image consists only of black and white, and only when the image is - // being scaled. Weird. But StretchBlt, above, doesn't have this - // problem. - AlphaBlend(dc, p_x, p_y, sc_width, sc_height, - mem_dc, 0, 0, image._width, image._height, - bf); - } - } - - SelectObject(mem_dc, nullptr); - DeleteDC(mem_dc); - - return true; -} - -/** - * Draws the progress bar and the label within the window. - */ -void P3DWinSplashWindow:: -paint_progress_bar(HDC dc) { - int bar_x, bar_y, bar_width, bar_height; - get_bar_placement(bar_x, bar_y, bar_width, bar_height); - - RECT bar_rect = { bar_x, bar_y, bar_x + bar_width, bar_y + bar_height }; - - // Clear the entire progress bar to white (or the background color). - FillRect(dc, &bar_rect, _bar_bg_brush); - - // Draw the interior of the progress bar in blue (or the bar color). - if (_drawn_progress_known) { - int progress_width = (int)(bar_width * _drawn_progress + 0.5); - if (progress_width != 0) { - RECT prog_rect = { bar_x, bar_y, bar_x + progress_width, bar_y + bar_height }; - FillRect(dc, &prog_rect, _bar_brush); - } - } else { - // Progress is unknown. Draw a moving block, not a progress bar filling - // up. - int block_width = (int)(bar_width * 0.1 + 0.5); - int block_travel = bar_width - block_width; - int progress = (int)(_received_data * _unknown_progress_rate); - progress = progress % (block_travel * 2); - if (progress > block_travel) { - progress = block_travel * 2 - progress; - } - - RECT prog_rect = { bar_x + progress, bar_y, - bar_x + progress + block_width, bar_y + bar_height }; - FillRect(dc, &prog_rect, _bar_brush); - } - - // Now draw a black (or foreground) border around the progress bar. - if (_bar_border >= 0) { - RECT border_rect = bar_rect; - - for (int i = 0; i < _bar_border; ++i) { - --border_rect.left; - --border_rect.top; - ++border_rect.right; - ++border_rect.bottom; - FrameRect(dc, &border_rect, _fg_brush); - } - } - - if (!_drawn_label.empty()) { - // Now draw the install_label right above it. - const char *text = _drawn_label.c_str(); - - // Measure the text, for centering. - SelectObject(dc, _font); - SIZE text_size; - GetTextExtentPoint32(dc, text, strlen(text), &text_size); - - int text_width = text_size.cx; - int text_height = text_size.cy; - int text_x = (_win_width - text_width) / 2; - int text_y = bar_y - (int)(text_height * 1.5) - _bar_border; - - // Clear the rectangle behind the text to white. - RECT text_rect = { text_x - 2, text_y - 2, text_x + text_width + 4, text_y + text_height + 4 }; - - FillRect(dc, &text_rect, _bg_brush); - - // And finally, draw the text. - SetTextColor(dc, RGB(_fgcolor_r, _fgcolor_g, _fgcolor_b)); - SetBkColor(dc, RGB(_bgcolor_r, _bgcolor_g, _bgcolor_b)); - DrawText(dc, text, -1, &text_rect, - DT_VCENTER | DT_CENTER | DT_SINGLELINE); - } -} - -/** - * The windows event-processing handler. - */ -LRESULT P3DWinSplashWindow:: -window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - switch (msg) { - case WM_DESTROY: - PostQuitMessage(0); - break; - - case WM_SIZE: - InvalidateRect(hwnd, nullptr, FALSE); - break; - - case WM_ERASEBKGND: - return true; - - case WM_PAINT: - { - PAINTSTRUCT ps; - HDC dc = BeginPaint(hwnd, &ps); - paint_window(dc); - EndPaint(hwnd, &ps); - } - return true; - - case WM_MOUSEMOVE: - set_mouse_data(LOWORD(lparam), HIWORD(lparam), _mouse_down); - break; - - case WM_LBUTTONDOWN: - SetCapture(hwnd); - set_mouse_data(_mouse_x, _mouse_y, true); - break; - - case WM_LBUTTONUP: - set_mouse_data(_mouse_x, _mouse_y, false); - ReleaseCapture(); - break; - - case WM_KILLFOCUS: - // Someone on the desktop is playing games with us. It keeps wanting to - // grab the keyboard focus back immediately after we successfully call - // SetFocus(). Well, we really mean it, darn it all. If we got a - // WM_KILLFOCUS within a few milliseconds of calling SetFocus(), well, - // call SetFocus() again, until it sticks. - { - int elapsed = GetTickCount() - _request_focus_tick; - if (elapsed < 200) { - if (SetFocus(_hwnd) == nullptr && GetLastError() != 0) { - nout << "Secondary SetFocus failed: " << GetLastError() << "\n"; - } - } - } - break; - - // Keyboard events that are to be proxied to the Panda window. - case WM_MOUSEWHEEL: - case WM_IME_SETCONTEXT: - case WM_IME_NOTIFY: - case WM_IME_STARTCOMPOSITION: - case WM_IME_ENDCOMPOSITION: - case WM_IME_COMPOSITION: - case WM_CHAR: - case WM_SYSKEYDOWN: - case WM_SYSCOMMAND: - case WM_KEYDOWN: - case WM_SYSKEYUP: - case WM_KEYUP: - if (_inst->get_session() != nullptr) { - _inst->get_session()->send_windows_message(_inst, msg, wparam, lparam); - } - break; - }; - - return DefWindowProc(hwnd, msg, wparam, lparam); -} - -/** - * The windows event-processing handler, static version. - */ -LRESULT P3DWinSplashWindow:: -st_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - LONG_PTR self = GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (self == nullptr) { - // We haven't assigned the pointer yet. - return DefWindowProc(hwnd, msg, wparam, lparam); - } - - return ((P3DWinSplashWindow *)self)->window_proc(hwnd, msg, wparam, lparam); -} - -/** - * Frees the previous image data. - */ -void P3DWinSplashWindow::WinImageData:: -dump_image() { - if (_bitmap != nullptr) { - DeleteObject(_bitmap); - _bitmap = nullptr; - } -} - -#endif // _WIN32 diff --git a/direct/src/plugin/p3dWinSplashWindow.h b/direct/src/plugin/p3dWinSplashWindow.h deleted file mode 100644 index 713157c921..0000000000 --- a/direct/src/plugin/p3dWinSplashWindow.h +++ /dev/null @@ -1,122 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dWinSplashWindow.h - * @author drose - * @date 2009-06-17 - */ - -#ifndef P3DWINSPLASHWINDOW_H -#define P3DWINSPLASHWINDOW_H - -#include "p3d_plugin_common.h" - -#ifdef _WIN32 - -#include "p3dSplashWindow.h" -#include "p3d_lock.h" - -#include - -/** - * This is the Windows implementation of the initial-download window. - */ -class P3DWinSplashWindow : public P3DSplashWindow { -public: - P3DWinSplashWindow(P3DInstance *inst, bool make_visible); - virtual ~P3DWinSplashWindow(); - - virtual void set_wparams(const P3DWindowParams &wparams); - virtual void set_visible(bool visible); - - virtual void set_image_filename(const std::string &image_filename, - ImagePlacement image_placement); - virtual void set_install_label(const std::string &install_label); - virtual void set_install_progress(double install_progress, - bool is_progress_known, size_t received_data); - virtual void request_keyboard_focus(); - - static void register_window_class(); - static void unregister_window_class(); - -protected: - virtual void button_click_detected(); - -private: - void start_thread(); - void stop_thread(); - -private: - class WinImageData; - - // These methods run only within the window thread. - void thread_run(); - static DWORD WINAPI win_thread_run(LPVOID data); - - void make_window(); - void update_image(WinImageData &image); - void close_window(); - - void paint_window(HDC dc); - bool paint_image(HDC dc, const WinImageData &image, bool use_alpha); - void paint_progress_bar(HDC dc); - LRESULT window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); - static LRESULT WINAPI st_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); - -private: - class WinImageData : public ImageData { - public: - inline WinImageData(); - inline ~WinImageData(); - void dump_image(); - - std::string _filename; - bool _filename_changed; - HBITMAP _bitmap; - }; - - WinImageData _background_image; - WinImageData _button_ready_image; - WinImageData _button_rollover_image; - WinImageData _button_click_image; - - bool _got_install; - std::string _install_label; - double _install_progress; - bool _progress_known; - size_t _received_data; - LOCK _install_lock; - - ButtonState _drawn_bstate; - std::string _drawn_label; - double _drawn_progress; - bool _drawn_progress_known; - size_t _drawn_received_data; - int _focus_seq; - - int _request_focus_tick; - - bool _thread_continue; - bool _thread_running; - HANDLE _thread; - DWORD _thread_id; - HWND _hwnd; - HFONT _font; - HBRUSH _fg_brush; - HBRUSH _bg_brush; - HBRUSH _bar_brush; - HBRUSH _bar_bg_brush; - - static bool _registered_window_class; -}; - -#include "p3dWinSplashWindow.I" - -#endif // _WIN32 - -#endif diff --git a/direct/src/plugin/p3dWindowParams.I b/direct/src/plugin/p3dWindowParams.I deleted file mode 100644 index 667d94210a..0000000000 --- a/direct/src/plugin/p3dWindowParams.I +++ /dev/null @@ -1,69 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dWindowParams.I - * @author drose - * @date 2009-06-22 - */ - -/** - * Returns the window_type that was passed to the constructor, or to - * set_window_type(). - */ -inline P3D_window_type P3DWindowParams:: -get_window_type() const { - return _window_type; -} - -/** - * Changes the window_type. - */ -inline void P3DWindowParams:: -set_window_type(P3D_window_type window_type) { - _window_type = window_type; -} - -/** - * Returns the window origin X coordinate that was passed to the constructor. - */ -inline int P3DWindowParams:: -get_win_x() const { - return _win_x; -} - -/** - * Returns the window origin Y coordinate that was passed to the constructor. - */ -inline int P3DWindowParams:: -get_win_y() const { - return _win_y; -} - -/** - * Returns the window width that was passed to the constructor. - */ -inline int P3DWindowParams:: -get_win_width() const { - return _win_width; -} - -/** - * Returns the window height that was passed to the constructor. - */ -inline int P3DWindowParams:: -get_win_height() const { - return _win_height; -} - -/** - * Returns the parent window handle that was passed to the constructor. - */ -inline const P3D_window_handle &P3DWindowParams:: -get_parent_window() const { - return _parent_window; -} diff --git a/direct/src/plugin/p3dWindowParams.cxx b/direct/src/plugin/p3dWindowParams.cxx deleted file mode 100644 index 29ed954823..0000000000 --- a/direct/src/plugin/p3dWindowParams.cxx +++ /dev/null @@ -1,109 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dWindowParams.cxx - * @author drose - * @date 2009-06-22 - */ - -#include "p3dWindowParams.h" - -/** - * - */ -P3DWindowParams:: -P3DWindowParams() : - _window_type(P3D_WT_hidden), - _win_x(-1), _win_y(-1), - _win_width(0), _win_height(0) -{ -} - -/** - * - */ -P3DWindowParams:: -P3DWindowParams(P3D_window_type window_type, - int win_x, int win_y, - int win_width, int win_height, - P3D_window_handle parent_window) : - _window_type(window_type), - _win_x(win_x), _win_y(win_y), - _win_width(win_width), _win_height(win_height), - _parent_window(parent_window) -{ -} - -/** - * - */ -void P3DWindowParams:: -operator = (const P3DWindowParams &other) { - _window_type = other._window_type; - _win_x = other._win_x; - _win_y = other._win_y; - _win_width = other._win_width; - _win_height = other._win_height; - _parent_window = other._parent_window; -} - -/** - * Returns a newly-allocated XML structure that corresponds to the window - * parameter data within this instance. - */ -TiXmlElement *P3DWindowParams:: -make_xml(P3DInstance *inst) { - TiXmlElement *xwparams = new TiXmlElement("wparams"); - - switch (_window_type) { - case P3D_WT_embedded: - xwparams->SetAttribute("window_type", "embedded"); - xwparams->SetAttribute("win_x", _win_x); - xwparams->SetAttribute("win_y", _win_y); - xwparams->SetAttribute("win_width", _win_width); - xwparams->SetAttribute("win_height", _win_height); -#ifdef _WIN32 - assert(_parent_window._window_handle_type == P3D_WHT_win_hwnd); - xwparams->SetAttribute("parent_hwnd", (int)_parent_window._handle._win_hwnd._hwnd); - -#elif defined(__APPLE__) - xwparams->SetAttribute("subprocess_window", inst->_shared_filename); - -#elif defined(HAVE_X11) - // TinyXml doesn't support a "long" attribute. We'll use stringstream to - // do it ourselves. - { - std::ostringstream strm; - assert(_parent_window._window_handle_type == P3D_WHT_x11_window); - strm << _parent_window._handle._x11_window._xwindow; - xwparams->SetAttribute("parent_xwindow", strm.str()); - } -#endif - break; - - case P3D_WT_toplevel: - xwparams->SetAttribute("window_type", "toplevel"); - xwparams->SetAttribute("win_x", _win_x); - xwparams->SetAttribute("win_y", _win_y); - xwparams->SetAttribute("win_width", _win_width); - xwparams->SetAttribute("win_height", _win_height); - break; - - case P3D_WT_fullscreen: - xwparams->SetAttribute("window_type", "fullscreen"); - xwparams->SetAttribute("win_width", _win_width); - xwparams->SetAttribute("win_height", _win_height); - break; - - case P3D_WT_hidden: - xwparams->SetAttribute("window_type", "hidden"); - break; - } - - return xwparams; -} diff --git a/direct/src/plugin/p3dWindowParams.h b/direct/src/plugin/p3dWindowParams.h deleted file mode 100644 index 91d8639b28..0000000000 --- a/direct/src/plugin/p3dWindowParams.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dWindowParams.h - * @author drose - * @date 2009-06-22 - */ - -#ifndef P3DWINDOWPARAMS_H -#define P3DWINDOWPARAMS_H - -#include "p3d_plugin_common.h" -#include "get_tinyxml.h" - -class P3DInstance *inst; - -/** - * Encapsulates the window parameters. - */ -class P3DWindowParams { -public: - P3DWindowParams(); - P3DWindowParams(P3D_window_type window_type, - int win_x, int win_y, - int win_width, int win_height, - P3D_window_handle parent_window); - - void operator = (const P3DWindowParams &other); - - inline P3D_window_type get_window_type() const; - inline void set_window_type(P3D_window_type window_type); - - inline int get_win_x() const; - inline int get_win_y() const; - inline int get_win_width() const; - inline int get_win_height() const; - inline const P3D_window_handle &get_parent_window() const; - - TiXmlElement *make_xml(P3DInstance *inst); - -private: - P3D_window_type _window_type; - int _win_x, _win_y; - int _win_width, _win_height; - P3D_window_handle _parent_window; -}; - -#include "p3dWindowParams.I" - -#endif diff --git a/direct/src/plugin/p3dX11SplashWindow.I b/direct/src/plugin/p3dX11SplashWindow.I deleted file mode 100644 index 22407f96ed..0000000000 --- a/direct/src/plugin/p3dX11SplashWindow.I +++ /dev/null @@ -1,27 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dX11SplashWindow.I - * @author drose - * @date 2009-09-08 - */ - -/** - * - */ -inline P3DX11SplashWindow::X11ImageData:: -X11ImageData() { - _filename_changed = false; -} - -/** - * - */ -inline P3DX11SplashWindow::X11ImageData:: -~X11ImageData() { -} diff --git a/direct/src/plugin/p3dX11SplashWindow.cxx b/direct/src/plugin/p3dX11SplashWindow.cxx deleted file mode 100644 index 07e5fc4d99..0000000000 --- a/direct/src/plugin/p3dX11SplashWindow.cxx +++ /dev/null @@ -1,1394 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dX11SplashWindow.cxx - * @author rdb - * @date 2009-07-08 - */ - -#include "p3dX11SplashWindow.h" - -#ifdef HAVE_X11 - -#include "get_tinyxml.h" -#include "binaryXml.h" -#include -#include -#include -#include -#include -#include - -using std::string; -using std::vector; - -/** - * - */ -P3DX11SplashWindow:: -P3DX11SplashWindow(P3DInstance *inst, bool make_visible) : - P3DSplashWindow(inst, make_visible) -{ - // Init for parent process - _subprocess_pid = -1; - - // Init for read thread - _started_read_thread = false; - INIT_THREAD(_read_thread); - - // Init for subprocess - _composite_image = nullptr; - _needs_new_composite = false; - _display = None; - _window = None; - _screen = 0; - _font = nullptr; - _graphics_context = None; - _bar_context = None; - _bar_bg_context = None; - _fg_pixel = -1; - _bg_pixel = -1; - _bar_pixel = -1; - _bar_bg_pixel = -1; - _own_display = false; - _install_progress = 0.0; - _progress_known = true; - _received_data = 0; -} - -/** - * - */ -P3DX11SplashWindow:: -~P3DX11SplashWindow() { - stop_subprocess(); -} - -/** - * Changes the window parameters, e.g. to resize or reposition the window; or - * sets the parameters for the first time, creating the initial window. - */ -void P3DX11SplashWindow:: -set_wparams(const P3DWindowParams &wparams) { - P3DSplashWindow::set_wparams(wparams); - - if (_subprocess_pid == -1) { - start_subprocess(); - } -} - -/** - * Makes the splash window visible or invisible, so as not to compete with the - * embedded Panda window in the same space. - */ -void P3DX11SplashWindow:: -set_visible(bool visible) { - P3DSplashWindow::set_visible(visible); - - TiXmlDocument doc; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "set_visible"); - xcommand->SetAttribute("visible", (int)_visible); - doc.LinkEndChild(xcommand); - write_xml(_pipe_write, &doc, nout); -} - -/** - * Specifies the name of a JPEG image file that is displayed in the center of - * the splash window. - */ -void P3DX11SplashWindow:: -set_image_filename(const string &image_filename, ImagePlacement image_placement) { - nout << "image_filename = " << image_filename << "\n"; - if (_subprocess_pid == -1) { - return; - } - - TiXmlDocument doc; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "set_image_filename"); - xcommand->SetAttribute("image_filename", image_filename); - xcommand->SetAttribute("image_placement", (int)image_placement); - doc.LinkEndChild(xcommand); - write_xml(_pipe_write, &doc, nout); - - check_stopped(); -} - -/** - * Specifies the text that is displayed above the install progress bar. - */ -void P3DX11SplashWindow:: -set_install_label(const string &install_label) { - if (_subprocess_pid == -1) { - return; - } - - TiXmlDocument doc; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "set_install_label"); - xcommand->SetAttribute("install_label", install_label); - doc.LinkEndChild(xcommand); - write_xml(_pipe_write, &doc, nout); - - check_stopped(); -} - -/** - * Moves the install progress bar from 0.0 to 1.0. - */ -void P3DX11SplashWindow:: -set_install_progress(double install_progress, - bool is_progress_known, size_t received_data) { - if (_subprocess_pid == -1) { - return; - } - - TiXmlDocument doc; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "set_install_progress"); - xcommand->SetDoubleAttribute("install_progress", install_progress); - xcommand->SetAttribute("progress_known", (int)is_progress_known); - xcommand->SetAttribute("received_data", (int)received_data); - doc.LinkEndChild(xcommand); - write_xml(_pipe_write, &doc, nout); - - check_stopped(); -} - -/** - * Sets whether the button should be visible and active (true) or invisible - * and inactive (false). If active, the button image will be displayed in the - * window, and a click event will be generated when the user clicks the - * button. - */ -void P3DX11SplashWindow:: -set_button_active(bool flag) { - if (_subprocess_pid == -1) { - return; - } - - TiXmlDocument doc; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "set_button_active"); - xcommand->SetAttribute("button_active", (int)flag); - doc.LinkEndChild(xcommand); - write_xml(_pipe_write, &doc, nout); - - check_stopped(); -} - -/** - * Called when a button click by the user is detected in set_mouse_data(), - * this method simply turns around and notifies the instance. It's a virtual - * method to give subclasses a chance to redirect this message to the main - * thread or process, as necessary. - */ -void P3DX11SplashWindow:: -button_click_detected() { - // This method is called in the child process, and must relay the - // information to the parent process. - TiXmlDocument doc; - TiXmlElement *xcommand = new TiXmlElement("click"); - doc.LinkEndChild(xcommand); - write_xml(_pipe_write, &doc, nout); -} - -/** - * Changes the button state as the mouse interacts with it. - */ -void P3DX11SplashWindow:: -set_bstate(ButtonState bstate) { - if (_bstate != bstate) { - // When the button state changes, we need to remake the composite image. - _needs_new_composite = true; - P3DSplashWindow::set_bstate(bstate); - } -} - -/** - * Spawns the subprocess that runs the window. We have to use a subprocess - * instead of just a sub-thread, to protect X11 against mutual access. - */ -void P3DX11SplashWindow:: -start_subprocess() { - assert(_subprocess_pid == -1); - - // Create a bi-directional pipe to communicate with the sub-process. - int to_fd[2]; - if (pipe(to_fd) < 0) { - perror("failed to create pipe"); - } - int from_fd[2]; - if (pipe(from_fd) < 0) { - perror("failed to create pipe"); - } - - // Fork and exec. - pid_t child = fork(); - if (child < 0) { - close(to_fd[0]); - close(to_fd[1]); - close(from_fd[0]); - close(from_fd[1]); - perror("fork"); - return; - } - - if (child == 0) { - // Here we are in the child process. - init_xml(); - - // Open the read end of the pipe, and close the write end. - _pipe_read.open_read(to_fd[0]); - close(to_fd[1]); - _pipe_write.open_write(from_fd[1]); - close(from_fd[0]); - - subprocess_run(); - _exit(0); - } - - // In the parent process. - _subprocess_pid = child; - _pipe_write.open_write(to_fd[1]); - close(to_fd[0]); - _pipe_read.open_read(from_fd[0]); - close(from_fd[1]); - - spawn_read_thread(); -} - -/** - * Terminates the subprocess. - */ -void P3DX11SplashWindow:: -stop_subprocess() { - if (_subprocess_pid == -1) { - // Already stopped. - return; - } - - // Ask the subprocess to stop. - TiXmlDocument doc; - TiXmlElement *xcommand = new TiXmlElement("command"); - xcommand->SetAttribute("cmd", "exit"); - doc.LinkEndChild(xcommand); - write_xml(_pipe_write, &doc, nout); - - // Also close the pipe, to help underscore the point. - _pipe_write.close(); - - static const int max_wait_ms = 2000; - - // Wait for a certain amount of time for the process to stop by itself. - struct timeval start; - gettimeofday(&start, nullptr); - int start_ms = start.tv_sec * 1000 + start.tv_usec / 1000; - - int status; - pid_t result = waitpid(_subprocess_pid, &status, WNOHANG); - while (result != _subprocess_pid) { - if (result == -1) { - perror("waitpid"); - break; - } - - struct timeval now; - gettimeofday(&now, nullptr); - int now_ms = now.tv_sec * 1000 + now.tv_usec / 1000; - int elapsed = now_ms - start_ms; - - if (elapsed > max_wait_ms) { - // Tired of waiting. Kill the process. - nout << "Force-killing splash window process, pid " << _subprocess_pid - << "\n"; - kill(_subprocess_pid, SIGKILL); - start_ms = now_ms; - } - - // Yield the timeslice and wait some more. - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 1; - select(0, nullptr, nullptr, nullptr, &tv); - result = waitpid(_subprocess_pid, &status, WNOHANG); - } - - nout << "Splash window process has successfully stopped.\n"; - if (WIFEXITED(status)) { - nout << " exited normally, status = " - << WEXITSTATUS(status) << "\n"; - } else if (WIFSIGNALED(status)) { - nout << " signalled by " << WTERMSIG(status) << ", core = " - << WCOREDUMP(status) << "\n"; - } else if (WIFSTOPPED(status)) { - nout << " stopped by " << WSTOPSIG(status) << "\n"; - } - - join_read_thread(); -} - -/** - * Shuts down the instance if the window is closed prematurely (for instance, - * due to user action). - */ -void P3DX11SplashWindow:: -check_stopped() { - if (_subprocess_pid == -1) { - // Already stopped. - return; - } - - int status; - int result = waitpid(_subprocess_pid, &status, WNOHANG); - if (result == 0) { - // Process is still running. - return; - } - - if (result == -1) { - // Error in waitpid. - perror("waitpid"); - return; - } - - // Process has stopped. - assert(result == _subprocess_pid); - - nout << "Splash window process has stopped unexpectedly.\n"; - if (WIFEXITED(status)) { - nout << " exited normally, status = " - << WEXITSTATUS(status) << "\n"; - } else if (WIFSIGNALED(status)) { - nout << " signalled by " << WTERMSIG(status) << ", core = " - << WCOREDUMP(status) << "\n"; - } else if (WIFSTOPPED(status)) { - nout << " stopped by " << WSTOPSIG(status) << "\n"; - } - - _subprocess_pid = -1; - join_read_thread(); - - _inst->request_stop_main_thread(); -} - -/** - * Starts the read thread. We need this thread to listen for feedback from - * the subprocess. (At the moment, the only kind of feedback we might receive - * is whether the button has been clicked.) - */ -void P3DX11SplashWindow:: -spawn_read_thread() { - SPAWN_THREAD(_read_thread, rt_thread_run, this); - _started_read_thread = true; -} - -/** - * Waits for the read thread to stop. - */ -void P3DX11SplashWindow:: -join_read_thread() { - if (!_started_read_thread) { - return; - } - - JOIN_THREAD(_read_thread); - _started_read_thread = false; -} - -/** - * The main function for the read thread. - */ -void P3DX11SplashWindow:: -rt_thread_run() { - while (true) { - TiXmlDocument *doc = read_xml(_pipe_read, nout); - if (doc == nullptr) { - // Some error on reading. The splash window must have gone away, e.g. - // because the user explicitly closed it; tell the instance to exit. - _inst->request_stop_sub_thread(); - return; - } - - // Successfully read an XML document. - rt_handle_request(doc); - } -} - -/** - * Processes a single request or notification received from an instance. - */ -void P3DX11SplashWindow:: -rt_handle_request(TiXmlDocument *doc) { - // Eh, don't even bother decoding the XML. We know it can only be a click - // notification. - delete doc; - - P3DSplashWindow::button_click_detected(); -} - -/** - * The subprocess's main run method. - */ -void P3DX11SplashWindow:: -subprocess_run() { - // Since we're now isolated in a subprocess, we can safely make all the X - // calls we like, and run independently of the browser process. - - make_window(); - setup_gc(); - if (_graphics_context == None) { - // No point in continuing if we couldn't get a graphics context. - close_window(); - return; - } - - ButtonState prev_bstate = BS_hidden; - string prev_label; - double prev_progress = 0.0; - bool prev_progress_known = true; - size_t prev_received_data = 0; - - bool needs_redraw = true; - bool needs_draw_label = false; - bool needs_redraw_progress = false; - bool needs_update_progress = false; - - _subprocess_continue = true; - while (_subprocess_continue) { - // First, scan for X events. - XEvent event; - while (XCheckWindowEvent(_display, _window, ~0, &event)) { - switch (event.type) { - case Expose: - case GraphicsExpose: - needs_redraw = true; - break; - - case ConfigureNotify: - if (event.xconfigure.width != _win_width || - event.xconfigure.height != _win_height) { - _win_width = event.xconfigure.width; - _win_height = event.xconfigure.height; - - set_button_range(_button_ready_image); - - // If the window changes size, we need to recompute the composed - // image. - _needs_new_composite = true; - } - needs_redraw = true; - break; - - case MotionNotify: - set_mouse_data(event.xmotion.x, event.xmotion.y, _mouse_down); - break; - - case ButtonPress: - set_mouse_data(_mouse_x, _mouse_y, true); - break; - - case ButtonRelease: - set_mouse_data(_mouse_x, _mouse_y, false); - break; - } - } - - update_image(_background_image); - update_image(_button_ready_image); - update_image(_button_rollover_image); - update_image(_button_click_image); - - if (_needs_new_composite) { - needs_redraw = true; - compose_image(); - } - - if (_bstate != prev_bstate) { - needs_redraw = true; - prev_bstate = _bstate; - } - - if (_install_label != prev_label) { - needs_redraw = true; - prev_label = _install_label; - } - - if (_progress_known != prev_progress_known) { - needs_update_progress = true; - needs_redraw_progress = true; - } else if (_progress_known) { - if (_install_progress != prev_progress) { - needs_update_progress = true; - if (_install_progress < prev_progress) { - needs_redraw_progress = true; - } - } - } else { - if (_received_data != prev_received_data) { - needs_update_progress = true; - needs_redraw_progress = true; - } - } - - if (needs_update_progress) { - prev_progress = _install_progress; - prev_progress_known = _progress_known; - prev_received_data = _received_data; - - if (_progress_known && _install_progress == 0.0) { - // If the progress bar drops to zero, repaint the screen to take the - // progress bar away. - needs_redraw = true; - } - } - - if (needs_redraw) { - redraw(); - XFlush(_display); - - needs_redraw = false; - needs_draw_label = true; - needs_redraw_progress = true; - } - - // Don't draw an install label or a progress bar unless we have some - // nonzero progress. - if (!_progress_known || _install_progress != 0.0) { - int bar_x, bar_y, bar_width, bar_height; - get_bar_placement(bar_x, bar_y, bar_width, bar_height); - - if (needs_draw_label) { - int direction, ascent, descent; - XCharStruct extents; - XTextExtents(_font, _install_label.c_str(), _install_label.size(), - &direction, &ascent, &descent, &extents); - - int text_width = extents.width; - int text_height = extents.ascent + extents.descent; - int text_x = (_win_width - text_width) / 2; - int text_y = bar_y - descent - _bar_border - 4; - - XClearArea(_display, _window, - text_x - 2, text_y - text_height - 2, - text_width + 4, text_height + 4, false); - XDrawString(_display, _window, _graphics_context, text_x, text_y, - _install_label.c_str(), _install_label.size()); - - needs_draw_label = false; - } - - if (needs_redraw_progress) { - // Draw the bar background. - if (_bar_bg_context != None) { - XFillRectangle(_display, _window, _bar_bg_context, - bar_x, bar_y, bar_width, bar_height); - } else { - XClearArea(_display, _window, - bar_x, bar_y, bar_width, bar_height, false); - } - - // Draw the border around the bar. - int border_x = bar_x - 1; - int border_y = bar_y - 1; - int border_width = bar_width + 1; - int border_height = bar_height + 1; - - for (int i = 0; i < _bar_border; ++i) { - XDrawRectangle(_display, _window, _graphics_context, - border_x, border_y, border_width, border_height); - border_x -= 1; - border_y -= 1; - border_width += 2; - border_height += 2; - } - needs_update_progress = true; - needs_redraw_progress = false; - } - - if (needs_update_progress) { - if (_progress_known) { - int progress_width = (int)((bar_width - 1) * _install_progress + 0.5); - XFillRectangle(_display, _window, _bar_context, - bar_x, bar_y, - progress_width + 1, bar_height); - } else { - // Progress is unknown. Draw a moving block, not a progress bar - // filling up. - int block_width = (int)(bar_width * 0.1 + 0.5); - int block_travel = (bar_width - 1) - block_width; - int progress = (int)(_received_data * _unknown_progress_rate); - progress = progress % (block_travel * 2); - if (progress > block_travel) { - progress = block_travel * 2 - progress; - } - - XFillRectangle(_display, _window, _bar_context, - bar_x + progress, bar_y, - block_width + 1, bar_height); - } - needs_update_progress = false; - } - } - - - // Now check for input from the parent. - bool input_ready = _pipe_read.has_gdata(); - if (!input_ready) { - int read_fd = _pipe_read.get_handle(); - fd_set fds; - FD_ZERO(&fds); - FD_SET(read_fd, &fds); - - // Sleep a bit to yield the timeslice if there's nothing new. - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 1000; // 1 usec is not enough. - - int result = select(read_fd + 1, &fds, nullptr, nullptr, &tv); - if (result > 0) { - // There is some noise on the pipe, so read it. - input_ready = true; - } else if (result == -1) { - // Error in select. - perror("select"); - } - } - - while (input_ready) { - // Empty the pipe of whatever is in it. - receive_command(); - input_ready = _pipe_read.has_gdata(); - } - - // Sleep a good amount in order not to lock up the system. - struct timespec req; - req.tv_sec = 0; - req.tv_nsec = 50000000; // 50 ms - nanosleep(&req, nullptr); - } - - close_window(); -} - -/** - * Receives a command from the parent. - */ -void P3DX11SplashWindow:: -receive_command() { - TiXmlDocument *doc = read_xml(_pipe_read, nout); - if (doc == nullptr) { - // Pipe closed or something. - _subprocess_continue = false; - return; - } - - TiXmlElement *xcommand = doc->FirstChildElement("command"); - if (xcommand != nullptr) { - const char *cmd = xcommand->Attribute("cmd"); - if (cmd != nullptr) { - if (strcmp(cmd, "exit") == 0) { - _subprocess_continue = false; - - } else if (strcmp(cmd, "set_visible") == 0) { - int visible = 0; - if (xcommand->Attribute("visible", &visible) != nullptr) { - _visible = visible; - if (_visible) { - XMapWindow(_display, _window); - } else { - XUnmapWindow(_display, _window); - } - } - - } else if (strcmp(cmd, "set_image_filename") == 0) { - const string *image_filename = xcommand->Attribute(string("image_filename")); - int image_placement; - if (image_filename != nullptr && - xcommand->QueryIntAttribute("image_placement", &image_placement) == TIXML_SUCCESS) { - - X11ImageData *image = nullptr; - switch ((ImagePlacement)image_placement) { - case IP_background: - image = &_background_image; - break; - - case IP_button_ready: - image = &_button_ready_image; - set_button_range(_button_ready_image); - break; - - case IP_button_rollover: - image = &_button_rollover_image; - break; - - case IP_button_click: - image = &_button_click_image; - break; - - case IP_none: - break; - } - if (image != nullptr) { - if (image->_filename != *image_filename) { - image->_filename = *image_filename; - image->_filename_changed = true; - } - } - } - - } else if (strcmp(cmd, "set_install_label") == 0) { - const char *str = xcommand->Attribute("install_label"); - if (str != nullptr) { - if (_install_label != string(str)) { - _install_label = str; - } - } - - } else if (strcmp(cmd, "set_install_progress") == 0) { - double install_progress = 0.0; - int progress_known = 1; - int received_data = 0; - xcommand->Attribute("install_progress", &install_progress); - xcommand->Attribute("progress_known", &progress_known); - xcommand->Attribute("received_data", &received_data); - - _install_progress = install_progress; - _progress_known = (progress_known != 0); - _received_data = (size_t)received_data; - - } else if (strcmp(cmd, "set_button_active") == 0) { - int button_active = 0; - xcommand->Attribute("button_active", &button_active); - - P3DSplashWindow::set_button_active(button_active != 0); - } - } - } -} - -/** - * Redraws the window. - */ -void P3DX11SplashWindow:: -redraw() { - if (_composite_image == nullptr) { - // Clear the whole window, if there's no image. - XClearWindow(_display, _window); - - } else { - // If we have an image, draw it. - int xo = (_win_width - _composite_width) / 2; - int yo = (_win_height - _composite_height) / 2; - XPutImage(_display, _window, _graphics_context, _composite_image, 0, 0, - xo, yo, _composite_width, _composite_height); - - // Then clear the rectangles around it carefully (rather than just - // clearing the whole window first, to avoid flicking). - if (yo != 0 && _win_width != 0) { - // Top - XClearArea(_display, _window, 0, 0, _win_width, yo, False); - // Bottom - XClearArea(_display, _window, 0, _win_height - yo, _win_width, yo, False); - } - if (xo != 0 && _composite_height != 0) { - // Left - XClearArea(_display, _window, 0, yo, xo, _composite_height, False); - // Right - XClearArea(_display, _window, _win_width - xo, yo, xo, _composite_height, False); - } - } -} - -/** - * Creates the window for displaying progress. Runs within the sub-process. - */ -void P3DX11SplashWindow:: -make_window() { - _win_width = 320; - _win_height = 240; - if (_wparams.get_win_width() != 0 && _wparams.get_win_height() != 0) { - _win_width = _wparams.get_win_width(); - _win_height = _wparams.get_win_height(); - } - - X11_Window parent = 0; - - // Hum, if we use the display provided by the browser, it causes a crash in - // some browsers when you make an Xlib call with the plugin window - // minimized. So I kept XOpenDisplay until we have a better workaround. - - // _display = (X11_Display*) _wparams.get_parent_window()._xdisplay; - // _own_display = false; if (_display == 0) { - _display = XOpenDisplay(nullptr); - _own_display = true; - // } - assert(_display != nullptr); - _screen = DefaultScreen(_display); - - int x = _wparams.get_win_x(); - int y = _wparams.get_win_y(); - if (x == -1) x = 0; - if (y == -1) y = 0; - if (x == -2) x = (int)(0.5 * (DisplayWidth(_display, _screen) - _win_width)); - if (y == -2) y = (int)(0.5 * (DisplayHeight(_display, _screen) - _win_height)); - - if (_wparams.get_window_type() == P3D_WT_embedded) { - // Create an embedded window. - const P3D_window_handle &handle = _wparams.get_parent_window(); - assert(handle._window_handle_type == P3D_WHT_x11_window); - parent = handle._handle._x11_window._xwindow; - } else { - // Create a toplevel window. - parent = XRootWindow(_display, _screen); - } - - assert(parent != None); - - int depth = DefaultDepth(_display, _screen); - Visual *dvisual = DefaultVisual(_display, _screen); - - long event_mask = - ButtonPressMask | ButtonReleaseMask | - PointerMotionMask | StructureNotifyMask | ExposureMask; - - // Allocate the foreground and background colors. - Colormap colormap = DefaultColormap(_display, _screen); - - XColor fg; - fg.red = _fgcolor_r * 0x101; - fg.green = _fgcolor_g * 0x101; - fg.blue = _fgcolor_b * 0x101; - fg.flags = DoRed | DoGreen | DoBlue; - _fg_pixel = -1; - if (XAllocColor(_display, colormap, &fg)) { - _fg_pixel = fg.pixel; - } - - XColor bg; - bg.red = _bgcolor_r * 0x101; - bg.green = _bgcolor_g * 0x101; - bg.blue = _bgcolor_b * 0x101; - bg.flags = DoRed | DoGreen | DoBlue; - _bg_pixel = -1; - if (XAllocColor(_display, colormap, &bg)) { - _bg_pixel = bg.pixel; - } - - // Initialize window attributes - XSetWindowAttributes wa; - wa.background_pixel = XWhitePixel(_display, _screen); - if (_bg_pixel != -1) { - wa.background_pixel = _bg_pixel; - } - wa.border_pixel = 0; - wa.event_mask = event_mask; - - unsigned long attrib_mask = CWBackPixel | CWBorderPixel | CWEventMask; - - _window = XCreateWindow - (_display, parent, x, y, _win_width, _win_height, - 0, depth, InputOutput, dvisual, attrib_mask, &wa); - - // Now hint the window manager about the window origin and size. This is - // necessary because window managers are free to ignore the window origin - // specified in the XCreateWindow call. - XSizeHints *size_hints_p = XAllocSizeHints(); - if (_wparams.get_win_x() != -1 || _wparams.get_win_y() != -1) { - // If the user requested (-1, -1), the default position, we let the window - // manager choose a position by omitting the pos hint. - size_hints_p->x = x; - size_hints_p->y = y; - size_hints_p->flags |= USPosition; - } - size_hints_p->width = _win_width; - size_hints_p->height = _win_height; - size_hints_p->flags |= USSize; - XSetWMNormalHints(_display, _window, size_hints_p); - XFree(size_hints_p); - - if (_visible) { - XMapWindow(_display, _window); - } -} - -/** - * Sets up the graphics context for drawing the text. - */ -void P3DX11SplashWindow:: -setup_gc() { - if (_graphics_context != None) { - return; - } - - char style = 'r'; - if (_font_style == FS_oblique) { - style = 'o'; - } else if (_font_style == FS_italic) { - style = 'i'; - } - - // Determine the order at which to try the various weights. From: - // https:developer.mozilla.orgen-USdocsWebCSSfont-weight - const char *const *try_weights; - const int num_weights = 7; - - if (_font_weight > 700) { - static const char *const weights[] = {"black", "bold", "demibold", "semibold", "medium", "regular", "extralight"}; - try_weights = weights; - - } else if (_font_weight > 600) { - static const char *const weights[] = {"bold", "black", "demibold", "semibold", "medium", "regular", "extralight"}; - try_weights = weights; - - } else if (_font_weight > 500) { - static const char *const weights[] = {"demibold", "semibold", "bold", "black", "medium", "regular", "extralight"}; - try_weights = weights; - - } else if (_font_weight == 500) { - static const char *const weights[] = {"medium", "regular", "extralight", "demibold", "semibold", "bold", "black"}; - try_weights = weights; - - } else if (_font_weight >= 400) { - static const char *const weights[] = {"regular", "medium", "extralight", "demibold", "semibold", "bold", "black"}; - try_weights = weights; - - } else { - static const char *const weights[] = {"extralight", "regular", "medium", "demibold", "semibold", "bold", "black"}; - try_weights = weights; - } - - char font_name[1024]; - - // Go through the weights array to find the best matching font. - for (int i = 0; i < num_weights; ++i) { - const char *weight_name = try_weights[i]; - - // Compose the proper pattern for finding the desired font face. - snprintf(font_name, 1024, "-*-%s-%s-%c-normal--%d-*-*-*-*-*-iso8859-1", - _font_family.c_str(), weight_name, style, _font_size); - - _font = XLoadQueryFont(_display, font_name); - if (_font != nullptr) { - break; - } - nout << "Font " << font_name << " unavailable.\n"; - - if (style == 'i' || style == 'o') { - // If oblique is not found, try italic, and vice versa. - char style2 = 216 - style; - snprintf(font_name, 1024, "-*-%s-%s-%c-normal--%d-*-*-*-*-*-iso8859-1", - _font_family.c_str(), weight_name, style2, _font_size); - - _font = XLoadQueryFont(_display, font_name); - if (_font != nullptr) { - break; - } - nout << "Font " << font_name << " unavailable.\n"; - } - } - - if (_font != nullptr) { - nout << "Loaded font " << font_name << "\n"; - } else { - nout << "Using fallback font 6x13.\n"; - _font = XLoadQueryFont(_display, "6x13"); - } - - XGCValues gcval; - gcval.font = _font->fid; - gcval.function = GXcopy; - gcval.plane_mask = AllPlanes; - gcval.foreground = BlackPixel(_display, _screen); - if (_fg_pixel != -1) { - gcval.foreground = _fg_pixel; - } - gcval.background = WhitePixel(_display, _screen); - if (_bg_pixel != -1) { - gcval.background = _bg_pixel; - } - _graphics_context = XCreateGC(_display, _window, - GCFont | GCFunction | GCPlaneMask | GCForeground | GCBackground, &gcval); - - // Also create a gc for filling in the interior of the progress bar in a - // pleasant blue color (or whatever color the user requested). - XColor bar; - bar.red = _barcolor_r * 0x101; - bar.green = _barcolor_g * 0x101; - bar.blue = _barcolor_b * 0x101; - bar.flags = DoRed | DoGreen | DoBlue; - - Colormap colormap = DefaultColormap(_display, _screen); - if (XAllocColor(_display, colormap, &bar)) { - _bar_pixel = bar.pixel; - gcval.foreground = bar.pixel; - } - - _bar_context = XCreateGC(_display, _window, - GCFont | GCFunction | GCPlaneMask | GCForeground | GCBackground, &gcval); - - // And another for the background color of the bar. - if (_bar_bgcolor_r != _bgcolor_r || _bar_bgcolor_g != _bgcolor_g || - _bar_bgcolor_b != _bgcolor_b) { - XColor bar_bg; - bar_bg.red = _bar_bgcolor_r * 0x101; - bar_bg.green = _bar_bgcolor_g * 0x101; - bar_bg.blue = _bar_bgcolor_b * 0x101; - bar_bg.flags = DoRed | DoGreen | DoBlue; - - if (XAllocColor(_display, colormap, &bar_bg)) { - _bar_bg_pixel = bar_bg.pixel; - gcval.foreground = bar_bg.pixel; - } - - _bar_bg_context = XCreateGC(_display, _window, - GCFont | GCFunction | GCPlaneMask | GCForeground | GCBackground, &gcval); - } -} - -/** - * Closes the window created above. - */ -void P3DX11SplashWindow:: -close_window() { - if (_composite_image != nullptr) { - XDestroyImage(_composite_image); - _composite_image = nullptr; - } - - if (_bar_context != None) { - if (_bar_context != _graphics_context) { - XFreeGC(_display, _bar_context); - } - _bar_context = None; - - // Also free the color we allocated. - Colormap colormap = DefaultColormap(_display, _screen); - XFreeColors(_display, colormap, &_bar_pixel, 1, 0); - } - - if (_bar_bg_context != None) { - if (_bar_bg_context != _graphics_context) { - XFreeGC(_display, _bar_bg_context); - } - _bar_bg_context = None; - - // Also free the color we allocated. - Colormap colormap = DefaultColormap(_display, _screen); - XFreeColors(_display, colormap, &_bar_bg_pixel, 1, 0); - } - - if (_fg_pixel != -1) { - Colormap colormap = DefaultColormap(_display, _screen); - XFreeColors(_display, colormap, &_fg_pixel, 1, 0); - } - - if (_bg_pixel != -1) { - Colormap colormap = DefaultColormap(_display, _screen); - XFreeColors(_display, colormap, &_bg_pixel, 1, 0); - } - - if (_graphics_context != None) { - XFreeGC(_display, _graphics_context); - _graphics_context = None; - } - - if (_window != None) { - XDestroyWindow(_display, _window); - _window = None; - } - - if (_display != None && _own_display) { - XCloseDisplay(_display); - _display = None; - } -} - -/** - * Loads the splash image, converts to to an XImage, and stores it in _image. - * Runs only in the child process. - * - * If the image is changed, sets needs_redraw to true. - */ -void P3DX11SplashWindow:: -update_image(X11ImageData &image) { - if (!image._filename_changed) { - // No changes. - return; - } - image._filename_changed = false; - - // We'll need to rebuild the composite image. - _needs_new_composite = true; - - // Go read the image. - if (!read_image_data(image, image._data, image._filename)) { - return; - } -} - -/** - * Constructs the XImage to display onscreen. It's a composition of the - * background image and/or one of the button images, scaled to fit the window. - */ -void P3DX11SplashWindow:: -compose_image() { - if (_composite_image != nullptr) { - XDestroyImage(_composite_image); - _composite_image = nullptr; - } - _needs_new_composite = false; - - vector image1; - int image1_width = 0, image1_height = 0; - scale_image(image1, image1_width, image1_height, _background_image); - - vector image2; - int image2_width = 0, image2_height = 0; - - switch (_bstate) { - case BS_hidden: - break; - case BS_ready: - scale_image(image2, image2_width, image2_height, _button_ready_image); - break; - case BS_rollover: - if (!scale_image(image2, image2_width, image2_height, _button_rollover_image)) { - scale_image(image2, image2_width, image2_height, _button_ready_image); - } - break; - case BS_click: - if (!scale_image(image2, image2_width, image2_height, _button_click_image)) { - scale_image(image2, image2_width, image2_height, _button_ready_image); - } - break; - } - - if (image1.empty() && image2.empty()) { - // We have no image. Never mind. - return; - } - - if (image2.empty()) { - // We have no button image; image1 will serve as the result. - - } else { - // We do have a button image. Compose the button image on top of the - // background image (or on top of a white image if we have no background). - - // We compose them here on the client, because X11 doesn't natively - // provide an alpha-blending mechanism (at least, not without the XRender - // extension). - vector image0; - int image0_width, image0_height; - compose_two_images(image0, image0_width, image0_height, - image1, image1_width, image1_height, - image2, image2_width, image2_height); - image1.swap(image0); - image1_width = image0_width; - image1_height = image0_height; - } - - // Now construct an XImage from the result. - Visual *dvisual = DefaultVisual(_display, _screen); - double r_ratio = dvisual->red_mask / 255.0; - double g_ratio = dvisual->green_mask / 255.0; - double b_ratio = dvisual->blue_mask / 255.0; - int data_length = 4 * image1_width * image1_height; - assert(data_length == image1.size()); - uint32_t *new_data = (uint32_t*)malloc(data_length); - - int j = 0; - for (int i = 0; i < data_length; i += 4) { - unsigned int r, g, b; - r = (unsigned int)(image1[i+0] * r_ratio); - g = (unsigned int)(image1[i+1] * g_ratio); - b = (unsigned int)(image1[i+2] * b_ratio); - new_data[j++] = ((r & dvisual->red_mask) | - (g & dvisual->green_mask) | - (b & dvisual->blue_mask)); - } - - // Now load the image. - _composite_image = XCreateImage(_display, CopyFromParent, DefaultDepth(_display, _screen), - ZPixmap, 0, (char *)new_data, image1_width, image1_height, 32, 0); - _composite_width = image1_width; - _composite_height = image1_height; -} - -/** - * Scales the image into the window size, and expands it to four channels. - * Returns true if the image is valid, false if it is empty. - */ -bool P3DX11SplashWindow:: -scale_image(vector &image0, int &image0_width, int &image0_height, - X11ImageData &image) { - if (image._data.empty()) { - return false; - } - - const unsigned char *orig_data = (const unsigned char *)image._data.data(); - int row_stride = image._width * image._num_channels; - int data_length = image._height * row_stride; - assert(data_length == image._data.size()); - - if (image._width <= _win_width && image._height <= _win_height) { - // It fits within the window - just keep it. - image0_width = image._width; - image0_height = image._height; - int new_row_stride = image0_width * 4; - int new_data_length = image0_height * new_row_stride; - - image0.clear(); - image0.reserve(new_data_length); - image0.insert(image0.begin(), new_data_length, 0); - unsigned char *new_data = &image0[0]; - - if (image._num_channels == 4) { - // Easy case. Already four channels. - assert(data_length == new_data_length && row_stride == new_row_stride); - memcpy(new_data, orig_data, data_length); - - } else if (image._num_channels == 3) { - // Expand three channels to four. - for (int yi = 0; yi < image._height; ++yi) { - const unsigned char *sp = orig_data + yi * row_stride; - unsigned char *dp = new_data + yi * new_row_stride; - for (int xi = 0; xi < image._width; ++xi) { - dp[0] = sp[0]; - dp[1] = sp[1]; - dp[2] = sp[2]; - dp[3] = 0xff; - sp += 3; - dp += 4; - } - } - } else if (image._num_channels == 1) { - // A grayscale image. Replicate out the channels. - for (int yi = 0; yi < image._height; ++yi) { - const unsigned char *sp = orig_data + yi * row_stride; - unsigned char *dp = new_data + yi * new_row_stride; - for (int xi = 0; xi < image._width; ++xi) { - dp[0] = sp[0]; - dp[1] = sp[0]; - dp[2] = sp[0]; - dp[3] = 0xff; - sp += 1; - dp += 4; - } - } - } - - } else { - // Yuck, the bad case - we need to scale it down. - double scale = std::min((double)_win_width / (double)image._width, - (double)_win_height / (double)image._height); - image0_width = (int)(image._width * scale); - image0_height = (int)(image._height * scale); - int new_row_stride = image0_width * 4; - int new_data_length = image0_height * new_row_stride; - - image0.clear(); - image0.reserve(new_data_length); - image0.insert(image0.begin(), new_data_length, 0); - unsigned char *new_data = &image0[0]; - - for (int yi = 0; yi < image0_height; ++yi) { - int orig_yi = (yi * image._height) / image0_height; - const unsigned char *sp = orig_data + orig_yi * row_stride; - unsigned char *dp = new_data + yi * new_row_stride; - for (int xi = 0; xi < image0_width; ++xi) { - int orig_xi = (xi * image._width) / image0_width; - const unsigned char *spx = sp + orig_xi * image._num_channels; - if (image._num_channels == 4) { - dp[0] = spx[0]; - dp[1] = spx[1]; - dp[2] = spx[2]; - dp[3] = spx[3]; - } else if (image._num_channels == 3) { - dp[0] = spx[0]; - dp[1] = spx[1]; - dp[2] = spx[2]; - dp[3] = 0xff; - } else if (image._num_channels == 1) { - dp[0] = spx[0]; - dp[1] = spx[0]; - dp[2] = spx[0]; - dp[3] = 0xff; - } - dp += 4; - } - } - } - - return true; -} - -/** - * Constructs into image0 the alpha-composite of image1 beneath image2. - */ -void P3DX11SplashWindow:: -compose_two_images(vector &image0, int &image0_width, int &image0_height, - const vector &image1, int image1_width, int image1_height, - const vector &image2, int image2_width, int image2_height) { - // First, the resulting image size is the larger of the two. - image0_width = std::max(image1_width, image2_width); - image0_height = std::max(image1_height, image2_height); - - int new_row_stride = image0_width * 4; - int new_data_length = image0_height * new_row_stride; - - // Now copy in the first image. If the first image exactly fills the output - // image, this is easy. - if (image1_width == image0_width && image1_height == image0_height) { - image0 = image1; - - } else { - // If the first image doesn't fill it, it's only a little bit more work. - // Start by finding the top-left pixel. - int xo = (image0_width - image1_width) / 2; - int yo = (image0_height - image1_height) / 2; - - // Initialize the image to all white pixels. - image0.clear(); - image0.reserve(new_data_length); - image0.insert(image0.begin(), new_data_length, 0xff); - - int image0_row_stride = image0_width * 4; - int image1_row_stride = image1_width * 4; - for (int yi = 0; yi < image1_height; ++yi) { - const unsigned char *sp = &image1[0] + yi * image1_row_stride; - unsigned char *dp = &image0[0] + (yi + yo) * image0_row_stride; - memcpy(dp + xo * 4, sp, image1_row_stride); - } - } - - // Now blend in the second image. Find the top-left pixel. - int xo = (image0_width - image2_width) / 2; - int yo = (image0_height - image2_height) / 2; - - int image0_row_stride = image0_width * 4; - int image2_row_stride = image2_width * 4; - - for (int yi = 0; yi < image2_height; ++yi) { - const unsigned char *sp = &image2[0] + yi * image2_row_stride; - unsigned char *dp = &image0[0] + (yi + yo) * image0_row_stride; - dp += xo * 4; - for (int xi = 0; xi < image2_width; ++xi) { - double alpha = (double)sp[3] / 255.0; - dp[0] = (unsigned char)(dp[0] + alpha * (sp[0] - dp[0])); - dp[1] = (unsigned char)(dp[1] + alpha * (sp[1] - dp[1])); - dp[2] = (unsigned char)(dp[2] + alpha * (sp[2] - dp[2])); - dp += 4; - sp += 4; - } - } -} - -#endif // HAVE_X11 diff --git a/direct/src/plugin/p3dX11SplashWindow.h b/direct/src/plugin/p3dX11SplashWindow.h deleted file mode 100644 index 72c6ac00d0..0000000000 --- a/direct/src/plugin/p3dX11SplashWindow.h +++ /dev/null @@ -1,144 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dX11SplashWindow.h - * @author rdb - * @date 2009-07-08 - */ - -#ifndef P3DX11SPLASHWINDOW_H -#define P3DX11SPLASHWINDOW_H - -#include "p3d_plugin_common.h" - -#ifdef HAVE_X11 - -#include "p3dSplashWindow.h" -#include "handleStream.h" -#include "plugin_get_x11.h" - -#include - -/** - * This is the Windows implementation of the initial-download window. - */ -class P3DX11SplashWindow : public P3DSplashWindow { -public: - P3DX11SplashWindow(P3DInstance *inst, bool make_visible); - virtual ~P3DX11SplashWindow(); - - virtual void set_wparams(const P3DWindowParams &wparams); - virtual void set_visible(bool visible); - virtual void set_image_filename(const std::string &image_filename, - ImagePlacement image_placement); - virtual void set_install_label(const std::string &install_label); - virtual void set_install_progress(double install_progress, - bool is_progress_known, size_t received_data); - - virtual void set_button_active(bool flag); - -protected: - virtual void button_click_detected(); - virtual void set_bstate(ButtonState bstate); - -private: - void start_subprocess(); - void stop_subprocess(); - void check_stopped(); - - void spawn_read_thread(); - void join_read_thread(); - -private: - // These methods run only within the read thread. - THREAD_CALLBACK_DECLARATION(P3DX11SplashWindow, rt_thread_run); - void rt_thread_run(); - void rt_terminate(); - void rt_handle_request(TiXmlDocument *doc); - -private: - // Data members that are stored in the parent process. - pid_t _subprocess_pid; - HandleStream _pipe_read; - HandleStream _pipe_write; - - // The remaining members are manipulated by or for the read thread. - bool _started_read_thread; - THREAD _read_thread; - -private: - // These methods run only within the subprocess. - class X11ImageData; - - void subprocess_run(); - void receive_command(); - - void redraw(); - void make_window(); - void setup_gc(); - void close_window(); - - void update_image(X11ImageData &image); - void compose_image(); - bool scale_image(std::vector &image0, int &image0_width, int &image0_height, - X11ImageData &image); - - void compose_two_images(std::vector &image0, int &image0_width, int &image0_height, - const std::vector &image1, int image1_width, int image1_height, - const std::vector &image2, int image2_width, int image2_height); - -private: - // Data members that are stored in the subprocess. - class X11ImageData : public ImageData { - public: - inline X11ImageData(); - inline ~X11ImageData(); - - std::string _filename; - bool _filename_changed; - std::string _data; - }; - - X11ImageData _background_image; - X11ImageData _button_ready_image; - X11ImageData _button_rollover_image; - X11ImageData _button_click_image; - - XImage *_composite_image; - int _composite_width, _composite_height; - bool _needs_new_composite; - - bool _subprocess_continue; - - bool _own_display; - std::string _install_label; - double _install_progress; - bool _progress_known; - size_t _received_data; - - std::string _label_text; - - X11_Display *_display; - int _screen; - XFontStruct *_font; - GC _graphics_context; - GC _bar_context; - GC _bar_bg_context; - unsigned long _fg_pixel; - unsigned long _bg_pixel; - unsigned long _bar_pixel; - unsigned long _bar_bg_pixel; - - X11_Window _window; -}; - -#include "p3dX11SplashWindow.I" - -#endif // HAVE_X11 - -#endif diff --git a/direct/src/plugin/p3d_lock.h b/direct/src/plugin/p3d_lock.h deleted file mode 100644 index 7c11ca5bb4..0000000000 --- a/direct/src/plugin/p3d_lock.h +++ /dev/null @@ -1,123 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3d_lock.h - * @author drose - * @date 2009-06-05 - */ - -#ifndef P3D_LOCK_H -#define P3D_LOCK_H - -// Provides some simple macros that implement platform-independet mutex locks, -// as well as platform-independent thread constructs. - -#ifdef _WIN32 - -// Windows case - -// Locks are straightforward. -class _lock { -public: - CRITICAL_SECTION _l; - int _count; -}; - -#define LOCK _lock -#define INIT_LOCK(lock) { InitializeCriticalSection(&(lock)._l); (lock)._count = 0; } -#define ACQUIRE_LOCK(lock) { EnterCriticalSection(&(lock)._l); ++((lock)._count); } -#define RELEASE_LOCK(lock) { --((lock)._count); LeaveCriticalSection(&(lock)._l); } -#define DESTROY_LOCK(lock) DeleteCriticalSection(&(lock)._l) - -/* -#define LOCK CRITICAL_SECTION -#define INIT_LOCK(lock) InitializeCriticalSection(&(lock)) -#define ACQUIRE_LOCK(lock) EnterCriticalSection(&(lock)) -#define RELEASE_LOCK(lock) LeaveCriticalSection(&(lock)) -#define DESTROY_LOCK(lock) DeleteCriticalSection(&(lock)) -*/ - -// Threads. -#define THREAD HANDLE -#define INIT_THREAD(thread) (thread) = nullptr; -#define SPAWN_THREAD(thread, callback_function, this) \ - (thread) = CreateThread(nullptr, 0, &win_ ## callback_function, (this), 0, nullptr) -#define JOIN_THREAD(thread) \ - { \ - assert((thread) != nullptr); \ - WaitForSingleObject((thread), INFINITE); \ - CloseHandle((thread)); \ - (thread) = nullptr; \ - } - -// Declare this macro within your class declaration. This implements the -// callback function wrapper necessary to hook into the above SPAWN_THREAD -// call. The wrapper will in turn call the method function you provide. -#define THREAD_CALLBACK_DECLARATION(class, callback_function) \ - static DWORD WINAPI \ - win_ ## callback_function(LPVOID data) { \ - ((class *)data)->callback_function(); \ - return 0; \ - } - - -#else // _WIN32 - -// Posix case -#include - -// We declare this to be a recursive lock, since we might make a request_ready -// call from within the API, which in turn is allowed to call back into the -// API. -#define LOCK pthread_mutex_t -#define INIT_LOCK(lock) { \ - pthread_mutexattr_t attr; \ - pthread_mutexattr_init(&attr); \ - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ - pthread_mutex_init(&(lock), &attr); \ - pthread_mutexattr_destroy(&attr); \ - } -#define ACQUIRE_LOCK(lock) pthread_mutex_lock(&(lock)) -#define RELEASE_LOCK(lock) pthread_mutex_unlock(&(lock)) -#define DESTROY_LOCK(lock) pthread_mutex_destroy(&(lock)) - -#define THREAD pthread_t -#define INIT_THREAD(thread) (thread) = 0; -#define SPAWN_THREAD(thread, callback_function, this) \ - { \ - pthread_attr_t attr; \ - pthread_attr_init(&attr); \ - pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); \ - pthread_create(&(thread), &attr, &posix_ ## callback_function, (void *)(this)); \ - pthread_attr_destroy(&attr); \ - } - -#define JOIN_THREAD(thread) \ - { \ - assert((thread) != 0); \ - void *return_val; \ - int success = pthread_join((thread), &return_val); \ - (thread) = 0; \ - if (success != 0) { \ - nout << "Failed to join: " << success << "\n"; \ - } else { \ - nout << "Successfully joined thread: " << return_val << "\n"; \ - } \ - } - -// As above, declare this macro within your class declaration. -#define THREAD_CALLBACK_DECLARATION(class, callback_function) \ - static void * \ - posix_ ## callback_function(void *data) { \ - ((class *)data)->callback_function(); \ - return nullptr; \ - } - -#endif // _WIN32 - -#endif diff --git a/direct/src/plugin/p3d_plugin.cxx b/direct/src/plugin/p3d_plugin.cxx deleted file mode 100644 index 7e6c01108b..0000000000 --- a/direct/src/plugin/p3d_plugin.cxx +++ /dev/null @@ -1,622 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3d_plugin.cxx - * @author drose - * @date 2009-05-29 - */ - -#include "p3d_plugin_common.h" -#include "p3d_plugin_config.h" -#include "p3dInstanceManager.h" -#include "p3dInstance.h" -#include "p3dWindowParams.h" -#include "p3dUndefinedObject.h" -#include "p3dNoneObject.h" -#include "p3dBoolObject.h" -#include "p3dIntObject.h" -#include "p3dFloatObject.h" -#include "p3dStringObject.h" - -#include -#include - -// Use a simple lock to protect the C-style API functions in this module from -// parallel access by multiple threads in the host. - -bool initialized_lock = false; -LOCK _api_lock; - -bool -P3D_initialize(int api_version, const char *contents_filename, - const char *host_url, P3D_verify_contents verify_contents, - const char *platform, const char *log_directory, - const char *log_basename, bool trusted_environment, - bool console_environment, - const char *root_dir, const char *host_dir, - const char *start_dir) { - if (api_version < 10 || api_version > P3D_API_VERSION) { - // Can't accept an incompatible version. - return false; - } - - if (api_version < 13) { - // Prior to version 13, verify_contents was a bool. Convert "true" to - // P3D_VC_normal and "false" to P3D_VC_none. - if ((int)verify_contents != 0) { - verify_contents = P3D_VC_normal; - } else { - verify_contents = P3D_VC_none; - } - } - - if (!initialized_lock) { - INIT_LOCK(_api_lock); - initialized_lock = true; - } - ACQUIRE_LOCK(_api_lock); - - if (contents_filename == nullptr){ - contents_filename = ""; - } - - if (host_url == nullptr){ - host_url = ""; - } - - if (platform == nullptr) { - platform = ""; - } - - if (log_directory == nullptr) { - log_directory = ""; - } - - if (log_basename == nullptr) { - log_basename = ""; - } - - if (api_version < 12 || root_dir == nullptr) { - root_dir = ""; - } - - if (api_version < 16 || host_dir == nullptr) { - host_dir = ""; - } - - if (api_version < 17 || start_dir == nullptr) { - start_dir = ""; - } - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - bool result = inst_mgr->initialize(api_version, contents_filename, host_url, - verify_contents, platform, - log_directory, log_basename, - trusted_environment, console_environment, - root_dir, host_dir, start_dir); - RELEASE_LOCK(_api_lock); - return result; -} - -void -P3D_finalize() { - nout << "P3D_finalize called\n"; - P3DInstanceManager::delete_global_ptr(); -} - -void -P3D_set_plugin_version(int major, int minor, int sequence, - bool official, const char *distributor, - const char *coreapi_host_url, - const char *coreapi_timestamp_str, - const char *coreapi_set_ver) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - if (distributor == nullptr) { - distributor = ""; - } - if (coreapi_host_url == nullptr) { - coreapi_host_url = ""; - } - - ACQUIRE_LOCK(_api_lock); - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - time_t coreapi_timestamp = 0; - if (inst_mgr->get_api_version() < 15) { - // Before version 15, this was passed as a time_t. - coreapi_timestamp = (time_t)coreapi_timestamp_str; - } else { - // Passing a time_t causes problems with disagreements about word size, so - // since version 15 we pass it as a string. - coreapi_timestamp = strtoul(coreapi_timestamp_str, nullptr, 10); - } - - if (inst_mgr->get_api_version() < 14 || coreapi_set_ver == nullptr) { - // Prior to version 14 this parameter was absent. - coreapi_set_ver = ""; - } - - inst_mgr->set_plugin_version(major, minor, sequence, official, distributor, - coreapi_host_url, coreapi_timestamp, - coreapi_set_ver); - RELEASE_LOCK(_api_lock); -} - -void -P3D_set_super_mirror(const char *super_mirror_url) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - if (super_mirror_url == nullptr) { - super_mirror_url = ""; - } - - ACQUIRE_LOCK(_api_lock); - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - inst_mgr->set_super_mirror(super_mirror_url); - RELEASE_LOCK(_api_lock); -} - -P3D_instance * -P3D_new_instance(P3D_request_ready_func *func, - const P3D_token tokens[], size_t num_tokens, - int argc, const char *argv[], void *user_data) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3DInstance *result = inst_mgr->create_instance(func, tokens, num_tokens, - argc, argv, user_data); - RELEASE_LOCK(_api_lock); - return result; -} - -bool -P3D_instance_start(P3D_instance *instance, bool is_local, - const char *p3d_filename, int p3d_offset) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - if (p3d_filename == nullptr) { - p3d_filename = ""; - } - ACQUIRE_LOCK(_api_lock); - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - if (inst_mgr->get_api_version() < 11) { - // Prior to version 11, there was no p3d_offset parameter. So, we default - // it to 0. - p3d_offset = 0; - } - - P3DInstance *inst = inst_mgr->validate_instance(instance); - bool result = false; - if (inst != nullptr) { - // We don't actually start it immediately; the instance will have to - // download the p3d url and read it, reading the python version, before it - // can start. - result = inst_mgr->set_p3d_filename(inst, is_local, - p3d_filename, p3d_offset); - } - RELEASE_LOCK(_api_lock); - return result; -} - -int -P3D_instance_start_stream(P3D_instance *instance, const char *p3d_url) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - if (p3d_url == nullptr) { - p3d_url = ""; - } - ACQUIRE_LOCK(_api_lock); - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3DInstance *inst = inst_mgr->validate_instance(instance); - int result = -1; - if (inst != nullptr) { - result = inst_mgr->make_p3d_stream(inst, p3d_url); - } - RELEASE_LOCK(_api_lock); - return result; -} - -void -P3D_instance_finish(P3D_instance *instance) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3DInstance *inst = inst_mgr->validate_instance(instance); - if (inst != nullptr) { - inst_mgr->finish_instance(inst); - } - RELEASE_LOCK(_api_lock); -} - -void -P3D_instance_setup_window(P3D_instance *instance, - P3D_window_type window_type, - int win_x, int win_y, - int win_width, int win_height, - const P3D_window_handle *parent_window) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - P3DWindowParams wparams(window_type, win_x, win_y, - win_width, win_height, *parent_window); - - ACQUIRE_LOCK(_api_lock); - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3DInstance *inst = inst_mgr->validate_instance(instance); - if (inst != nullptr) { - inst->set_wparams(wparams); - } - RELEASE_LOCK(_api_lock); -} - -P3D_object_type -P3D_object_get_type(P3D_object *object) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - P3D_object_type result = P3D_OBJECT_GET_TYPE(object); - RELEASE_LOCK(_api_lock); - return result; -} - -bool -P3D_object_get_bool(P3D_object *object) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - bool result = P3D_OBJECT_GET_BOOL(object); - RELEASE_LOCK(_api_lock); - return result; -} - -int -P3D_object_get_int(P3D_object *object) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - int result = P3D_OBJECT_GET_INT(object); - RELEASE_LOCK(_api_lock); - return result; -} - -double -P3D_object_get_float(P3D_object *object) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - double result = P3D_OBJECT_GET_FLOAT(object); - RELEASE_LOCK(_api_lock); - return result; -} - -int -P3D_object_get_string(P3D_object *object, char *buffer, int buffer_size) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - int result = P3D_OBJECT_GET_STRING(object, buffer, buffer_size); - RELEASE_LOCK(_api_lock); - return result; -} - -int -P3D_object_get_repr(P3D_object *object, char *buffer, int buffer_size) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - int result = P3D_OBJECT_GET_REPR(object, buffer, buffer_size); - RELEASE_LOCK(_api_lock); - return result; -} - -P3D_object * -P3D_object_get_property(P3D_object *object, const char *property) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - P3D_object *result = P3D_OBJECT_GET_PROPERTY(object, property); - RELEASE_LOCK(_api_lock); - return result; -} - -bool -P3D_object_set_property(P3D_object *object, const char *property, - bool needs_response, P3D_object *value) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - bool result = P3D_OBJECT_SET_PROPERTY(object, property, needs_response, value); - RELEASE_LOCK(_api_lock); - return result; -} - -bool -P3D_object_has_method(P3D_object *object, const char *method_name) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - bool result = P3D_OBJECT_HAS_METHOD(object, method_name); - RELEASE_LOCK(_api_lock); - return result; -} - -P3D_object * -P3D_object_call(P3D_object *object, const char *method_name, - bool needs_response, - P3D_object *params[], int num_params) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - P3D_object *result = P3D_OBJECT_CALL(object, method_name, needs_response, - params, num_params); - RELEASE_LOCK(_api_lock); - return result; -} - -P3D_object * -P3D_object_eval(P3D_object *object, const char *expression) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - P3D_object *result = P3D_OBJECT_EVAL(object, expression); - RELEASE_LOCK(_api_lock); - return result; -} - - -void -P3D_object_incref(P3D_object *object) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - if (object != nullptr) { - ACQUIRE_LOCK(_api_lock); - P3D_OBJECT_INCREF(object); - RELEASE_LOCK(_api_lock); - } -} - -void -P3D_object_decref(P3D_object *object) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - if (object != nullptr) { - ACQUIRE_LOCK(_api_lock); - P3D_OBJECT_DECREF(object); - RELEASE_LOCK(_api_lock); - } -} - -P3D_class_definition * -P3D_make_class_definition() { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3D_class_definition *result = inst_mgr->make_class_definition(); - - RELEASE_LOCK(_api_lock); - return result; -} - -P3D_object * -P3D_new_undefined_object() { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3D_object *result = inst_mgr->new_undefined_object(); - - RELEASE_LOCK(_api_lock); - return result; -} - -P3D_object * -P3D_new_none_object() { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3D_object *result = inst_mgr->new_none_object(); - - RELEASE_LOCK(_api_lock); - return result; -} - -P3D_object * -P3D_new_bool_object(bool value) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3D_object *result = inst_mgr->new_bool_object(value); - - RELEASE_LOCK(_api_lock); - return result; -} - -P3D_object * -P3D_new_int_object(int value) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - - P3D_object *result = new P3DIntObject(value); - - RELEASE_LOCK(_api_lock); - return result; -} - -P3D_object * -P3D_new_float_object(double value) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - - P3D_object *result = new P3DFloatObject(value); - - RELEASE_LOCK(_api_lock); - return result; -} - -P3D_object * -P3D_new_string_object(const char *str, int length) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - - P3D_object *result = new P3DStringObject(std::string(str, length)); - - RELEASE_LOCK(_api_lock); - return result; -} - -P3D_object * -P3D_instance_get_panda_script_object(P3D_instance *instance) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3DInstance *inst = inst_mgr->validate_instance(instance); - P3D_object *result = nullptr; - if (inst != nullptr) { - result = inst->get_panda_script_object(); - } - - RELEASE_LOCK(_api_lock); - return result; -} - -void -P3D_instance_set_browser_script_object(P3D_instance *instance, - P3D_object *object) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3DInstance *inst = inst_mgr->validate_instance(instance); - if (inst != nullptr) { - inst->set_browser_script_object(object); - } - - RELEASE_LOCK(_api_lock); -} - - -P3D_request * -P3D_instance_get_request(P3D_instance *instance) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3DInstance *inst = inst_mgr->validate_instance(instance); - P3D_request *result = nullptr; - if (inst != nullptr) { - result = inst->get_request(); - } - - RELEASE_LOCK(_api_lock); - return result; -} - -P3D_instance * -P3D_check_request(double timeout) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3D_instance *inst = inst_mgr->check_request(); - - if (inst != nullptr || timeout <= 0.0) { - RELEASE_LOCK(_api_lock); - return inst; - } - -#ifdef _WIN32 - int stop_tick = int(GetTickCount() + timeout * 1000.0); -#else - struct timeval stop_time; - gettimeofday(&stop_time, nullptr); - - int seconds = (int)floor(timeout); - stop_time.tv_sec += seconds; - stop_time.tv_usec += (int)((timeout - seconds) * 1000.0); - if (stop_time.tv_usec > 1000) { - stop_time.tv_usec -= 1000; - ++stop_time.tv_sec; - } -#endif - - // Now we have to block until a request is available. - RELEASE_LOCK(_api_lock); - inst_mgr->wait_request(timeout); - ACQUIRE_LOCK(_api_lock); - inst = inst_mgr->check_request(); - - while (inst == nullptr && inst_mgr->get_num_instances() != 0) { -#ifdef _WIN32 - int remaining_ticks = stop_tick - GetTickCount(); - if (remaining_ticks <= 0) { - break; - } - timeout = remaining_ticks * 0.001; -#else - struct timeval now; - gettimeofday(&now, nullptr); - - struct timeval remaining; - remaining.tv_sec = stop_time.tv_sec - now.tv_sec; - remaining.tv_usec = stop_time.tv_usec - now.tv_usec; - - if (remaining.tv_usec < 0) { - remaining.tv_usec += 1000; - --remaining.tv_sec; - } - if (remaining.tv_sec < 0) { - break; - } - timeout = remaining.tv_sec + remaining.tv_usec * 0.001; -#endif - - RELEASE_LOCK(_api_lock); - inst_mgr->wait_request(timeout); - ACQUIRE_LOCK(_api_lock); - inst = inst_mgr->check_request(); - } - - RELEASE_LOCK(_api_lock); - return inst; -} - -void -P3D_request_finish(P3D_request *request, bool handled) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - if (request != nullptr) { - P3DInstance::finish_request(request, handled); - } - RELEASE_LOCK(_api_lock); -} - -bool -P3D_instance_feed_url_stream(P3D_instance *instance, int unique_id, - P3D_result_code result_code, - int http_status_code, - size_t total_expected_data, - const void *this_data, - size_t this_data_size) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3DInstance *inst = inst_mgr->validate_instance(instance); - bool result = false; - if (inst != nullptr) { - result = inst-> - feed_url_stream(unique_id, result_code, http_status_code, - total_expected_data, - (const unsigned char *)this_data, this_data_size); - } - - RELEASE_LOCK(_api_lock); - return result; -} - -bool -P3D_instance_handle_event(P3D_instance *instance, - const P3D_event_data *event) { - assert(P3DInstanceManager::get_global_ptr()->is_initialized()); - ACQUIRE_LOCK(_api_lock); - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - P3DInstance *inst = inst_mgr->validate_instance(instance); - bool result = false; - if (inst != nullptr) { - result = inst->handle_event(*event); - } - - RELEASE_LOCK(_api_lock); - return result; -} diff --git a/direct/src/plugin/p3d_plugin.h b/direct/src/plugin/p3d_plugin.h deleted file mode 100644 index 1d7866e05c..0000000000 --- a/direct/src/plugin/p3d_plugin.h +++ /dev/null @@ -1,1127 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3d_plugin.h - * @author drose - * @date 2009-05-28 - */ - -#ifndef P3D_PLUGIN_H -#define P3D_PLUGIN_H - -/* This file defines the C-level API to Panda's core plugin API. This - API is intended to provide basic functionality for loading and - running Panda's .p3d files, particularly within a browser. - - This core API is intended to be loaded and run within a browser, as - a standalone DLL. It will in turn be responsible for fetching and - installing the appropriate version of Panda and Python, as well as - any required supporting libraries. - - Note that this code defines only the interface between the actual - browser plugin and the Panda code. It contains no code to directly - interface with any browser. The actual plugin itself will be a - separate piece of code, written in ActiveX or NPAPI or whatever API - is required for a given browser; and this code will be designed to - download and link with this DLL. - - The browser or launching application will be referred to as the - "host" in this documentation. The host should load this core API - DLL only once, but may then use it to create multiple simultaneous - different instances of Panda windows. - - Filenames passed through this interface are in native OS-specific - form, e.g. with a leading drive letter and backslashes, not in - Panda's Unix-like form (except on Unix-based OSes, of course). -*/ - -#include - -#ifdef _WIN32 -#include - -#ifdef BUILDING_P3D_PLUGIN -#define EXPCL_P3D_PLUGIN __declspec(dllexport) -#else -#define EXPCL_P3D_PLUGIN __declspec(dllimport) -#endif - -#else /* _WIN32 */ -#if defined(BUILDING_P3D_PLUGIN) && defined(__GNUC__) -#define EXPCL_P3D_PLUGIN __attribute__((visibility("default"))) -#else -#define EXPCL_P3D_PLUGIN -#endif - -#endif /* _WIN32 */ - -#ifdef __APPLE__ -#include -#endif /* __APPLE__ */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* In the following function prototypes, all functions are declared - initially as typedefs only, and then the actual function references - are finally declared at the end of this file, but only if - P3D_PLUGIN_PROTOTYPES is defined. This is intended to allow - including this file without building an implicit reference to the - functions themselves, allowing the core API library to be loaded - via an explicit LoadLibrary() or equivalent call. */ - - -/* This symbol serves to validate that runtime and compile-time - libraries match. It should be passed to P3D_initialize() - (below). This number will be incremented whenever there are changes - to any of the interface specifications defined in this header - file. */ -#define P3D_API_VERSION 17 - -/************************ GLOBAL FUNCTIONS **************************/ - -/* The following interfaces are global to the core API space, as - opposed to being specific to a particular instance. */ - -/* This is passed for verify_contents, below. */ -typedef enum { - P3D_VC_none, - P3D_VC_normal, - P3D_VC_force, - P3D_VC_never, -} P3D_verify_contents; - -/* This function should be called immediately after the core API is - loaded. You should pass P3D_API_VERSION as the first parameter, so - the DLL can verify that it has been built with the same version of - the API as the host. - - The contents_filename, if not NULL or empty, names a contents.xml - file that has already been downloaded and verified from the server. - If this is NULL, a new file will be downloaded as needed. - - If host_url is not NULL or empty, it specifies the root URL of - the download server that provided the contents_filename. - - If verify_contents is P3D_VC_none, then the server will not be - contacted unless the current contents.xml cannot be read at all. - If it is P3D_VC_normal, the server will be contacted whenever the - contents.xml has expired. If it is P3D_VC_force, each server will - be contacted initially in all cases, and subseqeuntly only whenever - contents.xml has expired for that server. The opposite of - P3D_VC_force is P3D_VC_never, which forces the plugin never to - contact the server and not to verify the contents at all. This - option should only be used if the host directory is prepopulated. - Normally, a web plugin should set this to P3D_VC_normal. - - If platform is not NULL or empty, it specifies the current platform - string; otherwise, the compiled-in default is used. This should - generally be left at NULL. - - If log_directory is not NULL or empty, it specifies the directory - into which all log files will be written; otherwise, the - compiled-in default will be used, or the system temp directory if - no default is compiled in. - - If log_basename is not NULL or empty, it specifies the filename in - log_directory to which the core API's logfile output will be - written. Otherwise, the compiled-in default is used; if there is - no compiled-in default, no logfile output will be generated by the - core API. Note that the individual instances also have their own - log_basename values. If log_history is greater than zero, the - most recent log_history (count) logs generated (per log_basename) - will be retained on disk, each named uniquely by appending a - timestamp to the log_basename before file creation. - - Next, trusted_environment should be set true to indicate that the - environment and p3d file are already trusted. If this is set, the - p3d file will be run without checking its signature. Normally, a - browser plugin should set this false. - - Furthermore, console_environment should be set true to indicate that - we are running within a text-based console, and expect to preserve - the current working directory, and also see standard output, or false - to indicate that we are running within a GUI environment, and - expect none of these. Normally, a browser plugin should set this - false. - - Finally, root_dir and host_dir can be set to override the default - root and package directories. Normally, you don't need to set them. - - This function returns true if the core API is valid and uses a - compatible API, false otherwise. If it returns false, the host - should not call any more functions in this API, and should - immediately unload the DLL and (if possible) download a new one. */ -typedef bool -P3D_initialize_func(int api_version, const char *contents_filename, - const char *host_url, P3D_verify_contents verify_contents, - const char *platform, - const char *log_directory, const char *log_basename, - bool trusted_environment, bool console_environment, - const char *root_dir, const char *host_dir, - const char *start_dir); - -/* This function should be called to unload the core API. It will - release all internally-allocated memory and return the core API to - its initial state. */ -typedef void -P3D_finalize_func(); - -/* This function establishes the version of the calling plugin, for - reporting to JavaScript and the like. */ -typedef void -P3D_set_plugin_version_func(int major, int minor, int sequence, - bool official, const char *distributor, - const char *coreapi_host_url, - const char *coreapi_timestamp, - const char *coreapi_set_ver); - -/* This function defines a "super mirror" URL: a special URL that is - consulted first whenever downloading any package referenced by a - p3d file. This setting is global, and affects all package - downloads across all instances. The main purpose of this is to - facilitate local distribution of the Panda3D runtime build, to - allow applications to ship themselves totally self-contained. If - you install the appropriate Panda3D package files into a directory - on disk, and set the "super mirror" to a file:// URL that - references that directory, then users will be able to run your p3d - file without necessarily having an internet connection. - - This normally should be set only by the panda3d standalone runtime - executable, not by a web plugin. */ -typedef void -P3D_set_super_mirror_func(const char *super_mirror_url); - -/********************** INSTANCE MANAGEMENT **************************/ - -/* The following interfaces define the API to manage individual - Panda3D instances. Each instance can display a separate 3-D - graphics window simultaneously on the host or on the desktop. The - instances operate generally independently of each other. */ - -/* This structure defines the handle to a single instance. The host - may access any members appearing here. */ -typedef struct { - /* true if the host should call P3D_instance_get_request().*/ - bool _request_pending; - - /* an opaque pointer the host may use to store private data that the - core API does not interpret. This pointer can be directly set, or - it can be initialized in the P3D_new_instance() call. */ - void *_user_data; - - /* Additional opaque data may be stored here. */ -} P3D_instance; - -/* This enum and set of structures abstract out the various window - handle types for the different platforms. */ - -typedef enum { - P3D_WHT_none = 0, - P3D_WHT_win_hwnd, - P3D_WHT_osx_port, - P3D_WHT_x11_window, - P3D_WHT_osx_cgcontext, -} P3D_window_handle_type; - -typedef struct { -#ifdef _WIN32 - HWND _hwnd; -#endif -} P3D_window_handle_win_hwnd; - - /* As provided by Mozilla, by default. Not compatible with Snow - Leopard. */ -typedef struct { -#if defined(__APPLE__) - GrafPtr _port; -#endif -} P3D_window_handle_osx_port; - - /* A CoreGraphics handle, as used by NPAPI with - NPDrawingModelCoreGraphics in effect. */ -typedef struct { -#if defined(__APPLE__) - CGContextRef _context; - WindowRef _window; -#endif -} P3D_window_handle_osx_cgcontext; - -typedef struct { -#if defined(HAVE_X11) - unsigned long _xwindow; - void *_xdisplay; -#endif -} P3D_window_handle_x11_window; - -typedef struct { - P3D_window_handle_type _window_handle_type; - union { - P3D_window_handle_win_hwnd _win_hwnd; - P3D_window_handle_osx_port _osx_port; - P3D_window_handle_x11_window _x11_window; - P3D_window_handle_osx_cgcontext _osx_cgcontext; - } _handle; -} P3D_window_handle; - -/* This enum lists the different kinds of window types that may be - requested for the instance. These define the way that the instance - will create its main Panda3D window. The instance will treat this - as a request only; it is always free to create whatever kind of - window it likes. */ -typedef enum { - /* Embedded: the Panda window is embedded within the host window. - This is the normal kind of window for an object embedded within a - browser page. Pass a valid window handle in for parent_window, - and valid coordinates on the parent window for win_x, win_y, - win_width, win_height. */ - P3D_WT_embedded, - - /* Toplevel: the Panda window is a toplevel window on the user's - desktop. Pass valid desktop coordinates in for win_x, win_y, - win_width, and win_height. If all of these are zero, the core - API will create a window wherever it sees fit. */ - P3D_WT_toplevel, - - /* Fullscreen: the Panda window is a fullscreen window, completely - overlaying the entire screen and changing the desktop resolution. - Pass a valid desktop size in for win_width and win_height (win_x - and win_y are ignored). If win_width and win_height are zero, - the core API will create a fullscreen window of its own preferred - size. */ - P3D_WT_fullscreen, - - /* Hidden: there is no window at all for the instance. */ - P3D_WT_hidden, - -} P3D_window_type; - - -/* This function pointer is passed to P3D_new_instance(), below. The - host must pass in a pointer to a valid function in the host's - address space, or NULL. If not NULL, this function will be called - asynchronously by the core API when it needs to make a request from - the host. After this notification has been received, the host - should call P3D_instance_get_request() (at its convenience) to - retrieve the actual Panda request. If the host passes NULL for - this function pointer, asynchronous notifications will not be - provided, and the host must be responsible for calling - P3D_instance_get_request() from time to time. */ - -/* Note that, unlike the other func typedefs in this header file, this - declaration is not naming a function within the core API itself. - Instead, it is a typedef for a function pointer that must be - supplied by the host. */ - -typedef void -P3D_request_ready_func(P3D_instance *instance); - -/* This structure is used to represent a single keyword/value pair - that appears within the embed syntax on the HTML page. An array of - these values is passed to the P3D instance to represent all of the - additional keywords that may appear within this syntax; it is up to - the Panda instance to interpret these additional keywords - correctly. */ -typedef struct { - const char *_keyword; - const char *_value; -} P3D_token; - -/* This function creates a new Panda3D instance. - - For tokens, pass an array of P3D_token elements (above), which - correspond to the user-supplied keyword/value pairs that may appear - in the embed token within the HTML syntax; the host is responsible - for allocating this array, and for deallocating it after this call - (the core API will make its own copy of the array). The tokens are - passed to the application, who is free to decide how to interpret - them; they have no meaning at the system level. - - The argc/argv parameters are intended for when the plugin is - invoked from the command line; they should be filled a standard - C-style argc/argv parameter list, corresponding to the command-line - parameters passed to the application. They may be 0 and NULL when - the plugin is invoked from the web. As above, this array and its - string data will be copied into the core API's own internal - storage, and need not persist after this call. - - The user_data pointer is any arbitrary pointer value; it will be - copied into the _user_data member of the new P3D_instance object. - This pointer is intended for the host to use to store private data - associated with each instance; the core API will not do anything with - this data. - */ - -typedef P3D_instance * -P3D_new_instance_func(P3D_request_ready_func *func, - const P3D_token tokens[], size_t num_tokens, - int argc, const char *argv[], - void *user_data); - -/* This function should be called at some point after - P3D_new_instance(); it actually starts the instance running. - Before this call, the instance will be in an indeterminate state. - - If is_local is true, then p3d_filename contains the name of a local - p3d file on disk. If false, then p3d_filename contains a URL that - should be downloaded to retrieve the p3d file. Also see - P3D_instance_start_stream(), below. - - p3d_offset is the offset within p3d_filename at which the p3d data - actually begins. It is normally 0 for an ordinary p3d file, but it - may be nonzero to indicate a p3d file embedded within another file - (such as is created by pdeploy). - - The return value is true on success, false on failure. */ -typedef bool -P3D_instance_start_func(P3D_instance *instance, bool is_local, - const char *p3d_filename, int p3d_offset); - -/* This function is an alternative to P3D_instance_start(); it - indicates an intention to feed the p3d file data to the instance as - a stream. The instance should return a unique integer ID for this - stream, as if the instance had requested the stream via a get_url - request (below). The plugin will then send the p3d file data to - the instance via a series of calls to - P3D_instance_feed_url_stream(), using the unique ID returned by - this function. When the stream has been transmitted successfully, - the instance will automatically start. - - The p3d_url string passed to this function is informational only. - The instance will not explicitly request this URL. */ -typedef int -P3D_instance_start_stream_func(P3D_instance *instance, const char *p3d_url); - -/* Call this function to interrupt a particular instance and stop it - from rendering, for instance when the user navigates away from the - page containing it. After calling this function, you should not - reference the P3D_instance pointer again. */ -typedef void -P3D_instance_finish_func(P3D_instance *instance); - -/* Call this function after creating an instance in order to set its - window size and placement. You must call this at least once in - order to actually manifest the instance onscreen. This may also be - called to reposition a window after its initial placement (e.g. if - the browser window has changed width and needs reformatting). */ -typedef void -P3D_instance_setup_window_func(P3D_instance *instance, - P3D_window_type window_type, - int win_x, int win_y, - int win_width, int win_height, - const P3D_window_handle *parent_window); - - -/********************** SCRIPTING SUPPORT **************************/ - -/* The following interfaces are provided to support controlling the - Panda instance via JavaScript or related interfaces on the browser. */ - -/* We require an "object" that contains some number of possible - different interfaces. An "object" might be a simple primitive like - an int, float, or string; or it might be a class object with - methods and properties. Instances of P3D_object are passed around - as parameters into and return values from functions. - - To implement a P3D_object, we need to first define a class - definition, which is a table of methods. Most classes are defined - internally by the core API, but the host must define at least one - class type as well, which provides callbacks into host-provided - objects. - - These function types define the methods available on a class. - These are function type declarations only; they do not correspond - to named functions within the core API DLL (but see the named - function pointers, further below). Instead, the function - pointers themselves are stored within the P3D_class_definition - structure. */ - -/* A forward declaration of P3D_object. */ -typedef struct _P3D_object P3D_object; - -/* A list of fundamental object types. */ -typedef enum { - P3D_OT_undefined, - P3D_OT_none, - P3D_OT_bool, - P3D_OT_int, - P3D_OT_float, - P3D_OT_string, - P3D_OT_object, -} P3D_object_type; - -/* Methods and functions that return a P3D_object return it with its - reference count already incremented by one for the benefit of the - caller, leaving the caller the owner of the implicit reference - count. This is referred to as returning a "new reference", using - the Python naming convention. Similarly, methods that receive a - P3D_object as a parameter will generally *not* adjust that object's - reference count (except to increment it the object is stored - internally), leaving the caller still the owner of the original - reference. Thus, it is the caller's responsibility to call - P3D_OBJECT_DECREF() on any P3D_objects it has received but no - longer wishes to keep. */ - -/* This method is called internally to deallocate the object and all - of its internal structures. Do not call it directly; call - P3D_OBJECT_DECREF() instead. */ -typedef void -P3D_object_finish_method(P3D_object *object); - -/* Returns the fundamental type of the object. This should be treated - as a hint to suggest how the object can most accurately be - represented; it does not limit the actual interfaces available to - an object. For instance, you may call P3D_OBJECT_GET_PROPERTY() - even if the object's type is not "object". */ -typedef P3D_object_type -P3D_object_get_type_method(P3D_object *object); - -/* Each of the following methods returns the object's value expressed - as the corresponding type. If the object is not precisely that - type, a coercion is performed. */ - -/* Return the object as a bool. */ -typedef bool -P3D_object_get_bool_method(P3D_object *object); - -/* Return the object as an integer. */ -typedef int -P3D_object_get_int_method(P3D_object *object); - -/* Return the object as a floating-point number. */ -typedef double -P3D_object_get_float_method(P3D_object *object); - -/* Get the object as a string. This method copies the string into the - provided buffer, and returns the actual length of the internal - string (not counting any terminating null character). If the - return value is larger than buffer_size, the string has been - truncated. If it is equal, there is no null character written to - the buffer (like strncpy). You may call this method first with - buffer = NULL and buffer_size = 0 to return just the required size - of the buffer. Note that P3D_object string data is internally - encoded using utf-8, by convention. */ -typedef int -P3D_object_get_string_method(P3D_object *object, - char *buffer, int buffer_size); - -/* As above, but instead of the literal object data, returns a - user-friendly reprensentation of the object as a string. For - instance, a string object returns the string itself to - P3D_OBJECT_GET_STRING(), but returns the string with quotation - marks and escape characters from P3D_OBJECT_GET_REPR(). - Mechanically, this function works the same way as get_string(). */ -typedef int -P3D_object_get_repr_method(P3D_object *object, - char *buffer, int buffer_size); - -/* Looks up a property on the object by name, i.e. a data member or a - method. The return value is a new-reference P3D_object if the - property exists, or NULL if it does not. */ -typedef P3D_object * -P3D_object_get_property_method(P3D_object *object, const char *property); - -/* Changes the value at the indicated property. The new value is - assigned to the indicating property, and its reference count is - correspondingly incremented. Any existing object previously - assigned to the corresponding property is replaced, and its - reference count decremented. If the value pointer is NULL, the - property is removed altogether. If needs_response is true, this - method returns true on success, false on failure. If - needs_response is false, the return value is always true regardless - of success or failure.*/ -typedef bool -P3D_object_set_property_method(P3D_object *object, const char *property, - bool needs_response, P3D_object *value); - -/* Returns true if the indicated method name exists on the object, - false otherwise. In the Python case, this actually returns true if - the object has the indicated property, and that property represents - a callable object. If method_name is empty or NULL, returns true - if the object itself is callable. */ -typedef bool -P3D_object_has_method_method(P3D_object *object, const char *method_name); - -/* Invokes a named method on the object. If method_name is empty or - NULL, invokes the object itself as a function. You must pass an - array of P3D_objects as the list of parameters, and ownership of - these objects' reference counts is not transferred with the call - (you must still DECREF these objects afterwards). - - If needs_response is true, the return value is a new-reference - P3D_object on success, or NULL on failure. If needs_response is - false, the return value is always NULL, and there is no way to - determine success or failure. */ -typedef P3D_object * -P3D_object_call_method(P3D_object *object, const char *method_name, - bool needs_response, - P3D_object *params[], int num_params); - -/* Evaluates an arbitrary JavaScript expression in the context of the - object. Python objects do not support this method. - - The return value is a new-reference P3D_object on success, or NULL - on failure. */ -typedef P3D_object * -P3D_object_eval_method(P3D_object *object, const char *expression); - -/* This defines the class structure that implements all of the above - methods. */ -typedef struct _P3D_class_definition { - P3D_object_finish_method *_finish; - - P3D_object_get_type_method *_get_type; - P3D_object_get_bool_method *_get_bool; - P3D_object_get_int_method *_get_int; - P3D_object_get_float_method *_get_float; - P3D_object_get_string_method *_get_string; - P3D_object_get_repr_method *_get_repr; - - P3D_object_get_property_method *_get_property; - P3D_object_set_property_method *_set_property; - - P3D_object_has_method_method *_has_method; - P3D_object_call_method *_call; - P3D_object_eval_method *_eval; - -} P3D_class_definition; - -/* And this structure defines the actual instances of P3D_object. */ -struct _P3D_object { - const P3D_class_definition *_class; - int _ref_count; - - /* Additional opaque data may be stored here. */ -}; - -/* These macros are defined for the convenience of invoking any of the - above method functions on an object. - - CAUTION! None of these macros are thread-safe; you may use them - only in a single-threaded application (or when only a single thread - of the application makes any calls into this API). For thread-safe - variants, see the similarly-named function calls below. */ - -#define P3D_OBJECT_GET_TYPE(object) ((object)->_class->_get_type((object))) -#define P3D_OBJECT_GET_BOOL(object) ((object)->_class->_get_bool((object))) -#define P3D_OBJECT_GET_INT(object) ((object)->_class->_get_int((object))) -#define P3D_OBJECT_GET_FLOAT(object) ((object)->_class->_get_float((object))) -#define P3D_OBJECT_GET_STRING(object, buffer, buffer_size) ((object)->_class->_get_string((object), (buffer), (buffer_size))) -#define P3D_OBJECT_GET_REPR(object, buffer, buffer_size) ((object)->_class->_get_repr((object), (buffer), (buffer_size))) - -#define P3D_OBJECT_GET_PROPERTY(object, property) ((object)->_class->_get_property((object), (property))) -#define P3D_OBJECT_SET_PROPERTY(object, property, needs_response, value) ((object)->_class->_set_property((object), (property), (needs_response), (value))) - -#define P3D_OBJECT_HAS_METHOD(object, method_name) ((object)->_class->_has_method((object), (method_name))) -#define P3D_OBJECT_CALL(object, method_name, needs_response, params, num_params) ((object)->_class->_call((object), (method_name), (needs_response), (params), (num_params))) -#define P3D_OBJECT_EVAL(object, expression) ((object)->_class->_eval((object), (expression))) - -/* These macros are provided to manipulate the reference count of the - indicated object. As above, these macros are NOT thread-safe. If - you need a thread-safe reference count adjustment, see the - similarly-named function calls below. - - Following Python's convention, XDECREF is provided to decrement the - reference count for a pointer that might be NULL (it does nothing - in the case of a NULL pointer). */ - -#define P3D_OBJECT_INCREF(object) (++(object)->_ref_count) -#define P3D_OBJECT_DECREF(object) { if (--(object)->_ref_count <= 0) { (object)->_class->_finish((object)); } } -#define P3D_OBJECT_XDECREF(object) { if ((object) != nullptr) { P3D_OBJECT_DECREF(object); } } - -/* End of method pointer definitions. The following function types - are once again meant to define actual function pointers to be found - within the core API DLL. */ - -/* Use these functions for thread-safe variants of the above macros. */ -typedef P3D_object_type -P3D_object_get_type_func(P3D_object *object); -typedef bool -P3D_object_get_bool_func(P3D_object *object); -typedef int -P3D_object_get_int_func(P3D_object *object); -typedef double -P3D_object_get_float_func(P3D_object *object); -typedef int -P3D_object_get_string_func(P3D_object *object, char *buffer, int buffer_size); -typedef int -P3D_object_get_repr_func(P3D_object *object, char *buffer, int buffer_size); -typedef P3D_object * -P3D_object_get_property_func(P3D_object *object, const char *property); -typedef bool -P3D_object_set_property_func(P3D_object *object, const char *property, - bool needs_response, P3D_object *value); -typedef bool -P3D_object_has_method_func(P3D_object *object, const char *method_name); -typedef P3D_object * -P3D_object_call_func(P3D_object *object, const char *method_name, - bool needs_response, - P3D_object *params[], int num_params); -typedef P3D_object * -P3D_object_eval_func(P3D_object *object, const char *expression); - -/* A NULL pointer passed into either incref or decref is safe and will - be quietly ignored. */ -typedef void -P3D_object_incref_func(P3D_object *object); -typedef void -P3D_object_decref_func(P3D_object *object); - - -/* Returns a new P3D_class_definition object, filled with generic - function pointers that have reasonable default behavior for all - methods. The host should use this function to get a clean - P3D_class_definition object before calling - P3D_instance_set_browser_script_object() (see below). Note that - this pointer will automatically be freed when P3D_finalize() is - called. */ -typedef P3D_class_definition * -P3D_make_class_definition_func(); - -/* Returns a new-reference P3D_object of type "undefined". This - corresponds to the undefined or void type on JavaScript. It is - similar to Python's None, but has a subtly different shade of - meaning; we map it to an explicit Undefined instance in - AppRunner.py. */ -typedef P3D_object * -P3D_new_undefined_object_func(); - -/* Returns a new-reference P3D_object of type none. This value has no - particular value and corresponds to Python's None type or - JavaScript's null type. */ -typedef P3D_object * -P3D_new_none_object_func(); - -/* Returns a new-reference P3D_object of type bool. */ -typedef P3D_object * -P3D_new_bool_object_func(bool value); - -/* Returns a new-reference P3D_object of type int. */ -typedef P3D_object * -P3D_new_int_object_func(int value); - -/* Returns a new-reference P3D_object of type float. */ -typedef P3D_object * -P3D_new_float_object_func(double value); - -/* Returns a new-reference P3D_object of type string. The supplied - string should be already encoded in utf-8. It is copied into the - object and stored internally. */ -typedef P3D_object * -P3D_new_string_object_func(const char *string, int length); - -/* Returns a borrowed-reference pointer--*not* a new reference--to the - top-level scriptable object of the instance. Scripts running on - the host may use this object to communicate with the instance, by - using the above methods to set or query properties, and/or call - methods, on the instance. The caller should increment the - reference count when storing this object. */ -typedef P3D_object * -P3D_instance_get_panda_script_object_func(P3D_instance *instance); - -/* The inverse functionality: this supplies an object pointer to the - instance to allow the Panda instance to control the browser. In - order to enable browser scriptability, the host must call this - method shortly after creating the instance, preferably before - calling P3D_instance_start(). - - The object parameter must have been created by the host. It will - have a custom P3D_class_definition pointer, which also must have - been created by the host. The best way to create an appropriate - class definition is call P3D_make_class_definition(), and then - replace the function pointers for at least _finish, _get_property, - _set_property, _has_method, and _call. Set these pointers to the - host's own functions that make the appropriate changes in the DOM, - or invoke the appropriate JavaScript functions. - - If this function is never called, the instance will not be able to - make outcalls to the DOM or to JavaScript, but scripts may still be - able to control the instance via P3D_instance_get_panda_script_object(), - above. - - Note that the object's constructor should initialize its reference - count to 1. The instance will increment the reference count as a - result of this call; the caller is responsible for calling DECREF - on the object after this call to remove its own reference. */ -typedef void -P3D_instance_set_browser_script_object_func(P3D_instance *instance, - P3D_object *object); - - -/********************** REQUEST HANDLING **************************/ - -/* The core API may occasionally have an asynchronous request to pass - up to the host. The following structures implement this interface. - The design is intended to support single-threaded as well as - multi-threaded implementations in the host; there is only the one - callback function, P3D_request_ready (above), which may be called - asynchronously by the core API. The host should be careful that - this callback function is protected from mutual access. The - callback function implementation may be as simple as setting a flag - that the host will later check within its main processing loop. - - Once P3D_request_ready() has been received, the host should call - P3D_instance_get_request() to query the nature of the request. - This call may be made synchronously, i.e. within the host's main - processing loop. After each request is serviced, the host should - release the request via P3D_request_finish() and then call - P3D_instance_get_request() again until that function returns NULL. - - The requests themselves are implemented via a hierarchy of structs. - Each request is stored in a different kind of struct, allowing the - different requests to store a variety of data. An enumerated value - indicates the particular request type retrieved. */ - -/* This represents the type of a request returned by - P3D_instance_get_request. More types may be added later. */ -typedef enum { - P3D_RT_stop, - P3D_RT_get_url, - P3D_RT_notify, - P3D_RT_refresh, - P3D_RT_callback, - P3D_RT_forget_package -} P3D_request_type; - -/* Structures corresponding to the request types in the above enum. */ - -/* A stop request. The instance would like to stop itself. No - additional data is required. The host should respond by calling - P3D_instance_finish(). */ -typedef struct { -} P3D_request_stop; - -/* A get_url request. The core API would like to retrieve data for a - particular URL. The core API is responsible for supplying a valid - URL string, and a unique integer ID. The unique ID is needed to - feed the results of the URL back to the core API. If possible, the - host should be prepared to handle multiple get_url requests in - parallel, but it is allowed to handle them all one at a time if - necessary. As data comes in from the url, the host should call - P3D_instance_feed_url_stream(). -*/ -typedef struct { - const char *_url; - int _unique_id; -} P3D_request_get_url; - -/* A general notification. This is just a message of some event - having occurred within the Panda3D instance. The core API will - automatically trigger a JavaScript callback based on this event. - It may be safely ignored by the application. -*/ -typedef struct { - const char *_message; -} P3D_request_notify; - -/* A refresh request. The instance would like to get a repaint event - on its own window. Only relevant for windowless plugins, e.g. on - OSX. */ -typedef struct { -} P3D_request_refresh; - -/* A callback request. The instance wants to yield control - temporarily back to JavaScript, to allow JavaScript to maintain - interactivity during a long computation, and expects to get control - again some short while later (when the callback request is pulled - off the queue and the request is handled). The data within this - structure is used internally by the core API and shouldn't be - interpreted by the caller. */ -typedef void P3D_callback_func(void *); -typedef struct { - P3D_callback_func *_func; - void *_data; -} P3D_request_callback; - -/* A forget-package request. This is called when a Python application - wishes to uninstall a specific package (for instance, to clean up - disk space). It is assumed that the application will be - responsible for deleting the actual files of the package; this is - just an instruction to the core API to remove the indicated package - from the in-memory cache. This is handled internally, and - shouldn't be interpreted by the caller. */ -typedef struct { - const char *_host_url; - const char *_package_name; - const char *_package_version; -} P3D_request_forget_package; - -/* This is the overall structure that represents a single request. It - is returned by P3D_instance_get_request(). */ -typedef struct { - P3D_instance *_instance; - P3D_request_type _request_type; - union { - P3D_request_stop _stop; - P3D_request_get_url _get_url; - P3D_request_notify _notify; - P3D_request_refresh _refresh; - P3D_request_callback _callback; - P3D_request_forget_package _forget_package; - } _request; -} P3D_request; - -/* After a call to P3D_request_ready(), or from time to time in - general, the host should call this function to see if there are any - pending requests from the core API. The function will return a - freshly-allocated request if there is a request ready, or NULL if - there are no requests. After a receipt of P3D_request_ready(), - the host should call this function repeatedly until it returns NULL - (there might be multiple requests for a single receipt of - P3D_request_ready()). Each request should be processed, then - released via P3D_request_finish(). */ -typedef P3D_request * -P3D_instance_get_request_func(P3D_instance *instance); - -/* This method may also be used to test whether a request is ready. - If any open instance has a pending request, this function will - return a pointer to one of them (which you may then pass to - P3D_instance_get_request_func). If no instances have a pending - request, this function will return NULL. This function will not - return until at least timeout seconds have elapsed, or a request is - available, whichever comes first. - - Note that, due to race conditions, it is possible for this function - to return a P3D_instance that does not in fact have any requests - pending (another thread may have checked the request first). You - should always verify that the return value of - P3D_instance_get_request() is not NULL. */ -typedef P3D_instance * -P3D_check_request_func(double timeout); - -/* A request retrieved by P3D_instance_get_request() should eventually - be passed here, after it has been handled, to deallocate its - resources and prevent a memory leak. The 'handled' flag should be - passed true if the host has handled the request, or false if it has - ignored it (e.g. because it does not implement support for this - particular request type). After calling this function, you should - not reference the P3D_request pointer again. */ -typedef void -P3D_request_finish_func(P3D_request *request, bool handled); - -/* This code is passed to P3D_instance_feed_url_stream, below, as data - is retrieved from the URL. */ -typedef enum { - /* in progress: the query is still in progress, and another call - will be made in the future. */ - P3D_RC_in_progress, - - /* done: the query is done, and all data has been retrieved without - error. This call represents the last of the data. */ - P3D_RC_done, - - /* generic_error: some error other than an HTTP error has occurred, - for instance, lack of connection to the server, or malformed URL. - No more data will be forthcoming. */ - P3D_RC_generic_error, - - /* An HTTP error has occurred, for instance 404. The particular - status code will be supplied in the http_status_code parameter. - There may or may not be data associated with this error as well. - However, no more data will be delivered after this call. */ - P3D_RC_http_error, - - /* The download was interrupted because we're about to shut down the - instance. The instance should not attempt to restart a new - download in response to this. */ - P3D_RC_shutdown - -} P3D_result_code; - -/* This function is used by the host to handle a get_url request, - above. As it retrieves data from the URL, it should call this - function from time to time to feed that data to the core API. - - instance and unique_id are from the original get_url() request. - - result_code and http_status_code indicates the current status of - the request, as described above; the call will be made again in the - future if its result_code is P3D_RC_in_progress. - - total_expected_data represents the host's best guess at the total - amount of data that will be retrieved. It is acceptable if this - guess doesn't match the actual data received at all. Set it to 0 - if the host has no idea. This value may change from one call to - the next. - - this_data and this_data_size describe the most recent block of data - retrieved from the URL. Each chunk of data passed to this function - is appended together by the core API to define the total set of - data retrieved from the URL. For a particular call to - feed_url_stream, this may contain no data at all - (e.g. this_data_size may be 0). - - The return value of this function is true if there are no problems - and the download should continue, false if there was an error - accepting the data and the host should abort. If this function - returns false on a P3D_RC_in_progress, there is no need to call - the function again with any future updates. - */ -typedef bool -P3D_instance_feed_url_stream_func(P3D_instance *instance, int unique_id, - P3D_result_code result_code, - int http_status_code, - size_t total_expected_data, - const void *this_data, - size_t this_data_size); - -/* This enum and set of structures abstract out the event pointer data - types for the different platforms, as passed to - P3D_instance_handle_event(), below. */ - -typedef enum { - P3D_ET_none = 0, - P3D_ET_osx_event_record, - P3D_ET_osx_cocoa, -} P3D_event_type; - -typedef struct { -#if defined(__APPLE__) - EventRecord *_event; -#endif -} P3D_event_osx_event_record; - -// This enum reimplements NPCocoaEventTyped, used below. -typedef enum { - P3DCocoaEventDrawRect = 1, - P3DCocoaEventMouseDown, - P3DCocoaEventMouseUp, - P3DCocoaEventMouseMoved, - P3DCocoaEventMouseEntered, - P3DCocoaEventMouseExited, - P3DCocoaEventMouseDragged, - P3DCocoaEventKeyDown, - P3DCocoaEventKeyUp, - P3DCocoaEventFlagsChanged, - P3DCocoaEventFocusChanged, - P3DCocoaEventWindowFocusChanged, - P3DCocoaEventScrollWheel, - P3DCocoaEventTextInput -} P3DCocoaEventType; - -// This structure reimplements NPCocoaEvent, to pass the complex cocoa event -// structures as generated by NPAPI. -typedef struct { - P3DCocoaEventType type; - unsigned int version; - union { - struct { - unsigned int modifierFlags; - double pluginX; - double pluginY; - int buttonNumber; - int clickCount; - double deltaX; - double deltaY; - double deltaZ; - } mouse; - struct { - unsigned int modifierFlags; - const wchar_t *characters; - const wchar_t *charactersIgnoringModifiers; - bool isARepeat; - unsigned short keyCode; - } key; - struct { -#ifdef __APPLE__ - CGContextRef context; -#endif // __APPLE__ - double x; - double y; - double width; - double height; - } draw; - struct { - bool hasFocus; - } focus; - struct { - const wchar_t *text; - } text; - } data; -} P3DCocoaEvent; - -typedef struct { - P3DCocoaEvent _event; -} P3D_event_osx_cocoa; - -typedef struct { - P3D_event_type _event_type; - union { - P3D_event_osx_event_record _osx_event_record; - P3D_event_osx_cocoa _osx_cocoa; - } _event; -} P3D_event_data; - -/* Use this function to supply a new os-specific window event to the - plugin. This is presently used only on OSX, where the window - events are needed for proper plugin handling. On Windows and X11, - window events are handled natively by the plugin. - - The return value is true if the handler has processed the event, - false if it has been ignored. */ -typedef bool -P3D_instance_handle_event_func(P3D_instance *instance, - const P3D_event_data *event); - -#ifdef P3D_FUNCTION_PROTOTYPES - -/* Define all of the actual prototypes for the above functions. */ -EXPCL_P3D_PLUGIN P3D_initialize_func P3D_initialize; -EXPCL_P3D_PLUGIN P3D_finalize_func P3D_finalize; -EXPCL_P3D_PLUGIN P3D_set_plugin_version_func P3D_set_plugin_version; -EXPCL_P3D_PLUGIN P3D_set_super_mirror_func P3D_set_super_mirror; - -EXPCL_P3D_PLUGIN P3D_new_instance_func P3D_new_instance; -EXPCL_P3D_PLUGIN P3D_instance_start_func P3D_instance_start; -EXPCL_P3D_PLUGIN P3D_instance_start_stream_func P3D_instance_start_stream; -EXPCL_P3D_PLUGIN P3D_instance_finish_func P3D_instance_finish; -EXPCL_P3D_PLUGIN P3D_instance_setup_window_func P3D_instance_setup_window; - -EXPCL_P3D_PLUGIN P3D_object_get_type_func P3D_object_get_type; -EXPCL_P3D_PLUGIN P3D_object_get_bool_func P3D_object_get_bool; -EXPCL_P3D_PLUGIN P3D_object_get_int_func P3D_object_get_int; -EXPCL_P3D_PLUGIN P3D_object_get_float_func P3D_object_get_float; -EXPCL_P3D_PLUGIN P3D_object_get_string_func P3D_object_get_string; -EXPCL_P3D_PLUGIN P3D_object_get_repr_func P3D_object_get_repr; -EXPCL_P3D_PLUGIN P3D_object_get_property_func P3D_object_get_property; -EXPCL_P3D_PLUGIN P3D_object_set_property_func P3D_object_set_property; -EXPCL_P3D_PLUGIN P3D_object_has_method_func P3D_object_has_method; -EXPCL_P3D_PLUGIN P3D_object_call_func P3D_object_call; -EXPCL_P3D_PLUGIN P3D_object_eval_func P3D_object_eval; -EXPCL_P3D_PLUGIN P3D_object_incref_func P3D_object_incref; -EXPCL_P3D_PLUGIN P3D_object_decref_func P3D_object_decref; - -EXPCL_P3D_PLUGIN P3D_make_class_definition_func P3D_make_class_definition; -EXPCL_P3D_PLUGIN P3D_new_undefined_object_func P3D_new_undefined_object; -EXPCL_P3D_PLUGIN P3D_new_none_object_func P3D_new_none_object; -EXPCL_P3D_PLUGIN P3D_new_bool_object_func P3D_new_bool_object; -EXPCL_P3D_PLUGIN P3D_new_int_object_func P3D_new_int_object; -EXPCL_P3D_PLUGIN P3D_new_float_object_func P3D_new_float_object; -EXPCL_P3D_PLUGIN P3D_new_string_object_func P3D_new_string_object; -EXPCL_P3D_PLUGIN P3D_instance_get_panda_script_object_func P3D_instance_get_panda_script_object; -EXPCL_P3D_PLUGIN P3D_instance_set_browser_script_object_func P3D_instance_set_browser_script_object; - -EXPCL_P3D_PLUGIN P3D_instance_get_request_func P3D_instance_get_request; -EXPCL_P3D_PLUGIN P3D_check_request_func P3D_check_request; -EXPCL_P3D_PLUGIN P3D_request_finish_func P3D_request_finish; -EXPCL_P3D_PLUGIN P3D_instance_feed_url_stream_func P3D_instance_feed_url_stream; -EXPCL_P3D_PLUGIN P3D_instance_handle_event_func P3D_instance_handle_event; - -#endif /* P3D_FUNCTION_PROTOTYPES */ - -// The default max_age, if none is specified in a particular contents.xml, is -// 5 seconds. This gives us enough time to start a few packages downloading, -// without re-querying the host for a new contents.xml at each operation. -#define P3D_CONTENTS_DEFAULT_MAX_AGE 5 - -#ifdef __cplusplus -}; /* end of extern "C" */ -#endif - -#endif /* P3D_PLUGIN_H */ diff --git a/direct/src/plugin/p3d_plugin_common.h b/direct/src/plugin/p3d_plugin_common.h deleted file mode 100644 index 6ddd0e057c..0000000000 --- a/direct/src/plugin/p3d_plugin_common.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3d_plugin_common.h - * @author drose - * @date 2009-05-29 - */ - -#ifndef P3D_PLUGIN_COMMON -#define P3D_PLUGIN_COMMON - -// This header file is included by all C++ files in this directory that -// contribute to p3d_plugin; it provides some common symbol declarations. - -#define P3D_FUNCTION_PROTOTYPES -// #define BUILDING_P3D_PLUGIN -#define TIXML_USE_STL - -// It's a good idea to pick up this header file, even though we don't actually -// link with dtool. This header file defines useful system-wide config -// settings. -#include "dtool_config.h" - -#include "p3d_plugin.h" -#include "p3d_lock.h" - -#include -#include -#include -#include - -// Appears in p3dInstanceManager.cxx. -extern std::ostream *nout_stream; -#define nout (*nout_stream) - -// Appears in p3d_plugin.cxx. -extern LOCK _api_lock; - -// A convenience function for formatting a generic P3D_object to an ostream. -inline std::ostream & -operator << (std::ostream &out, P3D_object &value) { - int size = P3D_OBJECT_GET_REPR(&value, nullptr, 0); - char *buffer = new char[size]; - P3D_OBJECT_GET_REPR(&value, buffer, size); - out.write(buffer, size); - delete[] buffer; - - return out; -} - -#endif diff --git a/direct/src/plugin/p3d_plugin_composite1.cxx b/direct/src/plugin/p3d_plugin_composite1.cxx deleted file mode 100644 index d49fadc3c2..0000000000 --- a/direct/src/plugin/p3d_plugin_composite1.cxx +++ /dev/null @@ -1,33 +0,0 @@ -#include "p3d_plugin.cxx" -#include "p3dAuthSession.cxx" -#include "p3dBoolObject.cxx" -#include "p3dConcreteSequence.cxx" -#include "p3dConcreteStruct.cxx" -#include "p3dConditionVar.cxx" -#include "p3dDownload.cxx" -#include "p3dFileDownload.cxx" -#include "p3dFileParams.cxx" -#include "p3dFloatObject.cxx" -#include "p3dHost.cxx" -#include "p3dInstance.cxx" -#include "p3dInstanceManager.cxx" -#include "p3dIntObject.cxx" -#include "p3dMultifileReader.cxx" -#include "p3dNoneObject.cxx" -#include "p3dObject.cxx" -#include "p3dOsxSplashWindow.cxx" -#include "p3dPackage.cxx" -#include "p3dPatchfileReader.cxx" -#include "p3dPatchFinder.cxx" -#include "p3dPythonObject.cxx" -#include "p3dReferenceCount.cxx" -#include "p3dSession.cxx" -#include "p3dSplashWindow.cxx" -#include "p3dStringObject.cxx" -#include "p3dTemporaryFile.cxx" -#include "p3dMainObject.cxx" -#include "p3dUndefinedObject.cxx" -#include "p3dWinSplashWindow.cxx" -#include "p3dX11SplashWindow.cxx" -#include "p3dWindowParams.cxx" -#include "xml_helpers.cxx" diff --git a/direct/src/plugin/p3dcert.plist b/direct/src/plugin/p3dcert.plist deleted file mode 100644 index d536f638df..0000000000 --- a/direct/src/plugin/p3dcert.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleDisplayName - P3DCert - CFBundleExecutable - p3dcert - CFBundleIdentifier - org.panda3d.runtime.p3dcert - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - P3DCert - CFBundlePackageType - APPL - CFBundleShortVersionString - 0.9.3 - CFBundleSignature - ???? - CFBundleVersion - 0.9.3 - LSUIElement - 1 - LSHasLocalizedDisplayName - - NSAppleScriptEnabled - - NSPrincipalClass - NSApplication - - diff --git a/direct/src/plugin/p3dpython.plist b/direct/src/plugin/p3dpython.plist deleted file mode 100644 index d86cc2d0ca..0000000000 --- a/direct/src/plugin/p3dpython.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleDisplayName - P3DPython - CFBundleExecutable - p3dpython - CFBundleIdentifier - org.panda3d.runtime.p3dpython - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - P3DPython - CFBundlePackageType - APPL - CFBundleShortVersionString - 0.9.3 - CFBundleSignature - ???? - CFBundleVersion - 0.9.3 - LSUIElement - 1 - LSHasLocalizedDisplayName - - NSAppleScriptEnabled - - NSPrincipalClass - NSApplication - - diff --git a/direct/src/plugin/p3dpython_composite1.cxx b/direct/src/plugin/p3dpython_composite1.cxx deleted file mode 100644 index 819289ffec..0000000000 --- a/direct/src/plugin/p3dpython_composite1.cxx +++ /dev/null @@ -1,6 +0,0 @@ -#include "binaryXml.cxx" -#include "handleStream.cxx" -#include "handleStreamBuf.cxx" -#include "p3dCInstance.cxx" -#include "p3dPythonRun.cxx" -#include "run_p3dpython.cxx" diff --git a/direct/src/plugin/parse_color.cxx b/direct/src/plugin/parse_color.cxx deleted file mode 100644 index 4a5f12b587..0000000000 --- a/direct/src/plugin/parse_color.cxx +++ /dev/null @@ -1,73 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file parse_color.cxx - * @author drose - * @date 2011-08-25 - */ - -#include "parse_color.h" -#include - -static bool parse_hexdigit(int &result, char digit); - -/** - * Parses a HTML color spec of the form #rgb or #rrggbb. Returns true on - * success, false on failure. On success, fills r, g, b with the color values - * in the range 0..255. On failure, r, g, b are undefined. - */ -bool -parse_color(int &r, int &g, int &b, const std::string &color) { - if (color.empty() || color[0] != '#') { - return false; - } - if (color.length() == 4) { - if (!parse_hexdigit(r, color[1]) || - !parse_hexdigit(g, color[2]) || - !parse_hexdigit(b, color[3])) { - return false; - } - r *= 0x11; - g *= 0x11; - b *= 0x11; - return true; - } - if (color.length() == 7) { - int rh, rl, gh, gl, bh, bl; - if (!parse_hexdigit(rh, color[1]) || - !parse_hexdigit(rl, color[2]) || - !parse_hexdigit(gh, color[3]) || - !parse_hexdigit(gl, color[4]) || - !parse_hexdigit(bh, color[5]) || - !parse_hexdigit(bl, color[6])) { - return false; - } - r = (rh << 4) | rl; - g = (gh << 4) | gl; - b = (bh << 4) | bl; - return true; - } - - return false; -} - -/** - * Parses a single hex digit. Returns true on success, false on failure. On - * success, fills result with the parsed value, an integer in the range 0..15. - */ -bool -parse_hexdigit(int &result, char digit) { - if (isdigit(digit)) { - result = digit - '0'; - return true; - } else if (isxdigit(digit)) { - result = tolower(digit) - 'a' + 10; - return true; - } - return false; -} diff --git a/direct/src/plugin/parse_color.h b/direct/src/plugin/parse_color.h deleted file mode 100644 index 794abf2055..0000000000 --- a/direct/src/plugin/parse_color.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file parse_color.h - * @author drose - * @date 2011-08-25 - */ - -#ifndef PARSE_COLOR_H -#define PARSE_COLOR_H - -#include - -bool parse_color(int &r, int &g, int &b, const std::string &color); - -#endif diff --git a/direct/src/plugin/plugin_common_composite1.cxx b/direct/src/plugin/plugin_common_composite1.cxx deleted file mode 100644 index 4686d49495..0000000000 --- a/direct/src/plugin/plugin_common_composite1.cxx +++ /dev/null @@ -1,4 +0,0 @@ -#include "load_plugin.cxx" -#include "fileSpec.cxx" -#include "find_root_dir.cxx" -#include "mkdir_complete.cxx" diff --git a/direct/src/plugin/plugin_get_x11.h b/direct/src/plugin/plugin_get_x11.h deleted file mode 100644 index 827c96fc27..0000000000 --- a/direct/src/plugin/plugin_get_x11.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file plugin_get_x11.h - * @author drose - * @date 2011-08-28 - */ - -#ifndef PLUGIN_GET_X11_H -#define PLUGIN_GET_X11_H - -#include "pandabase.h" -#include "p3d_plugin_config.h" - -#ifdef HAVE_X11 -// This header file is designed to help work around some of the namespace -// spamming that X11 causes, by renaming the symbols that X11 declares that -// are known to conflict with other library names (like Apple's Core Graphics, -// for instance). - -// In order for this to work, everyone who uses X11 within Panda should -// include this file instead of including the X11 headers directly. - -#include "pre_x11_include.h" - -#include -#include - -#include "post_x11_include.h" - -#endif // HAVE_X11 - -#endif diff --git a/direct/src/plugin/run_p3dpython.cxx b/direct/src/plugin/run_p3dpython.cxx deleted file mode 100644 index 1a0432a2d4..0000000000 --- a/direct/src/plugin/run_p3dpython.cxx +++ /dev/null @@ -1,33 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file run_p3dpython.cxx - * @author drose - * @date 2009-08-29 - */ - -#include "p3dPythonRun.h" -#include "run_p3dpython.h" - -/** - * This externally-visible function is the main entry point to this DLL, and - * it starts the whole thing running. Returns the exit status, which will be - * 0 on success, 1 or otherwise on failure. - */ -int -run_p3dpython(const char *program_name, const char *archive_file, - FHandle input_handle, FHandle output_handle, - const char *log_pathname, bool interactive_console) { - P3DPythonRun::_global_ptr = - new P3DPythonRun(program_name, archive_file, input_handle, output_handle, - log_pathname, interactive_console); - int result = P3DPythonRun::_global_ptr->run_python(); - delete P3DPythonRun::_global_ptr; - P3DPythonRun::_global_ptr = nullptr; - return result; -} diff --git a/direct/src/plugin/run_p3dpython.h b/direct/src/plugin/run_p3dpython.h deleted file mode 100644 index 8c1a6c2b69..0000000000 --- a/direct/src/plugin/run_p3dpython.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file run_p3dpython.h - * @author drose - * @date 2009-08-29 - */ - -#ifndef RUN_P3DPYTHON_H -#define RUN_P3DPYTHON_H - -// This header file defines the prototype for run_p3dpython(), the main entry -// point to this DLL. - -#include "fhandle.h" - -#ifdef _WIN32 -#define EXPCL_P3DPYTHON __declspec(dllexport) -#else -#define EXPCL_P3DPYTHON -#endif - -typedef int -run_p3dpython_func(const char *program_name, const char *archive_file, - FHandle input_handle, FHandle output_handle, - const char *log_pathname, bool interactive_console); - -extern "C" EXPCL_P3DPYTHON int -run_p3dpython(const char *program_name, const char *archive_file, - FHandle input_handle, FHandle output_handle, - const char *log_pathname, bool interactive_console); - -#endif diff --git a/direct/src/plugin/stb_image.h b/direct/src/plugin/stb_image.h deleted file mode 100644 index c3945c2e25..0000000000 --- a/direct/src/plugin/stb_image.h +++ /dev/null @@ -1,6326 +0,0 @@ -/* stb_image - v2.02 - public domain image loader - http://nothings.org/stb_image.h - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8-bit-per-channel (16 bpc not supported) - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - - Revision 2.00 release notes: - - - Progressive JPEG is now supported. - - - PPM and PGM binary formats are now supported, thanks to Ken Miller. - - - x86 platforms now make use of SSE2 SIMD instructions for - JPEG decoding, and ARM platforms can use NEON SIMD if requested. - This work was done by Fabian "ryg" Giesen. SSE2 is used by - default, but NEON must be enabled explicitly; see docs. - - With other JPEG optimizations included in this version, we see - 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup - on a JPEG on an ARM machine, relative to previous versions of this - library. The same results will not obtain for all JPGs and for all - x86/ARM machines. (Note that progressive JPEGs are significantly - slower to decode than regular JPEGs.) This doesn't mean that this - is the fastest JPEG decoder in the land; rather, it brings it - closer to parity with standard libraries. If you want the fastest - decode, look elsewhere. (See "Philosophy" section of docs below.) - - See final bullet items below for more info on SIMD. - - - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing - the memory allocator. Unlike other STBI libraries, these macros don't - support a context parameter, so if you need to pass a context in to - the allocator, you'll have to store it in a global or a thread-local - variable. - - - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and - STBI_NO_LINEAR. - STBI_NO_HDR: suppress implementation of .hdr reader format - STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API - - - You can suppress implementation of any of the decoders to reduce - your code footprint by #defining one or more of the following - symbols before creating the implementation. - - STBI_NO_JPEG - STBI_NO_PNG - STBI_NO_BMP - STBI_NO_PSD - STBI_NO_TGA - STBI_NO_GIF - STBI_NO_HDR - STBI_NO_PIC - STBI_NO_PNM (.ppm and .pgm) - - - You can request *only* certain decoders and suppress all other ones - (this will be more forward-compatible, as addition of new decoders - doesn't require you to disable them explicitly): - - STBI_ONLY_JPEG - STBI_ONLY_PNG - STBI_ONLY_BMP - STBI_ONLY_PSD - STBI_ONLY_TGA - STBI_ONLY_GIF - STBI_ONLY_HDR - STBI_ONLY_PIC - STBI_ONLY_PNM (.ppm and .pgm) - - Note that you can define multiples of these, and you will get all - of them ("only x" and "only y" is interpreted to mean "only x&y"). - - - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still - want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB - - - Compilation of all SIMD code can be suppressed with - #define STBI_NO_SIMD - It should not be necessary to disable SIMD unless you have issues - compiling (e.g. using an x86 compiler which doesn't support SSE - intrinsics or that doesn't support the method used to detect - SSE2 support at run-time), and even those can be reported as - bugs so I can refine the built-in compile-time checking to be - smarter. - - - The old STBI_SIMD system which allowed installing a user-defined - IDCT etc. has been removed. If you need this, don't upgrade. My - assumption is that almost nobody was doing this, and those who - were will find the built-in SIMD more satisfactory anyway. - - - RGB values computed for JPEG images are slightly different from - previous versions of stb_image. (This is due to using less - integer precision in SIMD.) The C code has been adjusted so - that the same RGB values will be computed regardless of whether - SIMD support is available, so your app should always produce - consistent results. But these results are slightly different from - previous versions. (Specifically, about 3% of available YCbCr values - will compute different RGB results from pre-1.49 versions by +-1; - most of the deviating values are one smaller in the G channel.) - - - If you must produce consistent results with previous versions of - stb_image, #define STBI_JPEG_OLD and you will get the same results - you used to; however, you will not get the SIMD speedups for - the YCbCr-to-RGB conversion step (although you should still see - significant JPEG speedup from the other changes). - - Please note that STBI_JPEG_OLD is a temporary feature; it will be - removed in future versions of the library. It is only intended for - near-term back-compatibility use. - - - Latest revision history: - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD - progressive JPEG - PGM/PPM support - STBI_MALLOC,STBI_REALLOC,STBI_FREE - STBI_NO_*, STBI_ONLY_* - GIF bugfix - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted) - optimize PNG - fix bug in interlaced PNG with user-specified channel count - 1.46 (2014-08-26) fix broken tRNS chunk in non-paletted PNG - 1.45 (2014-08-16) workaround MSVC-ARM internal compiler error by wrapping malloc - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Bug fixes & warning fixes - Sean Barrett (jpeg, png, bmp) Marc LeBlanc - Nicolas Schulz (hdr, psd) Christpher Lloyd - Jonathan Dummer (tga) Dave Moore - Jean-Marc Lienher (gif) Won Chun - Tom Seddon (pic) the Horde3D community - Thatcher Ulrich (psd) Janez Zemva - Ken Miller (pgm, ppm) Jonathan Blow - Laurent Gomila - Aruelien Pocheville - Extensions, features Ryamond Barbiero - Jetro Lauha (stbi_info) David Woo - Martin "SpartanJ" Golini (stbi_info) Martin Golini - James "moose2000" Brown (iPhone PNG) Roy Eltham - Ben "Disch" Wenger (io callbacks) Luke Graham - Omar Cornut (1/2/4-bit PNG) Thomas Ruf - John Bartholomew - Ken Hamada - Optimizations & bugfixes Cort Stratton - Fabian "ryg" Giesen Blazej Dariusz Roszkowski - Arseny Kapoulkine Thibault Reuille - Paul Du Bois - Guillaume George - If your name should be here but Jerry Jansson - isn't, let Sean know. Hayaki Saito - Johan Duparc - Ronny Chevalier - Michal Cichon - Tero Hanninen - Sergio Gonzalez - Cass Everitt - Engin Manap - -License: - This software is in the public domain. Where that dedication is not - recognized, you are granted a perpetual, irrevocable license to copy - and modify this file however you want. - -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 16-bit-per-channel PNG -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - no 1-bit BMP -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *comp -- outputs # of image components in image file -// int req_comp -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. -// If req_comp is non-zero, *comp has the number of components that _would_ -// have been output otherwise. E.g. if you set req_comp to 4, you will always -// get RGBA output, but you can check *comp to see if it's trivially opaque -// because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() -// can be queried for an extremely brief, end-user unfriendly explanation -// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid -// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy to use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// make more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// The output of the JPEG decoder is slightly different from versions where -// SIMD support was introduced (that is, for versions before 1.49). The -// difference is only +-1 in the 8-bit RGB channels, and only on a small -// fraction of pixels. You can force the pre-1.49 behavior by defining -// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path -// and hence cost some performance. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// - - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for req_comp - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -typedef unsigned char stbi_uc; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_HDR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// NOT THREADSAFE -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); - -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && defined(STBI_REALLOC) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,sz) realloc(p,sz) -#define STBI_FREE(p) free(p) -#endif - -#if defined(__GNUC__) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; -// this is just broken and gcc are jerks for not fixing it properly -// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -static int stbi__sse2_available() -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -static int stbi__sse2_available() -{ -#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later - // GCC 4.8+ has a nice way to do this - return __builtin_cpu_supports("sse2"); -#else - // portable way to do this, preferably without using GCC inline ASM? - // just bail for now. - return 0; -#endif -} -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -// assume GCC or Clang on ARM targets -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - fseek((FILE*) user, n, SEEK_CUR); -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; -} - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -// this is not threadsafe -static const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static unsigned char *stbi_load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -#ifndef STBI_NO_STDIO - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi_load_main(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} -#endif //!STBI_NO_STDIO - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi_load_main(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi_load_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_LINEAR -static float *stbi_loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) - return stbi__hdr_load(s,x,y,comp,req_comp); - #endif - data = stbi_load_main(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi_loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi_loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi_loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_file(&s,f); - return stbi__hdr_test(&s); - #else - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - return 0; - #endif -} - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -#ifndef STBI_NO_LINEAR -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} - -static void stbi__skip(stbi__context *s, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} - -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} - -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} - -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} - -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); -} - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - - -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc(req_comp * x * y); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define COMBO(a,b) ((a)*8+(b)) - #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (COMBO(img_n, req_comp)) { - CASE(1,2) dest[0]=src[0], dest[1]=255; break; - CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; - CASE(2,1) dest[0]=src[0]; break; - CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; - CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; - CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; - CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; - CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; - default: STBI_ASSERT(0); - } - #undef CASE - } - - STBI_FREE(data); - return good; -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi_uc dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0,code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (-1 << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - diff = t ? stbi__extend_receive(j, t) : 0; - - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else - r = 16; // r=15 is the code for 16 0s - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - ++k; - } else { - if (r == 0) { - if (s) - data[stbi__jpeg_dezigzag[k++]] = (short) s; - break; - } - --r; - ++k; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) << 12) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0] << 2; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4; - int t = q & 15,i; - if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); - L -= 65; - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - stbi__skip(z->s, stbi__get16be(z->s)-2); - return 1; - } - return 0; -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - c = stbi__get8(s); - if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - for (i=0; i < s->img_n; ++i) { - z->img_comp[i].id = stbi__get8(s); - if (z->img_comp[i].id != i+1) // JFIF requires - if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! - return stbi__err("bad component ID","Corrupt JPEG"); - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); - - if (z->img_comp[i].raw_data == NULL) { - for(--i; i >= 0; --i) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].data = NULL; - } - return stbi__err("outofmem", "Out of memory"); - } - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - z->img_comp[i].linebuf = NULL; - if (z->progressive) { - z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; - z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; - z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } else { - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } else if (x != 0) { - return stbi__err("junk before marker", "Corrupt JPEG"); - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -#ifdef STBI_JPEG_OLD -// this is the same YCbCr-to-RGB calculation that stb_image has used -// historically before the algorithm changes in 1.49 -#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 16) + 32768; // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr*float2fixed(1.40200f); - g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); - b = y_fixed + cb*float2fixed(1.77200f); - r >>= 16; - g >>= 16; - b >>= 16; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#else -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* float2fixed(1.40200f); - g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* float2fixed(1.40200f); - g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - #ifndef STBI_JPEG_OLD - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - #ifndef STBI_JPEG_OLD - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - int i; - for (i=0; i < j->s->img_n; ++i) { - if (j->img_comp[i].raw_data) { - STBI_FREE(j->img_comp[i].raw_data); - j->img_comp[i].raw_data = NULL; - j->img_comp[i].data = NULL; - } - if (j->img_comp[i].raw_coeff) { - STBI_FREE(j->img_comp[i].raw_coeff); - j->img_comp[i].raw_coeff = 0; - j->img_comp[i].coeff = 0; - } - if (j->img_comp[i].linebuf) { - STBI_FREE(j->img_comp[i].linebuf); - j->img_comp[i].linebuf = NULL; - } - } -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n; - - if (z->s->img_n == 3 && n < 3) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4]; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n; // report original components, not output - return output; - } -} - -static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__jpeg j; - j.s = s; - stbi__setup_jpeg(&j); - return load_jpeg_image(&j, x,y,comp,req_comp); -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg j; - j.s = s; - stbi__setup_jpeg(&j); - r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); - stbi__rewind(s); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__jpeg j; - j.s = s; - return stbi__jpeg_info_raw(&j, x, y, comp); -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - STBI_ASSERT(sizes[i] <= (1 << i)); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt JPEG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int k = stbi__bit_reverse(next_code[s],s); - while (k < (1 << STBI__ZFAST_BITS)) { - z->fast[k] = fastv; - k += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) stbi__fill_bits(a); - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - int cur, limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *) STBI_REALLOC(z->zout_start, limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - do *zout++ = v; while (--len); - } else { - do *zout++ = *p++; while (--len); - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < hlit + hdist) { - int c = stbi__zhuffman_decode(a, &z_codelength); - STBI_ASSERT(c >= 0 && c < 19); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else if (c == 16) { - c = stbi__zreceive(a,2)+3; - memset(lencodes+n, lencodes[n-1], c); - n += c; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - memset(lencodes+n, 0, c); - n += c; - } else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - memset(lencodes+n, 0, c); - n += c; - } - } - if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncomperssed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - STBI_ASSERT(a->num_bits == 0); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -// @TODO: should statically initialize these for optimal thread safety -static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; -static void stbi__init_zdefaults(void) -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncomperssed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - if (s->img_x == x && s->img_y == y) { - if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } else { // interlaced: - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } - - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior = cur - stride; - int filter = *raw++; - int filter_bytes = img_n; - int width = x; - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*img_n; - #define CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; - } - #undef CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ - for (k=0; k < img_n; ++k) - switch (filter) { - CASE(STBI__F_none) cur[k] = raw[k]; break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break; - } - #undef CASE - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - // insert alpha = 255 - stbi_uc *cur = a->out + stride*j; - int i; - if (img_n == 1) { - for (i=x-1; i >= 0; --i) { - cur[i*2+1] = 255; - cur[i*2+0] = cur[i]; - } - } else { - STBI_ASSERT(img_n == 3); - for (i=x-1; i >= 0; --i) { - cur[i*4+3] = 255; - cur[i*4+2] = cur[i*3+2]; - cur[i*4+1] = cur[i*3+1]; - cur[i*4+0] = cur[i*3+0]; - } - } - } - } - } - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, - a->out + (j*x+i)*out_n, out_n); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - p[0] = p[2] * 255 / a; - p[1] = p[1] * 255 / a; - p[2] = t * 255 / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, depth=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - for (k=0; k < s->img_n; ++k) - tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if (ioff + c.length > idata_limit) { - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - p = (stbi_uc *) STBI_REALLOC(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0; - if (has_trans) - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } - STBI_FREE(z->expanded); z->expanded = NULL; - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) -{ - unsigned char *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_out_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -static int stbi__shiftsigned(int v, int shift, int bits) -{ - int result; - int z=0; - - if (shift < 0) v <<= -shift; - else v >>= shift; - result = v; - - z = bits; - while (z < 8) { - result += v >> z; - z += bits; - } - return result; -} - -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; - stbi_uc pal[256][4]; - int psize=0,i,j,compress=0,width; - int bpp, flip_vertically, pad, target, offset, hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - offset = stbi__get32le(s); - hsz = stbi__get32le(s); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - bpp = stbi__get16le(s); - if (bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - if (hsz == 12) { - if (bpp < 24) - psize = (offset - 14 - 24) / 3; - } else { - compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (bpp == 16 || bpp == 32) { - mr = mg = mb = 0; - if (compress == 0) { - if (bpp == 32) { - mr = 0xffu << 16; - mg = 0xffu << 8; - mb = 0xffu << 0; - ma = 0xffu << 24; - fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 - STBI_NOTUSED(fake_a); - } else { - mr = 31u << 10; - mg = 31u << 5; - mb = 31u << 0; - } - } else if (compress == 3) { - mr = stbi__get32le(s); - mg = stbi__get32le(s); - mb = stbi__get32le(s); - // not documented, but generated by photoshop and handled by mspaint - if (mr == mg && mg == mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - STBI_ASSERT(hsz == 108 || hsz == 124); - mr = stbi__get32le(s); - mg = stbi__get32le(s); - mb = stbi__get32le(s); - ma = stbi__get32le(s); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - if (bpp < 16) - psize = (offset - 14 - hsz) >> 2; - } - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); - if (bpp == 4) width = (s->img_x + 1) >> 1; - else if (bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, offset - 14 - hsz); - if (bpp == 24) width = 3 * s->img_x; - else if (bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (bpp == 24) { - easy = 1; - } else if (bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - if (target == 4) out[z++] = a; - } - } else { - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (stbi__uint32) (bpp == 16 ? stbi__get16le(s) : stbi__get32le(s)); - int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp; - int sz; - stbi__get8(s); // discard Offset - sz = stbi__get8(s); // color type - if( sz > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - sz = stbi__get8(s); // image type - // only RGB or grey allowed, +/- RLE - if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; - stbi__skip(s,9); - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - sz = stbi__get8(s); // bits per pixel - // only RGB or RGBA or grey allowed - if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { - stbi__rewind(s); - return 0; - } - tga_comp = sz; - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp / 8; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res; - int sz; - stbi__get8(s); // discard Offset - sz = stbi__get8(s); // color type - if ( sz > 1 ) return 0; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE - stbi__get16be(s); // discard palette start - stbi__get16be(s); // discard palette length - stbi__get8(s); // discard bits per palette color entry - stbi__get16be(s); // discard x origin - stbi__get16be(s); // discard y origin - if ( stbi__get16be(s) < 1 ) return 0; // test width - if ( stbi__get16be(s) < 1 ) return 0; // test height - sz = stbi__get8(s); // bits per pixel - if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) - res = 0; - else - res = 1; - stbi__rewind(s); - return res; -} - -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp = tga_bits_per_pixel / 8; - int tga_inverted = stbi__get8(s); - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4]; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - /* int tga_alpha_bits = tga_inverted & 15; */ - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // error check - if ( //(tga_indexed) || - (tga_width < 1) || (tga_height < 1) || - (tga_image_type < 1) || (tga_image_type > 3) || - ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && - (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) - ) - { - return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA - } - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) - { - tga_comp = tga_palette_bits / 8; - } - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - tga_data = (unsigned char*)stbi__malloc( tga_width * tga_height * tga_comp ); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE) { - for (i=0; i < tga_height; ++i) { - int y = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + y*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_palette_bits / 8 ); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (!stbi__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in 1 byte, then perform the lookup - int pal_idx = stbi__get8(s); - if ( pal_idx >= tga_palette_len ) - { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_bits_per_pixel / 8; - for (j = 0; j*8 < tga_bits_per_pixel; ++j) - { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else - { - // read in the data raw - for (j = 0; j*8 < tga_bits_per_pixel; ++j) - { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if (tga_comp >= 3) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - int pixelCount; - int channelCount, compression; - int channel, i, count, len; - int w,h; - stbi_uc *out; - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - // Make sure the depth is 8 bits. - if (stbi__get16be(s) != 8) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Create the destination image. - out = (stbi_uc *) stbi__malloc(4 * w*h); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; - } else { - // Read the RLE data. - count = 0; - while (count < pixelCount) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len ^= 0x0FF; - len += 2; - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out + channel; - if (channel > channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; - } else { - // Read the data. - for (i = 0; i < pixelCount; i++) - *p = stbi__get8(s), p += 4; - } - } - } - - if (req_comp && req_comp != 4) { - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = channelCount; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - int i; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) -{ - stbi_uc *result; - int i, x,y; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc(x*y*4); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[4096]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif g; - if (!stbi__gif_header(s, &g, comp, 1)) { - stbi__rewind( s ); - return 0; - } - if (x) *x = g.w; - if (y) *y = g.h; - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - p = &g->out[g->cur_x + g->cur_y]; - c = &g->color_table[g->codes[code].suffix * 4]; - - if (c[3] >= 128) { - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (code = 0; code < clear; code++) { - g->codes[code].prefix = -1; - g->codes[code].first = (stbi_uc) code; - g->codes[code].suffix = (stbi_uc) code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -static void stbi__fill_gif_background(stbi__gif *g) -{ - int i; - stbi_uc *c = g->pal[g->bgindex]; - // @OPTIMIZE: write a dword at a time - for (i = 0; i < g->w * g->h * 4; i += 4) { - stbi_uc *p = &g->out[i]; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) -{ - int i; - stbi_uc *old_out = 0; - - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - stbi__fill_gif_background(g); - } else { - // animated-gif-only path - if (((g->eflags & 0x1C) >> 2) == 3) { - old_out = g->out; - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - memcpy(g->out, old_out, g->w*g->h*4); - } - } - - for (;;) { - switch (stbi__get8(s)) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - for (i=0; i < 256; ++i) // @OPTIMIZE: stbi__jpeg_reset only the previous transparent - g->pal[i][3] = 255; - if (g->transparent >= 0 && (g->eflags & 0x01)) - g->pal[g->transparent][3] = 0; - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (o == NULL) return NULL; - - if (req_comp && req_comp != 4) - o = stbi__convert_format(o, 4, req_comp, g->w, g->h); - return o; - } - - case 0x21: // Comment Extension. - { - int len; - if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - stbi__get16le(s); // delay - g->transparent = stbi__get8(s); - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) - stbi__skip(s, len); - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - - u = stbi__gif_load_next(s, &g, comp, req_comp); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - } - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s) -{ - const char *signature = "#?RADIANCE\n"; - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s); - stbi__rewind(s); - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - - - // Check identifier - if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - // Read data - hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); - - for (k = 0; k < 4; ++k) { - i = 0; - while (i < width) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - - if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') { - stbi__rewind( s ); - return 0; - } - stbi__skip(s,12); - hsz = stbi__get32le(s); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) { - stbi__rewind( s ); - return 0; - } - if (hsz == 12) { - *x = stbi__get16le(s); - *y = stbi__get16le(s); - } else { - *x = stbi__get32le(s); - *y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) { - stbi__rewind( s ); - return 0; - } - *comp = stbi__get16le(s) / 8; - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - if (stbi__get16be(s) != 8) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained; - stbi__pic_packet packets[10]; - - stbi__skip(s, 92); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) return 0; - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *out; - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) - return 0; - *x = s->img_x; - *y = s->img_y; - *comp = s->img_n; - - out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); - - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv; - char c, p, t; - - stbi__rewind( s ); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else - return 1; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 2008-08-02 - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 first released version -*/ diff --git a/direct/src/plugin/twirl.xcf b/direct/src/plugin/twirl.xcf deleted file mode 100644 index deff7efebf..0000000000 Binary files a/direct/src/plugin/twirl.xcf and /dev/null differ diff --git a/direct/src/plugin/wstring_encode.cxx b/direct/src/plugin/wstring_encode.cxx deleted file mode 100644 index e844d643bd..0000000000 --- a/direct/src/plugin/wstring_encode.cxx +++ /dev/null @@ -1,70 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file wstring_encode.cxx - * @author drose - * @date 2011-08-29 - */ - -#include "wstring_encode.h" - -#include -#include -#include - -#ifdef _WIN32 -#include -#endif // _WIN32 - -#ifdef _WIN32 -/** - * Encodes std::wstring to std::string using UTF-8. - */ -bool -wstring_to_string(std::string &result, const std::wstring &source) { - bool success = false; - int size = WideCharToMultiByte(CP_UTF8, 0, source.data(), source.length(), - nullptr, 0, nullptr, nullptr); - if (size > 0) { - char *buffer = new char[size]; - int rc = WideCharToMultiByte(CP_UTF8, 0, source.data(), source.length(), - buffer, size, nullptr, nullptr); - if (rc != 0) { - result.assign(buffer, size); - success = true; - } - delete[] buffer; - } - - return success; -} -#endif // _WIN32 - -#ifdef _WIN32 -/** - * Decodes std::string to std::wstring using UTF-8. - */ -bool -string_to_wstring(std::wstring &result, const std::string &source) { - bool success = false; - int size = MultiByteToWideChar(CP_UTF8, 0, source.data(), source.length(), - nullptr, 0); - if (size > 0) { - wchar_t *buffer = new wchar_t[size]; - int rc = MultiByteToWideChar(CP_UTF8, 0, source.data(), source.length(), - buffer, size); - if (rc != 0) { - result.assign(buffer, size); - success = true; - } - delete[] buffer; - } - - return success; -} -#endif // _WIN32 diff --git a/direct/src/plugin/wstring_encode.h b/direct/src/plugin/wstring_encode.h deleted file mode 100644 index b9641bd43d..0000000000 --- a/direct/src/plugin/wstring_encode.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file wstring_encode.h - * @author drose - * @date 2009-06-29 - */ - -#ifndef WSTRING_ENCODE_H -#define WSTRING_ENCODE_H - -#include - -// Presently, these two functions are implemented only for Windows, which is -// the only place they are needed. (Only Windows requires wstrings for -// filenames.) -#ifdef _WIN32 -bool wstring_to_string(std::string &result, const std::wstring &source); -bool string_to_wstring(std::wstring &result, const std::string &source); - -// We declare this inline so it won't conflict with the similar function -// defined in Panda's textEncoder.h. -inline std::ostream &operator << (std::ostream &out, const std::wstring &str) { - std::string result; - if (wstring_to_string(result, str)) { - out << result; - } - return out; -} - -#endif // _WIN32 - -#endif diff --git a/direct/src/plugin/xml_helpers.cxx b/direct/src/plugin/xml_helpers.cxx deleted file mode 100644 index 9a997189da..0000000000 --- a/direct/src/plugin/xml_helpers.cxx +++ /dev/null @@ -1,40 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file xml_helpers.cxx - * @author drose - * @date 2012-09-28 - */ - -#include "p3d_plugin_common.h" -#include "xml_helpers.h" - - -/** - * Examines the indicated attrib from the XML attrib and returns its true or - * false value. Returns default_value if the attrib is not present or is - * empty. - */ -bool -parse_bool_attrib(TiXmlElement *xelem, const std::string &attrib, - bool default_value) { - const char *value = xelem->Attribute(attrib.c_str()); - if (value == nullptr || *value == '\0') { - return default_value; - } - - char *endptr; - int result = strtol(value, &endptr, 10); - if (*endptr == '\0') { - // A valid integer. - return (result != 0); - } - - // An invalid integer. - return default_value; -} diff --git a/direct/src/plugin/xml_helpers.h b/direct/src/plugin/xml_helpers.h deleted file mode 100644 index d64047f55a..0000000000 --- a/direct/src/plugin/xml_helpers.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file xml_helpers.h - * @author drose - * @date 2012-09-28 - */ - -#ifndef XML_HELPERS_H -#define XML_HELPERS_H - -#include "get_tinyxml.h" - -bool parse_bool_attrib(TiXmlElement *xelem, const std::string &attrib, - bool default_value); - -#endif diff --git a/direct/src/plugin_activex/P3DActiveX.cpp b/direct/src/plugin_activex/P3DActiveX.cpp deleted file mode 100644 index 500effc38f..0000000000 --- a/direct/src/plugin_activex/P3DActiveX.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file P3DActiveX.cpp - * @author atrestman - * @date 2009-09-14 - */ - -// P3DActiveX.cpp : Implementation of CP3DActiveXApp and DLL registration. - -#include "stdafx.h" -#include "P3DActiveX.h" - -#include -#include -#include - - -#ifdef _DEBUG -#define new DEBUG_NEW -#endif - - -CP3DActiveXApp NEAR theApp; - -const GUID CDECL BASED_CODE _tlid = - { 0x22A8FC5F, 0xBC33, 0x479A, { 0x83, 0x17, 0x2B, 0xC8, 0x16, 0xB8, 0xAB, 0x8A } }; -const WORD _wVerMajor = 1; -const WORD _wVerMinor = 0; - -// CLSID_SafeItem - Necessary for safe ActiveX control - -// Id taken from IMPLEMENT_OLECREATE_EX function in xxxCtrl.cpp - - -const CATID CLSID_SafeItem = -{ 0x924b4927, 0xd3ba, 0x41ea, 0x9f, 0x7e, 0x8a, 0x89, 0x19, 0x4a, 0xb3, 0xac }; - -// HRESULT CreateComponentCategory - Used to register ActiveX control as safe - - -HRESULT CreateComponentCategory(CATID catid, WCHAR *catDescription) -{ - ICatRegister *pcr = NULL ; - HRESULT hr = S_OK ; - - hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, - NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr); - if (FAILED(hr)) - return hr; - - // Make sure the HKCR\Component Categories\{..catid...} - - // key is registered. - - CATEGORYINFO catinfo; - catinfo.catid = catid; - catinfo.lcid = 0x0409 ; // english - - size_t len; - // Make sure the provided description is not too long. - - // Only copy the first 127 characters if it is. - - // The second parameter of StringCchLength is the maximum - - // number of characters that may be read into catDescription. - - // There must be room for a NULL-terminator. The third parameter - - // contains the number of characters excluding the NULL-terminator. - - hr = StringCchLengthW(catDescription, STRSAFE_MAX_CCH, &len); - if (SUCCEEDED(hr)) - { - if (len>127) - { - len = 127; - } - } - else - { - // TODO: Write an error handler; - - } - // The second parameter of StringCchCopy is 128 because you need - - // room for a NULL-terminator. - - hr = StringCchCopyW(catinfo.szDescription, len + 1, catDescription); - // Make sure the description is null terminated. - - catinfo.szDescription[len + 1] = '\0'; - - hr = pcr->RegisterCategories(1, &catinfo); - pcr->Release(); - - return hr; -} - -// HRESULT RegisterCLSIDInCategory - - -// Register your component categories information - - -HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid) -{ -// Register your component categories information. - - ICatRegister *pcr = NULL ; - HRESULT hr = S_OK ; - hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, - NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr); - if (SUCCEEDED(hr)) - { - // Register this category as being "implemented" by the class. - - CATID rgcatid[1] ; - rgcatid[0] = catid; - hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid); - } - - if (pcr != NULL) - pcr->Release(); - - return hr; -} - -// HRESULT UnRegisterCLSIDInCategory - Remove entries from the registry - - -HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid) -{ - ICatRegister *pcr = NULL ; - HRESULT hr = S_OK ; - - hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, - NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr); - if (SUCCEEDED(hr)) - { - // Unregister this category as being "implemented" by the class. - - CATID rgcatid[1] ; - rgcatid[0] = catid; - hr = pcr->UnRegisterClassImplCategories(clsid, 1, rgcatid); - } - - if (pcr != NULL) - pcr->Release(); - - return hr; -} - - -// CP3DActiveXApp::InitInstance - DLL initialization - -BOOL CP3DActiveXApp::InitInstance() -{ - BOOL bInit = COleControlModule::InitInstance(); - - if (bInit) - { - // TODO: Add your own module initialization code here. - - // Seed the lame random number generator in rand(); we use it to select - // a mirror for downloading. - srand((unsigned int)time(NULL)); - } - - return bInit; -} - - - -// CP3DActiveXApp::ExitInstance - DLL termination - -int CP3DActiveXApp::ExitInstance() -{ - // TODO: Add your own module termination code here. - - return COleControlModule::ExitInstance(); -} - - - -// DllRegisterServer - Adds entries to the system registry - -STDAPI DllRegisterServer(void) -{ - HRESULT hr; // HResult used by Safety Functions - - - AFX_MANAGE_STATE(_afxModuleAddrThis); - - if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid)) - return ResultFromScode(SELFREG_E_TYPELIB); - - if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE)) - return ResultFromScode(SELFREG_E_CLASS); - - // Mark the control as safe for initializing. - - - hr = CreateComponentCategory(CATID_SafeForInitializing, - L"Controls safely initializable from persistent data!"); - if (FAILED(hr)) - return hr; - - hr = RegisterCLSIDInCategory(CLSID_SafeItem, - CATID_SafeForInitializing); - if (FAILED(hr)) - return hr; - - // Mark the control as safe for scripting. - - - hr = CreateComponentCategory(CATID_SafeForScripting, - L"Controls safely scriptable!"); - if (FAILED(hr)) - return hr; - - hr = RegisterCLSIDInCategory(CLSID_SafeItem, - CATID_SafeForScripting); - if (FAILED(hr)) - return hr; - - return NOERROR; -} - - - -// DllUnregisterServer - Removes entries from the system registry - -STDAPI DllUnregisterServer(void) -{ - HRESULT hr; // HResult used by Safety Functions - - - AFX_MANAGE_STATE(_afxModuleAddrThis); - - // Remove entries from the registry. - - hr = UnRegisterCLSIDInCategory(CLSID_SafeItem, - CATID_SafeForInitializing); - if (FAILED(hr)) - return hr; - - hr = UnRegisterCLSIDInCategory(CLSID_SafeItem, - CATID_SafeForScripting); - if (FAILED(hr)) - return hr; - - if (!AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor)) - return ResultFromScode(SELFREG_E_TYPELIB); - - if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE)) - return ResultFromScode(SELFREG_E_CLASS); - - return NOERROR; -} diff --git a/direct/src/plugin_activex/P3DActiveX.def b/direct/src/plugin_activex/P3DActiveX.def deleted file mode 100644 index e8ca82f83a..0000000000 --- a/direct/src/plugin_activex/P3DActiveX.def +++ /dev/null @@ -1,9 +0,0 @@ -; P3DActiveX.def : Declares the module parameters. - -LIBRARY "P3DActiveX.OCX" - -EXPORTS - DllCanUnloadNow PRIVATE - DllGetClassObject PRIVATE - DllRegisterServer PRIVATE - DllUnregisterServer PRIVATE diff --git a/direct/src/plugin_activex/P3DActiveX.h b/direct/src/plugin_activex/P3DActiveX.h deleted file mode 100644 index 1024021791..0000000000 --- a/direct/src/plugin_activex/P3DActiveX.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file P3DActiveX.h - * @author atrestman - * @date 2009-09-14 - */ - -#pragma once - -// P3DActiveX.h : main header file for P3DActiveX.DLL - -#if !defined( __AFXCTL_H__ ) -#error include 'afxctl.h' before including this file -#endif - -#include "resource.h" // main symbols - - -// CP3DActiveXApp : See P3DActiveX.cpp for implementation. - -class CP3DActiveXApp : public COleControlModule -{ -public: - BOOL InitInstance(); - int ExitInstance(); -}; - -extern const GUID CDECL _tlid; -extern const WORD _wVerMajor; -extern const WORD _wVerMinor; diff --git a/direct/src/plugin_activex/P3DActiveX.idl b/direct/src/plugin_activex/P3DActiveX.idl deleted file mode 100644 index 4406b55abe..0000000000 --- a/direct/src/plugin_activex/P3DActiveX.idl +++ /dev/null @@ -1,51 +0,0 @@ -// P3DActiveX.idl : type library source for ActiveX Control project. - -// This file will be processed by the MIDL compiler tool to -// produce the type library (P3DActiveX.tlb) that will become a resource in -// P3DActiveX.ocx. - -#include -#include - -[ uuid(22A8FC5F-BC33-479A-8317-2BC816B8AB8A), version(1.0), - helpfile("P3DActiveX.hlp"), - helpstring("P3DActiveX ActiveX Control module"), - control ] -library P3DActiveXLib -{ - importlib(STDOLE_TLB); - - // Primary dispatch interface for CP3DActiveXCtrl - - [ uuid(76904D54-0CC5-4DBB-B022-F48B1E95183B), - helpstring("Dispatch interface for P3DActiveX Control")] - dispinterface _DP3DActiveX - { - properties: - [id(1), helpstring("property main")] IDispatch*main; - methods: - }; - - // Event dispatch interface for CP3DActiveXCtrl - - [ uuid(1B2413ED-51C8-495E-B917-983C459B8FC7), - helpstring("Event interface for P3DActiveX Control") ] - dispinterface _DP3DActiveXEvents - { - properties: - // Event interface has no properties - - methods: - }; - - // Class information for CP3DActiveXCtrl - - [ uuid(924B4927-D3BA-41EA-9F7E-8A89194AB3AC), - helpstring("P3DActiveX Control"), control ] - coclass P3DActiveX - { - [default] dispinterface _DP3DActiveX; - [default, source] dispinterface _DP3DActiveXEvents; - }; - -}; diff --git a/direct/src/plugin_activex/P3DActiveX.inf b/direct/src/plugin_activex/P3DActiveX.inf deleted file mode 100644 index 3ec50e6282..0000000000 --- a/direct/src/plugin_activex/P3DActiveX.inf +++ /dev/null @@ -1,34 +0,0 @@ -[Add.Code] -P3DActiveX.ocx=P3DActiveX.ocx -P3DActiveX.inf=P3DActiveX.inf -mfc71.dll=mfc71.dll -msvcr71.dll=msvcr71.dll -msvcp71.dll=msvcp71.dll - -[P3DActiveX.ocx] -file=thiscab -clsid={924B4927-D3BA-41EA-9F7E-8A89194AB3AC} -RegisterServer=yes -FileVersion=1,0,0,0 - -[P3DActiveX.inf] -file=thiscab - -[mfc71.dll] -file=thiscab -DestDir=11 -RegisterServer=yes -FileVersion=7,10,3077,0 - -[msvcr71.dll] -file=thiscab -DestDir=11 -RegisterServer=yes -FileVersion=7,10,3052,4 - -[msvcp71.dll] -file=thiscab -DestDir=11 -RegisterServer=yes -FileVersion=7,10,3077,0 - diff --git a/direct/src/plugin_activex/P3DActiveX.sln b/direct/src/plugin_activex/P3DActiveX.sln deleted file mode 100644 index 5263bf1e8b..0000000000 --- a/direct/src/plugin_activex/P3DActiveX.sln +++ /dev/null @@ -1,21 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 8.00 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "P3DActiveX", "P3DActiveX.vcproj", "{74451B00-2D87-412B-9359-B5CA2C2FEC2A}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfiguration) = preSolution - Debug = Debug - Release = Release - EndGlobalSection - GlobalSection(ProjectConfiguration) = postSolution - {74451B00-2D87-412B-9359-B5CA2C2FEC2A}.Debug.ActiveCfg = Debug|Win32 - {74451B00-2D87-412B-9359-B5CA2C2FEC2A}.Debug.Build.0 = Debug|Win32 - {74451B00-2D87-412B-9359-B5CA2C2FEC2A}.Release.ActiveCfg = Release|Win32 - {74451B00-2D87-412B-9359-B5CA2C2FEC2A}.Release.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - EndGlobalSection - GlobalSection(ExtensibilityAddIns) = postSolution - EndGlobalSection -EndGlobal diff --git a/direct/src/plugin_activex/P3DActiveX.vcproj b/direct/src/plugin_activex/P3DActiveX.vcproj deleted file mode 100644 index ccb33b68a5..0000000000 --- a/direct/src/plugin_activex/P3DActiveX.vcproj +++ /dev/null @@ -1,275 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/direct/src/plugin_activex/P3DActiveXCtrl.bmp b/direct/src/plugin_activex/P3DActiveXCtrl.bmp deleted file mode 100644 index 3ffeb056fd..0000000000 Binary files a/direct/src/plugin_activex/P3DActiveXCtrl.bmp and /dev/null differ diff --git a/direct/src/plugin_activex/P3DActiveXCtrl.cpp b/direct/src/plugin_activex/P3DActiveXCtrl.cpp deleted file mode 100644 index a446bacbfb..0000000000 --- a/direct/src/plugin_activex/P3DActiveXCtrl.cpp +++ /dev/null @@ -1,617 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file P3DActiveXCtrl.cpp - * @author atrestman - * @date 2009-09-14 - */ - -// P3DActiveXCtrl.cpp : Implementation of the CP3DActiveXCtrl ActiveX Control -// class. - -#include "stdafx.h" -#include "P3DActiveX.h" -#include "P3DActiveXCtrl.h" -#include "P3DActiveXPropPage.h" -#include "PPBrowserObject.h" - -#include -#include -#include - -#include - -class CPropbagPropExchange : public CPropExchange -{ -public: - CPropbagPropExchange(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog, - BOOL bLoading, BOOL bSaveAllProperties=FALSE); - ~CPropbagPropExchange(); - -// Operations - virtual BOOL ExchangeProp(LPCTSTR pszPropName, VARTYPE vtProp, - void* pvProp, const void* pvDefault = NULL); - virtual BOOL ExchangeBlobProp(LPCTSTR pszPropName, HGLOBAL* phBlob, - HGLOBAL hBlobDefault = NULL); - virtual BOOL ExchangeFontProp(LPCTSTR pszPropName, CFontHolder& font, - const FONTDESC* pFontDesc, LPFONTDISP pFontDispAmbient); - virtual BOOL ExchangePersistentProp(LPCTSTR pszPropName, - LPUNKNOWN* ppUnk, REFIID iid, LPUNKNOWN pUnkDefault); - -// Implementation - LPPROPERTYBAG m_pPropBag; - LPERRORLOG m_pErrorLog; - BOOL m_bSaveAllProperties; -}; - -#ifdef _DEBUG -#define new DEBUG_NEW -#endif - - -IMPLEMENT_DYNCREATE(CP3DActiveXCtrl, COleControl) - - - -// Message map - -BEGIN_MESSAGE_MAP(CP3DActiveXCtrl, COleControl) - ON_MESSAGE(OCM_COMMAND, OnOcmCommand) - ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties) - ON_WM_CREATE() - ON_MESSAGE(WM_PANDA_NOTIFICATION, OnPandaNotification) -END_MESSAGE_MAP() - - - -// Dispatch map - -BEGIN_DISPATCH_MAP(CP3DActiveXCtrl, COleControl) - DISP_PROPERTY_NOTIFY_ID(CP3DActiveXCtrl, "main", dispidmain, m_pPandaObject, OnmainChanged, VT_DISPATCH) -END_DISPATCH_MAP() - - - -// Event map - -BEGIN_EVENT_MAP(CP3DActiveXCtrl, COleControl) -END_EVENT_MAP() - - - -// Property pages - -// TODO: Add more property pages as needed. Remember to increase the count! -BEGIN_PROPPAGEIDS(CP3DActiveXCtrl, 1) - PROPPAGEID(CP3DActiveXPropPage::guid) -END_PROPPAGEIDS(CP3DActiveXCtrl) - - - -// Initialize class factory and guid - -IMPLEMENT_OLECREATE_EX(CP3DActiveXCtrl, "P3DACTIVEX.P3DActiveXCtrl.1", - 0x924b4927, 0xd3ba, 0x41ea, 0x9f, 0x7e, 0x8a, 0x89, 0x19, 0x4a, 0xb3, 0xac) - - - -// Type library ID and version - -IMPLEMENT_OLETYPELIB(CP3DActiveXCtrl, _tlid, _wVerMajor, _wVerMinor) - - - -// Interface IDs - -const IID BASED_CODE IID_DP3DActiveX = - { 0x76904D54, 0xCC5, 0x4DBB, { 0xB0, 0x22, 0xF4, 0x8B, 0x1E, 0x95, 0x18, 0x3B } }; -const IID BASED_CODE IID_DP3DActiveXEvents = - { 0x1B2413ED, 0x51C8, 0x495E, { 0xB9, 0x17, 0x98, 0x3C, 0x45, 0x9B, 0x8F, 0xC7 } }; - - - -// Control type information - -static const DWORD BASED_CODE _dwP3DActiveXOleMisc = - OLEMISC_ACTIVATEWHENVISIBLE | - OLEMISC_SETCLIENTSITEFIRST | - OLEMISC_INSIDEOUT | - OLEMISC_CANTLINKINSIDE | - OLEMISC_RECOMPOSEONRESIZE; - -IMPLEMENT_OLECTLTYPE(CP3DActiveXCtrl, IDS_P3DACTIVEX, _dwP3DActiveXOleMisc) - - - -// CP3DActiveXCtrl::CP3DActiveXCtrlFactory::UpdateRegistry - Adds or removes -// system registry entries for CP3DActiveXCtrl - -BOOL CP3DActiveXCtrl::CP3DActiveXCtrlFactory::UpdateRegistry(BOOL bRegister) -{ - // TODO: Verify that your control follows apartment-model threading rules. - // Refer to MFC TechNote 64 for more information. If your control does - // not conform to the apartment-model rules, then you must modify the code - // below, changing the 6th parameter from afxRegApartmentThreading to 0. - - if (bRegister) - return AfxOleRegisterControlClass( - AfxGetInstanceHandle(), - m_clsid, - m_lpszProgID, - IDS_P3DACTIVEX, - IDB_P3DACTIVEX, - afxRegApartmentThreading, - _dwP3DActiveXOleMisc, - _tlid, - _wVerMajor, - _wVerMinor); - else - return AfxOleUnregisterClass(m_clsid, m_lpszProgID); -} - - - -// CP3DActiveXCtrl::CP3DActiveXCtrl - Constructor - -CP3DActiveXCtrl::CP3DActiveXCtrl() : m_instance( *this ), m_pPandaObject( NULL ) -{ - InitializeIIDs(&IID_DP3DActiveX, &IID_DP3DActiveXEvents); - // TODO: Initialize your control's instance data here. - _state = S_init; - - // The init thread is initially not running. - _init_not_running.SetEvent(); -} - -// CP3DActiveXCtrl::~CP3DActiveXCtrl - Destructor - -CP3DActiveXCtrl::~CP3DActiveXCtrl() -{ - // TODO: Cleanup your control's instance data here. - if ( m_pPandaObject ) - { - m_pPandaObject->Release(); - } -} - - - -// CP3DActiveXCtrl::OnDraw - Drawing function - -void CP3DActiveXCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid) -{ - if (!pdc) - return; - - switch (_state) { - case S_init: - { - _state = S_loading; - // The first time we get the Draw message, we know we're sufficiently - // set up to start downloading the instance. - m_instance.read_tokens(); - get_twirl_bitmaps(); - - // Start the twirl timer going. - SetTimer(1, 100, timer_callback); - _init_time = GetTickCount(); - - // But do most of the setup in a child thread, so we don't lock up the - // browser GUI while the instance gets itself downloaded and such. - _init_not_running.ResetEvent(); // Now the init thread is running. - if (_beginthread(st_init, 0, this) == -1L) { - nout << "Couldn't start thread.\n"; - _init_not_running.SetEvent(); - _state = S_failed; - } - } - break; - - case S_loading: - // Waiting for the init thread to finish. - break; - - case S_ready: - // Now the instance has downloaded, so set it going. - { - _state = S_started; - KillTimer(1); - m_instance.Start( m_instance.GetP3DFilename( ) ); - } - break; - - case S_started: - // The instance is running, no need to draw anything. - KillTimer(1); - DoSuperclassPaint(pdc, rcBounds); - return; - - case S_failed: - // Something went wrong. Go ahead and draw the failed icon. - KillTimer(1); - break; - } - - // The instance is setting itself up. In the meantime, draw the - // background and the twirling icon. - - // Paint the background. - CBrush brBackGnd(RGB(m_instance._bgcolor_r, m_instance._bgcolor_g, m_instance._bgcolor_b)); - pdc->FillRect(rcBounds, &brBackGnd); - - DWORD now = GetTickCount(); - - // Don't draw the twirling icon until at least half a second has passed, - // so we don't distract people by drawing it unnecessarily. - if (_state == S_failed || (now - _init_time) >= 500) { - int step = (now / 100) % twirl_num_steps; - if (_state == S_failed) { - step = twirl_num_steps; - } - - // Create an in-memory DC compatible with the display DC we're using to - // paint - CDC dcMemory; - dcMemory.CreateCompatibleDC(pdc); - - // Select the bitmap into the in-memory DC - dcMemory.SelectObject(&_twirl_bitmaps[step]); - - // Find a centerpoint for the bitmap in the client area - CRect rect; - GetClientRect(&rect); - int nX = rect.left + (rect.Width() - twirl_width) / 2; - int nY = rect.top + (rect.Height() - twirl_height) / 2; - - // Copy the bits from the in-memory DC into the on-screen DC to actually - // do the painting. Use the centerpoint we computed for the target - // offset. - pdc->BitBlt(nX, nY, twirl_width, twirl_height, &dcMemory, - 0, 0, SRCCOPY); - } -} - -void CP3DActiveXCtrl::OnClose( DWORD dwSaveOption ) -{ - m_instance.Stop(); - - // Make sure the init thread has finished. - if (_state == S_loading) { - nout << "Waiting for thread stop\n" << flush; - ::WaitForSingleObject( _init_not_running.m_hObject, INFINITE ); - nout << "Done waiting for thread stop\n" << flush; - } - - COleControl::OnClose( dwSaveOption ); -} - - -// CP3DActiveXCtrl::DoPropExchange - Persistence support - -void CP3DActiveXCtrl::DoPropExchange(CPropExchange* pPX) -{ - ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor)); - COleControl::DoPropExchange(pPX); - - // TODO: Call PX_ functions for each persistent custom property. - - ExchangeProperties( pPX ); -} - - - -// CP3DActiveXCtrl::GetControlFlags - Flags to customize MFC's implementation -// of ActiveX controls. -DWORD CP3DActiveXCtrl::GetControlFlags() -{ - DWORD dwFlags = COleControl::GetControlFlags(); - - - // The control will not be redrawn when making the transition between the - // active and inactivate state. - dwFlags |= noFlickerActivate; - return dwFlags; -} - - - -// CP3DActiveXCtrl::OnResetState - Reset control to default state - -void CP3DActiveXCtrl::OnResetState() -{ - COleControl::OnResetState(); // Resets defaults found in DoPropExchange - - // TODO: Reset any other control state here. -} - - - -// CP3DActiveXCtrl::PreCreateWindow - Modify parameters for CreateWindowEx - -BOOL CP3DActiveXCtrl::PreCreateWindow(CREATESTRUCT& cs) -{ - cs.lpszClass = _T("STATIC"); - return COleControl::PreCreateWindow(cs); -} - - - -// CP3DActiveXCtrl::IsSubclassedControl - This is a subclassed control - -BOOL CP3DActiveXCtrl::IsSubclassedControl() -{ - return TRUE; -} - - - -// CP3DActiveXCtrl::OnOcmCommand - Handle command messages - -LRESULT CP3DActiveXCtrl::OnOcmCommand(WPARAM wParam, LPARAM lParam) -{ -#ifdef _WIN32 - WORD wNotifyCode = HIWORD(wParam); -#else - WORD wNotifyCode = HIWORD(lParam); -#endif - - // TODO: Switch on wNotifyCode here. - - return 0; -} - -// CP3DActiveXCtrl message handlers - -int CP3DActiveXCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) -{ - if (COleControl::OnCreate(lpCreateStruct) == -1) - return -1; - - // TODO: Add your specialized creation code here - - int error(0); - m_spClientSite = GetClientSite(); - if ( !m_spClientSite ) - { - return ( error = -1 ); - } - CComPtr pOleContainer; - HRESULT hr = m_spClientSite->GetContainer( &pOleContainer ); - if ( FAILED( hr ) || !pOleContainer ) - { - return ( error = -1 ); - } - CComPtr pHtml2Doc; - hr = pOleContainer->QueryInterface( IID_IHTMLDocument, ( void** )&pHtml2Doc ); - if ( FAILED( hr ) || !pHtml2Doc ) - { - return ( error = -1 ); - } - BSTR url; - hr = pHtml2Doc->get_URL( &url ); - if ( FAILED( hr ) || !url ) - { - return ( error = -1 ); - } - m_hostingPageUrl = url; - m_hostingPageUrl.Replace( '\\', '/' ); - - ::SysFreeString( url ); - - CComPtr pHtmlElementCollect; - hr = pHtml2Doc->get_scripts( &pHtmlElementCollect ); - if ( FAILED( hr ) || !pHtmlElementCollect ) - { - return ( error = -1 ); - } - long collectionLength = 0; - hr = pHtmlElementCollect->get_length( &collectionLength ); - if ( FAILED( hr ) || !pHtmlElementCollect ) - { - return ( error = -1 ); - } - if ( collectionLength < 1 ) - { - // javascript engine was not specified on the page. hence we need to - // initialize it by infusing javascript element tags - - CComPtr spHtmlElement; - hr = pHtml2Doc->createElement( CComBSTR( "script" ), &spHtmlElement ); - if ( SUCCEEDED( hr ) && spHtmlElement ) - { - hr = spHtmlElement->put_language( CComBSTR( "javascript" ) ); - if ( SUCCEEDED( hr ) ) - { - CComPtr spElementDomNode; - hr = spHtmlElement->QueryInterface( IID_IHTMLDOMNode, ( void** )&spElementDomNode ); - if ( SUCCEEDED( hr ) && spElementDomNode ) - { - CComPtr spHtmlDomNode; - hr = pHtml2Doc->QueryInterface( IID_IHTMLDOMNode, ( void** )&spHtmlDomNode ); - if ( SUCCEEDED( hr ) && spHtmlDomNode ) - { - CComPtr newNode; - hr = spHtmlDomNode->appendChild( spElementDomNode, &newNode ); - } - } - } - } - } - return 0; -} - -LRESULT CP3DActiveXCtrl::OnPandaNotification(WPARAM wParam, LPARAM lParam) -{ - PPInstance::HandleRequestLoop(); - - return 0; -} - -// The static init method. -void CP3DActiveXCtrl:: -st_init(void *data) { - CP3DActiveXCtrl *self = (CP3DActiveXCtrl *)data; - self->init(); - self->_init_not_running.SetEvent(); -} - -// The init method. This is called once at startup, in a child thread. -int CP3DActiveXCtrl::init( ) -{ - int error( 0 ); - std::string p3dDllFilename; - - error = m_instance.DownloadP3DComponents( p3dDllFilename ); - if ( !error && !( p3dDllFilename.empty() ) ) - { - error = m_instance.LoadPlugin( p3dDllFilename ); - if ( !error ) - { - m_pPandaObject = new PPandaObject( this, NULL ); - if (_state == S_loading) { - // Ready to start. - _state = S_ready; - return error; - } - } - } - - if (_state == S_loading) { - // Something went wrong. - _state = S_failed; - } - return error; -} - - -HRESULT CP3DActiveXCtrl::ExchangeProperties( CPropExchange* pPX ) -{ - USES_CONVERSION; - HRESULT hr = E_FAIL; - - if ( !pPX ) - { - return hr; - } - CPropbagPropExchange* pPropExchange = dynamic_cast( pPX ); - if ( !pPropExchange || !pPropExchange->m_pPropBag ) - { - return hr; - } - - // get the property bag version 2 - IPropertyBag2 * pPropBag2; - hr = pPropExchange->m_pPropBag->QueryInterface( IID_IPropertyBag2, (void**)&pPropBag2); - if (! SUCCEEDED (hr)) { - return hr; - } - - // get the number of parameters - unsigned long aNum; - hr = pPropBag2->CountProperties (& aNum); - ATLASSERT (hr >= 0); - if (! SUCCEEDED (hr)) { - return hr; - } - if (aNum == 0) return S_OK; - - // allocate the parameters names - PROPBAG2 * aPropNames = new PROPBAG2[aNum]; - unsigned long aReaded; - - // get the param names - hr = pPropBag2->GetPropertyInfo (0, aNum, aPropNames, & aReaded); - ATLASSERT (hr >= 0); - if (! SUCCEEDED (hr)) { - delete[] aPropNames; - return hr; - } - // allocate the variants and result array - CComVariant * aVal = new CComVariant[aNum]; - HRESULT * hvs = new HRESULT[aNum]; - hr = pPropBag2->Read (aNum, aPropNames, NULL, aVal, hvs); - ATLASSERT (hr >= 0); - if (! SUCCEEDED (hr)) { - delete[] hvs; - delete[] aVal; - delete[] aPropNames; - return hr; - } - - // get the params ; put them in the ParamList - for (unsigned long ind = 0; ind < aNum; ind++) { - std::pair< CString, CString> p( CString( aPropNames[ind].pstrName ), CString( aVal[ind] ) ); - m_parameters.push_back( p ); -// do_what_you_want_with (OLE2T (aPropNames[ind].pstrName), aVal[ind]); - } - // delete the unused arrays - delete[] hvs; - delete[] aVal; - delete[] aPropNames; - - return hr; -} - -P3D_object* CP3DActiveXCtrl::GetP3DObject( ) -{ - if (_state == S_started) { - return m_instance.m_p3dObject; - } - return NULL; -} - -IOleClientSite* CP3DActiveXCtrl::GetClientSte( ) -{ - return GetClientSite(); -} - -void CP3DActiveXCtrl::OnmainChanged(void) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - // TODO: Add your property handler code here - - SetModifiedFlag(); -} - -void CP3DActiveXCtrl:: -get_twirl_bitmaps() { - static const size_t twirl_size = twirl_width * twirl_height; - unsigned char twirl_data[twirl_size * 3]; - unsigned char new_data[twirl_size * 4]; - - for (int step = 0; step < twirl_num_steps + 1; ++step) { - get_twirl_data(twirl_data, twirl_size, step, - m_instance._fgcolor_r, m_instance._fgcolor_g, m_instance._fgcolor_b, - m_instance._bgcolor_r, m_instance._bgcolor_g, m_instance._bgcolor_b); - - // Expand out the RGB channels into RGBA. - for (int yi = 0; yi < twirl_height; ++yi) { - const unsigned char *sp = twirl_data + yi * twirl_width * 3; - unsigned char *dp = new_data + yi * twirl_width * 4; - for (int xi = 0; xi < twirl_width; ++xi) { - // RGB <= BGR. - dp[0] = sp[2]; - dp[1] = sp[1]; - dp[2] = sp[0]; - dp[3] = (unsigned char)0xff; - sp += 3; - dp += 4; - } - } - - // Now load the image. - _twirl_bitmaps[step].CreateBitmap(twirl_width, twirl_height, 1, 32, new_data); - } -} - -void CP3DActiveXCtrl:: -timer_callback(HWND hwnd, UINT msg, UINT_PTR id, DWORD time) { - // Just invalidate the region and make it draw again. - ::InvalidateRect(hwnd, NULL, FALSE); -} diff --git a/direct/src/plugin_activex/P3DActiveXCtrl.h b/direct/src/plugin_activex/P3DActiveXCtrl.h deleted file mode 100644 index dc1bd9df39..0000000000 --- a/direct/src/plugin_activex/P3DActiveXCtrl.h +++ /dev/null @@ -1,116 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file P3DActiveXCtrl.h - * @author atrestman - * @date 2009-09-14 - */ - -#pragma once - -// P3DActiveXCtrl.h : Declaration of the CP3DActiveXCtrl ActiveX Control -// class. -#include "PPInstance.h" -#include "PPPandaObject.h" -#include "PPInterface.h" -#include "get_twirl_data.h" -#include - -#include - -// CP3DActiveXCtrl : See P3DActiveXCtrl.cpp for implementation. - -class CP3DActiveXCtrl : public COleControl, - public PPInterface -{ - DECLARE_DYNCREATE(CP3DActiveXCtrl) - -// Constructor -public: - CP3DActiveXCtrl(); - -// Overrides -public: - virtual void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid); - virtual void OnClose( DWORD dwSaveOption ); - virtual BOOL PreCreateWindow(CREATESTRUCT& cs); - virtual void DoPropExchange(CPropExchange* pPX); - virtual void OnResetState(); - virtual DWORD GetControlFlags(); - - int BeginDownload(CString& url); - - PPInstance m_instance; - -// Implementation -protected: - ~CP3DActiveXCtrl(); - - DECLARE_OLECREATE_EX(CP3DActiveXCtrl) // Class factory and guid - DECLARE_OLETYPELIB(CP3DActiveXCtrl) // GetTypeInfo - DECLARE_PROPPAGEIDS(CP3DActiveXCtrl) // Property page IDs - DECLARE_OLECTLTYPE(CP3DActiveXCtrl) // Type name and misc status - - // Subclassed control support - BOOL IsSubclassedControl(); - LRESULT OnOcmCommand(WPARAM wParam, LPARAM lParam); - -// Message maps - DECLARE_MESSAGE_MAP() - -// Dispatch maps - DECLARE_DISPATCH_MAP() - -// Event maps - DECLARE_EVENT_MAP() - -// Dispatch and event IDs -public: - enum { - dispidmain = 1 - }; - afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); - - static void st_init(void *data); - int init(); - virtual P3D_object* GetP3DObject( ); - virtual IOleClientSite* GetClientSte(); - - // ActiveX properties - std::vector< std::pair < CString, CString > > m_parameters; - - CString m_hostingPageUrl; - -protected: - HRESULT ExchangeProperties( CPropExchange* pPropBag ); - - LRESULT OnPandaNotification(WPARAM wParam, LPARAM lParam); - void OnmainChanged(void); - - void get_twirl_bitmaps(); - static void CALLBACK timer_callback(HWND hwnd, UINT msg, UINT_PTR id, DWORD time); - - PPandaObject* m_pPandaObject; - - CComPtr m_spClientSite; - - CBitmap _twirl_bitmaps[twirl_num_steps + 1]; - - enum State { - S_init, // before starting the download - S_loading, // the instance is downloading - // From S_loading, only the "init" thread may change the state. - - S_ready, // the instance is ready to run - S_started, // the instance has successfully started - S_failed, // something went wrong - }; - State _state; - DWORD _init_time; - CEvent _init_not_running; // set when the init thread has finished, or before it has started. -}; diff --git a/direct/src/plugin_activex/P3DActiveXPropPage.cpp b/direct/src/plugin_activex/P3DActiveXPropPage.cpp deleted file mode 100644 index 181f249838..0000000000 --- a/direct/src/plugin_activex/P3DActiveXPropPage.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file P3DActiveXPropPage.cpp - * @author atrestman - * @date 2009-09-14 - */ - -// P3DActiveXPropPage.cpp : Implementation of the CP3DActiveXPropPage property -// page class. - -#include "stdafx.h" -#include "P3DActiveX.h" -#include "P3DActiveXPropPage.h" - -#ifdef _DEBUG -#define new DEBUG_NEW -#endif - - -IMPLEMENT_DYNCREATE(CP3DActiveXPropPage, COlePropertyPage) - - - -// Message map - -BEGIN_MESSAGE_MAP(CP3DActiveXPropPage, COlePropertyPage) -END_MESSAGE_MAP() - - - -// Initialize class factory and guid - -IMPLEMENT_OLECREATE_EX(CP3DActiveXPropPage, "P3DACTIVEX.P3DActiveXPropPage.1", - 0xd0111370, 0xe705, 0x485e, 0x96, 0xfd, 0xa2, 0xef, 0xb1, 0x71, 0x26, 0x9a) - - - -// CP3DActiveXPropPage::CP3DActiveXPropPageFactory::UpdateRegistry - Adds or -// removes system registry entries for CP3DActiveXPropPage - -BOOL CP3DActiveXPropPage::CP3DActiveXPropPageFactory::UpdateRegistry(BOOL bRegister) -{ - if (bRegister) - return AfxOleRegisterPropertyPageClass(AfxGetInstanceHandle(), - m_clsid, IDS_P3DACTIVEX_PPG); - else - return AfxOleUnregisterClass(m_clsid, NULL); -} - - - -// CP3DActiveXPropPage::CP3DActiveXPropPage - Constructor - -CP3DActiveXPropPage::CP3DActiveXPropPage() : - COlePropertyPage(IDD, IDS_P3DACTIVEX_PPG_CAPTION) -{ -} - - - -// CP3DActiveXPropPage::DoDataExchange - Moves data between page and -// properties - -void CP3DActiveXPropPage::DoDataExchange(CDataExchange* pDX) -{ - DDP_PostProcessing(pDX); -} - - - -// CP3DActiveXPropPage message handlers diff --git a/direct/src/plugin_activex/P3DActiveXPropPage.h b/direct/src/plugin_activex/P3DActiveXPropPage.h deleted file mode 100644 index 56b1400a26..0000000000 --- a/direct/src/plugin_activex/P3DActiveXPropPage.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file P3DActiveXPropPage.h - * @author atrestman - * @date 2009-09-14 - */ - -#pragma once - -// P3DActiveXPropPage.h : Declaration of the CP3DActiveXPropPage property page -// class. -#include "PPPandaObject.h" - -// CP3DActiveXPropPage : See P3DActiveXPropPage.cpp for implementation. - -class CP3DActiveXPropPage : public COlePropertyPage -{ - DECLARE_DYNCREATE(CP3DActiveXPropPage) - DECLARE_OLECREATE_EX(CP3DActiveXPropPage) - -// Constructor -public: - CP3DActiveXPropPage(); - -// Dialog Data - enum { IDD = IDD_PROPPAGE_P3DACTIVEX }; - -// Implementation -protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - -// Message maps -protected: - DECLARE_MESSAGE_MAP() -}; diff --git a/direct/src/plugin_activex/PPBrowserObject.cpp b/direct/src/plugin_activex/PPBrowserObject.cpp deleted file mode 100644 index 02debb6dc9..0000000000 --- a/direct/src/plugin_activex/PPBrowserObject.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPBrowserObject.cpp - * @author atrestman - * @date 2009-09-14 - */ - -#include "stdafx.h" - -#include "PPBrowserObject.h" -#include "PPInstance.h" -#include "P3DActiveXCtrl.h" - -#include - -// The following functions are C-style wrappers around the above -// PPBrowserObject methods; they are defined to allow us to create the C-style -// P3D_class_definition method table to store in the P3D_object structure. -static void object_finish(P3D_object *object) -{ - delete ((PPBrowserObject *)object); -} - -static int object_get_repr(P3D_object *object, char *buffer, int buffer_length) -{ - return ((const PPBrowserObject *)object)->get_repr(buffer, buffer_length); -} - -static P3D_object* object_get_property(P3D_object *object, const char *property) -{ - return ((const PPBrowserObject *)object)->get_property(property); -} - -static bool object_set_property(P3D_object* object, const char* property, - bool needs_response, P3D_object *value) -{ - return ((PPBrowserObject *)object)->set_property(property, needs_response, value); -} - -static P3D_object* object_call(P3D_object* object, const char* method_name, - bool needs_response, - P3D_object *params[], int num_params) -{ - if (method_name == NULL) - { - method_name = ""; - } - P3D_object *response = ((const PPBrowserObject *)object)->call(method_name, params, num_params); - if (!needs_response) - { - // No response was expected. Throw away the response we received, so - // we can be consistent with defined semantics. - P3D_OBJECT_XDECREF(response); - response = NULL; - } - return response; -} - -static P3D_object* object_eval( P3D_object *object, const char *expression ) -{ - return ( ( const PPBrowserObject* )object )->eval( expression ); -} - -P3D_class_definition* PPBrowserObject::_browser_object_class; - -PPBrowserObject::PPBrowserObject( PPInterface* interfac, IDispatch* pDispatch ) : - m_interface( interfac ), m_pDispatch( pDispatch ) -{ - _class = get_class_definition( ); - _ref_count = 1; -} - -PPBrowserObject::PPBrowserObject( const PPBrowserObject& copy ) : - m_interface( copy.m_interface ), m_pDispatch( copy.m_pDispatch ) -{ - _class = get_class_definition( ); - _ref_count = 1; -} - -PPBrowserObject::~PPBrowserObject( ) -{ - assert( _ref_count == 0 ); -} - -P3D_object* PPBrowserObject::get_property( const std::string &property ) const -{ - assert( m_interface ); - - COleVariant varResult; - CComPtr pDispatch( m_pDispatch ); - HRESULT hr = m_interface->HasProperty( pDispatch, CString( property.c_str() ) ); - if( FAILED( hr ) ) - { - return NULL; - } - - hr = m_interface->GetProperty( pDispatch, CString( property.c_str() ), varResult ); - if ( FAILED( hr ) ) - { - return NULL; - } - - return m_interface->variant_to_p3dobj( &varResult ); -} - -bool PPBrowserObject::set_property( const std::string& property, bool needs_response, - P3D_object* value ) -{ - assert( m_interface ); - - CComPtr pDispatch( m_pDispatch ); - COleVariant varValue, varResult; - m_interface->p3dobj_to_variant( &varValue, value); - - HRESULT hr = m_interface->SetProperty( pDispatch, CString( property.c_str() ), varValue, varResult ); - if ( SUCCEEDED( hr ) ) - { - return true; - } - return false; -} - -P3D_object* PPBrowserObject::call( const std::string &method_name, P3D_object* params[], int num_params ) const -{ - assert( m_interface ); - - COleVariant* varParams = new COleVariant[ num_params ]; - COleVariant varResult; - - // First, convert all of the parameters. - for ( int i = 0; i < num_params; ++i ) - { - m_interface->p3dobj_to_variant( &varParams[i], params[i] ); - } - - CComPtr pDispatch( m_pDispatch ); - HRESULT hr = m_interface->CallMethod( pDispatch, CString( method_name.c_str() ), varResult, num_params, varParams ); - - delete [] varParams; - if ( FAILED( hr ) ) - { - return NULL; } - - return m_interface->variant_to_p3dobj( &varResult ); -} - -P3D_object* PPBrowserObject::eval( const std::string &expression ) const -{ - assert( m_interface ); - - COleVariant varResult; - CComPtr pDispatch( m_pDispatch ); - - CString evalExpression( expression.c_str() ); - HRESULT hr = m_interface->EvalExpression( pDispatch, evalExpression , varResult ); - if ( FAILED( hr ) ) - { - return NULL; - } - - return m_interface->variant_to_p3dobj( &varResult ); -} - -int PPBrowserObject::get_repr( char* buffer, int buffer_length ) const -{ - assert( m_interface ); - - VARIANT var; - var.vt = VT_DISPATCH; - var.pdispVal = m_pDispatch; - - COleVariant varDispatch( var ); - - CString result( m_interface->get_repr( varDispatch ) ); - strncpy( buffer, result, buffer_length ); - return (int)result.GetLength(); -} - -void PPBrowserObject::clear_class_definition() -{ - _browser_object_class = NULL; -} - -P3D_class_definition* PPBrowserObject::get_class_definition() -{ - if ( _browser_object_class == NULL ) - { - // Create a default class_definition object, and fill in the - // appropriate pointers. - _browser_object_class = P3D_make_class_definition_ptr(); - _browser_object_class->_finish = &object_finish; - - _browser_object_class->_get_repr = &object_get_repr; - _browser_object_class->_get_property = &object_get_property; - _browser_object_class->_set_property = &object_set_property; - _browser_object_class->_call = &object_call; - _browser_object_class->_eval = &object_eval; - } - return _browser_object_class; -} diff --git a/direct/src/plugin_activex/PPBrowserObject.h b/direct/src/plugin_activex/PPBrowserObject.h deleted file mode 100644 index ac4d6426c7..0000000000 --- a/direct/src/plugin_activex/PPBrowserObject.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPBrowserObject.h - * @author atrestman - * @date 2009-09-14 - */ - -#pragma once - -#include "p3d_plugin.h" -#include - -class PPInterface; - -class PPBrowserObject : public P3D_object -{ -public: - PPBrowserObject( PPInterface* interfac, IDispatch* pDisp ); - PPBrowserObject( const PPBrowserObject& copy ); - ~PPBrowserObject( ); - - int get_repr( char* buffer, int buffer_length ) const; - P3D_object* get_property( const std::string &property ) const; - bool set_property( const std::string& property, bool needs_response, - P3D_object* value ); - - P3D_object* call( const std::string &method_name, - P3D_object* params[], int num_params ) const; - P3D_object* eval( const std::string &expression ) const; - - static void clear_class_definition(); - -protected: - PPBrowserObject( ); - static P3D_class_definition* get_class_definition( ); - - PPInterface* m_interface; - static P3D_class_definition* _browser_object_class; - - CComPtr m_pDispatch; -}; diff --git a/direct/src/plugin_activex/PPDownloadCallback.cpp b/direct/src/plugin_activex/PPDownloadCallback.cpp deleted file mode 100644 index d7739e2039..0000000000 --- a/direct/src/plugin_activex/PPDownloadCallback.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPDownloadCallback.cpp - * @author atrestman - * @date 2009-09-14 - */ - -#include "stdafx.h" -#include "PPDownloadCallback.h" -#include "PPInstance.h" - - -PPDownloadCallback::PPDownloadCallback( PPDownloadCallbackSync& downloadSync ) - : m_downloadSync( downloadSync ), m_dwTotalRead( 0 ), - m_dwTotalInStream( 0 ), m_ulObjRefCount( 1 ) -{ -} - -PPDownloadCallback::~PPDownloadCallback() -{ - m_spStream.Release(); -} - -STDMETHODIMP PPDownloadCallback::QueryInterface(REFIID riid, void **ppvObject) -{ - TRACE(_T("IUnknown::QueryInterface\n")); - - *ppvObject = NULL; - - // IUnknown - if (::IsEqualIID(riid, __uuidof(IUnknown))) - { - TRACE(_T("IUnknown::QueryInterface(IUnknown)\n")); - - *ppvObject = this; - } - // IBindStatusCallback - else if (::IsEqualIID(riid, __uuidof(IBindStatusCallback))) - { - TRACE(_T("IUnknown::QueryInterface(IBindStatusCallback)\n")); - - *ppvObject = static_cast(this); - } - - if (*ppvObject) - { - (*reinterpret_cast(ppvObject))->AddRef(); - - return S_OK; - } - - return E_NOINTERFACE; -} - -STDMETHODIMP_(ULONG) PPDownloadCallback::AddRef() -{ - TRACE(_T("IUnknown::AddRef\n")); - - return ++m_ulObjRefCount; -} - -STDMETHODIMP_(ULONG) PPDownloadCallback::Release() -{ - TRACE(_T("IUnknown::Release\n")); - - return --m_ulObjRefCount; -} - -STDMETHODIMP PPDownloadCallback::OnStartBinding(DWORD, IBinding *) -{ - TRACE(_T("IBindStatusCallback::OnStartBinding\n")); - - m_downloadSync.Begin(); - - return S_OK; -} - -STDMETHODIMP PPDownloadCallback::GetPriority(LONG *) -{ - TRACE(_T("IBindStatusCallback::GetPriority\n")); - - return E_NOTIMPL; -} - -STDMETHODIMP PPDownloadCallback::OnLowResource(DWORD) -{ - TRACE(_T("IBindStatusCallback::OnLowResource\n")); - - return S_OK; -} - -STDMETHODIMP PPDownloadCallback::OnProgress(ULONG ulProgress, - ULONG ulProgressMax, - ULONG ulStatusCode, - LPCWSTR szStatusText) -{ -#ifdef _DEBUG - static const LPCTSTR plpszStatus[] = - { - _T("BINDSTATUS_FINDINGRESOURCE"), // 1 - _T("BINDSTATUS_CONNECTING"), - _T("BINDSTATUS_REDIRECTING"), - _T("BINDSTATUS_BEGINDOWNLOADDATA"), - _T("BINDSTATUS_DOWNLOADINGDATA"), - _T("BINDSTATUS_ENDDOWNLOADDATA"), - _T("BINDSTATUS_BEGINDOWNLOADCOMPONENTS"), - _T("BINDSTATUS_INSTALLINGCOMPONENTS"), - _T("BINDSTATUS_ENDDOWNLOADCOMPONENTS"), - _T("BINDSTATUS_USINGCACHEDCOPY"), - _T("BINDSTATUS_SENDINGREQUEST"), - _T("BINDSTATUS_CLASSIDAVAILABLE"), - _T("BINDSTATUS_MIMETYPEAVAILABLE"), - _T("BINDSTATUS_CACHEFILENAMEAVAILABLE"), - _T("BINDSTATUS_BEGINSYNCOPERATION"), - _T("BINDSTATUS_ENDSYNCOPERATION"), - _T("BINDSTATUS_BEGINUPLOADDATA"), - _T("BINDSTATUS_UPLOADINGDATA"), - _T("BINDSTATUS_ENDUPLOADINGDATA"), - _T("BINDSTATUS_PROTOCOLCLASSID"), - _T("BINDSTATUS_ENCODING"), - _T("BINDSTATUS_VERFIEDMIMETYPEAVAILABLE"), - _T("BINDSTATUS_CLASSINSTALLLOCATION"), - _T("BINDSTATUS_DECODING"), - _T("BINDSTATUS_LOADINGMIMEHANDLER"), - _T("BINDSTATUS_CONTENTDISPOSITIONATTACH"), - _T("BINDSTATUS_FILTERREPORTMIMETYPE"), - _T("BINDSTATUS_CLSIDCANINSTANTIATE"), - _T("BINDSTATUS_IUNKNOWNAVAILABLE"), - _T("BINDSTATUS_DIRECTBIND"), - _T("BINDSTATUS_RAWMIMETYPE"), - _T("BINDSTATUS_PROXYDETECTING"), - _T("BINDSTATUS_ACCEPTRANGES"), - _T("???") // unknown - }; -#endif - - TRACE(_T("IBindStatusCallback::OnProgress\n")); - - TRACE(_T("ulProgress: %lu, ulProgressMax: %lu\n"), - ulProgress, ulProgressMax); - - TRACE(_T("ulStatusCode: %lu "), ulStatusCode); - - if (ulStatusCode < UF_BINDSTATUS_FIRST || - ulStatusCode > UF_BINDSTATUS_LAST) - { - ulStatusCode = UF_BINDSTATUS_LAST + 1; - } - - m_dwTotalInStream = ulProgressMax; - m_downloadSync.ProgressNotify( ulProgress, ulProgressMax ); - -#ifdef _DEBUG - TRACE(_T("(%s), szStatusText: %ls\n"), - plpszStatus[ulStatusCode - UF_BINDSTATUS_FIRST], - szStatusText); -#endif - - return S_OK; -} - -STDMETHODIMP PPDownloadCallback::OnStopBinding(HRESULT, LPCWSTR) -{ - TRACE(_T("IBindStatusCallback::OnStopBinding\n")); - - m_downloadSync.End( ); - - return S_OK; -} - -STDMETHODIMP PPDownloadCallback::GetBindInfo(DWORD *, BINDINFO *) -{ - TRACE(_T("IBindStatusCallback::GetBindInfo\n")); - - return S_OK; -} - -STDMETHODIMP PPDownloadCallback::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, - FORMATETC *, STGMEDIUM *pstgmed) -{ - TRACE(_T("IBindStatusCallback::OnDataAvailable\n")); - - HRESULT hr = S_OK; - - // Get the Stream passed - if (BSCF_FIRSTDATANOTIFICATION & grfBSCF) - { - if (!m_spStream && pstgmed->tymed == TYMED_ISTREAM) - { - m_spStream = pstgmed->pstm; - } - } - - DWORD dwRead = dwSize - m_dwTotalRead; // Minimum amount available that hasn't been read - DWORD dwActuallyRead = 0; // Placeholder for amount read during this pull - - // If there is some data to be read then go ahead and read them - if (m_spStream) - { - if (dwRead > 0) - { - BYTE* pBytes = NULL; - ATLTRY(pBytes = new BYTE[dwRead + 1]); - if (pBytes == NULL) - return E_OUTOFMEMORY; - hr = m_spStream->Read(pBytes, dwRead, &dwActuallyRead); - if (SUCCEEDED(hr)) - { - pBytes[dwActuallyRead] = 0; - if (dwActuallyRead>0) - { - bool ret = m_downloadSync.DataNotify( m_dwTotalInStream, (const void*)pBytes, dwActuallyRead ); - if (!ret) - { - hr = E_ABORT; - } - - m_dwTotalRead += dwActuallyRead; - } - } - delete[] pBytes; - } - } - - if (BSCF_LASTDATANOTIFICATION & grfBSCF || E_ABORT == hr ) - { - m_spStream.Release(); - } - return hr; -} - -STDMETHODIMP PPDownloadCallback::OnObjectAvailable(REFIID, IUnknown *) -{ - TRACE(_T("IBindStatusCallback::OnObjectAvailable\n")); - - return S_OK; -} diff --git a/direct/src/plugin_activex/PPDownloadCallback.h b/direct/src/plugin_activex/PPDownloadCallback.h deleted file mode 100644 index 0ea6d3de4b..0000000000 --- a/direct/src/plugin_activex/PPDownloadCallback.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPDownloadCallback.h - * @author atrestman - * @date 2009-09-14 - */ - -#pragma once - -#include - -enum -{ - UF_BINDSTATUS_FIRST = BINDSTATUS_FINDINGRESOURCE, - UF_BINDSTATUS_LAST = BINDSTATUS_ACCEPTRANGES -}; - -class PPDownloadCallbackSync -{ -public: - virtual bool Begin( ) = 0; - virtual bool DataNotify( size_t expectedDataSize, const void* data, size_t dataSize ) = 0; - virtual void ProgressNotify( size_t progress, size_t maxProgress ) = 0; - virtual bool End( ) = 0; -}; - -class PPDownloadCallback : public IBindStatusCallback -{ -public: - PPDownloadCallback( PPDownloadCallbackSync& downloadSync ); - virtual ~PPDownloadCallback(); - - // IUnknown methods - STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject); - STDMETHOD_(ULONG, AddRef)(); - STDMETHOD_(ULONG, Release)(); - - // IBindStatusCallback methods - STDMETHOD(OnStartBinding)(DWORD, IBinding *); - STDMETHOD(GetPriority)(LONG *); - STDMETHOD(OnLowResource)(DWORD); - STDMETHOD(OnProgress)(ULONG ulProgress, - ULONG ulProgressMax, - ULONG ulStatusCode, - LPCWSTR szStatusText); - STDMETHOD(OnStopBinding)(HRESULT, LPCWSTR); - STDMETHOD(GetBindInfo)(DWORD *, BINDINFO *); - STDMETHOD(OnDataAvailable)(DWORD, DWORD, FORMATETC *, STGMEDIUM *); - STDMETHOD(OnObjectAvailable)(REFIID, IUnknown *); - -protected: - ULONG m_ulObjRefCount; - CComPtr m_spStream; - -private: - PPDownloadCallbackSync& m_downloadSync; - - DWORD m_dwTotalRead; - DWORD m_dwTotalInStream; - -}; diff --git a/direct/src/plugin_activex/PPDownloadRequest.cpp b/direct/src/plugin_activex/PPDownloadRequest.cpp deleted file mode 100644 index 5a25bfd2cc..0000000000 --- a/direct/src/plugin_activex/PPDownloadRequest.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPDownloadRequest.cpp - * @author atrestman - * @date 2009-09-14 - */ - -#include "stdafx.h" - -#include "PPDownloadRequest.h" -#include "PPInstance.h" -#include "wstring_encode.h" - -bool PPDownloadRequest::Begin( ) -{ - m_instance.m_eventStop.ResetEvent( ); - m_instance.m_eventDownloadStopped.ResetEvent( ); - return true; -} - -bool PPDownloadRequest::DataNotify( size_t expectedDataSize, const void* data, size_t dataSize ) -{ - bool ret = false; - if ( m_instance.m_eventStop.m_hObject != NULL ) - { - if ( ::WaitForSingleObject( m_instance.m_eventStop.m_hObject, 0 ) == WAIT_OBJECT_0 ) - { - return ret; // canceled by the user - } - } - switch ( this->m_requestType ) - { - case ( RequestType::P3DObject ): - { - if ( m_p3dRequest ) - { - ret = P3D_instance_feed_url_stream_ptr( m_p3dRequest->_instance, - m_p3dRequest->_request._get_url._unique_id, - P3D_RC_in_progress, - 0, - expectedDataSize, - data, - dataSize ); - } - } - break; - case ( RequestType::File ): - { - if ( m_hFile == INVALID_HANDLE_VALUE ) - { - wstring filename_w; - string_to_wstring(filename_w, m_fileName); - m_hFile = ::CreateFileW( filename_w.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); - if ( m_hFile == INVALID_HANDLE_VALUE ) - { - return ret; - } - } - DWORD numberOfBytesWritten = 0; - if ( ::WriteFile( m_hFile, data, dataSize, &numberOfBytesWritten, NULL ) == TRUE ) - { - ret = true; - } - } - break; - case ( RequestType::Data ): - { - if ( m_data ) - { - std::string bits( static_cast< const char* >( data ), dataSize ); - *m_data << bits; - ret = true; - } - } - break; - } - return ret; -} - -void PPDownloadRequest::ProgressNotify( size_t progress, size_t maxProgress ) -{ - if ( ::IsWindow( m_instance.m_parentWnd ) ) - { - SendMessage( m_instance.m_parentWnd, WM_PROGRESS, (WPARAM)(progress * 100.0 / maxProgress), 0 ); - } -} - -bool PPDownloadRequest::End( ) -{ - if ( m_hFile != INVALID_HANDLE_VALUE ) - { - ::CloseHandle( m_hFile ); - m_hFile = INVALID_HANDLE_VALUE; - } - m_instance.m_eventDownloadStopped.SetEvent( ); - return true; -} diff --git a/direct/src/plugin_activex/PPDownloadRequest.h b/direct/src/plugin_activex/PPDownloadRequest.h deleted file mode 100644 index 26d5081fc7..0000000000 --- a/direct/src/plugin_activex/PPDownloadRequest.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPDownloadRequest.h - * @author atrestman - * @date 2009-09-14 - */ - -#pragma once - -#include -#include - -#include "PPDownloadCallback.h" -#include "PPInstance.h" - -class PPDownloadRequest : public PPDownloadCallbackSync -{ -public: - enum RequestType - { - File, - Data, - P3DObject - }; - - PPDownloadRequest( PPInstance& instance, P3D_request* p3dRequest ) : - m_instance( instance ), m_p3dRequest( p3dRequest ), m_data( nullptr ), - m_requestType( RequestType::P3DObject ), m_hFile( INVALID_HANDLE_VALUE ) - { - } - - PPDownloadRequest( PPInstance& instance, const std::string& fileName ) : - m_instance( instance ), m_p3dRequest( nullptr ), m_fileName( fileName ), - m_data( nullptr ), m_requestType( RequestType::File ), m_hFile( INVALID_HANDLE_VALUE ) - { - } - - PPDownloadRequest( PPInstance& instance, std::strstream* data ) : - m_instance( instance ), m_p3dRequest ( nullptr ), m_data( data ), - m_requestType( RequestType::Data ), m_hFile( INVALID_HANDLE_VALUE ) - { - } - - virtual ~PPDownloadRequest( ) - { - End(); - } - - // PPDownloadCallbackSync interface - virtual bool Begin( ); - virtual bool DataNotify( size_t expectedDataSize, const void* data, size_t size ); - virtual void ProgressNotify( size_t progress, size_t maxProgress ); - virtual bool End( ); - - PPInstance& m_instance; - RequestType m_requestType; - -protected: - P3D_request* m_p3dRequest; - std::string m_fileName; - std::strstream* m_data; - - HANDLE m_hFile; - -private: - PPDownloadRequest(); -}; diff --git a/direct/src/plugin_activex/PPInstance.cpp b/direct/src/plugin_activex/PPInstance.cpp deleted file mode 100644 index 1c1c6fc071..0000000000 --- a/direct/src/plugin_activex/PPInstance.cpp +++ /dev/null @@ -1,995 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPInstance.cpp - * @author atrestman - * @date 2009-09-14 - */ - -#include "stdafx.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "resource.h" -#include "PPInstance.h" -#include "P3DActiveXCtrl.h" -#include "PPBrowserObject.h" -#include "PPDownloadRequest.h" - -#include "p3d_plugin_config.h" -#include "get_tinyxml.h" -#include "load_plugin.h" -#include "find_root_dir.h" -#include "mkdir_complete.h" -#include "parse_color.h" -#include "wstring_encode.h" - -// We can include this header file to get the DTOOL_PLATFORM definition, even -// though we don't link with dtool. -#include "dtool_platform.h" -#include "pandaVersion.h" - -#define P3D_CONTENTS_FILENAME "contents.xml" -#define P3D_DEFAULT_PLUGIN_FILENAME "p3d_plugin.dll" - -static int s_instanceCount = 0; -void P3D_NotificationSync(P3D_instance *instance) -{ - static bool handleRequestOnUIThread = true; - if ( instance ) - { - CP3DActiveXCtrl* parent = static_cast(instance->_user_data); - if ( parent ) - { - if ( ::IsWindow( parent->m_hWnd ) ) - { - ::PostMessage( parent->m_hWnd, WM_PANDA_NOTIFICATION, 0, 0 ); - } - else - { - nout << "Can handle P3D_Notification on UI thread. Controls window is invalid\n"; - } - } - else - { - nout << "Can handle P3D_Notification on UI thread. Instance's user data is not a Control\n"; - } - } -} - -PPInstance::PPInstance( CP3DActiveXCtrl& parentCtrl ) : - m_parentCtrl( parentCtrl ), m_p3dInstance( NULL ), m_p3dObject( NULL ), m_isInit( false ) -{ - // We need the root dir first. - m_rootDir = find_root_dir( ); - string_to_wstring(m_rootDir_w, m_rootDir); - - // Then open the logfile. - m_logger.Open( m_rootDir ); - - m_pluginLoaded = false; - _contents_expiration = 0; - _failed = false; - - _tokens = NULL; - _num_tokens = 0; - - // Ensure this event is initially in the "set" state, in case we never get a - // download request before we get a close request. - m_eventDownloadStopped.SetEvent( ); - m_eventStop.ResetEvent(); - - nout << "Plugin is built with " << PANDA_PACKAGE_HOST_URL << "\n"; -} - -PPInstance::~PPInstance() { - assert(_tokens == NULL); -} - -// This is called at setup time to read the set of web tokens from the ActiveX -// control. -void PPInstance:: -read_tokens() { - assert(_tokens == NULL); - _num_tokens = (int)m_parentCtrl.m_parameters.size(); - _tokens = new P3D_token[ _num_tokens ]; - for (int i = 0; i < _num_tokens; i++ ) { - std::pair< CString, CString > keyAndValue = m_parentCtrl.m_parameters[ i ]; - // Make the token lowercase, since HTML is case-insensitive but we're - // not. - string keyword; - for (const char *p = m_parentCtrl.m_parameters[ i ].first; *p; ++p) { - keyword += tolower(*p); - } - - _tokens[i]._keyword = strdup( keyword.c_str() ); - _tokens[i]._value = strdup( m_parentCtrl.m_parameters[ i ].second ); - } - - // fgcolor and bgcolor are useful to know here (in case we have to draw a - // twirling icon). - - // The default bgcolor is white. - _bgcolor_r = _bgcolor_g = _bgcolor_b = 0xff; - if (has_token("bgcolor")) { - int r, g, b; - if (parse_color(r, g, b, lookup_token("bgcolor"))) { - _bgcolor_r = r; - _bgcolor_g = g; - _bgcolor_b = b; - } - } - - // The default fgcolor is either black or white, according to the - // brightness of the bgcolor. - if (_bgcolor_r + _bgcolor_g + _bgcolor_b > 0x80 + 0x80 + 0x80) { - _fgcolor_r = _fgcolor_g = _fgcolor_b = 0x00; - } else { - _fgcolor_r = _fgcolor_g = _fgcolor_b = 0xff; - } - if (has_token("fgcolor")) { - int r, g, b; - if (parse_color(r, g, b, lookup_token("fgcolor"))) { - _fgcolor_r = r; - _fgcolor_g = g; - _fgcolor_b = b; - } - } -} - -int PPInstance::DownloadFile( const std::string& from, const std::string& to ) -{ - int error( 0 ); - HRESULT hr( S_OK ); - - nout << "Downloading " << from << " into " << to << "\n"; - { - PPDownloadRequest p3dFileDownloadRequest( *this, to ); - PPDownloadCallback dcForFile( p3dFileDownloadRequest ); - hr = ::URLOpenStream( m_parentCtrl.GetControllingUnknown(), from.c_str(), 0, &dcForFile ); - } - if ( FAILED( hr ) ) - { - error = 1; - nout << "Error downloading " << from << " :" << hr << "\n"; - } - return error; -} - -int PPInstance::CopyFile( const std::string& from, const std::string& to ) -{ - ifstream in(from.c_str(), ios::in | ios::binary); - ofstream out(to.c_str(), ios::out | ios::binary); - - static const size_t buffer_size = 4096; - char buffer[buffer_size]; - - in.read(buffer, buffer_size); - size_t count = in.gcount(); - while (count != 0) { - out.write(buffer, count); - if (out.fail()) { - return 1; - } - in.read(buffer, buffer_size); - count = in.gcount(); - } - - if (!in.eof()) { - return 1; - } - - return 0; -} - -/** - * Attempts to open and read the contents.xml file on disk. Copies the file - * to its standard location on success. Returns true on success, false on - * failure. - */ -bool PPInstance:: -read_contents_file(const string &contents_filename, bool fresh_download) { - TiXmlDocument doc(contents_filename.c_str()); - if (!doc.LoadFile()) { - return false; - } - - bool found_core_package = false; - - TiXmlElement *xcontents = doc.FirstChildElement("contents"); - if (xcontents != NULL) { - int max_age = P3D_CONTENTS_DEFAULT_MAX_AGE; - xcontents->Attribute("max_age", &max_age); - - // Get the latest possible expiration time, based on the max_age - // indication. Any expiration time later than this is in error. - time_t now = time(NULL); - _contents_expiration = now + (time_t)max_age; - - if (fresh_download) { - // Update the XML with the new download information. - TiXmlElement *xorig = xcontents->FirstChildElement("orig"); - while (xorig != NULL) { - xcontents->RemoveChild(xorig); - xorig = xcontents->FirstChildElement("orig"); - } - - xorig = new TiXmlElement("orig"); - xcontents->LinkEndChild(xorig); - - xorig->SetAttribute("expiration", (int)_contents_expiration); - - } else { - // Read the expiration time from the XML. - int expiration = 0; - TiXmlElement *xorig = xcontents->FirstChildElement("orig"); - if (xorig != NULL) { - xorig->Attribute("expiration", &expiration); - } - - _contents_expiration = min(_contents_expiration, (time_t)expiration); - } - - nout << "read contents.xml, max_age = " << max_age - << ", expires in " << max(_contents_expiration, now) - now - << " s\n"; - - // Look for the entry; it might point us at a different download - // URL, and it might mention some mirrors. - find_host(xcontents); - - // Now look for the core API package. - _coreapi_set_ver = ""; - TiXmlElement *xpackage = xcontents->FirstChildElement("package"); - while (xpackage != NULL) { - const char *name = xpackage->Attribute("name"); - if (name != NULL && strcmp(name, "coreapi") == 0) { - const char *platform = xpackage->Attribute("platform"); - if (platform != NULL && strcmp(platform, DTOOL_PLATFORM) == 0) { - _coreapi_dll.load_xml(xpackage); - const char *set_ver = xpackage->Attribute("set_ver"); - if (set_ver != NULL) { - _coreapi_set_ver = set_ver; - } - found_core_package = true; - break; - } - } - - xpackage = xpackage->NextSiblingElement("package"); - } - } - - if (!found_core_package) { - // Couldn't find the coreapi package description. - nout << "No coreapi package defined in contents file for " - << DTOOL_PLATFORM << "\n"; - return false; - } - - // Check the coreapi_set_ver token. If it is given, it specifies a minimum - // Core API version number we expect to find. If we didn't find that - // number, perhaps our contents.xml is out of date. - string coreapi_set_ver = lookup_token("coreapi_set_ver"); - if (!coreapi_set_ver.empty()) { - nout << "Instance asked for Core API set_ver " << coreapi_set_ver - << ", we found " << _coreapi_set_ver << "\n"; - // But don't bother if we just freshly downloaded it. - if (!fresh_download) { - if (compare_seq(coreapi_set_ver, _coreapi_set_ver) > 0) { - // The requested set_ver value is higher than the one we have on file; - // our contents.xml file must be out of date after all. - nout << "expiring contents.xml\n"; - _contents_expiration = 0; - } - } - } - - // Success. Now save the file in its proper place. - string standard_filename = m_rootDir + "/contents.xml"; - - mkfile_complete(standard_filename, nout); - if (!doc.SaveFile(standard_filename.c_str())) { - nout << "Couldn't rewrite " << standard_filename << "\n"; - return false; - } - - return true; -} - -/** - * Scans the element for the matching element. - */ -void PPInstance:: -find_host(TiXmlElement *xcontents) { - string host_url = PANDA_PACKAGE_HOST_URL; - TiXmlElement *xhost = xcontents->FirstChildElement("host"); - if (xhost != NULL) { - const char *url = xhost->Attribute("url"); - if (url != NULL && host_url == string(url)) { - // We're the primary host. This is the normal case. - read_xhost(xhost); - return; - - } else { - // We're not the primary host; perhaps we're an alternate host. - TiXmlElement *xalthost = xhost->FirstChildElement("alt_host"); - while (xalthost != NULL) { - const char *url = xalthost->Attribute("url"); - if (url != NULL && host_url == string(url)) { - // Yep, we're this alternate host. - read_xhost(xhost); - return; - } - xalthost = xalthost->NextSiblingElement("alt_host"); - } - } - - // Hmm, didn't find the URL we used mentioned. Assume we're the primary - // host. - read_xhost(xhost); - } -} - -/** - * Reads the host data from the (or ) entry in the - * contents.xml file. - */ -void PPInstance:: -read_xhost(TiXmlElement *xhost) { - // Get the "download" URL, which is the source from which we download - // everything other than the contents.xml file. - const char *download_url = xhost->Attribute("download_url"); - if (download_url != NULL) { - _download_url_prefix = download_url; - } else { - _download_url_prefix = PANDA_PACKAGE_HOST_URL; - } - if (!_download_url_prefix.empty()) { - if (_download_url_prefix[_download_url_prefix.size() - 1] != '/') { - _download_url_prefix += "/"; - } - } - - TiXmlElement *xmirror = xhost->FirstChildElement("mirror"); - while (xmirror != NULL) { - const char *url = xmirror->Attribute("url"); - if (url != NULL) { - add_mirror(url); - } - xmirror = xmirror->NextSiblingElement("mirror"); - } -} - -/** - * Adds a new URL to serve as a mirror for this host. The mirrors will be - * consulted first, before consulting the host directly. - */ -void PPInstance:: -add_mirror(std::string mirror_url) { - // Ensure the URL ends in a slash. - if (!mirror_url.empty() && mirror_url[mirror_url.size() - 1] != '/') { - mirror_url += '/'; - } - - // Add it to the _mirrors list, but only if it's not already there. - if (std::find(_mirrors.begin(), _mirrors.end(), mirror_url) == _mirrors.end()) { - _mirrors.push_back(mirror_url); - } -} - -/** - * Selects num_mirrors elements, chosen at random, from the _mirrors list. - * Adds the selected mirrors to result. If there are fewer than num_mirrors - * elements in the list, adds only as many mirrors as we can get. - */ -void PPInstance:: -choose_random_mirrors(std::vector &result, int num_mirrors) { - std::vector selected; - - size_t num_to_select = min(_mirrors.size(), (size_t)num_mirrors); - while (num_to_select > 0) { - size_t i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size()); - while (std::find(selected.begin(), selected.end(), i) != selected.end()) { - // Already found this i, find a new one. - i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size()); - } - selected.push_back(i); - result.push_back(_mirrors[i]); - --num_to_select; - } -} - -int PPInstance::DownloadP3DComponents( std::string& p3dDllFilename ) -{ - int error(0); - - // Get the pathname of the local copy of the contents.xml file. - std::string finalContentsFileName( m_rootDir ); - finalContentsFileName += "/"; - finalContentsFileName += P3D_CONTENTS_FILENAME; - - // Check to see if the version on disk is already current enough. - bool already_got = false; - if (read_contents_file(finalContentsFileName, false)) { - if (time(NULL) < _contents_expiration) { - // Got the file, and it's good. - already_got = true; - } - } - - if (!already_got) { - // OK, we need to download a new contents.xml file. Start off by - // downloading it into a local temporary file. - WCHAR local_filename_w[ MAX_PATH ]; - if (!::GetTempFileNameW( m_rootDir_w.c_str(), L"p3d", 0, local_filename_w )) { - nout << "GetTempFileName failed (folder is " << m_rootDir << ")\n"; - return 1; - } - - std::string local_filename; - wstring_to_string(local_filename, local_filename_w); - - std::string hostUrl( PANDA_PACKAGE_HOST_URL ); - if (!hostUrl.empty() && hostUrl[hostUrl.size() - 1] != '/') { - hostUrl += '/'; - } - - // Append a query string to the contents.xml URL to uniquify it and - // ensure we don't get a cached version. - std::ostringstream strm; - strm << hostUrl << P3D_CONTENTS_FILENAME << "?" << time(NULL); - std::string remoteContentsUrl( strm.str() ); - - error = DownloadFile( remoteContentsUrl, local_filename ); - if ( !error ) { - if ( !read_contents_file( local_filename, true ) ) - error = 1; - } - - if ( error ) { - // If we couldn't download or read the contents.xml file, check to see - // if there's a good one on disk already, as a fallback. - if ( !read_contents_file( finalContentsFileName, false ) ) - error = 1; - } - - // We don't need the temporary file any more. - ::DeleteFileW( local_filename_w ); - } - - if (!error) { - // OK, at this point we have successfully read contents.xml, and we have - // a good file spec in _coreapi_dll. - if (_coreapi_dll.quick_verify(m_rootDir)) { - // The DLL is already on-disk, and is good. - p3dDllFilename = _coreapi_dll.get_pathname(m_rootDir); - } else { - // The DLL is not already on-disk, or it's stale. Go get it. - std::string p3dLocalModuleFileName(_coreapi_dll.get_pathname(m_rootDir)); - mkfile_complete(p3dLocalModuleFileName, nout); - - // Try one of the mirrors first. - std::vector mirrors; - choose_random_mirrors(mirrors, 2); - - error = 1; - for (std::vector::iterator si = mirrors.begin(); - si != mirrors.end() && error; - ++si) { - std::string url = (*si) + _coreapi_dll.get_filename(); - error = DownloadFile(url, p3dLocalModuleFileName); - if (!error && !_coreapi_dll.full_verify(m_rootDir)) { - // If it's not right after downloading, it's an error. - error = 1; - } - } - - // If that failed, go get it from the authoritative host. - if (error) { - std::string url = _download_url_prefix + _coreapi_dll.get_filename(); - error = DownloadFile(url, p3dLocalModuleFileName); - if (!error && !_coreapi_dll.full_verify(m_rootDir)) { - error = 1; - } - } - - // If *that* failed, go get it again from the same URL, this time with - // a query prefix to bust through any caches. - if (error) { - std::ostringstream strm; - strm << _download_url_prefix << _coreapi_dll.get_filename(); - strm << "?" << time(NULL); - - std::string url = strm.str(); - error = DownloadFile(url, p3dLocalModuleFileName); - if (!error && !_coreapi_dll.full_verify(m_rootDir)) { - nout << "After download, " << _coreapi_dll.get_filename() - << " is no good.\n"; - error = 1; - } - } - - if (!error) { - // Downloaded successfully. - p3dDllFilename = _coreapi_dll.get_pathname(m_rootDir); - } - } - } - - if (error) { - set_failed(); - } - return error; -} - -int PPInstance::LoadPlugin( const std::string& dllFilename ) -{ - CSingleLock lock(&_load_mutex); - lock.Lock(); - if ( !m_pluginLoaded ) - { - ref_plugin(); - m_pluginLoaded = true; - } - - int error = 0; - if (!is_plugin_loaded()) { - - std::string pathname = dllFilename; -#ifdef P3D_PLUGIN_P3D_PLUGIN - // This is a convenience macro for development. If defined and - // nonempty, it indicates the name of the plugin DLL that we will - // actually run, even after downloading a possibly different (presumably - // older) version. Its purpose is to simplify iteration on the plugin - // DLL. - string override_filename = P3D_PLUGIN_P3D_PLUGIN; - if (!override_filename.empty()) { - pathname = override_filename; - } -#endif // P3D_PLUGIN_P3D_PLUGIN - - nout << "Attempting to load core API from " << pathname << "\n"; - string contents_filename = m_rootDir + "/contents.xml"; - if (!load_plugin(pathname, contents_filename, PANDA_PACKAGE_HOST_URL, - P3D_VC_normal, "", "", "", false, false, - m_rootDir, "", "", nout)) { - nout << "Unable to launch core API in " << pathname << "\n"; - error = 1; - } else { - - // Format the coreapi_timestamp as a string, for passing as a - // parameter. - ostringstream stream; - stream << _coreapi_dll.get_timestamp(); - string coreapi_timestamp = stream.str(); - -#ifdef PANDA_OFFICIAL_VERSION - static const bool official = true; -#else - static const bool official = false; -#endif - P3D_set_plugin_version_ptr(P3D_PLUGIN_MAJOR_VERSION, P3D_PLUGIN_MINOR_VERSION, - P3D_PLUGIN_SEQUENCE_VERSION, official, - PANDA_DISTRIBUTOR, - PANDA_PACKAGE_HOST_URL, coreapi_timestamp.c_str(), - _coreapi_set_ver.c_str()); - - } - } - - if (error) { - set_failed(); - } - return error ; -} - -int PPInstance::UnloadPlugin() -{ - CSingleLock lock(&_load_mutex); - lock.Lock(); - - int error( 0 ); - - if ( m_pluginLoaded ) - { - m_pluginLoaded = false; - m_isInit = false; - unref_plugin(); - } - return error; -} - -// Increments the reference count on the "plugin" library (i.e. the core -// API). Call unref_plugin() later to decrement this count. -void PPInstance:: -ref_plugin() { - s_instanceCount += 1; -} - -// Decrements the reference count on the "plugin" library. This must -// correspond to an earlier call to ref_plugin(). When the last reference is -// removed, the plugin will be unloaded. -void PPInstance:: -unref_plugin() { - assert( s_instanceCount > 0 ); - s_instanceCount -= 1; - - if ( s_instanceCount == 0 && is_plugin_loaded() ) { - nout << "Unloading core API\n"; - unload_plugin(nout); - - // This pointer is no longer valid and must be reset for next time. - PPBrowserObject::clear_class_definition(); - } -} - -int PPInstance::Start( const std::string& p3dFilename ) -{ - { - CSingleLock lock(&_load_mutex); - lock.Lock(); - - assert(!m_isInit); - m_isInit = true; - } - - P3D_window_handle parent_window; - memset(&parent_window, 0, sizeof(parent_window)); - parent_window._window_handle_type = P3D_WHT_win_hwnd; - parent_window._handle._win_hwnd._hwnd = m_parentCtrl.m_hWnd; - - RECT rect; - GetClientRect( m_parentCtrl.m_hWnd, &rect ); - - nout << "Creating new P3D instance object for " << p3dFilename << "\n"; - m_p3dInstance = P3D_new_instance_ptr( &P3D_NotificationSync, _tokens, _num_tokens, 0, NULL, (void*)&m_parentCtrl ); - - if ( !m_p3dInstance ) - { - nout << "Error creating P3D instance: " << GetLastError() << "\n"; - return 1; - } - CComPtr pDispatch; - PPBrowserObject *pobj = new PPBrowserObject( &m_parentCtrl, pDispatch ); - P3D_instance_set_browser_script_object_ptr( m_p3dInstance, pobj ); - P3D_OBJECT_DECREF( pobj ); - - m_p3dObject = P3D_instance_get_panda_script_object_ptr( m_p3dInstance ); - P3D_OBJECT_INCREF( m_p3dObject ); - - P3D_instance_setup_window_ptr( m_p3dInstance, P3D_WT_embedded, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, &parent_window ); - - nout << "Starting new P3D instance " << p3dFilename << "\n"; - if ( !P3D_instance_start_ptr( m_p3dInstance, false, p3dFilename.c_str(), 0 ) ) - { - nout << "Error starting P3D instance: " << GetLastError() << "\n"; - return 1; - } - - return 0; -} - -int PPInstance::Stop( ) -{ - m_eventStop.SetEvent( ); - ::WaitForSingleObject( m_eventDownloadStopped.m_hObject, INFINITE ); - if ( m_p3dInstance ) - { - P3D_instance_finish_ptr( m_p3dInstance ); - m_p3dInstance = NULL; - } - if ( m_p3dObject ) - { - P3D_OBJECT_DECREF( m_p3dObject ); - m_p3dObject = NULL; - } - if ( m_pluginLoaded ) - { - UnloadPlugin(); - } - - if (_tokens != NULL) { - for ( int j = 0; j < _num_tokens; ++j) { - delete [] _tokens[j]._keyword; - delete [] _tokens[j]._value; - } - delete [] _tokens; - _tokens = NULL; - _num_tokens = 0; - } - - return 0; -} - -std::string PPInstance::GetHostUrl( ) -{ - CString hostingPageLocation = m_parentCtrl.m_hostingPageUrl.Left( m_parentCtrl.m_hostingPageUrl.ReverseFind( '/' ) );; - std::string p3dRemoteFilename( hostingPageLocation ); - p3dRemoteFilename += "/"; - return p3dRemoteFilename; -} - -std::string PPInstance::GetP3DFilename( ) -{ - std::string p3dFilename; - for ( UINT i = 0; i < m_parentCtrl.m_parameters.size(); i++ ) - { - std::pair< CString, CString > keyAndValue = m_parentCtrl.m_parameters[ i ]; - if ( keyAndValue.first == "data" ) - { - p3dFilename = keyAndValue.second; - } - } - return p3dFilename; -} - -void PPInstance::HandleRequestLoop() { - P3D_instance *p3d_inst = P3D_check_request_ptr(0.0); - while ( p3d_inst != NULL ) { - P3D_request *request = P3D_instance_get_request_ptr(p3d_inst); - if ( request != NULL ) { - CP3DActiveXCtrl* parent = ( CP3DActiveXCtrl* )(p3d_inst->_user_data); - if ( parent ) { - if (!parent->m_instance.HandleRequest( request )) { - // If handling the request is meant to yield control temporarily to - // JavaScript (e.g. P3D_RT_callback), then do so now. - return; - } - - } else { - nout << "Error handling P3D request. Instance's user data is not a Control \n"; - } - } - p3d_inst = P3D_check_request_ptr(0.0); - } -} - -void PPInstance::HandleRequestGetUrl( void* data ) -{ - HRESULT hr( S_OK ); - ThreadedRequestData *trdata = static_cast( data ); - PPInstance *self = trdata->_self; - P3D_request *request = trdata->_request; - std::string host_url = trdata->_host_url; - delete trdata; - - if ( !request ) - { - return; - } - int unique_id = request->_request._get_url._unique_id; - std::string url = request->_request._get_url._url; - CP3DActiveXCtrl* parent = static_cast ( request->_instance->_user_data ); - - if ( !parent ) - { - return; - } - - nout << "Handling P3D_RT_get_url request from " << url << "\n"; - - // Convert a relative URL into a full URL. - size_t colon = url.find(':'); - size_t slash = url.find('/'); - if (colon == std::string::npos || colon > slash) { - // Not a full URL, so it's a relative URL. Prepend the current URL. - if (url.empty() || url[0] == '/') { - // It starts with a slash, so go back to the root of this particular - // host. - colon = host_url.find(':'); - if (colon != std::string::npos && - colon + 2 < host_url.size() && - host_url[colon + 1] == '/' && host_url[colon + 2] == '/') { - slash = host_url.find('/', colon + 3); - url = host_url.substr(0, slash) + url; - } - } else { - // It doesn't start with a slash, so it's relative to this page. - url = host_url + url; - } - nout << "Made fullpath: " << url << "\n"; - } - - { - PPDownloadRequest p3dObjectDownloadRequest( parent->m_instance, request ); - PPDownloadCallback bsc( p3dObjectDownloadRequest ); - hr = ::URLOpenStream( parent->GetControllingUnknown(), url.c_str(), 0, &bsc ); - } - P3D_result_code result_code = P3D_RC_done; - if ( FAILED( hr ) ) - { - nout << "Error handling P3D_RT_get_url request" << " :" << hr << "\n"; - result_code = P3D_RC_generic_error; - } - - P3D_instance_feed_url_stream_ptr( - request->_instance, - request->_request._get_url._unique_id, - result_code, - 0, - 0, - (const void*)NULL, - 0 - ); - P3D_request_finish_ptr( request, true ); -} - -bool PPInstance:: -HandleRequest( P3D_request *request ) { - if ( !request ) { - return false; - } - bool handled = false; - bool continue_loop = true; - - switch ( request->_request_type ) { - case P3D_RT_stop: - { - P3D_instance_finish_ptr( request->_instance ); - handled = true; - break; - } - case P3D_RT_get_url: - { - // We always handle url requests on a thread. - ThreadedRequestData *trdata = new ThreadedRequestData; - trdata->_self = this; - trdata->_request = request; - trdata->_host_url = GetHostUrl(); - - _beginthread( HandleRequestGetUrl, 0, trdata ); - handled = true; - return true; - } - case P3D_RT_callback: - { - // In the case of a callback, yield control temporarily to JavaScript. - continue_loop = false; - break; - } - default: - { - // Some request types are not handled. - break; - } - }; - - P3D_request_finish_ptr( request, handled ); - return continue_loop; -} - -/** - * Compares the two dotted-integer sequence values numerically. Returns -1 if - * seq_a sorts first, 1 if seq_b sorts first, 0 if they are equivalent. - */ -int PPInstance:: -compare_seq(const string &seq_a, const string &seq_b) { - const char *num_a = seq_a.c_str(); - const char *num_b = seq_b.c_str(); - int comp = compare_seq_int(num_a, num_b); - while (comp == 0) { - if (*num_a != '.') { - if (*num_b != '.') { - // Both strings ran out together. - return 0; - } - // a ran out first. - return -1; - } else if (*num_b != '.') { - // b ran out first. - return 1; - } - - // Increment past the dot. - ++num_a; - ++num_b; - comp = compare_seq(num_a, num_b); - } - - return comp; -} - -/** - * Numerically compares the formatted integer value at num_a with num_b. - * Increments both num_a and num_b to the next character following the valid - * integer. - */ -int PPInstance:: -compare_seq_int(const char *&num_a, const char *&num_b) { - long int a; - char *next_a; - long int b; - char *next_b; - - a = strtol((char *)num_a, &next_a, 10); - b = strtol((char *)num_b, &next_b, 10); - - num_a = next_a; - num_b = next_b; - - if (a < b) { - return -1; - } else if (b < a) { - return 1; - } else { - return 0; - } -} - -/** - * Called when something has gone wrong that prevents the plugin instance from - * running. Specifically, this means it failed to load the core API. - */ -void PPInstance:: -set_failed() { - if (!_failed) { - _failed = true; - - nout << "Plugin failed.\n"; - - // Look for the "onpluginfail" token. - string expression = lookup_token("onpluginfail"); - - if (!expression.empty()) { - // Now attempt to evaluate the expression. - COleVariant varResult; - CComPtr pDispatch; - - CString evalExpression( expression.c_str() ); - HRESULT hr = m_parentCtrl.EvalExpression( pDispatch, evalExpression , varResult ); - if (FAILED(hr)) { - nout << "Unable to eval " << expression << "\n"; - } else { - nout << "Eval " << expression << "\n"; - } - } - } -} - - -/** - * Returns the value associated with the first appearance of the named token, - * or empty string if the token does not appear. - */ -std::string PPInstance:: -lookup_token(const std::string &keyword) const { - for (int i = 0; i < _num_tokens; ++i) { - if (strcmp(_tokens[i]._keyword, keyword.c_str()) == 0) { - return _tokens[i]._value; - } - } - - return string(); -} - -/** - * Returns true if the named token appears in the list, false otherwise. - */ -bool PPInstance:: -has_token(const std::string &keyword) const { - for (int i = 0; i < _num_tokens; ++i) { - if (strcmp(_tokens[i]._keyword, keyword.c_str()) == 0) { - return true; - } - } - - return false; -} diff --git a/direct/src/plugin_activex/PPInstance.h b/direct/src/plugin_activex/PPInstance.h deleted file mode 100644 index c8da8ca650..0000000000 --- a/direct/src/plugin_activex/PPInstance.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPInstance.h - * @author atrestman - * @date 2009-09-14 - */ - -#pragma once - -#include -#include -#include -#include - -#include "p3d_plugin.h" -#include "PPDownloadCallback.h" -#include "PPLogger.h" -#include "fileSpec.h" -#include "load_plugin.h" - -#define WM_PY_LAUNCHED (WM_USER + 1) -#define WM_PROGRESS (WM_USER + 2) -#define WM_PANDA_NOTIFICATION (WM_USER + 3) - -class CP3DActiveXCtrl; - -class PPInstance -{ -public: - PPInstance( CP3DActiveXCtrl& parentCtrl ); - virtual ~PPInstance( ); - - void read_tokens(); - int DownloadP3DComponents( std::string& p3dDllFilename ); - - int LoadPlugin( const std::string& dllFilename ); - int UnloadPlugin( void ); - - static void ref_plugin(); - static void unref_plugin(); - - int Start( const std::string& p3dFileName ); - int Stop( ); - - std::string GetHostUrl( ); - std::string GetP3DFilename( ); - - static void HandleRequestLoop(); - - inline bool IsInit() { return m_isInit; } - - HWND m_parentWnd; - CEvent m_eventStop; - CEvent m_eventDownloadStopped; - - P3D_object* m_p3dObject; - - // Set from fgcolor & bgcolor. - int _fgcolor_r, _fgcolor_b, _fgcolor_g; - int _bgcolor_r, _bgcolor_b, _bgcolor_g; - -protected: - PPInstance( ); - PPInstance( const PPInstance& ); - - int DownloadFile( const std::string& from, const std::string& to ); - int CopyFile( const std::string& from, const std::string& to ); - - bool read_contents_file(const std::string &contents_filename, bool fresh_download); - void find_host(TiXmlElement *xcontents); - void read_xhost(TiXmlElement *xhost); - void add_mirror(std::string mirror_url); - void choose_random_mirrors(std::vector &result, int num_mirrors); - - bool HandleRequest( P3D_request *request ); - static void HandleRequestGetUrl( void *data ); - - static int compare_seq(const std::string &seq_a, const std::string &seq_b); - static int compare_seq_int(const char *&num_a, const char *&num_b); - - void set_failed(); - - std::string lookup_token(const std::string &keyword) const; - bool has_token(const std::string &keyword) const; - - P3D_instance* m_p3dInstance; - CP3DActiveXCtrl& m_parentCtrl; - PPLogger m_logger; - - bool m_isInit; - bool m_pluginLoaded; - CMutex _load_mutex; - - std::string _download_url_prefix; - typedef std::vector Mirrors; - Mirrors _mirrors; - std::string _coreapi_set_ver; - FileSpec _coreapi_dll; - time_t _contents_expiration; - bool _failed; - - P3D_token *_tokens; - int _num_tokens; - - std::string m_rootDir; - std::wstring m_rootDir_w; - - class ThreadedRequestData { - public: - PPInstance *_self; - P3D_request *_request; - std::string _host_url; - }; -}; diff --git a/direct/src/plugin_activex/PPInterface.cpp b/direct/src/plugin_activex/PPInterface.cpp deleted file mode 100644 index 01eee14d08..0000000000 --- a/direct/src/plugin_activex/PPInterface.cpp +++ /dev/null @@ -1,595 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPInterface.cpp - * @author atrestman - * @date 2009-09-14 - */ - -#include "stdafx.h" - -#include "PPInterface.h" -#include "PPPandaObject.h" -#include "PPBrowserObject.h" -#include "PPInstance.h" -#include "P3DActiveXCtrl.h" - -#include -#include - -PPInterface::PPInterface( ) -{ -} - -PPInterface::~PPInterface( ) -{ -} - - -HRESULT PPInterface::GetIdOfName( IDispatch* pDisp, CString& ptName, DISPID* dispID ) -{ - if( !pDisp ) - { - return E_FAIL; - } - OLECHAR* pElementName = ptName.AllocSysString(); - - HRESULT hr = pDisp->GetIDsOfNames(IID_NULL, &pElementName, 1, LOCALE_USER_DEFAULT, dispID); - SysFreeString( pElementName ); - - return hr; -} - -HRESULT PPInterface::Invoke(int nType, IDispatch* pDisp, CString& ptName, VARIANT* pvResult, int cArgs, VARIANT* params) -{ - if( !pDisp ) - { - return E_FAIL; - } - - DISPPARAMS dp = { NULL, NULL, 0, 0 }; - DISPID dispidNamed = DISPID_PROPERTYPUT; - DISPID dispID; - CComPtr pDispEx; - - HRESULT hr = GetIdOfName( pDisp, ptName, &dispID ); - if ( DISP_E_UNKNOWNNAME == hr ) - { - hr = pDisp->QueryInterface( IID_IDispatchEx, ( void** )&pDispEx ); - if ( SUCCEEDED( hr ) && pDispEx ) - { - OLECHAR* pElementName = ptName.AllocSysString(); - hr = pDispEx->GetDispID( pElementName, fdexNameEnsure, &dispID ); - SysFreeString( pElementName ); - } - } - if ( FAILED( hr ) ) - { - return hr; - } - - // Allocate memory for arguments... - VARIANT *pArgs = new VARIANT[ cArgs + 1 ]; - - // Reversing the arguments!!! NOTE: http:msdn.microsoft.comen- - // uslibrarycc237569(PROT.10).aspx pDispParams: MUST point to a DISPPARAMS - // structure that defines the arguments passed to the method. Arguments - // MUST be stored in pDispParams->rgvarg in reverse order, so that the - // first argument is the one with the highest index in the array. Byref - // arguments MUST be marked in this array as VT_EMPTY entries, and stored - // in rgVarRef instead. - - for( int i = 0; i < cArgs; i++ ) - { - pArgs[i] = params[ cArgs - 1 - i ]; - } - - // Build DISPPARAMS - dp.cArgs = cArgs; - dp.rgvarg = pArgs; - - // Handle special-case for property-puts! - if( nType & DISPATCH_PROPERTYPUT || nType & DISPATCH_PROPERTYPUTREF ) - { - dp.cNamedArgs = 1; - dp.rgdispidNamedArgs = &dispidNamed; - } - - // Make the call! - if ( pDispEx ) - { - hr = pDispEx->InvokeEx(dispID, LOCALE_USER_DEFAULT, - nType, &dp, pvResult, NULL, NULL); - if ( FAILED( hr ) ) - { - pDispEx->DeleteMemberByDispID( dispID ); - } - } - else - { - hr = pDisp->Invoke( dispID, IID_NULL, LOCALE_USER_DEFAULT, - nType, &dp, pvResult, NULL, NULL ); - } - delete [] pArgs; - - return hr; -} - -HRESULT PPInterface::GetHtmlDocDispatch( CComPtr& pDispScript ) -{ - HRESULT hr = S_OK; - - CComPtr pOleClientSite = GetClientSte( ); - if (pOleClientSite == NULL) { - return E_FAIL; - } - - CComPtr pOleContainer; - hr = pOleClientSite->GetContainer(& pOleContainer ); - ASSERT( SUCCEEDED( hr ) && pOleContainer ); - - CComPtr pHtmlDoc; - hr = pOleContainer->QueryInterface( IID_IHTMLDocument, ( void** )&pHtmlDoc ); - ASSERT( SUCCEEDED( hr ) && pHtmlDoc ); - - // Get the script object (this returns the script object, NOT the script - // element(s) that the get_scripts method does). - hr = pHtmlDoc->get_Script( &pDispScript ); - ASSERT( SUCCEEDED( hr ) && pDispScript ); - - CComPtr pDispExScript; - hr = pDispScript->QueryInterface( IID_IDispatchEx, ( void** )&pDispExScript ); - ASSERT( SUCCEEDED( hr ) && pDispExScript ); - - pDispScript = pDispExScript; - - CComPtr pTypeInfo; - hr = pDispScript->GetTypeInfo( 0, 0, &pTypeInfo ); - - return hr; -} - -HRESULT PPInterface::HasProperty( CComPtr& pDispatch, CString& name ) -{ - HRESULT hr = S_OK; - if ( pDispatch == NULL ) - { - hr = GetHtmlDocDispatch( pDispatch ); - } - - DISPID dispID; - if ( SUCCEEDED( hr ) ) - { - hr = GetIdOfName( pDispatch, name, &dispID ); - } - return hr; -} - -HRESULT PPInterface::CallMethod( CComPtr& pDispatch, CString& name, COleVariant& varResult, int numParams, COleVariant* params ) -{ - HRESULT hr = S_OK; - if ( pDispatch == NULL ) - { - hr = GetHtmlDocDispatch( pDispatch ); - } - if ( SUCCEEDED( hr ) ) - { - hr = Invoke( DISPATCH_METHOD, pDispatch, name, &varResult, numParams, params ); - } - return hr; -} - -HRESULT PPInterface::GetProperty( CComPtr& pDispatch, CString& name, COleVariant& varResult ) -{ - HRESULT hr = S_OK; - if ( pDispatch == NULL ) - { - hr = GetHtmlDocDispatch( pDispatch ); - } - if ( SUCCEEDED( hr ) ) - { - hr = Invoke( DISPATCH_PROPERTYGET, pDispatch, name, &varResult, 0, NULL ); - } - return hr; -} - -HRESULT PPInterface::SetProperty( CComPtr& pDispatch, CString& name, COleVariant& varValue, COleVariant& varResult ) -{ - HRESULT hr = S_OK; - if ( pDispatch == NULL ) - { - hr = GetHtmlDocDispatch( pDispatch ); - } - if ( SUCCEEDED( hr ) ) - { - if ( varValue.vt == VT_DISPATCH ) - { - hr = Invoke( DISPATCH_PROPERTYPUTREF, pDispatch, name, &varResult, 1, &varValue ); - } - else - { - hr = Invoke( DISPATCH_PROPERTYPUT, pDispatch, name, &varResult, 1, &varValue ); - } - } - return hr; -} - -HRESULT PPInterface::EvalExpression( CComPtr& pDispatch, CString& expression, COleVariant& varResult ) -{ - HRESULT hr = S_OK; - if ( pDispatch == NULL ) - { - hr = GetHtmlDocDispatch( pDispatch ); - } - if ( SUCCEEDED( hr ) ) - { - COleVariant param( expression ); - hr = Invoke( DISPATCH_METHOD, pDispatch, CString("eval"), &varResult, 1, ¶m ); - } - return hr; -} - -HRESULT PPInterface::P3DHasMethod( P3D_object* p3dObject, CString& name, bool& result ) -{ - if ( !p3dObject ) - { - p3dObject = GetP3DObject( ); - if ( !p3dObject ) - { - return E_FAIL; - } - } - result = P3D_OBJECT_HAS_METHOD( p3dObject, name ); - return S_OK; -} - -HRESULT PPInterface::P3DCallMethod( P3D_object* p3dObject, CString& name, DISPPARAMS FAR* pdispparams, VARIANT FAR* varResult ) -{ - if ( !p3dObject ) - { - p3dObject = GetP3DObject( ); - if ( !p3dObject ) - { - return E_FAIL; - } - } - P3D_object** params = new P3D_object*[pdispparams->cArgs]; - for ( UINT i = 0; i < pdispparams->cArgs; i++ ) - { - // Reversing the arguments!!! http:msdn.microsoft.comen- - // uslibrarycc237569(PROT.10).aspx pDispParams: MUST point to a - // DISPPARAMS structure that defines the arguments passed to the - // method. Arguments MUST be stored in pDispParams->rgvarg in reverse - // order, so that the first argument is the one with the highest index - // in the array. Byref arguments MUST be marked in this array as - // VT_EMPTY entries, and stored in rgVarRef instead. - - COleVariant vaArg( pdispparams->rgvarg[pdispparams->cArgs - 1 - i] ); - params[i] = variant_to_p3dobj( &vaArg ); - } - bool needResult( false ); - if ( varResult ) - { - needResult = true; - } - P3D_object* p3dObjectResult = P3D_OBJECT_CALL( p3dObject, name, needResult, params, pdispparams->cArgs ); - if ( p3dObjectResult ) - { - p3dobj_to_variant( varResult, p3dObjectResult ); - P3D_OBJECT_DECREF( p3dObjectResult ); - } - for ( UINT i = 0; i < pdispparams->cArgs; i++ ) - { - P3D_OBJECT_DECREF( params[i] ); - } - delete [] params; - - return S_OK; -} - -HRESULT PPInterface::P3DGetProperty( P3D_object* p3dObject, CString& name, VARIANT FAR* varResult ) -{ - if ( !p3dObject ) - { - p3dObject = GetP3DObject( ); - if ( !p3dObject ) - { - return E_FAIL; - } - } - P3D_object* p3dObjectResult = P3D_OBJECT_GET_PROPERTY( p3dObject, name ); - if ( p3dObjectResult ) - { - p3dobj_to_variant( varResult, p3dObjectResult ); - P3D_OBJECT_DECREF( p3dObjectResult ); - } - return S_OK; -} - -HRESULT PPInterface::P3DSetProperty( P3D_object* p3dObject, CString& name, DISPPARAMS FAR* pdispparams, bool& result ) -{ - if ( !p3dObject ) - { - p3dObject = GetP3DObject( ); - if ( !p3dObject ) - { - return E_FAIL; - } - } - COleVariant vaArg( pdispparams->rgvarg ); - P3D_object* param = variant_to_p3dobj( &vaArg ); - result = P3D_OBJECT_SET_PROPERTY( p3dObject, name, true, param ); - P3D_OBJECT_DECREF( param ); - - if (!result) { - return E_FAIL; - } - - return S_OK; -} - -void PPInterface::p3dobj_to_variant(VARIANT* result, P3D_object* object) -{ - if ( !result ) - { - return; - } - switch ( P3D_OBJECT_GET_TYPE( object ) ) - { - case P3D_OT_undefined: - { - result->vt = VT_EMPTY; - break; - } - case P3D_OT_none: - { - result->vt = VT_EMPTY; - break; - } - case P3D_OT_bool: - { - result->vt = VT_BOOL; - result->bVal = P3D_OBJECT_GET_BOOL( object ); - break; - } - case P3D_OT_int: - { - result->vt = VT_I4; - result->lVal = P3D_OBJECT_GET_INT( object ); - break; - } - case P3D_OT_float: - { - result->vt = VT_R4; - result->fltVal = P3D_OBJECT_GET_FLOAT( object ); - break; - } - case P3D_OT_string: - { - int size = P3D_OBJECT_GET_STRING(object, NULL, 0); - char *buffer = new char[size]; - P3D_OBJECT_GET_STRING(object, buffer, size); - - int wsize = MultiByteToWideChar(CP_UTF8, 0, buffer, size, NULL, 0); - WCHAR *wbuffer = new WCHAR[wsize + 1]; - MultiByteToWideChar(CP_UTF8, 0, buffer, size, wbuffer, wsize); - wbuffer[wsize] = 0; - - result->vt = VT_BSTR; - result->bstrVal = SysAllocString(wbuffer); - - delete[] buffer; - delete[] wbuffer; - break; - } - case P3D_OT_object: - { - PPBrowserObject *ppBrowserObject = (PPBrowserObject *)object; - PPandaObject* ppPandaObject = new PPandaObject( this, ppBrowserObject ); - result->vt = VT_DISPATCH; - result->pdispVal = ppPandaObject; - break; - } - default: - { - result->vt = VT_EMPTY; - break; - } - } -} - -P3D_object* PPInterface::variant_to_p3dobj(COleVariant* variant) -{ - if ( !variant ) - { - return P3D_new_none_object_ptr(); - } - switch( variant->vt ) - { - case VT_VOID: - { - return P3D_new_undefined_object_ptr(); - break; - } - case VT_EMPTY: - { - // return P3D_new_none_object_ptr(); A.T. Panda really expect - // undefined object here - return P3D_new_undefined_object_ptr(); - break; - } - case VT_BOOL: - { - return P3D_new_bool_object_ptr( variant->bVal ); - break; - } - case VT_I2: - { - return P3D_new_int_object_ptr( variant->iVal ); - break; - } - case VT_I4: - { - return P3D_new_int_object_ptr( variant->lVal ); - break; - } - case VT_I8: - { - return P3D_new_int_object_ptr( variant->llVal ); - break; - } - case VT_R4: - { - return P3D_new_float_object_ptr( variant->fltVal ); - break; - } - case VT_R8: - { - return P3D_new_float_object_ptr( variant->dblVal ); - break; - } - case VT_BSTR: - { - BSTR bstr = variant->bstrVal; - UINT blen = SysStringLen(bstr); - - int slen = WideCharToMultiByte(CP_UTF8, 0, bstr, blen, - 0, 0, NULL, NULL); - char *sbuffer = new char[slen]; - WideCharToMultiByte(CP_UTF8, 0, bstr, blen, - sbuffer, slen, NULL, NULL); - - P3D_object *object = P3D_new_string_object_ptr(sbuffer, slen); - delete[] sbuffer; - return object; - break; - } - case VT_DISPATCH: - { - // The following commented-out code crashes IE7: - /* - CComPtr pDispObject( variant->pdispVal ); - CComPtr pTypeInfo; - HRESULT hr = pDispObject->GetTypeInfo( 0, 0, &pTypeInfo ); - if ( SUCCEEDED( hr ) && pTypeInfo ) - { - TYPEATTR* pTypeAttr; - hr = pTypeInfo->GetTypeAttr( &pTypeAttr ); - - pTypeInfo->ReleaseTypeAttr( pTypeAttr ); - } - */ - return new PPBrowserObject( this, variant->pdispVal ); - break; - } - default: - { - return P3D_new_undefined_object_ptr(); - break; - } - } -} - -CString PPInterface::get_repr(COleVariant& variant) -{ - std::strstream repr; - repr << "IDispatch"; - switch( variant.vt ) - { - case VT_VOID: - { - repr << ":VT_VOID"; - break; - } - case VT_EMPTY: - { - repr << ":VT_EMPTY"; - break; - } - case VT_BOOL: - { - repr << ":VT_BOOL:" << variant.bVal; - break; - } - case VT_I2: - { - repr << ":VT_I2:" << variant.iVal; - break; - } - case VT_I4: - { - repr << ":VT_I4:" << variant.lVal; - break; - } - case VT_I8: - { - repr << ":VT_I8:" << variant.llVal; - break; - } - case VT_R4: - { - repr << ":VT_R4:" << variant.fltVal; - break; - } - case VT_R8: - { - repr << ":VT_R8:" << variant.dblVal; - break; - } - case VT_BSTR: - { - CString str( variant ); - repr << ":VT_BSTR:" << str; - break; - } - case VT_DISPATCH: - { - if ( variant.pdispVal ) - { - CComPtr pDispObject( variant.pdispVal ); - CComPtr pTypeInfo; - HRESULT hr = pDispObject->GetTypeInfo( 0, 0, &pTypeInfo ); - if ( SUCCEEDED( hr ) && pTypeInfo ) - { - TYPEATTR* pTypeAttr; - hr = pTypeInfo->GetTypeAttr( &pTypeAttr ); - if ( pTypeAttr ) - { - OLECHAR szGuid[40] = { 0 }; - int nCount = ::StringFromGUID2( pTypeAttr->guid, szGuid, sizeof( szGuid ) ); - CString guid( szGuid, nCount ); - - repr << ":VT_DISPATCH:GUID:" << guid; - pTypeInfo->ReleaseTypeAttr( pTypeAttr ); - } - else - { - repr << ":VT_DISPATCH:" << variant.pdispVal; - } - } - else - { - repr << ":VT_DISPATCH:" << variant.pdispVal; - } - } - else - { - repr << ":UNKNOWN"; - } - - break; - } - default: - { - repr << ":UNKNOWN"; - break; - } - } - - return CString ( repr.str(), repr.pcount() ); -} diff --git a/direct/src/plugin_activex/PPInterface.h b/direct/src/plugin_activex/PPInterface.h deleted file mode 100644 index 0e516532a7..0000000000 --- a/direct/src/plugin_activex/PPInterface.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPInterface.h - * @author atrestman - * @date 2009-09-14 - */ - -#pragma once - -#include "p3d_plugin.h" - -class PPInterface -{ -public: - PPInterface(); - virtual ~PPInterface(); - - virtual P3D_object* GetP3DObject() = 0; - virtual IOleClientSite* GetClientSte() = 0; - - HRESULT HasProperty( CComPtr& pDispatch, CString& name ); - HRESULT CallMethod( CComPtr& pDispatch, CString& name, COleVariant& varResult, int num_params, COleVariant* params ); - HRESULT GetProperty( CComPtr& pDispatch, CString& name, COleVariant& varResult ); - HRESULT SetProperty( CComPtr& pDispatch, CString& name, COleVariant& varValue, COleVariant& varResult ); - HRESULT EvalExpression( CComPtr& pDispatch, CString& expression, COleVariant& varResult ); - - HRESULT P3DHasMethod( P3D_object* p3dObject, CString& name, bool& result ); - HRESULT P3DCallMethod( P3D_object* p3dObject, CString& name, DISPPARAMS FAR* pdispparams, VARIANT FAR* varResult ); - HRESULT P3DGetProperty( P3D_object* p3dObject, CString& name, VARIANT FAR* varResult ); - HRESULT P3DSetProperty( P3D_object* p3dObject, CString& name, DISPPARAMS FAR* pdispparams, bool& result ); - - void p3dobj_to_variant(VARIANT* result, P3D_object* object); - P3D_object* variant_to_p3dobj(COleVariant* variant); - CString get_repr(COleVariant& variant); - -private: - HRESULT GetIdOfName( IDispatch* pDisp, CString& ptName, DISPID* dispID ); - HRESULT Invoke( int nType, IDispatch* pDisp, CString& ptName, VARIANT* pvResult, int cArgs, VARIANT* params ); - HRESULT GetHtmlDocDispatch( CComPtr& pHTMLDocDispatch ); - -}; diff --git a/direct/src/plugin_activex/PPLogger.cpp b/direct/src/plugin_activex/PPLogger.cpp deleted file mode 100644 index a49f85829d..0000000000 --- a/direct/src/plugin_activex/PPLogger.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPLogger.cpp - * @author atrestman - * @date 2009-09-14 - */ - -#include "stdafx.h" - -#include "PPLogger.h" -#include "mkdir_complete.h" -#include "wstring_encode.h" - -#include - -std::ofstream PPLogger::m_logfile; -bool PPLogger::m_isOpen = false; - -PPLogger::PPLogger( ) -{ -} - -PPLogger::~PPLogger( ) -{ -} - -void PPLogger::Open( const std::string &rootDir ) -{ - if (!m_isOpen) { - // Note that this logfile name may not be specified at runtime. It must - // be compiled in if it is specified at all. - - std::string log_directory; - // Allow the developer to compile in the log directory. -#ifdef P3D_PLUGIN_LOG_DIRECTORY - if (log_directory.empty()) { - log_directory = P3D_PLUGIN_LOG_DIRECTORY; - } -#endif - - // Failing that, we write logfiles to Panda3Dlog. - if (log_directory.empty()) { - log_directory = rootDir + "/log"; - } - mkdir_complete(log_directory, std::cerr); - - // Ensure that the log directory ends with a slash. - if (!log_directory.empty() && log_directory[log_directory.size() - 1] != '/') { -#ifdef _WIN32 - if (log_directory[log_directory.size() - 1] != '\\') -#endif - log_directory += "/"; - } - - // Construct the logfile pathname. - - std::string log_basename; -#ifdef P3D_PLUGIN_LOG_BASENAME1 - if (log_basename.empty()) { - log_basename = P3D_PLUGIN_LOG_BASENAME1; - } -#endif - if (log_basename.empty()) { - log_basename = "p3dplugin"; - } - - if (!log_basename.empty()) { - std::string log_pathname = log_directory; - log_pathname += log_basename; - log_pathname += ".log"; - - m_logfile.close(); - m_logfile.clear(); - wstring log_pathname_w; - string_to_wstring(log_pathname_w, log_pathname); - m_logfile.open(log_pathname_w.c_str(), std::ios::out | std::ios::trunc); - m_logfile.setf(std::ios::unitbuf); - } - - // If we didn't have a logfile name compiled in, we throw away log output - // by the simple expedient of never actually opening the ofstream. - m_isOpen = true; - } -} diff --git a/direct/src/plugin_activex/PPLogger.h b/direct/src/plugin_activex/PPLogger.h deleted file mode 100644 index 2cc49de77a..0000000000 --- a/direct/src/plugin_activex/PPLogger.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPLogger.h - * @author atrestman - * @date 2009-09-14 - */ - -#pragma once - -#include -#include -#include - -#define P3D_DEFAULT_PLUGIN_LOG_FILENAME "p3dActiveXlog.log" - -class PPLogger -{ -public: - PPLogger( ); - virtual ~PPLogger( ); - - void Open( const std::string &rootDir ); - static std::ofstream& Log( ) { return m_logfile; } - -protected: - static bool m_isOpen; - static std::ofstream m_logfile; -}; - -#define nout PPLogger::Log( ) diff --git a/direct/src/plugin_activex/PPPandaObject.cpp b/direct/src/plugin_activex/PPPandaObject.cpp deleted file mode 100644 index e65d6d7974..0000000000 --- a/direct/src/plugin_activex/PPPandaObject.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPPandaObject.cpp - * @author atrestman - * @date 2009-09-14 - */ - -#include "stdafx.h" -#include "PPPandaObject.h" -#include "PPInstance.h" -#include "load_plugin.h" - -PPandaObject::PPandaObject( PPInterface* interfac, P3D_object* p3dObject ) : - m_interface( interfac ), m_p3dObject( p3dObject ), m_refs( 0 ), m_ptinfo( NULL ) -{ - if ( m_p3dObject ) - { - P3D_OBJECT_INCREF( m_p3dObject ); - } - AddRef(); - PPInstance::ref_plugin(); -} - -PPandaObject::~PPandaObject() -{ - if( m_ptinfo ) - { - m_ptinfo->Release(); - } - - // Clean up the p3d_object, but only if we haven't already unloaded the - // plugin. - if ( m_p3dObject ) - { - P3D_OBJECT_DECREF( m_p3dObject ); - } - - PPInstance::unref_plugin(); -} - -// Dispatch Methods - -STDMETHODIMP_(unsigned long) PPandaObject::AddRef() -{ - return ++m_refs; -} - -STDMETHODIMP_(unsigned long) PPandaObject::Release() -{ - --m_refs; - if( m_refs <= 0 ) - { - delete this; - return 0; - } - return m_refs; -} - -STDMETHODIMP PPandaObject::QueryInterface(REFIID riid, void FAR* FAR* ppv) -{ - if(!IsEqualIID(riid, IID_IUnknown)) - { - if(!IsEqualIID(riid, IID_IDispatch)) - { - *ppv = NULL; - return E_NOINTERFACE; - } - } - *ppv = this; - AddRef(); - return NOERROR; -} - -STDMETHODIMP PPandaObject::GetIDsOfNames( - REFIID riid, - OLECHAR FAR* FAR* rgszNames, - unsigned int cNames, - LCID lcid, - DISPID FAR* rgdispid) -{ - UNUSED(lcid); - - if(!IsEqualIID(riid, IID_NULL)) - { - return DISP_E_UNKNOWNINTERFACE; - } - - for ( UINT i = 0; i < cNames; i++ ) - { - UINT j = 0; - for ( j = 0; j < m_idsOfNames.size(); j++ ) - { - if ( m_idsOfNames[j] == rgszNames[i] ) - { - rgdispid[i] = j; - break; - } - } - if ( j >= m_idsOfNames.size() ) - { - CString name( rgszNames[i] ); - m_idsOfNames.push_back( name ); - rgdispid[i] = j; - } - } - return S_OK; -} - -STDMETHODIMP PPandaObject::GetTypeInfo(unsigned int iTInfo, LCID lcid, ITypeInfo FAR* FAR* ppTInfo) -{ - UNUSED(lcid); - - if ( ppTInfo == NULL ) - { - return E_INVALIDARG; - } - *ppTInfo = NULL; - - if( iTInfo != 0 ) - { - return DISP_E_BADINDEX; - } - if ( m_ptinfo == NULL ) - { - return E_FAIL; - } - m_ptinfo->AddRef(); // AddRef and return pointer to cached - // typeinfo for this object. - *ppTInfo = m_ptinfo; - - return NOERROR; -} - -STDMETHODIMP PPandaObject::GetTypeInfoCount(unsigned int FAR* pctinfo) -{ - // This object has a single *introduced* interface - *pctinfo = 1; - - return NOERROR; -} - -STDMETHODIMP PPandaObject::Invoke( - DISPID dispidMember, - REFIID riid, - LCID lcid, - unsigned short wFlags, - DISPPARAMS FAR* pdispparams, - VARIANT FAR* pvarResult, - EXCEPINFO FAR* pexcepinfo, - unsigned int FAR* puArgErr) -{ - UNUSED(lcid); - HRESULT hr( S_OK ); - - - if(!IsEqualIID(riid, IID_NULL)) - { - return DISP_E_UNKNOWNINTERFACE; - } - if ( !m_interface ) - { - return E_FAIL; - } - - if ( dispidMember >= m_idsOfNames.size( ) ) - { - return E_FAIL; - } - - CString& name( m_idsOfNames[dispidMember] ); - - switch ( wFlags ) - { - case ( DISPATCH_METHOD ): - case ( DISPATCH_METHOD | DISPATCH_PROPERTYGET ): - { - // NOTE: http:msdn.microsoft.comen-uslibraryms221479.aspx The - // member is invoked as a method. If a property has the same - // name, both the DISPATCH_METHOD and the DISPATCH_PROPERTYGET - // flag may be set. - - bool hasMethod( false ); - hr = m_interface->P3DHasMethod( m_p3dObject, name, hasMethod ); - if ( SUCCEEDED( hr ) && hasMethod ) - { - hr = m_interface->P3DCallMethod( m_p3dObject, name, pdispparams, pvarResult ); - } - } - break; - case ( DISPATCH_PROPERTYGET ): - { - hr = m_interface->P3DGetProperty( m_p3dObject, name, pvarResult ); - } - break; - case ( DISPATCH_PROPERTYPUT ): - { - bool result( false ); - hr = m_interface->P3DSetProperty( m_p3dObject, name, pdispparams, result ); - } - break; - } - return hr; -} diff --git a/direct/src/plugin_activex/PPPandaObject.h b/direct/src/plugin_activex/PPPandaObject.h deleted file mode 100644 index ea64b83e16..0000000000 --- a/direct/src/plugin_activex/PPPandaObject.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file PPPandaObject.h - * @author atrestman - * @date 2009-09-14 - */ - -#pragma once - -#include -#include - -#include "p3d_plugin.h" -#include "PPInterface.h" - -class PPandaObject : public IDispatch -{ -public: - - // Methods - PPandaObject( PPInterface* interfac, P3D_object* p3dObject ); - virtual ~PPandaObject(); - - // IUnknown methods - STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj); - STDMETHOD_(unsigned long, AddRef)(void); - STDMETHOD_(unsigned long, Release)(void); - - // IDispatch methods - STDMETHOD(GetTypeInfoCount)(unsigned int FAR* pcTypeInfo); - - STDMETHOD(GetTypeInfo)( - unsigned int iTypeInfo, - LCID lcid, - ITypeInfo FAR* FAR* ppTypeInfo); - - STDMETHOD(GetIDsOfNames)( - REFIID riid, - OLECHAR FAR* FAR* rgszNames, - unsigned int cNames, - LCID lcid, - DISPID FAR* rgdispid); - - STDMETHOD(Invoke)( - DISPID dispidMember, - REFIID riid, - LCID lcid, - unsigned short wFlags, - DISPPARAMS FAR* pdispparams, - VARIANT FAR* pvarResult, - EXCEPINFO FAR* pexcepinfo, - unsigned int FAR* puArgErr); - -private: - PPandaObject(); - - // Props - Dispatch related - unsigned long m_refs; - ITypeInfo FAR* m_ptinfo; - std::vector m_idsOfNames; - PPInterface* m_interface; - P3D_object *m_p3dObject; -}; diff --git a/direct/src/plugin_activex/ReadMe.txt b/direct/src/plugin_activex/ReadMe.txt deleted file mode 100644 index c40fee8cfc..0000000000 --- a/direct/src/plugin_activex/ReadMe.txt +++ /dev/null @@ -1,79 +0,0 @@ -======================================================================== - ACTIVEX CONTROL DLL : MyActiveX Project Overview -======================================================================== - -ControlWizard has created this project for your MyActiveX ActiveX Control -DLL, which contains 1 control. - -This skeleton project not only demonstrates the basics of writing an -ActiveX Control, but is also a starting point for writing the specific -features of your control. - -This file contains a summary of what you will find in each of the files -that make up your MyActiveX ActiveX Control DLL. - -P3DActiveX.vcproj - This is the main project file for VC++ projects generated using an - Application Wizard. It contains information about the version of - Visual C++ that generated the file, and information about the platforms, - configurations, and project features selected with the Application Wizard. - -P3DActiveX.h - This is the main include file for the ActiveX Control DLL. It - includes other project-specific includes such as resource.h. - -P3DActiveX.cpp - This is the main source file that contains code for DLL initialization, - termination and other bookkeeping. - -P3DActiveX.rc - This is a listing of the Microsoft Windows resources that the project - uses. This file can be directly edited with the Visual C++ resource - editor. - -P3DActiveX.def - This file contains information about the ActiveX Control DLL that - must be provided to run with Microsoft Windows. - -P3DActiveX.idl - This file contains the Object Description Language source code for the - type library of your control. - -///////////////////////////////////////////////////////////////////////////// -CP3DActiveXCtrl control: - -P3DActiveXCtrl.h - This file contains the declaration of the CP3DActiveXCtrl C++ class. - -P3DActiveXCtrl.cpp - This file contains the implementation of the CP3DActiveXCtrl C++ class. - -P3DActiveXPropPage.h - This file contains the declaration of the CP3DActiveXPropPage C++ class. - -P3DActiveXPropPage.cpp - This file contains the implementation of the CP3DActiveXPropPage C++ class. - -CP3DActiveXCtrl.bmp - This file contains a bitmap that a container will use to represent the - CP3DActiveXCtrl control when it appears on a tool palette. This bitmap - is included by the main resource file P3DActiveX.rc. - -///////////////////////////////////////////////////////////////////////////// -Other standard files: - -stdafx.h, stdafx.cpp - These files are used to build a precompiled header (PCH) file - named P3DActiveX.pch and a precompiled types (PCT) file named stdafx.obj. - -resource.h - This is the standard header file, which defines new resource IDs. - The Visual C++ resource editor reads and updates this file. - -///////////////////////////////////////////////////////////////////////////// -Other notes: - -ControlWizard uses "TODO:" to indicate parts of the source code you -should add to or customize. - -///////////////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin_activex/p3dactivex_composite1.cxx b/direct/src/plugin_activex/p3dactivex_composite1.cxx deleted file mode 100644 index c18e6d9bf4..0000000000 --- a/direct/src/plugin_activex/p3dactivex_composite1.cxx +++ /dev/null @@ -1,10 +0,0 @@ -#include "P3DActiveX.cpp" -#include "P3DActiveXCtrl.cpp" -#include "P3DActiveXPropPage.cpp" -#include "PPBrowserObject.cpp" -#include "PPDownloadCallback.cpp" -#include "PPDownloadRequest.cpp" -#include "PPInstance.cpp" -#include "PPInterface.cpp" -#include "PPLogger.cpp" -#include "PPPandaObject.cpp" diff --git a/direct/src/plugin_activex/resource.h b/direct/src/plugin_activex/resource.h deleted file mode 100644 index cb7e075db2..0000000000 --- a/direct/src/plugin_activex/resource.h +++ /dev/null @@ -1,27 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by P3DActiveX.rc -// -#define IDS_P3DACTIVEX 1 -#define IDB_P3DACTIVEX 1 -#define IDS_P3DACTIVEX_PPG 2 -#define IDS_P3DACTIVEX_PPG_CAPTION 200 -#define IDD_PROPPAGE_P3DACTIVEX 200 -#define IDR_GIF1 201 -#define IDC_PANDALAUNCH_BUTTON 202 -#define IDC_PANDAOUT 203 -#define IDC_EDIT1 204 -#define IDC_EDIT2 205 -#define IDC_CLEAR 206 -#define IDC_PANDACONTAINER 207 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 203 -#define _APS_NEXT_COMMAND_VALUE 32768 -#define _APS_NEXT_CONTROL_VALUE 208 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/direct/src/plugin_activex/stdafx.cpp b/direct/src/plugin_activex/stdafx.cpp deleted file mode 100644 index 40ffb29d93..0000000000 --- a/direct/src/plugin_activex/stdafx.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// stdafx.cpp : source file that includes just the standard includes -// P3DActiveX.pch will be the pre-compiled header -// stdafx.obj will contain the pre-compiled type information - -#include "stdafx.h" diff --git a/direct/src/plugin_activex/stdafx.h b/direct/src/plugin_activex/stdafx.h deleted file mode 100644 index a651a9c0f4..0000000000 --- a/direct/src/plugin_activex/stdafx.h +++ /dev/null @@ -1,41 +0,0 @@ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, -// but are changed infrequently - -#pragma once - -#ifndef VC_EXTRALEAN -#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers -#endif - -// Modify the following defines if you have to target a platform prior to the ones specified below. -// Refer to MSDN for the latest info on corresponding values for different platforms. -#ifndef WINVER // Allow use of features specific to Windows 95 and Windows NT 4 or later. -#define WINVER 0x0501 // Change this to the appropriate value to target Windows XP or later. -#endif - -#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. -#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target Windows 2000 or later. -#endif - -#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows XP or later. -#define _WIN32_WINDOWS 0x0501 // Change this to the appropriate value to target Windows Me or later. -#endif - -#ifndef _WIN32_IE // Allow use of features specific to IE 4.0 or later. -#define _WIN32_IE 0x0500 // Change this to the appropriate value to target IE 5.0 or later. -#endif - -#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit - -#include // MFC support for ActiveX Controls -#include // MFC extensions -#include // MFC support for Internet Explorer 4 Comon Controls -#ifndef _AFX_NO_AFXCMN_SUPPORT -#include // MFC support for Windows Common Controls -#endif // _AFX_NO_AFXCMN_SUPPORT - -#include - -#include - diff --git a/direct/src/plugin_installer/FileAssociation.nsh b/direct/src/plugin_installer/FileAssociation.nsh deleted file mode 100644 index 71a9162efc..0000000000 --- a/direct/src/plugin_installer/FileAssociation.nsh +++ /dev/null @@ -1,190 +0,0 @@ -/* -_____________________________________________________________________________ - - File Association -_____________________________________________________________________________ - - Based on code taken from http://nsis.sourceforge.net/File_Association - - Usage in script: - 1. !include "FileAssociation.nsh" - 2. [Section|Function] - ${FileAssociationFunction} "Param1" "Param2" "..." $var - [SectionEnd|FunctionEnd] - - FileAssociationFunction=[RegisterExtension|UnRegisterExtension] - -_____________________________________________________________________________ - - ${RegisterExtension} "[executable]" "[extension]" "[description]" - -"[executable]" ; executable which opens the file format - ; -"[extension]" ; extension, which represents the file format to open - ; -"[description]" ; description for the extension. This will be display in Windows Explorer. - ; - - - ${UnRegisterExtension} "[extension]" "[description]" - -"[extension]" ; extension, which represents the file format to open - ; -"[description]" ; description for the extension. This will be display in Windows Explorer. - ; - -_____________________________________________________________________________ - - Macros -_____________________________________________________________________________ - - Change log window verbosity (default: 3=no script) - - Example: - !include "FileAssociation.nsh" - !insertmacro RegisterExtension - ${FileAssociation_VERBOSE} 4 # all verbosity - !insertmacro UnRegisterExtension - ${FileAssociation_VERBOSE} 3 # no script -*/ - - -!ifndef FileAssociation_INCLUDED -!define FileAssociation_INCLUDED - -!include Util.nsh - -!verbose push -!verbose 3 -!ifndef _FileAssociation_VERBOSE - !define _FileAssociation_VERBOSE 3 -!endif -!verbose ${_FileAssociation_VERBOSE} -!define FileAssociation_VERBOSE `!insertmacro FileAssociation_VERBOSE` -!verbose pop - -!macro FileAssociation_VERBOSE _VERBOSE - !verbose push - !verbose 3 - !undef _FileAssociation_VERBOSE - !define _FileAssociation_VERBOSE ${_VERBOSE} - !verbose pop -!macroend - - - -!macro RegisterExtensionCall _EXECUTABLE _EXTENSION _DESCRIPTION - !verbose push - !verbose ${_FileAssociation_VERBOSE} - Push `${_DESCRIPTION}` - Push `${_EXTENSION}` - Push `${_EXECUTABLE}` - ${CallArtificialFunction} RegisterExtension_ - !verbose pop -!macroend - -!macro UnRegisterExtensionCall _EXTENSION _DESCRIPTION - !verbose push - !verbose ${_FileAssociation_VERBOSE} - Push `${_EXTENSION}` - Push `${_DESCRIPTION}` - ${CallArtificialFunction} UnRegisterExtension_ - !verbose pop -!macroend - - - -!define RegisterExtension `!insertmacro RegisterExtensionCall` -!define un.RegisterExtension `!insertmacro RegisterExtensionCall` - -!macro RegisterExtension -!macroend - -!macro un.RegisterExtension -!macroend - -!macro RegisterExtension_ - !verbose push - !verbose ${_FileAssociation_VERBOSE} - - Exch $R2 ;exe - Exch - Exch $R1 ;ext - Exch - Exch 2 - Exch $R0 ;desc - Exch 2 - Push $0 - Push $1 - - ReadRegStr $1 HKCR $R1 "" ; read current file association - StrCmp "$1" "" NoBackup ; is it empty - StrCmp "$1" "$R0" NoBackup ; is it our own - WriteRegStr HKCR $R1 "backup_val" "$1" ; backup current value -NoBackup: - WriteRegStr HKCR $R1 "" "$R0" ; set our file association - - ReadRegStr $0 HKCR $R0 "" - StrCmp $0 "" 0 Skip - WriteRegStr HKCR "$R0" "" "$R0" - WriteRegStr HKCR "$R0\shell" "" "open" - WriteRegStr HKCR "$R0\DefaultIcon" "" "$R2,0" -Skip: - WriteRegStr HKCR "$R0\shell\open\command" "" '"$R2" "%1"' - WriteRegStr HKCR "$R0\shell\edit" "" "Edit $R0" - WriteRegStr HKCR "$R0\shell\edit\command" "" '"$R2" "%1"' - - Pop $1 - Pop $0 - Pop $R2 - Pop $R1 - Pop $R0 - - !verbose pop -!macroend - - - -!define UnRegisterExtension `!insertmacro UnRegisterExtensionCall` -!define un.UnRegisterExtension `!insertmacro UnRegisterExtensionCall` - -!macro UnRegisterExtension -!macroend - -!macro un.UnRegisterExtension -!macroend - -!macro UnRegisterExtension_ - !verbose push - !verbose ${_FileAssociation_VERBOSE} - - Exch $R1 ;desc - Exch - Exch $R0 ;ext - Exch - Push $0 - Push $1 - - ReadRegStr $1 HKCR $R0 "" - StrCmp $1 $R1 0 NoOwn ; only do this if we own it - ReadRegStr $1 HKCR $R0 "backup_val" - StrCmp $1 "" 0 Restore ; if backup="" then delete the whole key - DeleteRegKey HKCR $R0 - Goto NoOwn - -Restore: - WriteRegStr HKCR $R0 "" $1 - DeleteRegValue HKCR $R0 "backup_val" - DeleteRegKey HKCR $R1 ;Delete key with association name settings - -NoOwn: - - Pop $1 - Pop $0 - Pop $R1 - Pop $R0 - - !verbose pop -!macroend - -!endif # !FileAssociation_INCLUDED diff --git a/direct/src/plugin_installer/VersionInfo.vbs b/direct/src/plugin_installer/VersionInfo.vbs deleted file mode 100644 index ab813d764a..0000000000 --- a/direct/src/plugin_installer/VersionInfo.vbs +++ /dev/null @@ -1,4 +0,0 @@ -set args = WScript.Arguments -Set fso = CreateObject("Scripting.FileSystemObject") -WScript.Echo fso.GetFileVersion(args(0)) -Wscript.Quit diff --git a/direct/src/plugin_installer/make_installer.py b/direct/src/plugin_installer/make_installer.py deleted file mode 100755 index 9410f4fb90..0000000000 --- a/direct/src/plugin_installer/make_installer.py +++ /dev/null @@ -1,601 +0,0 @@ -#! /usr/bin/env python - -import os -import sys -import shutil -import platform -import tempfile -from optparse import OptionParser -import subprocess - -try: - from hashlib import sha1 as sha -except ImportError: - from sha import sha - -usage = """ -This command creates a graphical installer for the -Panda3D plugin and runtime environment. - - %prog [opts]""" - -parser = OptionParser(usage = usage) -parser.add_option('-n', '--short', dest = 'short_name', - help = 'The product short name', - default = 'Panda3D') -parser.add_option('-N', '--long', dest = 'long_name', - help = 'The product long name', - default = 'Panda3D Game Engine') -parser.add_option('-v', '--version', dest = 'version', - help = 'The product version', - default = None) -parser.add_option('-p', '--publisher', dest = 'publisher', - help = 'The name of the publisher', - default = 'Carnegie Mellon Entertainment Technology Center') -parser.add_option('', '--install', dest = 'install_dir', - help = "The install directory on the user's machine (Windows only)", - default = '$PROGRAMFILES\\Panda3D') -parser.add_option('-l', '--license', dest = 'license', - help = 'A file containing the license or EULA text', - default = None) -parser.add_option('-w', '--website', dest = 'website', - help = 'The product website', - default = 'https://www.panda3d.org') -parser.add_option('', '--start', dest = 'start', - help = 'Specify this option to add a start menu', - action = 'store_true', default = False) -parser.add_option('', '--welcome_image', dest = 'welcome_image', - help = 'The image to display on the installer, 170x312 BMP', - default = None) -parser.add_option('', '--install_icon', dest = 'install_icon', - help = 'The icon to give to the installer', - default = None) -parser.add_option('', '--nsis', dest = 'nsis', - help = 'The path to the makensis executable', - default = None) -parser.add_option('', '--cab', dest = 'cab', - help = 'Generate an ActiveX CAB file (Windows only). If --spc and --pvk are not also specified, the CAB file will be unsigned.', - action = 'store_true', default = False) -parser.add_option('', '--spc', dest = 'spc', - help = 'Sign the CAB file generated by --cab with the indicated spc file (Windows only). You must also specify --pvk.', - default = None) -parser.add_option('', '--pvk', dest = 'pvk', - help = 'Specifies the private key to be used in conjuction with --spc to sign a CAB file (Windows only).', - default = None) -parser.add_option('', '--mssdk', dest = 'mssdk', - help = 'The path to the MS Platform SDK directory (Windows only). mssdk/bin should contain cabarc.exe and signcode.exe.', - default = None) -parser.add_option('', '--regview', dest = 'regview', - help = 'Which registry view to use, 64 or 32.', - default = None) - -(options, args) = parser.parse_args() - -this_dir = os.path.split(sys.argv[0])[0] - -assert options.version, "A version number must be supplied!" - -############################################################################## -# -# This Info.plist file is used only for the OSX 10.4 version of packagemaker. -# -############################################################################## - -Info_plist = """ - - - - CFBundleIdentifier - %(package_id)s - CFBundleShortVersionString - %(version)s - IFPkgFlagRelocatable - - IFPkgFlagAuthorizationAction - RootAuthorization - - -""" - -############################################################################## -# -# This Description.plist file is used only for the OSX 10.4 version of packagemaker. -# -############################################################################## - -Description_plist = """ - - - - IFPkgDescriptionDescription - - IFPkgDescriptionTitle - %(long_name)s - - -""" - - -############################################################################## -# -# Locate the relevant files. -# -############################################################################## - -def findExecutable(filename): - """ Searches for the named .exe or .dll file along the system PATH - and returns its full path if found, or None if not found. """ - - if sys.platform == "win32": - for p in os.defpath.split(";") + os.environ["PATH"].split(";"): - if os.path.isfile(os.path.join(p, filename)): - return os.path.join(p, filename) - else: - for p in os.defpath.split(":") + os.environ["PATH"].split(":"): - if os.path.isfile(os.path.join(p, filename)): - return os.path.join(p, filename) - return None - - -if not options.nsis: - makensis = findExecutable('makensis.exe') - if sys.platform == "win32": - if not makensis: - try: - import pandac - makensis = os.path.dirname(os.path.dirname(pandac.__file__)) - makensis = os.path.join(makensis, "nsis", "makensis.exe") - if not os.path.isfile(makensis): - makensis = None - except ImportError: pass - if not makensis: - thirdparty = os.environ.get("MAKEPANDA_THIRDPARTY", "thirdparty") - makensis = os.path.join(thirdparty, "win-nsis", "makensis.exe") - if not os.path.isfile(makensis): - makensis = None - options.nsis = makensis - -if not options.license: - try: - import pandac - options.license = os.path.join(os.path.dirname(os.path.dirname(pandac.__file__)), "LICENSE") - if not os.path.isfile(options.license): - options.license = None - except: pass - if not options.license: - options.license = os.path.join("doc", "LICENSE") - if not os.path.isfile(options.license): - options.license = None - if options.license: - options.license = os.path.abspath(options.license) - -if sys.platform == "win32" and not options.welcome_image: - filename = os.path.join('models', 'plugin_images', 'installer.bmp') - if not os.path.exists(filename): - sys.exit("Couldn't find installer.bmp for welcome_image.") - options.welcome_image = os.path.abspath(filename) - -def parseDependenciesWindows(tempFile): - """ Reads the indicated temporary file, the output from - dumpbin /dependents, to determine the list of dll's this - executable file depends on. """ - - lines = open(tempFile, 'rU').readlines() - li = 0 - while li < len(lines): - line = lines[li] - li += 1 - if line.find(' has the following dependencies') != -1: - break - - if li < len(lines): - line = lines[li] - if line.strip() == '': - # Skip a blank line. - li += 1 - - # Now we're finding filenames, until the next blank line. - filenames = [] - while li < len(lines): - line = lines[li] - li += 1 - line = line.strip() - if line == '': - # We're done. - return filenames - filenames.append(line) - - # Hmm, we ran out of data. Oh well. - if not filenames: - # Some parse error. - return None - - # At least we got some data. - return filenames - -def parseDependenciesUnix(tempFile): - """ Reads the indicated temporary file, the output from - otool -XL or ldd, to determine the list of dll's this - executable file depends on. """ - - lines = open(tempFile, 'rU').readlines() - filenames = [] - for l in lines: - filenames.append(l.strip().split(' ', 1)[0]) - return filenames - -def addDependencies(path, pathname, file, pluginDependencies, dependentFiles, required=True): - """ Checks the named file for DLL dependencies, and adds any - appropriate dependencies found into pluginDependencies and - dependentFiles. """ - - tempFile = tempfile.mktemp('.txt', 'p3d_') - if sys.platform == "darwin": - command = 'otool -XL "%s" >"%s"' - elif sys.platform == "win32": - command = 'dumpbin /dependents "%s" >"%s"' - else: - command = 'ldd "%s" >"%s"' - command = command % (pathname, tempFile) - try: - os.system(command) - except: - pass - filenames = None - - if os.path.isfile(tempFile): - if sys.platform == "win32": - filenames = parseDependenciesWindows(tempFile) - else: - filenames = parseDependenciesUnix(tempFile) - os.unlink(tempFile) - if filenames is None: - sys.exit("Unable to determine dependencies from %s" % (pathname)) - - # Look for MSVC[RP]*.dll, and MFC*.dll. These dependent files - # have to be included too. Also, any Panda-based libraries, or - # the Python DLL, should be included, in case panda3d.exe wasn't - # built static. The Panda-based libraries begin with "lib" and - # are all lowercase, or start with libpanda/libp3d. - for dfile in filenames: - dfilelower = dfile.lower() - if dfilelower not in dependentFiles: - if dfilelower.startswith('msvc') or \ - dfilelower.startswith('mfc') or \ - dfilelower.startswith('zlib1') or \ - (dfile.startswith('lib') and dfile == dfilelower) or \ - dfilelower.startswith('libpanda') or \ - dfilelower.startswith('libp3d') or \ - dfilelower.startswith('python'): - pathname = None - for pitem in path: - pathname = os.path.join(pitem, dfile) - if os.path.exists(pathname): - break - pathname = None - if not pathname: - if required: - sys.exit("Couldn't find %s." % (dfile)) - sys.stderr.write("Warning: couldn't find %s." % (dfile)) - continue - pathname = os.path.abspath(pathname) - dependentFiles[dfilelower] = pathname - - # Also recurse. - addDependencies(path, pathname, file, pluginDependencies, dependentFiles) - - if dfilelower in dependentFiles and dfilelower not in pluginDependencies[file]: - pluginDependencies[file].append(dfilelower) - -def getDllVersion(filename): - """ Returns the DLL version number in the indicated DLL, as a - string of comma-separated integers. Windows only. """ - - # This relies on the VBScript program in the same directory as - # this script. - thisdir = os.path.split(sys.argv[0])[0] - versionInfo = os.path.join(thisdir, 'VersionInfo.vbs') - tempfile = 'tversion.txt' - tempdata = open(tempfile, 'w+') - cmd = 'cscript //nologo "%s" "%s"' % (versionInfo, filename) - print(cmd) - result = subprocess.call(cmd, stdout = tempdata) - if result: - sys.exit(result) - - tempdata.seek(0) - data = tempdata.read() - tempdata.close() - os.unlink(tempfile) - return ','.join(data.strip().split('.')) - - -def makeCabFile(ocx, pluginDependencies): - """ Creates an ActiveX CAB file. Windows only. """ - - ocxFullpath = findExecutable(ocx) - cabFilename = os.path.splitext(ocx)[0] + '.cab' - - cabarc = 'cabarc' - signcode = 'signcode' - if options.mssdk: - cabarc = options.mssdk + '/bin/cabarc' - signcode = options.mssdk + '/bin/signcode' - - # First, we must generate an INF file. - infFile = 'temp.inf' - inf = open(infFile, 'w') - - info.write('[Add.Code]\n%s=%s\n%s=%s\n' % (infFile, infFile, ocx, ocx)) - dependencies = pluginDependencies[ocx] - for filename in dependencies: - inf.write('%s=%s\n' % (filename, filename)) - inf.write('\n[%s]\nfile=thiscab\n' % (infFile)) - inf.write('\n[%s]\nfile=thiscab\nclsid={924B4927-D3BA-41EA-9F7E-8A89194AB3AC}\nRegisterServer=yes\nFileVersion=%s\n' % (ocx, getDllVersion(ocxFullpath))) - - fullpaths = [] - for filename in dependencies: - fullpath = findExecutable(filename) - fullpaths.append(fullpath) - inf.write('\n[%s]\nfile=thiscab\nDestDir=11\nRegisterServer=yes\nFileVersion=%s\n' % (filename, getDllVersion(fullpath))) - inf.close() - - # Now process the inf file with cabarc. - try: - os.unlink(cabFilename) - except OSError: - pass - - cmd = '"%s" -s 6144 n "%s"' % (cabarc, cabFilename) - for fullpath in fullpaths: - cmd += ' "%s"' % (fullpath) - cmd += ' "%s" %s' % (ocxFullpath, infFile) - print(cmd) - result = subprocess.call(cmd) - if result: - sys.exit(result) - if not os.path.exists(cabFilename): - print("Couldn't generate %s" % (cabFilename)) - sys.exit(1) - - print("Successfully generated %s" % (cabFilename)) - - if options.spc and options.pvk: - # Now we have to sign the cab file. - cmd = '"%s" -spc "%s" -k "%s" "%s"' % (signcode, options.spc, options.pvk, cabFilename) - print(cmd) - result = subprocess.call(cmd) - if result: - sys.exit(result) - -def makeInstaller(): - # Locate the plugin(s). - pluginFiles = {} - pluginDependencies = {} - dependentFiles = {} - - # These are the primary files that make - # up the plugin/runtime. - if sys.platform == "darwin": - npapi = 'nppanda3d.plugin' - panda3d = 'panda3d' - panda3dapp = 'Panda3D.app' - baseFiles = [npapi, panda3d, panda3dapp] - elif sys.platform == 'win32': - ocx = 'p3dactivex.ocx' - npapi = 'nppanda3d.dll' - panda3d = 'panda3d.exe' - panda3dw = 'panda3dw.exe' - baseFiles = [ocx, npapi, panda3d, panda3dw] - else: - baseFiles = [] - - path = [] - pathsep = ':' - if sys.platform == "win32": - pathsep = ';' - if 'PATH' in os.environ: - path += os.environ['PATH'].split(pathsep) - if sys.platform != "win32" and 'LD_LIBRARY_PATH' in os.environ: - path += os.environ['LD_LIBRARY_PATH'].split(pathsep) - if sys.platform == "darwin" and 'DYLD_LIBRARY_PATH' in os.environ: - path += os.environ['DYLD_LIBRARY_PATH'].split(pathsep) - path += os.defpath.split(pathsep) - for file in baseFiles: - pathname = None - for pitem in path: - pathname = os.path.join(pitem, file) - if os.path.exists(pathname): - break - pathname = None - if not pathname: - sys.exit("Couldn't find %s." % (file)) - - pathname = os.path.abspath(pathname) - pluginFiles[file] = pathname - pluginDependencies[file] = [] - - if sys.platform == "win32": - # Also look for the dll's that these plugins reference. - addDependencies(path, pathname, file, pluginDependencies, dependentFiles) - - if sys.platform == "darwin": - tmproot = "/var/tmp/Panda3D Runtime/" - - # Apparently, we have to rename this package with each - # version, or Snow Leopard won't think the versions are - # increasing. I don't really understand why this is so. It - # might be related to the use of the Tiger PackageMaker's - # output being run on Snow Leopard. Whatever. Numbering the - # package files works around the problem and doesn't seem to - # cause additional problems, so we'll do that. - pkgname = 'p3d-setup-%s.pkg' % (options.version) - - if os.path.exists(tmproot): - shutil.rmtree(tmproot) - if os.path.isfile(pkgname): - os.remove(pkgname) - elif os.path.isdir(pkgname): - shutil.rmtree(pkgname) - if not os.path.exists(tmproot): - os.makedirs(tmproot) - dst_npapi = os.path.join(tmproot, "Library", "Internet Plug-Ins", npapi) - dst_panda3d = os.path.join(tmproot, "usr", "local", "bin", panda3d) - dst_panda3dapp = os.path.join(tmproot, "Applications", panda3dapp) - if not os.path.exists(dst_npapi): os.makedirs(os.path.dirname(dst_npapi)) - if not os.path.exists(dst_panda3d): os.makedirs(os.path.dirname(dst_panda3d)) - if not os.path.exists(dst_panda3dapp): os.makedirs(os.path.dirname(dst_panda3dapp)) - shutil.copytree(pluginFiles[npapi], dst_npapi) - shutil.copyfile(pluginFiles[panda3d], dst_panda3d) - os.chmod(dst_panda3d, 493) # 0o755 - shutil.copytree(pluginFiles[panda3dapp], dst_panda3dapp) - - tmpresdir = tempfile.mktemp('', 'p3d-resources') - if os.path.exists(tmpresdir): - shutil.rmtree(tmpresdir) - os.makedirs(tmpresdir) - if options.license: - shutil.copyfile(options.license, os.path.join(tmpresdir, "License.txt")) - - package_id = 'org.panda3d.pkg.runtime' #TODO: maybe more customizable? - - infoFilename = None - descriptionFilename = None - packagemaker = "/Applications/Xcode.app/Contents/Applications/PackageMaker.app/Contents/MacOS/PackageMaker" - if not os.path.exists(packagemaker): - packagemaker = "/Developer/usr/bin/packagemaker" - - if os.path.exists(packagemaker): - # PackageMaker 3.0 or better, e.g. OSX 10.5. - CMD = packagemaker - CMD += ' --id "%s"' % package_id - CMD += ' --version "%s"' % options.version - CMD += ' --title "%s"' % options.long_name - CMD += ' --out "%s"' % (pkgname) - CMD += ' --target 10.5' # The earliest version of OSX supported by Panda - CMD += ' --domain system' - CMD += ' --root "%s"' % tmproot - CMD += ' --resources "%s"' % tmpresdir - CMD += ' --no-relocate' - else: - # PackageMaker 2.0, e.g. OSX 10.4. - packagemaker = "/Developer/Tools/packagemaker" - infoFilename = '/tmp/Info_plist' - info = open(infoFilename, 'w') - info.write(Info_plist % { - 'package_id' : package_id, - 'version' : options.version, - }) - info.close() - descriptionFilename = '/tmp/Description_plist' - description = open(descriptionFilename, 'w') - description.write(Description_plist % { - 'long_name' : options.long_name, - 'short_name' : options.short_name, - }) - description.close() - CMD = packagemaker - CMD += ' -build' - CMD += ' -f "%s"' % (tmproot) - CMD += ' -r "%s"' % (tmpresdir) - CMD += ' -p "%s"' % (pkgname) - CMD += ' -i "%s"' % (infoFilename) - CMD += ' -d "%s"' % (descriptionFilename) - - print("") - print(CMD) - - # Don't check the exit status of packagemaker; it's not always - # reliable. - subprocess.call(CMD, shell = True) - shutil.rmtree(tmproot) - - if infoFilename: - os.unlink(infoFilename) - if descriptionFilename: - os.unlink(descriptionFilename) - if os.path.exists(tmpresdir): - shutil.rmtree(tmpresdir) - - if not os.path.exists(pkgname): - print("Unable to create %s." % (pkgname)) - sys.exit(1) - - # Pack the .pkg into a .dmg - if not os.path.exists(tmproot): os.makedirs(tmproot) - if os.path.isdir(pkgname): - shutil.copytree(pkgname, os.path.join(tmproot, pkgname)) - else: - shutil.copyfile(pkgname, os.path.join(tmproot, pkgname)) - - tmpdmg = tempfile.mktemp('', 'p3d-setup') + ".dmg" - CMD = 'hdiutil create "%s" -srcfolder "%s"' % (tmpdmg, tmproot) - print("") - print(CMD) - result = subprocess.call(CMD, shell = True) - if result: - sys.exit(result) - shutil.rmtree(tmproot) - - # Compress the .dmg (and make it read-only) - if os.path.exists("p3d-setup.dmg"): - os.remove("p3d-setup.dmg") - CMD = 'hdiutil convert "%s" -format UDBZ -o "p3d-setup.dmg"' % tmpdmg - print("") - print(CMD) - result = subprocess.call(CMD, shell = True) - if result: - sys.exit(result) - - elif sys.platform == 'win32': - # Now build the NSIS command. - CMD = "\"" + options.nsis + "\" /V3 " - CMD += '/DPRODUCT_NAME="' + options.long_name + '" ' - CMD += '/DPRODUCT_NAME_SHORT="' + options.short_name + '" ' - CMD += '/DPRODUCT_PUBLISHER="' + options.publisher + '" ' - CMD += '/DPRODUCT_WEB_SITE="' + options.website + '" ' - CMD += '/DPRODUCT_VERSION="' + options.version + '" ' - CMD += '/DINSTALL_DIR="' + options.install_dir + '" ' - CMD += '/DLICENSE_FILE="' + options.license + '" ' - CMD += '/DOCX="' + ocx + '" ' - CMD += '/DOCX_PATH="' + pluginFiles[ocx] + '" ' - CMD += '/DNPAPI="' + npapi + '" ' - CMD += '/DNPAPI_PATH="' + pluginFiles[npapi] + '" ' - CMD += '/DPANDA3D="' + panda3d + '" ' - CMD += '/DPANDA3D_PATH="' + pluginFiles[panda3d] + '" ' - CMD += '/DPANDA3DW="' + panda3dw + '" ' - CMD += '/DPANDA3DW_PATH="' + pluginFiles[panda3dw] + '" ' - - if options.regview: - CMD += '/DREGVIEW=%s ' % (options.regview) - - for i, dep in enumerate(dependentFiles.items()): - CMD += '/DDEP%s="%s" ' % (i, dep[0]) - CMD += '/DDEP%sP="%s" ' % (i, dep[1]) - - for i, dep in enumerate(pluginDependencies[npapi]): - CMD += '/DNPAPI_DEP%s="%s" ' % (i, dep) - - if options.start: - CMD += '/DADD_START_MENU ' - - if options.welcome_image: - CMD += '/DMUI_WELCOMEFINISHPAGE_BITMAP="' + options.welcome_image + '" ' - CMD += '/DMUI_UNWELCOMEFINISHPAGE_BITMAP="' + options.welcome_image + '" ' - if options.install_icon: - CMD += '/DINSTALL_ICON="' + options.install_icon + '" ' - - CMD += '"' + this_dir + '\\p3d_installer.nsi"' - - print("") - print(CMD) - print("packing...") - result = subprocess.call(CMD) - if result: - sys.exit(result) - - if options.cab: - # Generate a CAB file and optionally sign it. - makeCabFile(ocx, pluginDependencies) - -makeInstaller() diff --git a/direct/src/plugin_installer/make_xpi.py b/direct/src/plugin_installer/make_xpi.py deleted file mode 100755 index 7cd02fd908..0000000000 --- a/direct/src/plugin_installer/make_xpi.py +++ /dev/null @@ -1,202 +0,0 @@ -#! /usr/bin/env python - -import os -import sys -import shutil -import platform -import tempfile -import zipfile -from optparse import OptionParser -import subprocess - -try: - from hashlib import sha1 as sha -except ImportError: - from sha import sha - -usage = """ -This command creates a Firefox XPI installer for the Panda3D Firefox -plugin. Also see make_installer.py. - - %prog [opts]""" - -parser = OptionParser(usage = usage) -parser.add_option('-v', '--version', dest = 'version', - help = 'The product version', - default = None) -parser.add_option('-i', '--plugin_root', dest = 'plugin_root', - help = 'The root of a directory hierarchy in which the Firefox plugins for various platforms can be found, to build a Firefox xpi file. This is normally the same as the staging directory populated by the -i parameter to ppackage. This directory should contain a directory called "plugin", which contains in turn a number of directories named for the platform, by the Panda plugin convention, e.g. linux_i386, osx_ppc, and so on. Each platform directory should contain a Firefox plugin, e.g. nppanda3d.so.') -parser.add_option('', '--host_url', dest = 'host_url', - help = "The URL at which plugin_root will be hosted. This is used to construct the update URL for the xpi file. This is required if you specify --plugin_root.") - -(options, args) = parser.parse_args() - -this_dir = os.path.split(sys.argv[0])[0] - -assert options.version, "A version number must be supplied!" -assert options.plugin_root, "The plugin_root must be supplied!" -assert options.host_url, "The host_url must be supplied!" - -# A mapping of Panda's platform strings to Firefox's equivalent -# strings. - -# I'm leaving out the Linux platforms for now. I think there is too -# much variance between distro's for this to be reliable; we'll make -# each Linux user install their distro-specific plugin instead of -# going through this mechanism. -FirefoxPlatformMap = { - 'win32' : 'WINNT_x86-msvc', - 'win_i386' : 'WINNT_x86-msvc', - 'win_amd64' : 'WINNT_x86_64-msvc', -# 'linux_i386' : 'Linux_x86-gcc3', -# 'linux_amd64' : 'Linux_x86_64-gcc3', -# 'linux_ppc' : 'Linux_ppc-gcc3', - 'osx_i386' : 'Darwin_x86-gcc3', - 'osx_amd64' : 'Darwin_x86_64-gcc3', - 'osx_ppc' : 'Darwin_ppc-gcc3', - 'freebsd_i386' : 'FreeBSD_x86-gcc3', - 'freebsd_amd64' : 'FreeBSD_x86_64-gcc3', - } - -############################################################################## -# -# This install.rdf file is used when building a Firefox XPI file. -# -############################################################################## - -install_rdf = """ - - - %(package_id)s - true - Panda3D Game Engine Plug-In - Runs 3-D games and interactive applets - %(version)s - - - {ec8030f7-c20a-464f-9b0e-13a3a9e97384} - 3.0 - * - - - http://www.panda3d.org/ - %(host_url)s/plugin/firefox/update.rdf - - -""" - -############################################################################## -# -# This update.rdf file is used when building a Firefox XPI file. -# -############################################################################## - -update_rdf = """ - - - - - - - - %(version)s - - - {ec8030f7-c20a-464f-9b0e-13a3a9e97384} - 3.0 - * - %(host_url)s/plugin/firefox/nppanda3d.xpi - sha1:%(xpi_hash)s - - - - - - - - -""" - -def makeXpiFile(): - """ Creates a Firefox XPI file, based on the various platform - version files. """ - - if not options.host_url: - print("Cannot generate xpi file without --host-url.") - sys.exit(1) - - print("Generating xpi file") - root = options.plugin_root - if os.path.isdir(os.path.join(root, 'plugin')): - root = os.path.join(root, 'plugin') - - xpi = zipfile.ZipFile('nppanda3d.xpi', 'w') - - package_id = 'runtime@panda3d.org' #TODO: maybe more customizable? - - tempFile = tempfile.mktemp('.txt', 'p3d_') - rdf = open(tempFile, 'w') - rdf.write(install_rdf % { - 'package_id' : package_id, - 'version' : options.version, - 'host_url' : options.host_url, - }) - rdf.close() - xpi.write(tempFile, 'install.rdf') - os.unlink(tempFile) - - subdirs = os.listdir(root) - for subdir in subdirs: - platform = FirefoxPlatformMap.get(subdir, None) - path = os.path.join(root, subdir) - if platform and os.path.isdir(path): - if subdir in ['win32', 'osx_i386']: - pluginsXpiDir = 'plugins' - else: - # Create the XPI directory platform//plugins - pluginsXpiDir = 'platform/%s/plugins' % (platform) - - # Copy the Firefox plugin into this directory. - if subdir.startswith('win32'): - pluginFilename = 'nppanda3d.dll' - elif subdir.startswith('osx'): - pluginFilename = 'nppanda3d.plugin' - else: - pluginFilename = 'nppanda3d.so' - - addZipTree(xpi, os.path.join(path, pluginFilename), - pluginsXpiDir + '/' + pluginFilename) - xpi.close() - - # Now that we've generated the xpi file, get its hash. - data = open('nppanda3d.xpi', 'rb').read() - xpi_hash = sha(data).hexdigest() - - # And now we can generate the update.rdf file. - update = open('update.rdf', 'w') - update.write(update_rdf % { - 'package_id' : package_id, - 'version' : options.version, - 'host_url' : options.host_url, - 'xpi_hash' : xpi_hash, - }) - update.close() - -def addZipTree(zip, sourceFile, zipName): - """ Adds the sourceFile to the zip archive at the indicated name. - If it is a directory, recursively adds all nested files as - well. """ - - if os.path.isdir(sourceFile): - subdirs = os.listdir(sourceFile) - for subdir in subdirs: - addZipTree(zip, os.path.join(sourceFile, subdir), - zipName + '/' + subdir) - - else: - # Not a directory, just add the file. - zip.write(sourceFile, zipName) - -makeXpiFile() - diff --git a/direct/src/plugin_installer/p3d_installer.nsi b/direct/src/plugin_installer/p3d_installer.nsi deleted file mode 100644 index b16c170ca5..0000000000 --- a/direct/src/plugin_installer/p3d_installer.nsi +++ /dev/null @@ -1,300 +0,0 @@ -!include "MUI.nsh" -!include LogicLib.nsh - -; Several variables are assumed to be pre-defined by the caller. See -; make_installer.py in this directory. - -!define UNINSTALL_SUCCESS "$(^Name) was successfully removed from your computer." -!define UNINSTALL_CONFIRM "Are you sure you want to completely remove $(^Name) and all of its components?" -!define UNINSTALL_LINK_NAME "Uninstall" -!define WEBSITE_LINK_NAME "Website" -!define PLID "@panda3d.org/Panda3D Runtime" - -; HM NIS Edit Wizard helper defines -!define APP_INTERNAL_NAME "Panda3D" - -!define PRODUCT_DIR_REGKEY_PANDA3D "Software\Microsoft\Windows\CurrentVersion\App Paths\${PANDA3D}" -!define PRODUCT_DIR_REGKEY_PANDA3DW "Software\Microsoft\Windows\CurrentVersion\App Paths\${PANDA3DW}" -!define PRODUCT_DIR_REGKEY_OCX "Software\Microsoft\Windows\CurrentVersion\App Paths\${OCX}" -!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" -!define PRODUCT_UNINST_ROOT_KEY "HKLM" -!define PROG_GROUPNAME "${PRODUCT_NAME}" - -SetCompressor lzma - -; MUI Settings -!define MUI_ABORTWARNING -!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico" -!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico" - -; Welcome page -!define MUI_WELCOMEPAGE_TITLE_3LINES -!insertmacro MUI_PAGE_WELCOME -!insertmacro MUI_PAGE_LICENSE "${LICENSE_FILE}" ; EULA -; Directory page -!insertmacro MUI_PAGE_DIRECTORY -; Instfiles page -!insertmacro MUI_PAGE_INSTFILES -; Finish page -;!define MUI_FINISHPAGE_NOAUTOCLOSE ;un-comment to put a pause after the file installation screen -!define MUI_FINISHPAGE_TITLE_3LINES -!insertmacro MUI_PAGE_FINISH - -; Uninstaller pages -!insertmacro MUI_UNPAGE_INSTFILES - -; Language files -!insertmacro MUI_LANGUAGE "English" - -; Reserve files -!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS - -; MUI end ------ - -Name "${PRODUCT_NAME}" -OutFile p3d-setup.exe -InstallDir "${INSTALL_DIR}" -!ifdef INSTALL_ICON - Icon "${INSTALL_ICON}" - UninstallIcon "${INSTALL_ICON}" -!endif -WindowIcon on - -ShowInstDetails show -ShowUnInstDetails show - -Function .onInit -!ifdef REGVIEW - SetRegView ${REGVIEW} -!endif - - ClearErrors - - ReadRegStr $0 ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "InstallLocation" - - IfErrors +2 0 - StrCpy $INSTDIR $0 - -FunctionEnd - -Section "MainSection" SEC01 - SetShellVarContext all - SetOutPath "$INSTDIR" - SetOverwrite ifdiff - -!ifdef OCX_PATH - File "${OCX_PATH}" -!endif - File "${NPAPI_PATH}" - File "${PANDA3D_PATH}" - File "${PANDA3DW_PATH}" - -; Auto-detected dependencies on the above executables. Python -; computes these values for us. -!ifdef DEP0P - File "${DEP0P}" -!endif -!ifdef DEP1P - File "${DEP1P}" -!endif -!ifdef DEP2P - File "${DEP2P}" -!endif -!ifdef DEP3P - File "${DEP3P}" -!endif -!ifdef DEP4P - File "${DEP4P}" -!endif -!ifdef DEP5P - File "${DEP5P}" -!endif -!ifdef DEP6P - File "${DEP6P}" -!endif -!ifdef DEP7P - File "${DEP7P}" -!endif - -!ifdef ADD_START_MENU -; Start->Programs links - CreateDirectory "$SMPROGRAMS\${PROG_GROUPNAME}" -; CreateShortCut "$SMPROGRAMS\${PROG_GROUPNAME}\${PRODUCT_NAME_SHORT}.lnk" "$INSTDIR\${LAUNCHER}" -!endif - -; Desktop Icon...commented out for now -; CreateShortCut "$DESKTOP\${PRODUCT_NAME_SHORT}.lnk" "$INSTDIR\${OCX}" - - # Make the directory "$INSTDIR" read write accessible by all users - AccessControl::GrantOnFile "$INSTDIR" "(BU)" "FullAccess" - -; File "..\..\..\path\to\file\Example.file" -SectionEnd - -Section -AdditionalIcons - WriteIniStr "$INSTDIR\${PRODUCT_NAME}.url" "InternetShortcut" "URL" "${PRODUCT_WEB_SITE}" -!ifdef ADD_START_MENU - CreateShortCut "$SMPROGRAMS\${PROG_GROUPNAME}\${WEBSITE_LINK_NAME}.lnk" "$INSTDIR\${PRODUCT_NAME}.url" - CreateShortCut "$SMPROGRAMS\${PROG_GROUPNAME}\${UNINSTALL_LINK_NAME}.lnk" "$INSTDIR\uninst.exe" -!endif -SectionEnd - -Section -Post -!ifdef REGVIEW - SetRegView ${REGVIEW} -!endif - - WriteUninstaller "$INSTDIR\uninst.exe" - - WriteRegStr HKCR ".p3d" "" "Panda3D applet" - WriteRegStr HKCR ".p3d" "Content Type" "application/x-panda3d" - WriteRegStr HKCR ".p3d" "PerceivedType" "application" - WriteRegStr HKCR "Panda3D applet" "" "Panda3D applet" - WriteRegStr HKCR "Panda3D applet\DefaultIcon" "" "$INSTDIR\${PANDA3DW}" - WriteRegStr HKCR "Panda3D applet\shell" "" "open" - WriteRegStr HKCR "Panda3D applet\shell\open\command" "" '"$INSTDIR\${PANDA3DW}" "%1"' - WriteRegExpandStr HKCR "Panda3D applet\shell\open2" "" "Open &with Command Prompt" - ;WriteRegExpandStr HKCR "Panda3D applet\shell\open2" "MUIVerb" "@%SystemRoot%\System32\wshext.dll,-4511" - WriteRegExpandStr HKCR "Panda3D applet\shell\open2\command" "" '"$INSTDIR\${PANDA3D}" "%1"' - - WriteRegStr HKLM "${PRODUCT_DIR_REGKEY_PANDA3D}" "" "$INSTDIR\${PANDA3D}" - WriteRegStr HKLM "${PRODUCT_DIR_REGKEY_PANDA3DW}" "" "$INSTDIR\${PANDA3DW}" - WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)" - WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe" - WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PANDA3D}" - WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" - WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}" - WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" - WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "InstallLocation" "$INSTDIR" - WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "NoModify" 1 - WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "NoRepair" 1 - - SectionGetSize SEC01 $0 - WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "EstimatedSize" $0 - - # Delete keys we used in older versions - DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY_OCX}" - DeleteRegKey HKCR "Panda3D applet\shell\edit" -SectionEnd - - -Function un.onUninstSuccess - HideWindow - MessageBox MB_ICONINFORMATION|MB_OK "${UNINSTALL_SUCCESS}" -FunctionEnd - -Function un.onInit - MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "${UNINSTALL_CONFIRM}" IDYES +2 - Abort -FunctionEnd - -Function .onInstSuccess - # Register ActiveX - ExecWait 'regsvr32 /s "$INSTDIR/${OCX}"' - -!ifdef REGVIEW - SetRegView ${REGVIEW} -!endif - - # Register Mozilla Plugin - WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "Description" "Runs 3-D games and interactive applets" - WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "Path" "$INSTDIR\${NPAPI}" - WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "ProductName" "${PRODUCT_NAME_SHORT}" - WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "Vendor" "${PRODUCT_PUBLISHER}" - WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "Version" "${PRODUCT_VERSION}" - WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}\MimeTypes\application/x-panda3d" "Description" "Panda3D applet" - - # Remove old stuff - DeleteRegKey HKLM "SOFTWARE\MozillaPlugins\${PLID},version=0.0" - -FunctionEnd - -Section Uninstall - SetShellVarContext all - - ExecWait 'regsvr32 /u /s "$INSTDIR/${OCX}"' - - Delete "$INSTDIR\${OCX}" - Delete "$INSTDIR\${NPAPI}" - Delete "$INSTDIR\${PANDA3D}" - Delete "$INSTDIR\${PANDA3DW}" -!ifdef DEP0 - Delete "$INSTDIR\${DEP0}" -!endif -!ifdef DEP1 - Delete "$INSTDIR\${DEP1}" -!endif -!ifdef DEP2 - Delete "$INSTDIR\${DEP2}" -!endif -!ifdef DEP3 - Delete "$INSTDIR\${DEP3}" -!endif -!ifdef DEP4 - Delete "$INSTDIR\${DEP4}" -!endif -!ifdef DEP5 - Delete "$INSTDIR\${DEP5}" -!endif -!ifdef DEP6 - Delete "$INSTDIR\${DEP6}" -!endif -!ifdef DEP7 - Delete "$INSTDIR\${DEP7}" -!endif - -!ifdef REGVIEW - SetRegView ${REGVIEW} -!endif - -# The following loop uninstalls the plugin where it may have been -# copied into one of the Mozilla Extensions dirs. Older versions of -# the installer would have done this, but now we just update the -# registry to point to $INSTDIR. -StrCpy $1 "0" -Mozilla-Uninstall-Loop: - EnumRegKey $0 HKLM "SOFTWARE\Mozilla" "$1" - StrCmp $0 "" Mozilla-Uninstall-End - IntOp $1 $1 + 1 - ReadRegStr $2 HKLM "SOFTWARE\Mozilla\$0\Extensions" "Plugins" - ${If} $2 != "" - Delete "$2\${NPAPI}" - # We can't delete the dependency files, because who knows--maybe - # some other plugins are also using the same files. - ${EndIf} - goto Mozilla-Uninstall-Loop -Mozilla-Uninstall-End: - - DeleteRegKey HKLM "SOFTWARE\MozillaPlugins\${PLID}" - DeleteRegKey HKLM "SOFTWARE\MozillaPlugins\${PLID},version=0.0" - - DeleteRegKey HKCR ".p3d" - DeleteRegKey HKCR "Panda3D applet" - - # Remove the user's "Panda3D" directory, where all of the downloaded - # contents are installed. Too bad we can't do this for every system - # user, not just the current user. - - RmDir /r "$LOCALAPPDATALow\${APP_INTERNAL_NAME}" - RmDir /r "$LOCALAPPDATA\${APP_INTERNAL_NAME}" - - Delete "$INSTDIR\uninst.exe" - Delete "$INSTDIR\${PRODUCT_NAME}.url" - -!ifdef ADD_START_MENU - Delete "$SMPROGRAMS\${PROG_GROUPNAME}\${UNINSTALL_LINK_NAME}.lnk" - Delete "$SMPROGRAMS\${PROG_GROUPNAME}\${WEBSITE_LINK_NAME}.lnk" -; Delete "$DESKTOP\${PRODUCT_NAME_SHORT}${PRODUCT_RELEASE}.lnk" -; Delete "$SMPROGRAMS\${PROG_GROUPNAME}\${PRODUCT_NAME_SHORT}.lnk" - RMDir "$SMPROGRAMS\${PROG_GROUPNAME}" -!endif - - RMDir "$INSTDIR" - - DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" - DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY_PANDA3D}" - DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY_PANDA3DW}" - DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY_OCX}" - SetAutoClose true -SectionEnd - diff --git a/direct/src/plugin_npapi/make_osx_bundle.py b/direct/src/plugin_npapi/make_osx_bundle.py deleted file mode 100755 index ce7a102b06..0000000000 --- a/direct/src/plugin_npapi/make_osx_bundle.py +++ /dev/null @@ -1,97 +0,0 @@ -#! /usr/bin/env python - -""" - -This script constructs the bundle directory structure for the OSX web -plugin that is built by the code in this directory. It takes no -parameters, and produces the plugin bundle in the same place. - -""" - -import getopt -import sys -import os -import glob -import shutil - -import direct -from panda3d.core import Filename, DSearchPath - -def usage(code, msg = ''): - sys.stderr.write(__doc__) - sys.stderr.write(msg + '\n') - sys.exit(code) - -def makeBundle(startDir): - fstartDir = Filename.fromOsSpecific(startDir) - - # Search for nppandad along $DYLD_LIBRARY_PATH. - path = DSearchPath() - if 'LD_LIBRARY_PATH' in os.environ: - path.appendPath(os.environ['LD_LIBRARY_PATH']) - if 'DYLD_LIBRARY_PATH' in os.environ: - path.appendPath(os.environ['DYLD_LIBRARY_PATH']) - nppanda3d = path.findFile('nppanda3d') - if not nppanda3d: - raise Exception("Couldn't find nppanda3d on path.") - - # Generate the bundle directory structure - rootFilename = Filename(fstartDir, 'bundle') - - if os.path.exists(rootFilename.toOsSpecific()): - shutil.rmtree(rootFilename.toOsSpecific()) - - bundleFilename = Filename(rootFilename, 'nppanda3d.plugin') - plistFilename = Filename(bundleFilename, 'Contents/Info.plist') - plistFilename.makeDir() - exeFilename = Filename(bundleFilename, 'Contents/MacOS/nppanda3d') - exeFilename.makeDir() - resourceFilename = Filename(bundleFilename, 'Contents/Resources/nppanda3d.rsrc') - resourceFilename.makeDir() - - # Compile the .r file to an .rsrc file. - os.system('/Developer/Tools/Rez -useDF -o %s %s' % ( - resourceFilename.toOsSpecific(), Filename(fstartDir, "nppanda3d.r").toOsSpecific())) - - if not resourceFilename.exists(): - raise IOError('Unable to run Rez') - - # Copy in Info.plist and the compiled executable. - shutil.copyfile(Filename(fstartDir, "nppanda3d.plist").toOsSpecific(), plistFilename.toOsSpecific()) - shutil.copyfile(nppanda3d.toOsSpecific(), exeFilename.toOsSpecific()) - - # All done! - bundleFilename.touch() - print(bundleFilename.toOsSpecific()) - -def buildDmg(startDir): - fstartDir = Filename.fromOsSpecific(startDir) - rootFilename = Filename(fstartDir, 'bundle') - output = Filename(fstartDir, 'nppanda3d.dmg') - output.unlink() - cmd = 'hdiutil create -fs HFS+ -srcfolder "%(dir)s" -volname "%(volname)s" "%(output)s"' % { - 'dir' : rootFilename.toOsSpecific(), - 'volname' : 'nppanda3d', - 'output' : output.toOsSpecific(), - } - os.system(cmd) - -if __name__ == '__main__': - try: - opts, args = getopt.getopt(sys.argv[1:], 'h') - except getopt.error as msg: - usage(1, msg) - - for opt, arg in opts: - if opt == '-h': - usage(0) - - if args: - usage(1, 'No arguments are expected.') - - startDir = os.path.split(sys.argv[0])[0] - makeBundle(startDir) - - # We don't need the dmg these days; the installer is better. - #buildDmg(startDir) - diff --git a/direct/src/plugin_npapi/npapi.h b/direct/src/plugin_npapi/npapi.h deleted file mode 100644 index f34603182d..0000000000 --- a/direct/src/plugin_npapi/npapi.h +++ /dev/null @@ -1,928 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef npapi_h_ -#define npapi_h_ - -#if defined(__OS2__) -#pragma pack(1) -#endif - -#include "nptypes.h" - -#if defined(__OS2__) || defined(OS2) -#ifndef XP_OS2 -#define XP_OS2 1 -#endif -#endif - -#if defined(_WIN32) && !defined(__SYMBIAN32__) -#include -#ifndef XP_WIN -#define XP_WIN 1 -#endif -#endif - -#if defined(__SYMBIAN32__) -#ifndef XP_SYMBIAN -#define XP_SYMBIAN 1 -#undef XP_WIN -#endif -#endif - -#if defined(__APPLE_CC__) && !defined(XP_UNIX) -#ifndef XP_MACOSX -#define XP_MACOSX 1 -#endif -#endif - -#if defined(XP_MACOSX) -#if !defined(NP_NO_OPENGL) -#define NP_NO_OPENGL 1 -#endif /* !defined(NP_NO_OPENGL) */ -#if defined(__LP64__) -#define NP_NO_QUICKDRAW -#define NP_NO_CARBON -#endif /* defined(__LP64__) */ -#endif - -#if defined(XP_MACOSX) -#include -#if !NP_NO_OPENGL -#include -#endif /* !NP_NO_OPENGL */ -#ifndef NP_NO_CARBON -#include -#endif -#endif - -#if defined(XP_UNIX) -#include -#if defined(MOZ_X11) -#include -#include -#endif -#endif - -#if defined(XP_SYMBIAN) -#include -#include -#endif - -/*----------------------------------------------------------------------*/ -/* Plugin Version Constants */ -/*----------------------------------------------------------------------*/ - -#define NP_VERSION_MAJOR 0 -#define NP_VERSION_MINOR 28 - - -/* The OS/2 version of Netscape uses RC_DATA to define the - mime types, file extensions, etc that are required. - Use a vertical bar to separate types, end types with \0. - FileVersion and ProductVersion are 32bit ints, all other - entries are strings that MUST be terminated with a \0. - -AN EXAMPLE: - -RCDATA NP_INFO_ProductVersion { 1,0,0,1,} - -RCDATA NP_INFO_MIMEType { "video/x-video|", - "video/x-flick\0" } -RCDATA NP_INFO_FileExtents { "avi|", - "flc\0" } -RCDATA NP_INFO_FileOpenName{ "MMOS2 video player(*.avi)|", - "MMOS2 Flc/Fli player(*.flc)\0" } - -RCDATA NP_INFO_FileVersion { 1,0,0,1 } -RCDATA NP_INFO_CompanyName { "Netscape Communications\0" } -RCDATA NP_INFO_FileDescription { "NPAVI32 Extension DLL\0" -RCDATA NP_INFO_InternalName { "NPAVI32\0" ) -RCDATA NP_INFO_LegalCopyright { "Copyright Netscape Communications \251 1996\0" -RCDATA NP_INFO_OriginalFilename { "NVAPI32.DLL" } -RCDATA NP_INFO_ProductName { "NPAVI32 Dynamic Link Library\0" } -*/ -/* RC_DATA types for version info - required */ -#define NP_INFO_ProductVersion 1 -#define NP_INFO_MIMEType 2 -#define NP_INFO_FileOpenName 3 -#define NP_INFO_FileExtents 4 -/* RC_DATA types for version info - used if found */ -#define NP_INFO_FileDescription 5 -#define NP_INFO_ProductName 6 -/* RC_DATA types for version info - optional */ -#define NP_INFO_CompanyName 7 -#define NP_INFO_FileVersion 8 -#define NP_INFO_InternalName 9 -#define NP_INFO_LegalCopyright 10 -#define NP_INFO_OriginalFilename 11 - -#ifndef RC_INVOKED - -/*----------------------------------------------------------------------*/ -/* Definition of Basic Types */ -/*----------------------------------------------------------------------*/ - -typedef unsigned char NPBool; -typedef int16_t NPError; -typedef int16_t NPReason; -typedef char* NPMIMEType; - -/*----------------------------------------------------------------------*/ -/* Structures and definitions */ -/*----------------------------------------------------------------------*/ - -#if !defined(__LP64__) -#if defined(XP_MACOSX) -#pragma options align=mac68k -#endif -#endif /* __LP64__ */ - -/* - * NPP is a plug-in's opaque instance handle - */ -typedef struct _NPP -{ - void* pdata; /* plug-in private data */ - void* ndata; /* netscape private data */ -} NPP_t; - -typedef NPP_t* NPP; - -typedef struct _NPStream -{ - void* pdata; /* plug-in private data */ - void* ndata; /* netscape private data */ - const char* url; - uint32_t end; - uint32_t lastmodified; - void* notifyData; - const char* headers; /* Response headers from host. - * Exists only for >= NPVERS_HAS_RESPONSE_HEADERS. - * Used for HTTP only; NULL for non-HTTP. - * Available from NPP_NewStream onwards. - * Plugin should copy this data before storing it. - * Includes HTTP status line and all headers, - * preferably verbatim as received from server, - * headers formatted as in HTTP ("Header: Value"), - * and newlines (\n, NOT \r\n) separating lines. - * Terminated by \n\0 (NOT \n\n\0). */ -} NPStream; - -typedef struct _NPByteRange -{ - int32_t offset; /* negative offset means from the end */ - uint32_t length; - struct _NPByteRange* next; -} NPByteRange; - -typedef struct _NPSavedData -{ - int32_t len; - void* buf; -} NPSavedData; - -typedef struct _NPRect -{ - uint16_t top; - uint16_t left; - uint16_t bottom; - uint16_t right; -} NPRect; - -typedef struct _NPSize -{ - int32_t width; - int32_t height; -} NPSize; - -typedef enum { - NPFocusNext = 0, - NPFocusPrevious = 1 -} NPFocusDirection; - -/* Return values for NPP_HandleEvent */ -#define kNPEventNotHandled 0 -#define kNPEventHandled 1 -/* Exact meaning must be spec'd in event model. */ -#define kNPEventStartIME 2 - -#if defined(XP_UNIX) -/* - * Unix specific structures and definitions - */ - -/* - * Callback Structures. - * - * These are used to pass additional platform specific information. - */ -enum { - NP_SETWINDOW = 1, - NP_PRINT -}; - -typedef struct -{ - int32_t type; -} NPAnyCallbackStruct; - -typedef struct -{ - int32_t type; -#if defined(MOZ_X11) - Display* display; - Visual* visual; - Colormap colormap; - unsigned int depth; -#endif -} NPSetWindowCallbackStruct; - -typedef struct -{ - int32_t type; - FILE* fp; -} NPPrintCallbackStruct; - -#endif /* XP_UNIX */ - -typedef enum { - NPDrawingModelDUMMY -#if defined(XP_MACOSX) -#ifndef NP_NO_QUICKDRAW - , NPDrawingModelQuickDraw = 0 -#endif - , NPDrawingModelCoreGraphics = 1 - , NPDrawingModelOpenGL = 2 - , NPDrawingModelCoreAnimation = 3 - , NPDrawingModelInvalidatingCoreAnimation = 4 -#endif -#if defined(XP_WIN) - , NPDrawingModelSyncWin = 5 -#endif -#if defined(MOZ_X11) - , NPDrawingModelSyncX = 6 -#endif -#if 0 /* OBSOLETE */ - , NPDrawingModelAsyncBitmapSurfaceOBSOLETE = 7 -#if defined(XP_WIN) - , NPDrawingModelAsyncWindowsDXGISurfaceOBSOLETE = 8 -#endif -#endif -} NPDrawingModel; - -#ifdef XP_MACOSX -typedef enum { -#ifndef NP_NO_CARBON - NPEventModelCarbon = 0, -#endif - NPEventModelCocoa = 1 -} NPEventModel; -#endif - -/* - * The following masks are applied on certain platforms to NPNV and - * NPPV selectors that pass around pointers to COM interfaces. Newer - * compilers on some platforms may generate vtables that are not - * compatible with older compilers. To prevent older plugins from - * not understanding a new browser's ABI, these masks change the - * values of those selectors on those platforms. To remain backwards - * compatible with different versions of the browser, plugins can - * use these masks to dynamically determine and use the correct C++ - * ABI that the browser is expecting. This does not apply to Windows - * as Microsoft's COM ABI will likely not change. - */ - -#define NP_ABI_GCC3_MASK 0x10000000 -/* - * gcc 3.x generated vtables on UNIX and OSX are incompatible with - * previous compilers. - */ -#if (defined(XP_UNIX) && defined(__GNUC__) && (__GNUC__ >= 3)) -#define _NP_ABI_MIXIN_FOR_GCC3 NP_ABI_GCC3_MASK -#else -#define _NP_ABI_MIXIN_FOR_GCC3 0 -#endif - -#if defined(XP_MACOSX) -#define NP_ABI_MACHO_MASK 0x01000000 -#define _NP_ABI_MIXIN_FOR_MACHO NP_ABI_MACHO_MASK -#else -#define _NP_ABI_MIXIN_FOR_MACHO 0 -#endif - -#define NP_ABI_MASK (_NP_ABI_MIXIN_FOR_GCC3 | _NP_ABI_MIXIN_FOR_MACHO) - -/* - * List of variable names for which NPP_GetValue shall be implemented - */ -typedef enum { - NPPVpluginNameString = 1, - NPPVpluginDescriptionString, - NPPVpluginWindowBool, - NPPVpluginTransparentBool, - NPPVjavaClass, - NPPVpluginWindowSize, - NPPVpluginTimerInterval, - NPPVpluginScriptableInstance = (10 | NP_ABI_MASK), - NPPVpluginScriptableIID = 11, - NPPVjavascriptPushCallerBool = 12, - NPPVpluginKeepLibraryInMemory = 13, - NPPVpluginNeedsXEmbed = 14, - - /* Get the NPObject for scripting the plugin. Introduced in NPAPI minor version 14. - */ - NPPVpluginScriptableNPObject = 15, - - /* Get the plugin value (as \0-terminated UTF-8 string data) for - * form submission if the plugin is part of a form. Use - * NPN_MemAlloc() to allocate memory for the string data. Introduced - * in NPAPI minor version 15. - */ - NPPVformValue = 16, - - NPPVpluginUrlRequestsDisplayedBool = 17, - - /* Checks if the plugin is interested in receiving the http body of - * all http requests (including failed ones, http status != 200). - */ - NPPVpluginWantsAllNetworkStreams = 18, - - /* Browsers can retrieve a native ATK accessibility plug ID via this variable. */ - NPPVpluginNativeAccessibleAtkPlugId = 19, - - /* Checks to see if the plug-in would like the browser to load the "src" attribute. */ - NPPVpluginCancelSrcStream = 20, - - NPPVsupportsAdvancedKeyHandling = 21, - - NPPVpluginUsesDOMForCursorBool = 22, - - /* Used for negotiating drawing models */ - NPPVpluginDrawingModel = 1000 -#if defined(XP_MACOSX) - /* Used for negotiating event models */ - , NPPVpluginEventModel = 1001 - /* In the NPDrawingModelCoreAnimation drawing model, the browser asks the plug-in for a Core Animation layer. */ - , NPPVpluginCoreAnimationLayer = 1003 -#endif - -#if defined(MOZ_PLATFORM_MAEMO) && ((MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)) - , NPPVpluginWindowlessLocalBool = 2002 -#endif -} NPPVariable; - -/* - * List of variable names for which NPN_GetValue should be implemented. - */ -typedef enum { - NPNVxDisplay = 1, - NPNVxtAppContext, - NPNVnetscapeWindow, - NPNVjavascriptEnabledBool, - NPNVasdEnabledBool, - NPNVisOfflineBool, - - NPNVserviceManager = (10 | NP_ABI_MASK), - NPNVDOMElement = (11 | NP_ABI_MASK), - NPNVDOMWindow = (12 | NP_ABI_MASK), - NPNVToolkit = (13 | NP_ABI_MASK), - NPNVSupportsXEmbedBool = 14, - - /* Get the NPObject wrapper for the browser window. */ - NPNVWindowNPObject = 15, - - /* Get the NPObject wrapper for the plugins DOM element. */ - NPNVPluginElementNPObject = 16, - - NPNVSupportsWindowless = 17, - - NPNVprivateModeBool = 18, - - NPNVsupportsAdvancedKeyHandling = 21, - - NPNVdocumentOrigin = 22, - - NPNVpluginDrawingModel = 1000 /* Get the current drawing model (NPDrawingModel) */ -#if defined(XP_MACOSX) - , NPNVcontentsScaleFactor = 1001 -#ifndef NP_NO_QUICKDRAW - , NPNVsupportsQuickDrawBool = 2000 -#endif - , NPNVsupportsCoreGraphicsBool = 2001 - , NPNVsupportsOpenGLBool = 2002 - , NPNVsupportsCoreAnimationBool = 2003 - , NPNVsupportsInvalidatingCoreAnimationBool = 2004 -#endif -#if 0 /* OBSOLETE */ - , NPNVsupportsAsyncBitmapSurfaceBoolOBSOLETE = 2007 -#if defined(XP_WIN) - , NPNVsupportsAsyncWindowsDXGISurfaceBoolOBSOLETE = 2008 -#endif -#endif -#if defined(XP_MACOSX) -#ifndef NP_NO_CARBON - , NPNVsupportsCarbonBool = 3000 /* TRUE if the browser supports the Carbon event model */ -#endif - , NPNVsupportsCocoaBool = 3001 /* TRUE if the browser supports the Cocoa event model */ - , NPNVsupportsUpdatedCocoaTextInputBool = 3002 /* TRUE if the browser supports the updated - Cocoa text input specification. */ - , NPNVsupportsCompositingCoreAnimationPluginsBool = 74656 /* TRUE if the browser supports - CA model compositing */ -#endif -#if defined(MOZ_PLATFORM_MAEMO) && ((MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)) - , NPNVSupportsWindowlessLocal = 2002 -#endif -} NPNVariable; - -typedef enum { - NPNURLVCookie = 501, - NPNURLVProxy -} NPNURLVariable; - -/* - * The type of Toolkit the widgets use - */ -typedef enum { - NPNVGtk12 = 1, - NPNVGtk2 -} NPNToolkitType; - -/* - * The type of a NPWindow - it specifies the type of the data structure - * returned in the window field. - */ -typedef enum { - NPWindowTypeWindow = 1, - NPWindowTypeDrawable -} NPWindowType; - -typedef struct _NPWindow -{ - void* window; /* Platform specific window handle */ - /* OS/2: x - Position of bottom left corner */ - /* OS/2: y - relative to visible netscape window */ - int32_t x; /* Position of top left corner relative */ - int32_t y; /* to a netscape page. */ - uint32_t width; /* Maximum window size */ - uint32_t height; - NPRect clipRect; /* Clipping rectangle in port coordinates */ -#if (defined(XP_UNIX) || defined(XP_SYMBIAN)) && !defined(XP_MACOSX) - void * ws_info; /* Platform-dependent additional data */ -#endif /* XP_UNIX */ - NPWindowType type; /* Is this a window or a drawable? */ -} NPWindow; - -typedef struct _NPImageExpose -{ - char* data; /* image pointer */ - int32_t stride; /* Stride of data image pointer */ - int32_t depth; /* Depth of image pointer */ - int32_t x; /* Expose x */ - int32_t y; /* Expose y */ - uint32_t width; /* Expose width */ - uint32_t height; /* Expose height */ - NPSize dataSize; /* Data buffer size */ - float translateX; /* translate X matrix value */ - float translateY; /* translate Y matrix value */ - float scaleX; /* scale X matrix value */ - float scaleY; /* scale Y matrix value */ -} NPImageExpose; - -typedef struct _NPFullPrint -{ - NPBool pluginPrinted;/* Set TRUE if plugin handled fullscreen printing */ - NPBool printOne; /* TRUE if plugin should print one copy to default - printer */ - void* platformPrint; /* Platform-specific printing info */ -} NPFullPrint; - -typedef struct _NPEmbedPrint -{ - NPWindow window; - void* platformPrint; /* Platform-specific printing info */ -} NPEmbedPrint; - -typedef struct _NPPrint -{ - uint16_t mode; /* NP_FULL or NP_EMBED */ - union - { - NPFullPrint fullPrint; /* if mode is NP_FULL */ - NPEmbedPrint embedPrint; /* if mode is NP_EMBED */ - } print; -} NPPrint; - -#if defined(XP_MACOSX) -#ifndef NP_NO_CARBON -typedef EventRecord NPEvent; -#endif -#elif defined(XP_SYMBIAN) -typedef QEvent NPEvent; -#elif defined(XP_WIN) -typedef struct _NPEvent -{ - uint16_t event; - uintptr_t wParam; - uintptr_t lParam; -} NPEvent; -#elif defined(XP_OS2) -typedef struct _NPEvent -{ - uint32_t event; - uint32_t wParam; - uint32_t lParam; -} NPEvent; -#elif defined(XP_UNIX) && defined(MOZ_X11) -typedef XEvent NPEvent; -#else -typedef void* NPEvent; -#endif - -#if defined(XP_MACOSX) -typedef void* NPRegion; -#ifndef NP_NO_QUICKDRAW -typedef RgnHandle NPQDRegion; -#endif -typedef CGPathRef NPCGRegion; -#elif defined(XP_WIN) -typedef HRGN NPRegion; -#elif defined(XP_UNIX) && defined(MOZ_X11) -typedef Region NPRegion; -#elif defined(XP_SYMBIAN) -typedef QRegion* NPRegion; -#else -typedef void *NPRegion; -#endif - -typedef struct _NPNSString NPNSString; -typedef struct _NPNSWindow NPNSWindow; -typedef struct _NPNSMenu NPNSMenu; - -#if defined(XP_MACOSX) -typedef NPNSMenu NPMenu; -#else -typedef void *NPMenu; -#endif - -typedef enum { - NPCoordinateSpacePlugin = 1, - NPCoordinateSpaceWindow, - NPCoordinateSpaceFlippedWindow, - NPCoordinateSpaceScreen, - NPCoordinateSpaceFlippedScreen -} NPCoordinateSpace; - -#if defined(XP_MACOSX) - -#ifndef NP_NO_QUICKDRAW -typedef struct NP_Port -{ - CGrafPtr port; - int32_t portx; /* position inside the topmost window */ - int32_t porty; -} NP_Port; -#endif /* NP_NO_QUICKDRAW */ - -/* - * NP_CGContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelCoreGraphics - * as its drawing model. - */ - -typedef struct NP_CGContext -{ - CGContextRef context; - void *window; /* A WindowRef under the Carbon event model. */ -} NP_CGContext; - -/* - * NP_GLContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelOpenGL as its - * drawing model. - */ - -#if !NP_NO_OPENGL -typedef struct NP_GLContext -{ - CGLContextObj context; -#ifdef NP_NO_CARBON - NPNSWindow *window; -#else - void *window; /* Can be either an NSWindow or a WindowRef depending on the event model */ -#endif -} NP_GLContext; -#endif /* !NP_NO_OPENGL */ - -typedef enum { - NPCocoaEventDrawRect = 1, - NPCocoaEventMouseDown, - NPCocoaEventMouseUp, - NPCocoaEventMouseMoved, - NPCocoaEventMouseEntered, - NPCocoaEventMouseExited, - NPCocoaEventMouseDragged, - NPCocoaEventKeyDown, - NPCocoaEventKeyUp, - NPCocoaEventFlagsChanged, - NPCocoaEventFocusChanged, - NPCocoaEventWindowFocusChanged, - NPCocoaEventScrollWheel, - NPCocoaEventTextInput -} NPCocoaEventType; - -typedef struct _NPCocoaEvent { - NPCocoaEventType type; - uint32_t version; - union { - struct { - uint32_t modifierFlags; - double pluginX; - double pluginY; - int32_t buttonNumber; - int32_t clickCount; - double deltaX; - double deltaY; - double deltaZ; - } mouse; - struct { - uint32_t modifierFlags; - NPNSString *characters; - NPNSString *charactersIgnoringModifiers; - NPBool isARepeat; - uint16_t keyCode; - } key; - struct { - CGContextRef context; - double x; - double y; - double width; - double height; - } draw; - struct { - NPBool hasFocus; - } focus; - struct { - NPNSString *text; - } text; - } data; -} NPCocoaEvent; - -#ifndef NP_NO_CARBON -/* Non-standard event types that can be passed to HandleEvent */ -enum NPEventType { - NPEventType_GetFocusEvent = (osEvt + 16), - NPEventType_LoseFocusEvent, - NPEventType_AdjustCursorEvent, - NPEventType_MenuCommandEvent, - NPEventType_ClippingChangedEvent, - NPEventType_ScrollingBeginsEvent = 1000, - NPEventType_ScrollingEndsEvent -}; -#endif /* NP_NO_CARBON */ - -#endif /* XP_MACOSX */ - -/* - * Values for mode passed to NPP_New: - */ -#define NP_EMBED 1 -#define NP_FULL 2 - -/* - * Values for stream type passed to NPP_NewStream: - */ -#define NP_NORMAL 1 -#define NP_SEEK 2 -#define NP_ASFILE 3 -#define NP_ASFILEONLY 4 - -#define NP_MAXREADY (((unsigned)(~0)<<1)>>1) - -/* - * Flags for NPP_ClearSiteData. - */ -#define NP_CLEAR_ALL 0 -#define NP_CLEAR_CACHE (1 << 0) - -#if !defined(__LP64__) -#if defined(XP_MACOSX) -#pragma options align=reset -#endif -#endif /* __LP64__ */ - -/*----------------------------------------------------------------------*/ -/* Error and Reason Code definitions */ -/*----------------------------------------------------------------------*/ - -/* - * Values of type NPError: - */ -#define NPERR_BASE 0 -#define NPERR_NO_ERROR (NPERR_BASE + 0) -#define NPERR_GENERIC_ERROR (NPERR_BASE + 1) -#define NPERR_INVALID_INSTANCE_ERROR (NPERR_BASE + 2) -#define NPERR_INVALID_FUNCTABLE_ERROR (NPERR_BASE + 3) -#define NPERR_MODULE_LOAD_FAILED_ERROR (NPERR_BASE + 4) -#define NPERR_OUT_OF_MEMORY_ERROR (NPERR_BASE + 5) -#define NPERR_INVALID_PLUGIN_ERROR (NPERR_BASE + 6) -#define NPERR_INVALID_PLUGIN_DIR_ERROR (NPERR_BASE + 7) -#define NPERR_INCOMPATIBLE_VERSION_ERROR (NPERR_BASE + 8) -#define NPERR_INVALID_PARAM (NPERR_BASE + 9) -#define NPERR_INVALID_URL (NPERR_BASE + 10) -#define NPERR_FILE_NOT_FOUND (NPERR_BASE + 11) -#define NPERR_NO_DATA (NPERR_BASE + 12) -#define NPERR_STREAM_NOT_SEEKABLE (NPERR_BASE + 13) -#define NPERR_TIME_RANGE_NOT_SUPPORTED (NPERR_BASE + 14) -#define NPERR_MALFORMED_SITE (NPERR_BASE + 15) - -/* - * Values of type NPReason: - */ -#define NPRES_BASE 0 -#define NPRES_DONE (NPRES_BASE + 0) -#define NPRES_NETWORK_ERR (NPRES_BASE + 1) -#define NPRES_USER_BREAK (NPRES_BASE + 2) - -/* - * Don't use these obsolete error codes any more. - */ -#define NP_NOERR NP_NOERR_is_obsolete_use_NPERR_NO_ERROR -#define NP_EINVAL NP_EINVAL_is_obsolete_use_NPERR_GENERIC_ERROR -#define NP_EABORT NP_EABORT_is_obsolete_use_NPRES_USER_BREAK - -/* - * Version feature information - */ -#define NPVERS_HAS_STREAMOUTPUT 8 -#define NPVERS_HAS_NOTIFICATION 9 -#define NPVERS_HAS_LIVECONNECT 9 -#define NPVERS_68K_HAS_LIVECONNECT 11 -#define NPVERS_HAS_WINDOWLESS 11 -#define NPVERS_HAS_XPCONNECT_SCRIPTING 13 -#define NPVERS_HAS_NPRUNTIME_SCRIPTING 14 -#define NPVERS_HAS_FORM_VALUES 15 -#define NPVERS_HAS_POPUPS_ENABLED_STATE 16 -#define NPVERS_HAS_RESPONSE_HEADERS 17 -#define NPVERS_HAS_NPOBJECT_ENUM 18 -#define NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL 19 -#define NPVERS_HAS_ALL_NETWORK_STREAMS 20 -#define NPVERS_HAS_URL_AND_AUTH_INFO 21 -#define NPVERS_HAS_PRIVATE_MODE 22 -#define NPVERS_MACOSX_HAS_COCOA_EVENTS 23 -#define NPVERS_HAS_ADVANCED_KEY_HANDLING 25 -#define NPVERS_HAS_URL_REDIRECT_HANDLING 26 -#define NPVERS_HAS_CLEAR_SITE_DATA 27 - -/*----------------------------------------------------------------------*/ -/* Function Prototypes */ -/*----------------------------------------------------------------------*/ - -#if defined(__OS2__) -#define NP_LOADDS _System -#else -#define NP_LOADDS -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* NPP_* functions are provided by the plugin and called by the navigator. */ - -#if defined(XP_UNIX) -const char* NPP_GetMIMEDescription(void); -#endif - -NPError NP_LOADDS NPP_New(NPMIMEType pluginType, NPP instance, - uint16_t mode, int16_t argc, char* argn[], - char* argv[], NPSavedData* saved); -NPError NP_LOADDS NPP_Destroy(NPP instance, NPSavedData** save); -NPError NP_LOADDS NPP_SetWindow(NPP instance, NPWindow* window); -NPError NP_LOADDS NPP_NewStream(NPP instance, NPMIMEType type, - NPStream* stream, NPBool seekable, - uint16_t* stype); -NPError NP_LOADDS NPP_DestroyStream(NPP instance, NPStream* stream, - NPReason reason); -int32_t NP_LOADDS NPP_WriteReady(NPP instance, NPStream* stream); -int32_t NP_LOADDS NPP_Write(NPP instance, NPStream* stream, int32_t offset, - int32_t len, void* buffer); -void NP_LOADDS NPP_StreamAsFile(NPP instance, NPStream* stream, - const char* fname); -void NP_LOADDS NPP_Print(NPP instance, NPPrint* platformPrint); -int16_t NP_LOADDS NPP_HandleEvent(NPP instance, void* event); -void NP_LOADDS NPP_URLNotify(NPP instance, const char* url, - NPReason reason, void* notifyData); -NPError NP_LOADDS NPP_GetValue(NPP instance, NPPVariable variable, void *value); -NPError NP_LOADDS NPP_SetValue(NPP instance, NPNVariable variable, void *value); -NPBool NP_LOADDS NPP_GotFocus(NPP instance, NPFocusDirection direction); -void NP_LOADDS NPP_LostFocus(NPP instance); -void NP_LOADDS NPP_URLRedirectNotify(NPP instance, const char* url, int32_t status, void* notifyData); -NPError NP_LOADDS NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge); -char** NP_LOADDS NPP_GetSitesWithData(void); -void NP_LOADDS NPP_DidComposite(NPP instance); - -/* NPN_* functions are provided by the navigator and called by the plugin. */ -void NP_LOADDS NPN_Version(int* plugin_major, int* plugin_minor, - int* netscape_major, int* netscape_minor); -NPError NP_LOADDS NPN_GetURLNotify(NPP instance, const char* url, - const char* target, void* notifyData); -NPError NP_LOADDS NPN_GetURL(NPP instance, const char* url, - const char* target); -NPError NP_LOADDS NPN_PostURLNotify(NPP instance, const char* url, - const char* target, uint32_t len, - const char* buf, NPBool file, - void* notifyData); -NPError NP_LOADDS NPN_PostURL(NPP instance, const char* url, - const char* target, uint32_t len, - const char* buf, NPBool file); -NPError NP_LOADDS NPN_RequestRead(NPStream* stream, NPByteRange* rangeList); -NPError NP_LOADDS NPN_NewStream(NPP instance, NPMIMEType type, - const char* target, NPStream** stream); -int32_t NP_LOADDS NPN_Write(NPP instance, NPStream* stream, int32_t len, - void* buffer); -NPError NP_LOADDS NPN_DestroyStream(NPP instance, NPStream* stream, - NPReason reason); -void NP_LOADDS NPN_Status(NPP instance, const char* message); -const char* NP_LOADDS NPN_UserAgent(NPP instance); -void* NP_LOADDS NPN_MemAlloc(uint32_t size); -void NP_LOADDS NPN_MemFree(void* ptr); -uint32_t NP_LOADDS NPN_MemFlush(uint32_t size); -void NP_LOADDS NPN_ReloadPlugins(NPBool reloadPages); -NPError NP_LOADDS NPN_GetValue(NPP instance, NPNVariable variable, - void *value); -NPError NP_LOADDS NPN_SetValue(NPP instance, NPPVariable variable, - void *value); -void NP_LOADDS NPN_InvalidateRect(NPP instance, NPRect *invalidRect); -void NP_LOADDS NPN_InvalidateRegion(NPP instance, - NPRegion invalidRegion); -void NP_LOADDS NPN_ForceRedraw(NPP instance); -void NP_LOADDS NPN_PushPopupsEnabledState(NPP instance, NPBool enabled); -void NP_LOADDS NPN_PopPopupsEnabledState(NPP instance); -void NP_LOADDS NPN_PluginThreadAsyncCall(NPP instance, - void (*func) (void *), - void *userData); -NPError NP_LOADDS NPN_GetValueForURL(NPP instance, NPNURLVariable variable, - const char *url, char **value, - uint32_t *len); -NPError NP_LOADDS NPN_SetValueForURL(NPP instance, NPNURLVariable variable, - const char *url, const char *value, - uint32_t len); -NPError NP_LOADDS NPN_GetAuthenticationInfo(NPP instance, - const char *protocol, - const char *host, int32_t port, - const char *scheme, - const char *realm, - char **username, uint32_t *ulen, - char **password, - uint32_t *plen); -uint32_t NP_LOADDS NPN_ScheduleTimer(NPP instance, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID)); -void NP_LOADDS NPN_UnscheduleTimer(NPP instance, uint32_t timerID); -NPError NP_LOADDS NPN_PopUpContextMenu(NPP instance, NPMenu* menu); -NPBool NP_LOADDS NPN_ConvertPoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace); -NPBool NP_LOADDS NPN_HandleEvent(NPP instance, void *event, NPBool handled); -NPBool NP_LOADDS NPN_UnfocusInstance(NPP instance, NPFocusDirection direction); -void NP_LOADDS NPN_URLRedirectResponse(NPP instance, void* notifyData, NPBool allow); - -#ifdef __cplusplus -} /* end extern "C" */ -#endif - -#endif /* RC_INVOKED */ -#if defined(__OS2__) -#pragma pack() -#endif - -#endif /* npapi_h_ */ diff --git a/direct/src/plugin_npapi/npfunctions.h b/direct/src/plugin_npapi/npfunctions.h deleted file mode 100644 index a40e402dca..0000000000 --- a/direct/src/plugin_npapi/npfunctions.h +++ /dev/null @@ -1,329 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef npfunctions_h_ -#define npfunctions_h_ - -#ifdef __OS2__ -#pragma pack(1) -#define NP_LOADDS _System -#else -#define NP_LOADDS -#endif - -#include "npapi.h" -#include "npruntime.h" - -typedef NPError (* NP_LOADDS NPP_NewProcPtr)(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved); -typedef NPError (* NP_LOADDS NPP_DestroyProcPtr)(NPP instance, NPSavedData** save); -typedef NPError (* NP_LOADDS NPP_SetWindowProcPtr)(NPP instance, NPWindow* window); -typedef NPError (* NP_LOADDS NPP_NewStreamProcPtr)(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype); -typedef NPError (* NP_LOADDS NPP_DestroyStreamProcPtr)(NPP instance, NPStream* stream, NPReason reason); -typedef int32_t (* NP_LOADDS NPP_WriteReadyProcPtr)(NPP instance, NPStream* stream); -typedef int32_t (* NP_LOADDS NPP_WriteProcPtr)(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer); -typedef void (* NP_LOADDS NPP_StreamAsFileProcPtr)(NPP instance, NPStream* stream, const char* fname); -typedef void (* NP_LOADDS NPP_PrintProcPtr)(NPP instance, NPPrint* platformPrint); -typedef int16_t (* NP_LOADDS NPP_HandleEventProcPtr)(NPP instance, void* event); -typedef void (* NP_LOADDS NPP_URLNotifyProcPtr)(NPP instance, const char* url, NPReason reason, void* notifyData); -/* Any NPObjects returned to the browser via NPP_GetValue should be retained - by the plugin on the way out. The browser is responsible for releasing. */ -typedef NPError (* NP_LOADDS NPP_GetValueProcPtr)(NPP instance, NPPVariable variable, void *ret_value); -typedef NPError (* NP_LOADDS NPP_SetValueProcPtr)(NPP instance, NPNVariable variable, void *value); -typedef NPBool (* NP_LOADDS NPP_GotFocusPtr)(NPP instance, NPFocusDirection direction); -typedef void (* NP_LOADDS NPP_LostFocusPtr)(NPP instance); -typedef void (* NP_LOADDS NPP_URLRedirectNotifyPtr)(NPP instance, const char* url, int32_t status, void* notifyData); -typedef NPError (* NP_LOADDS NPP_ClearSiteDataPtr)(const char* site, uint64_t flags, uint64_t maxAge); -typedef char** (* NP_LOADDS NPP_GetSitesWithDataPtr)(void); -typedef void (* NP_LOADDS NPP_DidCompositePtr)(NPP instance); - -typedef NPError (* NP_LOADDS NPN_GetValueProcPtr)(NPP instance, NPNVariable variable, void *ret_value); -typedef NPError (* NP_LOADDS NPN_SetValueProcPtr)(NPP instance, NPPVariable variable, void *value); -typedef NPError (* NP_LOADDS NPN_GetURLNotifyProcPtr)(NPP instance, const char* url, const char* window, void* notifyData); -typedef NPError (* NP_LOADDS NPN_PostURLNotifyProcPtr)(NPP instance, const char* url, const char* window, uint32_t len, const char* buf, NPBool file, void* notifyData); -typedef NPError (* NP_LOADDS NPN_GetURLProcPtr)(NPP instance, const char* url, const char* window); -typedef NPError (* NP_LOADDS NPN_PostURLProcPtr)(NPP instance, const char* url, const char* window, uint32_t len, const char* buf, NPBool file); -typedef NPError (* NP_LOADDS NPN_RequestReadProcPtr)(NPStream* stream, NPByteRange* rangeList); -typedef NPError (* NP_LOADDS NPN_NewStreamProcPtr)(NPP instance, NPMIMEType type, const char* window, NPStream** stream); -typedef int32_t (* NP_LOADDS NPN_WriteProcPtr)(NPP instance, NPStream* stream, int32_t len, void* buffer); -typedef NPError (* NP_LOADDS NPN_DestroyStreamProcPtr)(NPP instance, NPStream* stream, NPReason reason); -typedef void (* NP_LOADDS NPN_StatusProcPtr)(NPP instance, const char* message); -/* Browser manages the lifetime of the buffer returned by NPN_UserAgent, don't - depend on it sticking around and don't free it. */ -typedef const char* (* NP_LOADDS NPN_UserAgentProcPtr)(NPP instance); -typedef void* (* NP_LOADDS NPN_MemAllocProcPtr)(uint32_t size); -typedef void (* NP_LOADDS NPN_MemFreeProcPtr)(void* ptr); -typedef uint32_t (* NP_LOADDS NPN_MemFlushProcPtr)(uint32_t size); -typedef void (* NP_LOADDS NPN_ReloadPluginsProcPtr)(NPBool reloadPages); -typedef void* (* NP_LOADDS NPN_GetJavaEnvProcPtr)(void); -typedef void* (* NP_LOADDS NPN_GetJavaPeerProcPtr)(NPP instance); -typedef void (* NP_LOADDS NPN_InvalidateRectProcPtr)(NPP instance, NPRect *rect); -typedef void (* NP_LOADDS NPN_InvalidateRegionProcPtr)(NPP instance, NPRegion region); -typedef void (* NP_LOADDS NPN_ForceRedrawProcPtr)(NPP instance); -typedef NPIdentifier (* NP_LOADDS NPN_GetStringIdentifierProcPtr)(const NPUTF8* name); -typedef void (* NP_LOADDS NPN_GetStringIdentifiersProcPtr)(const NPUTF8** names, int32_t nameCount, NPIdentifier* identifiers); -typedef NPIdentifier (* NP_LOADDS NPN_GetIntIdentifierProcPtr)(int32_t intid); -typedef bool (* NP_LOADDS NPN_IdentifierIsStringProcPtr)(NPIdentifier identifier); -typedef NPUTF8* (* NP_LOADDS NPN_UTF8FromIdentifierProcPtr)(NPIdentifier identifier); -typedef int32_t (* NP_LOADDS NPN_IntFromIdentifierProcPtr)(NPIdentifier identifier); -typedef NPObject* (* NP_LOADDS NPN_CreateObjectProcPtr)(NPP npp, NPClass *aClass); -typedef NPObject* (* NP_LOADDS NPN_RetainObjectProcPtr)(NPObject *obj); -typedef void (* NP_LOADDS NPN_ReleaseObjectProcPtr)(NPObject *obj); -typedef bool (* NP_LOADDS NPN_InvokeProcPtr)(NPP npp, NPObject* obj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result); -typedef bool (* NP_LOADDS NPN_InvokeDefaultProcPtr)(NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result); -typedef bool (* NP_LOADDS NPN_EvaluateProcPtr)(NPP npp, NPObject *obj, NPString *script, NPVariant *result); -typedef bool (* NP_LOADDS NPN_GetPropertyProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName, NPVariant *result); -typedef bool (* NP_LOADDS NPN_SetPropertyProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName, const NPVariant *value); -typedef bool (* NP_LOADDS NPN_RemovePropertyProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName); -typedef bool (* NP_LOADDS NPN_HasPropertyProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName); -typedef bool (* NP_LOADDS NPN_HasMethodProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName); -typedef void (* NP_LOADDS NPN_ReleaseVariantValueProcPtr)(NPVariant *variant); -typedef void (* NP_LOADDS NPN_SetExceptionProcPtr)(NPObject *obj, const NPUTF8 *message); -typedef void (* NP_LOADDS NPN_PushPopupsEnabledStateProcPtr)(NPP npp, NPBool enabled); -typedef void (* NP_LOADDS NPN_PopPopupsEnabledStateProcPtr)(NPP npp); -typedef bool (* NP_LOADDS NPN_EnumerateProcPtr)(NPP npp, NPObject *obj, NPIdentifier **identifier, uint32_t *count); -typedef void (* NP_LOADDS NPN_PluginThreadAsyncCallProcPtr)(NPP instance, void (*func)(void *), void *userData); -typedef bool (* NP_LOADDS NPN_ConstructProcPtr)(NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result); -typedef NPError (* NP_LOADDS NPN_GetValueForURLPtr)(NPP npp, NPNURLVariable variable, const char *url, char **value, uint32_t *len); -typedef NPError (* NP_LOADDS NPN_SetValueForURLPtr)(NPP npp, NPNURLVariable variable, const char *url, const char *value, uint32_t len); -typedef NPError (* NP_LOADDS NPN_GetAuthenticationInfoPtr)(NPP npp, const char *protocol, const char *host, int32_t port, const char *scheme, const char *realm, char **username, uint32_t *ulen, char **password, uint32_t *plen); -typedef uint32_t (* NP_LOADDS NPN_ScheduleTimerPtr)(NPP instance, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID)); -typedef void (* NP_LOADDS NPN_UnscheduleTimerPtr)(NPP instance, uint32_t timerID); -typedef NPError (* NP_LOADDS NPN_PopUpContextMenuPtr)(NPP instance, NPMenu* menu); -typedef NPBool (* NP_LOADDS NPN_ConvertPointPtr)(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace); -typedef NPBool (* NP_LOADDS NPN_HandleEventPtr)(NPP instance, void *event, NPBool handled); -typedef NPBool (* NP_LOADDS NPN_UnfocusInstancePtr)(NPP instance, NPFocusDirection direction); -typedef void (* NP_LOADDS NPN_URLRedirectResponsePtr)(NPP instance, void* notifyData, NPBool allow); - -typedef void (* NP_LOADDS NPN_DummyPtr)(void); - -typedef struct _NPPluginFuncs { - uint16_t size; - uint16_t version; - NPP_NewProcPtr newp; - NPP_DestroyProcPtr destroy; - NPP_SetWindowProcPtr setwindow; - NPP_NewStreamProcPtr newstream; - NPP_DestroyStreamProcPtr destroystream; - NPP_StreamAsFileProcPtr asfile; - NPP_WriteReadyProcPtr writeready; - NPP_WriteProcPtr write; - NPP_PrintProcPtr print; - NPP_HandleEventProcPtr event; - NPP_URLNotifyProcPtr urlnotify; - void* javaClass; - NPP_GetValueProcPtr getvalue; - NPP_SetValueProcPtr setvalue; - NPP_GotFocusPtr gotfocus; - NPP_LostFocusPtr lostfocus; - NPP_URLRedirectNotifyPtr urlredirectnotify; - NPP_ClearSiteDataPtr clearsitedata; - NPP_GetSitesWithDataPtr getsiteswithdata; - NPP_DidCompositePtr didComposite; -} NPPluginFuncs; - -typedef struct _NPNetscapeFuncs { - uint16_t size; - uint16_t version; - NPN_GetURLProcPtr geturl; - NPN_PostURLProcPtr posturl; - NPN_RequestReadProcPtr requestread; - NPN_NewStreamProcPtr newstream; - NPN_WriteProcPtr write; - NPN_DestroyStreamProcPtr destroystream; - NPN_StatusProcPtr status; - NPN_UserAgentProcPtr uagent; - NPN_MemAllocProcPtr memalloc; - NPN_MemFreeProcPtr memfree; - NPN_MemFlushProcPtr memflush; - NPN_ReloadPluginsProcPtr reloadplugins; - NPN_GetJavaEnvProcPtr getJavaEnv; - NPN_GetJavaPeerProcPtr getJavaPeer; - NPN_GetURLNotifyProcPtr geturlnotify; - NPN_PostURLNotifyProcPtr posturlnotify; - NPN_GetValueProcPtr getvalue; - NPN_SetValueProcPtr setvalue; - NPN_InvalidateRectProcPtr invalidaterect; - NPN_InvalidateRegionProcPtr invalidateregion; - NPN_ForceRedrawProcPtr forceredraw; - NPN_GetStringIdentifierProcPtr getstringidentifier; - NPN_GetStringIdentifiersProcPtr getstringidentifiers; - NPN_GetIntIdentifierProcPtr getintidentifier; - NPN_IdentifierIsStringProcPtr identifierisstring; - NPN_UTF8FromIdentifierProcPtr utf8fromidentifier; - NPN_IntFromIdentifierProcPtr intfromidentifier; - NPN_CreateObjectProcPtr createobject; - NPN_RetainObjectProcPtr retainobject; - NPN_ReleaseObjectProcPtr releaseobject; - NPN_InvokeProcPtr invoke; - NPN_InvokeDefaultProcPtr invokeDefault; - NPN_EvaluateProcPtr evaluate; - NPN_GetPropertyProcPtr getproperty; - NPN_SetPropertyProcPtr setproperty; - NPN_RemovePropertyProcPtr removeproperty; - NPN_HasPropertyProcPtr hasproperty; - NPN_HasMethodProcPtr hasmethod; - NPN_ReleaseVariantValueProcPtr releasevariantvalue; - NPN_SetExceptionProcPtr setexception; - NPN_PushPopupsEnabledStateProcPtr pushpopupsenabledstate; - NPN_PopPopupsEnabledStateProcPtr poppopupsenabledstate; - NPN_EnumerateProcPtr enumerate; - NPN_PluginThreadAsyncCallProcPtr pluginthreadasynccall; - NPN_ConstructProcPtr construct; - NPN_GetValueForURLPtr getvalueforurl; - NPN_SetValueForURLPtr setvalueforurl; - NPN_GetAuthenticationInfoPtr getauthenticationinfo; - NPN_ScheduleTimerPtr scheduletimer; - NPN_UnscheduleTimerPtr unscheduletimer; - NPN_PopUpContextMenuPtr popupcontextmenu; - NPN_ConvertPointPtr convertpoint; - NPN_HandleEventPtr handleevent; - NPN_UnfocusInstancePtr unfocusinstance; - NPN_URLRedirectResponsePtr urlredirectresponse; - NPN_DummyPtr initasyncsurfaceOBSOLETE; - NPN_DummyPtr finalizeasyncsurfaceOBSOLETE; - NPN_DummyPtr setcurrentasyncsurfaceOBSOLETE; -} NPNetscapeFuncs; - -#ifdef XP_MACOSX -/* - * Mac OS X version(s) of NP_GetMIMEDescription(const char *) - * These can be called to retreive MIME information from the plugin dynamically - * - * Note: For compatibility with Quicktime, BPSupportedMIMEtypes is another way - * to get mime info from the plugin only on OSX and may not be supported - * in furture version -- use NP_GetMIMEDescription instead - */ -enum -{ - kBPSupportedMIMETypesStructVers_1 = 1 -}; -typedef struct _BPSupportedMIMETypes -{ - SInt32 structVersion; /* struct version */ - Handle typeStrings; /* STR# formated handle, allocated by plug-in */ - Handle infoStrings; /* STR# formated handle, allocated by plug-in */ -} BPSupportedMIMETypes; -OSErr BP_GetSupportedMIMETypes(BPSupportedMIMETypes *mimeInfo, UInt32 flags); -#define NP_GETMIMEDESCRIPTION_NAME "NP_GetMIMEDescription" -typedef const char* (*NP_GetMIMEDescriptionProcPtr)(void); -typedef OSErr (*BP_GetSupportedMIMETypesProcPtr)(BPSupportedMIMETypes*, UInt32); -#endif - -#if defined(_WIN32) -#define OSCALL WINAPI -#else -#if defined(__OS2__) -#define OSCALL _System -#else -#define OSCALL -#endif -#endif - -#if defined(XP_UNIX) -/* GCC 3.3 and later support the visibility attribute. */ -#if defined(__GNUC__) && ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) -#define NP_VISIBILITY_DEFAULT __attribute__((visibility("default"))) -#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) -#define NP_VISIBILITY_DEFAULT __global -#else -#define NP_VISIBILITY_DEFAULT -#endif -#define NP_EXPORT(__type) NP_VISIBILITY_DEFAULT __type -#endif - -#if defined(_WIN32) || defined (__OS2__) -#ifdef __cplusplus -extern "C" { -#endif -/* plugin meta member functions */ -#if defined(__OS2__) -typedef struct _NPPluginData { /* Alternate OS2 Plugin interface */ - char *pMimeTypes; - char *pFileExtents; - char *pFileOpenTemplate; - char *pProductName; - char *pProductDescription; - unsigned long dwProductVersionMS; - unsigned long dwProductVersionLS; -} NPPluginData; -typedef NPError (OSCALL *NP_GetPluginDataFunc)(NPPluginData*); -NPError OSCALL NP_GetPluginData(NPPluginData * pPluginData); -#endif -typedef NPError (OSCALL *NP_GetEntryPointsFunc)(NPPluginFuncs*); -NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs); -typedef NPError (OSCALL *NP_InitializeFunc)(NPNetscapeFuncs*); -NPError OSCALL NP_Initialize(NPNetscapeFuncs* bFuncs); -typedef NPError (OSCALL *NP_ShutdownFunc)(void); -NPError OSCALL NP_Shutdown(void); -typedef const char* (*NP_GetMIMEDescriptionFunc)(void); -const char* NP_GetMIMEDescription(void); -#ifdef __cplusplus -} -#endif -#endif - -#if defined(__OS2__) -#pragma pack() -#endif - -#ifdef XP_UNIX -#ifdef __cplusplus -extern "C" { -#endif -typedef char* (*NP_GetPluginVersionFunc)(void); -NP_EXPORT(char*) NP_GetPluginVersion(void); -typedef const char* (*NP_GetMIMEDescriptionFunc)(void); -NP_EXPORT(const char*) NP_GetMIMEDescription(void); -#ifdef XP_MACOSX -typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*); -NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs); -typedef NPError (*NP_GetEntryPointsFunc)(NPPluginFuncs*); -NP_EXPORT(NPError) NP_GetEntryPoints(NPPluginFuncs* pFuncs); -#else -typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*, NPPluginFuncs*); -NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs); -#endif -typedef NPError (*NP_ShutdownFunc)(void); -NP_EXPORT(NPError) NP_Shutdown(void); -typedef NPError (*NP_GetValueFunc)(void *, NPPVariable, void *); -NP_EXPORT(NPError) NP_GetValue(void *future, NPPVariable aVariable, void *aValue); -#ifdef __cplusplus -} -#endif -#endif - -#endif /* npfunctions_h_ */ diff --git a/direct/src/plugin_npapi/nppanda3d.def b/direct/src/plugin_npapi/nppanda3d.def deleted file mode 100644 index ee2da2e02e..0000000000 --- a/direct/src/plugin_npapi/nppanda3d.def +++ /dev/null @@ -1,9 +0,0 @@ -; This file is required on Windows to export the appropriate symbols -; from the DLL. - -LIBRARY nppanda3d - -EXPORTS - NP_GetEntryPoints @1 - NP_Initialize @2 - NP_Shutdown @3 diff --git a/direct/src/plugin_npapi/nppanda3d.plist b/direct/src/plugin_npapi/nppanda3d.plist deleted file mode 100644 index 73c6745d02..0000000000 --- a/direct/src/plugin_npapi/nppanda3d.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en-US - CFBundleExecutable - nppanda3d - CFBundleIdentifier - org.panda3d.nppanda3d - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Panda3D - CFBundlePackageType - BRPL - CFBundleVersion - 1.0 - WebPluginName - Panda3D Game Engine Plug-In - WebPluginDescription - Runs 3-D games and interactive applets - WebPluginMIMETypes - - application/x-panda3d - - WebPluginExtensions - - p3d - - WebPluginTypeDescription - Panda3D applet - - - - diff --git a/direct/src/plugin_npapi/nppanda3d.r b/direct/src/plugin_npapi/nppanda3d.r deleted file mode 100644 index 6768adc357..0000000000 --- a/direct/src/plugin_npapi/nppanda3d.r +++ /dev/null @@ -1,19 +0,0 @@ -/* Apparently Firefox ignores the Info.plist file, - and looks only in the .rsrc file within the bundle, - which is compiled from the following source file. */ - -#include - -resource 'STR#' (126) { - { "Runs 3-D games and interactive applets", - "Panda3D Game Engine Plug-In" } -}; - -resource 'STR#' (127) { - { "Panda3D applet" } -}; - -resource 'STR#' (128) { - { "application/x-panda3d", "p3d" } -}; - diff --git a/direct/src/plugin_npapi/nppanda3d_common.h b/direct/src/plugin_npapi/nppanda3d_common.h deleted file mode 100644 index e6eeca6d99..0000000000 --- a/direct/src/plugin_npapi/nppanda3d_common.h +++ /dev/null @@ -1,118 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file nppanda3d_common.h - * @author drose - * @date 2009-06-19 - */ - -#ifndef NPPANDA3D_COMMON -#define NPPANDA3D_COMMON - -// This header file is included by all C++ files in this directory - -// It's a good idea to pick up this header file, even though we don't actually -// link with dtool. This header file defines useful system-wide config -// settings. -#include "dtool_config.h" - -// We include this header file directly out of its source directory, so we -// don't have to link with the library that builds it. -#include "../plugin/p3d_plugin.h" - -#include -#include -#include -#include - -// Appears in startup.cxx. -extern std::ostream *nout_stream; -#define nout (*nout_stream) - -extern std::string global_root_dir; -extern bool has_plugin_thread_async_call; - -#ifdef _WIN32 - -// Gecko requires all these symbols to be defined for Windows. -#define MOZILLA_STRICT_API -#define XP_WIN -#define _X86_ -#define _WINDOWS -#define _USRDLL -#define NPBASIC_EXPORTS - -// Panda already defines this one. #define WIN32 - -#include - -#elif defined(__APPLE__) - -// On Mac, Gecko requires this symbol to be defined. -#define XP_MACOSX - -#else - -#define XP_UNIX - -#endif // _WIN32, __APPLE__ - -#include "npapi.h" -#if NP_VERSION_MAJOR == 0 && NP_VERSION_MINOR <= 19 - #include -#else - // Somewhere between version 0.19 and 0.22, Mozilla renamed npupp.h to - // npfunctions.h. - #include "npfunctions.h" -#endif - -#if NP_VERSION_MAJOR == 0 && NP_VERSION_MINOR <= 19 - #ifdef _WIN32 - // Also somewhere in there, they started defining and using int16_t - // instead of int16, and so on. They already had int32_t from earlier, - // but the typedef is not quite the same as int32 (!), so we have to use - // #define to keep things compatible. - typedef int16 int16_t; - typedef uint16 uint16_t; -// #define int32_t int32 #define uint32_t uint32 - #endif // _WIN32 -#endif // NP_VERSION - -#include "load_plugin.h" - -// Mozilla's version of NPAPI has these names lowercase. WebKit's version has -// them uppercase. What a mess. We have to define a duplicate of the -// structure to allow us to reference them consistently. -struct UC_NPString { - const NPUTF8 *UTF8Characters; - uint32_t UTF8Length; -}; - - -// If we are building with a version of Gecko that supports the asynchronous -// callback function, we should use it--it's just so handy. -#if defined(NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL) && NP_VERSION_MINOR >= NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL -#define HAS_PLUGIN_THREAD_ASYNC_CALL 1 -#endif - -// We also need to know whether we have Apple's new Cocoa-based drawing and -// event callbacks. -#if defined(NPVERS_MACOSX_HAS_EVENT_MODELS) && NP_VERSION_MINOR >= NPVERS_MACOSX_HAS_EVENT_MODELS -#define MACOSX_HAS_EVENT_MODELS 1 -#endif - -// No one defined a symbol for the introduction of the Cocoa drawing, but it -// appears to have been version 19. -#if NP_VERSION_MINOR >= 19 -#define MACOSX_HAS_COREGRAPHICS_DRAWING_MODEL 1 -#endif - -// Appears in startup.cxx. -extern NPNetscapeFuncs *browser; - -#endif diff --git a/direct/src/plugin_npapi/nppanda3d_composite1.cxx b/direct/src/plugin_npapi/nppanda3d_composite1.cxx deleted file mode 100644 index 3bde838995..0000000000 --- a/direct/src/plugin_npapi/nppanda3d_composite1.cxx +++ /dev/null @@ -1,8 +0,0 @@ -#include "ppBrowserObject.cxx" -#include "ppDownloadRequest.cxx" -#include "ppInstance.cxx" -#include "ppPandaObject.cxx" -#include "ppToplevelObject.cxx" -#include "startup.cxx" - - diff --git a/direct/src/plugin_npapi/npruntime.h b/direct/src/plugin_npapi/npruntime.h deleted file mode 100644 index 2039225cbe..0000000000 --- a/direct/src/plugin_npapi/npruntime.h +++ /dev/null @@ -1,393 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Copyright (c) 2004, Apple Computer, Inc. and The Mozilla Foundation. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the names of Apple Computer, Inc. ("Apple") or The Mozilla - * Foundation ("Mozilla") nor the names of their contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE, MOZILLA AND THEIR CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE, MOZILLA OR - * THEIR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ -#ifndef _NP_RUNTIME_H_ -#define _NP_RUNTIME_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "nptypes.h" - -/* - This API is used to facilitate binding code written in C to script - objects. The API in this header does not assume the presence of a - user agent. That is, it can be used to bind C code to scripting - environments outside of the context of a user agent. - - However, the normal use of the this API is in the context of a - scripting environment running in a browser or other user agent. - In particular it is used to support the extended Netscape - script-ability API for plugins (NP-SAP). NP-SAP is an extension - of the Netscape plugin API. As such we have adopted the use of - the "NP" prefix for this API. - - The following NP{N|P}Variables were added to the Netscape plugin - API (in npapi.h): - - NPNVWindowNPObject - NPNVPluginElementNPObject - NPPVpluginScriptableNPObject - - These variables are exposed through NPN_GetValue() and - NPP_GetValue() (respectively) and are used to establish the - initial binding between the user agent and native code. The DOM - objects in the user agent can be examined and manipulated using - the NPN_ functions that operate on NPObjects described in this - header. - - To the extent possible the assumptions about the scripting - language used by the scripting environment have been minimized. -*/ - -#define NP_BEGIN_MACRO do { -#define NP_END_MACRO } while (0) - -/* - Objects (non-primitive data) passed between 'C' and script is - always wrapped in an NPObject. The 'interface' of an NPObject is - described by an NPClass. -*/ -typedef struct NPObject NPObject; -typedef struct NPClass NPClass; - -typedef char NPUTF8; -typedef struct _NPString { - const NPUTF8 *UTF8Characters; - uint32_t UTF8Length; -} NPString; - -typedef enum { - NPVariantType_Void, - NPVariantType_Null, - NPVariantType_Bool, - NPVariantType_Int32, - NPVariantType_Double, - NPVariantType_String, - NPVariantType_Object -} NPVariantType; - -typedef struct _NPVariant { - NPVariantType type; - union { - bool boolValue; - int32_t intValue; - double doubleValue; - NPString stringValue; - NPObject *objectValue; - } value; -} NPVariant; - -/* - NPN_ReleaseVariantValue is called on all 'out' parameters - references. Specifically it is to be called on variants that own - their value, as is the case with all non-const NPVariant* - arguments after a successful call to any methods (except this one) - in this API. - - After calling NPN_ReleaseVariantValue, the type of the variant - will be NPVariantType_Void. -*/ -void NPN_ReleaseVariantValue(NPVariant *variant); - -#define NPVARIANT_IS_VOID(_v) ((_v).type == NPVariantType_Void) -#define NPVARIANT_IS_NULL(_v) ((_v).type == NPVariantType_Null) -#define NPVARIANT_IS_BOOLEAN(_v) ((_v).type == NPVariantType_Bool) -#define NPVARIANT_IS_INT32(_v) ((_v).type == NPVariantType_Int32) -#define NPVARIANT_IS_DOUBLE(_v) ((_v).type == NPVariantType_Double) -#define NPVARIANT_IS_STRING(_v) ((_v).type == NPVariantType_String) -#define NPVARIANT_IS_OBJECT(_v) ((_v).type == NPVariantType_Object) - -#define NPVARIANT_TO_BOOLEAN(_v) ((_v).value.boolValue) -#define NPVARIANT_TO_INT32(_v) ((_v).value.intValue) -#define NPVARIANT_TO_DOUBLE(_v) ((_v).value.doubleValue) -#define NPVARIANT_TO_STRING(_v) ((_v).value.stringValue) -#define NPVARIANT_TO_OBJECT(_v) ((_v).value.objectValue) - -#define VOID_TO_NPVARIANT(_v) \ -NP_BEGIN_MACRO \ - (_v).type = NPVariantType_Void; \ - (_v).value.objectValue = nullptr; \ -NP_END_MACRO - -#define NULL_TO_NPVARIANT(_v) \ -NP_BEGIN_MACRO \ - (_v).type = NPVariantType_Null; \ - (_v).value.objectValue = nullptr; \ -NP_END_MACRO - -#define BOOLEAN_TO_NPVARIANT(_val, _v) \ -NP_BEGIN_MACRO \ - (_v).type = NPVariantType_Bool; \ - (_v).value.boolValue = !!(_val); \ -NP_END_MACRO - -#define INT32_TO_NPVARIANT(_val, _v) \ -NP_BEGIN_MACRO \ - (_v).type = NPVariantType_Int32; \ - (_v).value.intValue = _val; \ -NP_END_MACRO - -#define DOUBLE_TO_NPVARIANT(_val, _v) \ -NP_BEGIN_MACRO \ - (_v).type = NPVariantType_Double; \ - (_v).value.doubleValue = _val; \ -NP_END_MACRO - -#define STRINGZ_TO_NPVARIANT(_val, _v) \ -NP_BEGIN_MACRO \ - (_v).type = NPVariantType_String; \ - NPString str = { _val, (uint32_t)(strlen(_val)) }; \ - (_v).value.stringValue = str; \ -NP_END_MACRO - -#define STRINGN_TO_NPVARIANT(_val, _len, _v) \ -NP_BEGIN_MACRO \ - (_v).type = NPVariantType_String; \ - NPString str = { _val, (uint32_t)(_len) }; \ - (_v).value.stringValue = str; \ -NP_END_MACRO - -#define OBJECT_TO_NPVARIANT(_val, _v) \ -NP_BEGIN_MACRO \ - (_v).type = NPVariantType_Object; \ - (_v).value.objectValue = _val; \ -NP_END_MACRO - - -/* - Type mappings (JavaScript types have been used for illustration - purposes): - - JavaScript to C (NPVariant with type:) - undefined NPVariantType_Void - null NPVariantType_Null - Boolean NPVariantType_Bool - Number NPVariantType_Double or NPVariantType_Int32 - String NPVariantType_String - Object NPVariantType_Object - - C (NPVariant with type:) to JavaScript - NPVariantType_Void undefined - NPVariantType_Null null - NPVariantType_Bool Boolean - NPVariantType_Int32 Number - NPVariantType_Double Number - NPVariantType_String String - NPVariantType_Object Object -*/ - -typedef void *NPIdentifier; - -/* - NPObjects have methods and properties. Methods and properties are - identified with NPIdentifiers. These identifiers may be reflected - in script. NPIdentifiers can be either strings or integers, IOW, - methods and properties can be identified by either strings or - integers (i.e. foo["bar"] vs foo[1]). NPIdentifiers can be - compared using ==. In case of any errors, the requested - NPIdentifier(s) will be NULL. NPIdentifier lifetime is controlled - by the browser. Plugins do not need to worry about memory management - with regards to NPIdentifiers. -*/ -NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name); -void NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, - NPIdentifier *identifiers); -NPIdentifier NPN_GetIntIdentifier(int32_t intid); -bool NPN_IdentifierIsString(NPIdentifier identifier); - -/* - The NPUTF8 returned from NPN_UTF8FromIdentifier SHOULD be freed. -*/ -NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier); - -/* - Get the integer represented by identifier. If identifier is not an - integer identifier, the behaviour is undefined. -*/ -int32_t NPN_IntFromIdentifier(NPIdentifier identifier); - -/* - NPObject behavior is implemented using the following set of - callback functions. - - The NPVariant *result argument of these functions (where - applicable) should be released using NPN_ReleaseVariantValue(). -*/ -typedef NPObject *(*NPAllocateFunctionPtr)(NPP npp, NPClass *aClass); -typedef void (*NPDeallocateFunctionPtr)(NPObject *npobj); -typedef void (*NPInvalidateFunctionPtr)(NPObject *npobj); -typedef bool (*NPHasMethodFunctionPtr)(NPObject *npobj, NPIdentifier name); -typedef bool (*NPInvokeFunctionPtr)(NPObject *npobj, NPIdentifier name, - const NPVariant *args, uint32_t argCount, - NPVariant *result); -typedef bool (*NPInvokeDefaultFunctionPtr)(NPObject *npobj, - const NPVariant *args, - uint32_t argCount, - NPVariant *result); -typedef bool (*NPHasPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name); -typedef bool (*NPGetPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name, - NPVariant *result); -typedef bool (*NPSetPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name, - const NPVariant *value); -typedef bool (*NPRemovePropertyFunctionPtr)(NPObject *npobj, - NPIdentifier name); -typedef bool (*NPEnumerationFunctionPtr)(NPObject *npobj, NPIdentifier **value, - uint32_t *count); -typedef bool (*NPConstructFunctionPtr)(NPObject *npobj, - const NPVariant *args, - uint32_t argCount, - NPVariant *result); - -/* - NPObjects returned by create, retain, invoke, and getProperty pass - a reference count to the caller. That is, the callee adds a - reference count which passes to the caller. It is the caller's - responsibility to release the returned object. - - NPInvokeFunctionPtr function may return 0 to indicate a void - result. - - NPInvalidateFunctionPtr is called by the scripting environment - when the native code is shutdown. Any attempt to message a - NPObject instance after the invalidate callback has been - called will result in undefined behavior, even if the native code - is still retaining those NPObject instances. (The runtime - will typically return immediately, with 0 or NULL, from an attempt - to dispatch to a NPObject, but this behavior should not be - depended upon.) - - The NPEnumerationFunctionPtr function may pass an array of - NPIdentifiers back to the caller. The callee allocs the memory of - the array using NPN_MemAlloc(), and it's the caller's responsibility - to release it using NPN_MemFree(). -*/ -struct NPClass -{ - uint32_t structVersion; - NPAllocateFunctionPtr allocate; - NPDeallocateFunctionPtr deallocate; - NPInvalidateFunctionPtr invalidate; - NPHasMethodFunctionPtr hasMethod; - NPInvokeFunctionPtr invoke; - NPInvokeDefaultFunctionPtr invokeDefault; - NPHasPropertyFunctionPtr hasProperty; - NPGetPropertyFunctionPtr getProperty; - NPSetPropertyFunctionPtr setProperty; - NPRemovePropertyFunctionPtr removeProperty; - NPEnumerationFunctionPtr enumerate; - NPConstructFunctionPtr construct; -}; - -#define NP_CLASS_STRUCT_VERSION 3 - -#define NP_CLASS_STRUCT_VERSION_ENUM 2 -#define NP_CLASS_STRUCT_VERSION_CTOR 3 - -#define NP_CLASS_STRUCT_VERSION_HAS_ENUM(npclass) \ - ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM) - -#define NP_CLASS_STRUCT_VERSION_HAS_CTOR(npclass) \ - ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR) - -struct NPObject { - NPClass *_class; - uint32_t referenceCount; - /* - * Additional space may be allocated here by types of NPObjects - */ -}; - -/* - If the class has an allocate function, NPN_CreateObject invokes - that function, otherwise a NPObject is allocated and - returned. This method will initialize the referenceCount member of - the NPObject to 1. -*/ -NPObject *NPN_CreateObject(NPP npp, NPClass *aClass); - -/* - Increment the NPObject's reference count. -*/ -NPObject *NPN_RetainObject(NPObject *npobj); - -/* - Decremented the NPObject's reference count. If the reference - count goes to zero, the class's destroy function is invoke if - specified, otherwise the object is freed directly. -*/ -void NPN_ReleaseObject(NPObject *npobj); - -/* - Functions to access script objects represented by NPObject. - - Calls to script objects are synchronous. If a function returns a - value, it will be supplied via the result NPVariant - argument. Successful calls will return true, false will be - returned in case of an error. - - Calls made from plugin code to script must be made from the thread - on which the plugin was initialized. -*/ - -bool NPN_Invoke(NPP npp, NPObject *npobj, NPIdentifier methodName, - const NPVariant *args, uint32_t argCount, NPVariant *result); -bool NPN_InvokeDefault(NPP npp, NPObject *npobj, const NPVariant *args, - uint32_t argCount, NPVariant *result); -bool NPN_Evaluate(NPP npp, NPObject *npobj, NPString *script, - NPVariant *result); -bool NPN_GetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, - NPVariant *result); -bool NPN_SetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, - const NPVariant *value); -bool NPN_RemoveProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); -bool NPN_HasProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); -bool NPN_HasMethod(NPP npp, NPObject *npobj, NPIdentifier methodName); -bool NPN_Enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier, - uint32_t *count); -bool NPN_Construct(NPP npp, NPObject *npobj, const NPVariant *args, - uint32_t argCount, NPVariant *result); - -/* - NPN_SetException may be called to trigger a script exception upon - return from entry points into NPObjects. Typical usage: - - NPN_SetException (npobj, message); -*/ -void NPN_SetException(NPObject *npobj, const NPUTF8 *message); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/direct/src/plugin_npapi/nptypes.h b/direct/src/plugin_npapi/nptypes.h deleted file mode 100644 index 4d87fb844d..0000000000 --- a/direct/src/plugin_npapi/nptypes.h +++ /dev/null @@ -1,121 +0,0 @@ -/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * mozilla.org. - * Portions created by the Initial Developer are Copyright (C) 2004 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Johnny Stenback (Original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef nptypes_h_ -#define nptypes_h_ - -/* - * Header file for ensuring that C99 types ([u]int32_t, [u]int64_t and bool) and - * true/false macros are available. - */ - -#if (defined(WIN32) && !defined(__GNUC__)) || defined(OS2) - /* - * Win32 and OS/2 don't know C99, so define [u]int_16/32/64 here. The bool - * is predefined tho, both in C and C++. - */ - typedef short int16_t; - typedef unsigned short uint16_t; - typedef int int32_t; - typedef unsigned int uint32_t; - typedef long long int64_t; - typedef unsigned long long uint64_t; -#elif defined(_AIX) || defined(__sun) || defined(__osf__) || defined(IRIX) || defined(HPUX) - /* - * AIX and SunOS ship a inttypes.h header that defines [u]int32_t, - * but not bool for C. - */ - #include - - #ifndef __cplusplus - typedef int bool; - #define true 1 - #define false 0 - #endif -#elif defined(bsdi) || defined(FREEBSD) || defined(OPENBSD) - /* - * BSD/OS, FreeBSD, and OpenBSD ship sys/types.h that define int32_t and - * u_int32_t. - */ - #include - - /* - * BSD/OS ships no header that defines uint32_t, nor bool (for C) - */ - #if defined(bsdi) - typedef u_int32_t uint32_t; - typedef u_int64_t uint64_t; - - #if !defined(__cplusplus) - typedef int bool; - #define true 1 - #define false 0 - #endif - #else - /* - * FreeBSD and OpenBSD define uint32_t and bool. - */ - #include - #include - #endif -#elif defined(BEOS) - #include -#else - /* - * For those that ship a standard C99 stdint.h header file, include - * it. Can't do the same for stdbool.h tho, since some systems ship - * with a stdbool.h file that doesn't compile! - */ - #include - - #ifndef __cplusplus - #if !defined(__GNUC__) || (__GNUC__ > 2 || __GNUC_MINOR__ > 95) - #include - #else - /* - * GCC 2.91 can't deal with a typedef for bool, but a #define - * works. - */ - #define bool int - #define true 1 - #define false 0 - #endif - #endif -#endif - -#endif /* nptypes_h_ */ diff --git a/direct/src/plugin_npapi/ppBrowserObject.I b/direct/src/plugin_npapi/ppBrowserObject.I deleted file mode 100644 index 431535af85..0000000000 --- a/direct/src/plugin_npapi/ppBrowserObject.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppBrowserObject.I - * @author drose - * @date 2009-07-05 - */ diff --git a/direct/src/plugin_npapi/ppBrowserObject.cxx b/direct/src/plugin_npapi/ppBrowserObject.cxx deleted file mode 100644 index 17104b9e6c..0000000000 --- a/direct/src/plugin_npapi/ppBrowserObject.cxx +++ /dev/null @@ -1,257 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppBrowserObject.cxx - * @author drose - * @date 2009-07-05 - */ - -#include "ppBrowserObject.h" -#include "ppInstance.h" -#include -#include // strncpy - -using std::string; - -// The following functions are C-style wrappers around the above -// PPBrowserObject methods; they are defined to allow us to create the C-style -// P3D_class_definition method table to store in the P3D_object structure. -static void -object_finish(P3D_object *object) { - delete ((PPBrowserObject *)object); -} - -static int -object_get_repr(P3D_object *object, char *buffer, int buffer_length) { - return ((const PPBrowserObject *)object)->get_repr(buffer, buffer_length); -} - -static P3D_object * -object_get_property(P3D_object *object, const char *property) { - return ((const PPBrowserObject *)object)->get_property(property); -} - -static bool -object_set_property(P3D_object *object, const char *property, - bool needs_response, P3D_object *value) { - return ((PPBrowserObject *)object)->set_property(property, needs_response, value); -} - -static P3D_object * -object_call(P3D_object *object, const char *method_name, - bool needs_response, - P3D_object *params[], int num_params) { - if (method_name == nullptr) { - method_name = ""; - } - P3D_object *response = ((const PPBrowserObject *)object)->call(method_name, params, num_params); - if (!needs_response) { - // No response was expected. Throw away the response we received, so we - // can be consistent with defined semantics. - P3D_OBJECT_XDECREF(response); - response = nullptr; - } - return response; -} - -static P3D_object * -object_eval(P3D_object *object, const char *expression) { - return ((const PPBrowserObject *)object)->eval(expression); -} - -P3D_class_definition *PPBrowserObject::_browser_object_class; - -/** - * - */ -PPBrowserObject:: -PPBrowserObject(PPInstance *inst, NPObject *npobj) : - _instance(inst), - _npobj(npobj) -{ - _class = get_class_definition(); - _ref_count = 1; - browser->retainobject(_npobj); -} - -/** - * - */ -PPBrowserObject:: -PPBrowserObject(const PPBrowserObject ©) : - _instance(copy._instance), - _npobj(copy._npobj) -{ - _class = get_class_definition(); - _ref_count = 1; - browser->retainobject(_npobj); -} - -/** - * - */ -PPBrowserObject:: -~PPBrowserObject() { - assert(_ref_count == 0); - browser->releaseobject(_npobj); -} - -/** - * Returns a user-friendly representation of the object, similar to - * get_string(), above. - */ -int PPBrowserObject:: -get_repr(char *buffer, int buffer_length) const { - std::ostringstream strm; - strm << "NPObject " << _npobj; - string result = strm.str(); - strncpy(buffer, result.c_str(), buffer_length); - return (int)result.size(); -} - -/** - * Returns the named property element in the object. The return value is a - * freshly-allocated PPBrowserObject object that must be deleted by the - * caller, or NULL on error. - */ -P3D_object *PPBrowserObject:: -get_property(const string &property) const { - NPIdentifier property_name = browser->getstringidentifier(property.c_str()); - if (!browser->hasproperty(_instance->get_npp_instance(), _npobj, - property_name)) { - // No such property. - return nullptr; - } - - NPVariant result; - if (!browser->getproperty(_instance->get_npp_instance(), _npobj, - property_name, &result)) { - // Failed to retrieve property. - return nullptr; - } - - P3D_object *object = _instance->variant_to_p3dobj(&result); - browser->releasevariantvalue(&result); - return object; -} - -/** - * Modifies (or deletes, if value is NULL) the named property element in the - * object. Returns true on success, false on failure. - */ -bool PPBrowserObject:: -set_property(const string &property, bool needs_response, P3D_object *value) { - NPIdentifier property_name = browser->getstringidentifier(property.c_str()); - bool result; - if (value != nullptr) { - // Set the property. - NPVariant npvalue; - _instance->p3dobj_to_variant(&npvalue, value); - result = browser->setproperty(_instance->get_npp_instance(), _npobj, - property_name, &npvalue); - browser->releasevariantvalue(&npvalue); - - } else { - // Delete the property. - result = browser->removeproperty(_instance->get_npp_instance(), _npobj, - property_name); - } - - return result; -} - -/** - * Invokes the named method on the object, passing the indicated parameters. - * If the method name is empty, invokes the object itself. Returns the return - * value on success, NULL on error. - */ -P3D_object *PPBrowserObject:: -call(const string &method_name, P3D_object *params[], int num_params) const { - // First, convert all of the parameters. - NPVariant *npparams = new NPVariant[num_params]; - for (int i = 0; i < num_params; ++i) { - _instance->p3dobj_to_variant(&npparams[i], params[i]); - } - - NPVariant result; - if (method_name.empty()) { - // Call the default method. - if (!browser->invokeDefault(_instance->get_npp_instance(), _npobj, - npparams, num_params, &result)) { - // Failed to invoke. - delete[] npparams; - return nullptr; - } - } else { - // Call the named method. - - NPIdentifier method_id = browser->getstringidentifier(method_name.c_str()); - if (!browser->invoke(_instance->get_npp_instance(), _npobj, method_id, - npparams, num_params, &result)) { - // Failed to invoke. - delete[] npparams; - return nullptr; - } - } - - delete[] npparams; - - P3D_object *object = _instance->variant_to_p3dobj(&result); - browser->releasevariantvalue(&result); - return object; -} - -/** - * Evaluates the indicated JavaScript expression in the context of the object. - */ -P3D_object *PPBrowserObject:: -eval(const string &expression) const { - NPString npexpr = { expression.c_str(), (uint32_t)expression.length() }; - - NPVariant result; - if (!browser->evaluate(_instance->get_npp_instance(), _npobj, - &npexpr, &result)) { - // Failed to eval. - return nullptr; - } - - P3D_object *object = _instance->variant_to_p3dobj(&result); - browser->releasevariantvalue(&result); - return object; -} - -/** - * Should be called when the core API is unloaded, and the associated class - * definition object is therefore invalidated. - */ -void PPBrowserObject:: -clear_class_definition() { - _browser_object_class = nullptr; -} - -/** - * Returns a pointer to the P3D_class_definition object that lists all of the - * C-style method pointers for this class object. - */ -P3D_class_definition *PPBrowserObject:: -get_class_definition() { - if (_browser_object_class == nullptr) { - // Create a default class_definition object, and fill in the appropriate - // pointers. - _browser_object_class = P3D_make_class_definition_ptr(); - _browser_object_class->_finish = &object_finish; - - _browser_object_class->_get_repr = &object_get_repr; - _browser_object_class->_get_property = &object_get_property; - _browser_object_class->_set_property = &object_set_property; - _browser_object_class->_call = &object_call; - _browser_object_class->_eval = &object_eval; - } - - return _browser_object_class; -} diff --git a/direct/src/plugin_npapi/ppBrowserObject.h b/direct/src/plugin_npapi/ppBrowserObject.h deleted file mode 100644 index 3b0835b450..0000000000 --- a/direct/src/plugin_npapi/ppBrowserObject.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppBrowserObject.h - * @author drose - * @date 2009-07-05 - */ - -#ifndef PPBROWSEROBJECT_H -#define PPBROWSEROBJECT_H - -#include "nppanda3d_common.h" - -class PPInstance; - -/** - * This is the interface layer between an NPObject and a P3D_object. It maps - * calls from P3D_object into the NPObject system, thus allowing Panda to view - * and operate on a browser object. - * - * Also see PPPandaObject, which maps calls the other way. - */ -class PPBrowserObject : public P3D_object { -public: - PPBrowserObject(PPInstance *inst, NPObject *npobj); - PPBrowserObject(const PPBrowserObject ©); - ~PPBrowserObject(); - - int get_repr(char *buffer, int buffer_length) const; - P3D_object *get_property(const std::string &property) const; - bool set_property(const std::string &property, bool needs_response, - P3D_object *value); - - P3D_object *call(const std::string &method_name, - P3D_object *params[], int num_params) const; - P3D_object *eval(const std::string &expression) const; - - static void clear_class_definition(); - -private: - static P3D_class_definition *get_class_definition(); - -private: - PPInstance *_instance; - NPObject *_npobj; - static P3D_class_definition *_browser_object_class; -}; - -#include "ppBrowserObject.I" - -#endif diff --git a/direct/src/plugin_npapi/ppDownloadRequest.I b/direct/src/plugin_npapi/ppDownloadRequest.I deleted file mode 100644 index d0610db522..0000000000 --- a/direct/src/plugin_npapi/ppDownloadRequest.I +++ /dev/null @@ -1,23 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppDownloadRequest.I - * @author drose - * @date 2009-06-23 - */ - -/** - * - */ -inline PPDownloadRequest:: -PPDownloadRequest(RequestType rtype, int user_id) : - _rtype(rtype), - _user_id(user_id), - _notified_done(false) -{ -} diff --git a/direct/src/plugin_npapi/ppDownloadRequest.cxx b/direct/src/plugin_npapi/ppDownloadRequest.cxx deleted file mode 100644 index 81e0605c27..0000000000 --- a/direct/src/plugin_npapi/ppDownloadRequest.cxx +++ /dev/null @@ -1,14 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppDownloadRequest.cxx - * @author drose - * @date 2009-06-23 - */ - -#include "ppDownloadRequest.h" diff --git a/direct/src/plugin_npapi/ppDownloadRequest.h b/direct/src/plugin_npapi/ppDownloadRequest.h deleted file mode 100644 index 55fe0f60fa..0000000000 --- a/direct/src/plugin_npapi/ppDownloadRequest.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppDownloadRequest.h - * @author drose - * @date 2009-06-23 - */ - -#ifndef PPDOWNLOADREQUEST_H -#define PPDOWNLOADREQUEST_H - -#include "nppanda3d_common.h" - -/** - * An instance of this object is assigned as the notifyData for URL requests, - * to help the plugin associate streams with requests. - */ -class PPDownloadRequest { -public: - enum RequestType { - RT_contents_file, - RT_core_dll, - RT_user, - RT_instance_data - }; - - inline PPDownloadRequest(RequestType rtype, int user_id = 0); - -public: - RequestType _rtype; - int _user_id; - - // This is sent true when we have notified the plugin that the stream is - // done. - bool _notified_done; -}; - -#include "ppDownloadRequest.I" - -#endif diff --git a/direct/src/plugin_npapi/ppInstance.I b/direct/src/plugin_npapi/ppInstance.I deleted file mode 100644 index 1c929d849b..0000000000 --- a/direct/src/plugin_npapi/ppInstance.I +++ /dev/null @@ -1,31 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppInstance.I - * @author drose - * @date 2009-06-19 - */ - -/** - * Returns the NPP object corresponding to this particular instance. - */ -inline NPP PPInstance:: -get_npp_instance() const { - return _npp_instance; -} - -/** - * Returns the current window parameters. - */ -const NPWindow *PPInstance:: -get_window() const { - if (_got_window) { - return &_window; - } - return nullptr; -} diff --git a/direct/src/plugin_npapi/ppInstance.cxx b/direct/src/plugin_npapi/ppInstance.cxx deleted file mode 100644 index 6a3d961e32..0000000000 --- a/direct/src/plugin_npapi/ppInstance.cxx +++ /dev/null @@ -1,3129 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppInstance.cxx - * @author drose - * @date 2009-06-19 - */ - -#include "ppInstance.h" -#include "ppPandaObject.h" -#include "ppToplevelObject.h" -#include "ppBrowserObject.h" -#include "startup.h" -#include "p3d_plugin_config.h" -#include "mkdir_complete.h" -#include "parse_color.h" -#include "nppanda3d_common.h" - -// We can include this header file to get the DTOOL_PLATFORM definition, even -// though we don't link with dtool. -#include "dtool_platform.h" -#include "pandaVersion.h" - -#include -#include -#include // strcmp() -#include - -#ifndef _WIN32 -#include -#include -#endif // _WIN32 - -#ifdef HAVE_X11 -#include -#include -#endif // HAVE_X11 - -using std::ios; -using std::ostream; -using std::ostringstream; -using std::string; -using std::vector; - - -PPInstance::FileDatas PPInstance::_file_datas; - -/** - * Creates a new instance of a Panda3D plugin window. The create_data - * structure is supplied from NPAPI, and defines the initial parameters - * specified in the HTML document. - */ -PPInstance:: -PPInstance(NPMIMEType pluginType, NPP instance, uint16_t mode, - int16_t argc, char *argn[], char *argv[], NPSavedData *saved, - P3D_window_handle_type window_handle_type, - P3D_event_type event_type) { - _p3d_inst = nullptr; - - _npp_instance = instance; - _npp_mode = mode; - _window_handle_type = window_handle_type; - _event_type = event_type; - _script_object = nullptr; - _contents_expiration = 0; - _failed = false; - _started = false; - - // Copy the tokens and save them within this object. - _tokens.reserve(argc); - for (int i = 0; i < argc; ++i) { - if (argn[i] != nullptr) { - const char *v = argv[i]; - if (v == nullptr) { - // Firefox might give us a NULL argv[i] in some cases. - v = ""; - } - - // Make the token lowercase, since HTML is case-insensitive but we're - // not. - string keyword; - for (const char *p = argn[i]; *p; ++p) { - keyword += tolower(*p); - } - - P3D_token token; - token._keyword = strdup(keyword.c_str()); - token._value = strdup(v); - _tokens.push_back(token); - } - } - - _root_dir = global_root_dir; - - _got_instance_url = false; - _p3d_instance_id = 0; - - // fgcolor and bgcolor are useful to know here (in case we have to draw a - // twirling icon). - - // The default bgcolor is white. - _bgcolor_r = _bgcolor_g = _bgcolor_b = 0xff; - if (has_token("bgcolor")) { - int r, g, b; - if (parse_color(r, g, b, lookup_token("bgcolor"))) { - _bgcolor_r = r; - _bgcolor_g = g; - _bgcolor_b = b; - } - } - - // The default fgcolor is either black or white, according to the brightness - // of the bgcolor. - if (_bgcolor_r + _bgcolor_g + _bgcolor_b > 0x80 + 0x80 + 0x80) { - _fgcolor_r = _fgcolor_g = _fgcolor_b = 0x00; - } else { - _fgcolor_r = _fgcolor_g = _fgcolor_b = 0xff; - } - if (has_token("fgcolor")) { - int r, g, b; - if (parse_color(r, g, b, lookup_token("fgcolor"))) { - _fgcolor_r = r; - _fgcolor_g = g; - _fgcolor_b = b; - } - } - - _use_xembed = false; - _got_window = false; - _python_window_open = false; -#ifdef HAVE_X11 - _twirl_subprocess_pid = -1; -#ifdef HAVE_GTK - _plug = nullptr; -#endif // HAVE_GTK -#endif // HAVE_X11 - -#ifdef _WIN32 - _hwnd = nullptr; - _bg_brush = nullptr; -#endif // _WIN32 - -#ifndef _WIN32 - // Save the startup time to improve precision of gettimeofday(). We also - // use this to measure elapsed time from the window parameters having been - // received. - struct timeval tv; - gettimeofday(&tv, nullptr); - _init_sec = tv.tv_sec; - _init_usec = tv.tv_usec; -#endif // !_WIN32 - -#ifdef __APPLE__ - // Get the run loop in the browser thread. (CFRunLoopGetMain() is only 10.5 - // or higher. Plus, the browser thread is not necessarily the "main" - // thread.) - _run_loop_main = CFRunLoopGetCurrent(); - CFRetain(_run_loop_main); - _request_timer = nullptr; - INIT_LOCK(_timer_lock); - - // Also set up a timer to twirl the icon until the instance loads. - _twirl_timer = nullptr; - CFRunLoopTimerContext timer_context; - memset(&timer_context, 0, sizeof(timer_context)); - timer_context.info = this; - _twirl_timer = CFRunLoopTimerCreate - (nullptr, 0, 0.1, 0, 0, st_twirl_timer_callback, &timer_context); - CFRunLoopAddTimer(_run_loop_main, _twirl_timer, kCFRunLoopCommonModes); -#endif // __APPLE__ - -#ifdef MACOSX_HAS_EVENT_MODELS - _got_twirl_images = false; -#endif // MACOSX_HAS_EVENT_MODELS -} - -/** - * - */ -PPInstance:: -~PPInstance() { - cleanup_window(); - -#ifdef __APPLE__ - if (_twirl_timer != nullptr) { - CFRunLoopTimerInvalidate(_twirl_timer); - CFRelease(_twirl_timer); - _twirl_timer = nullptr; - } - if (_request_timer != nullptr) { - CFRunLoopTimerInvalidate(_request_timer); - CFRelease(_request_timer); - _request_timer = nullptr; - } - _run_loop_main = CFRunLoopGetCurrent(); - CFRelease(_run_loop_main); - DESTROY_LOCK(_timer_lock); -#endif // __APPLE__ - -#ifdef MACOSX_HAS_EVENT_MODELS - osx_release_twirl_images(); -#endif // MACOSX_HAS_EVENT_MODELS - - if (_p3d_inst != nullptr) { - P3D_instance_finish_ptr(_p3d_inst); - _p3d_inst = nullptr; - } - - assert(_streams.empty()); - assert(_file_datas.empty()); - - if (_script_object != nullptr) { - browser->releaseobject(_script_object); - } - - // Free the tokens we allocated. - Tokens::iterator ti; - for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) { - free((char *)(*ti)._keyword); - free((char *)(*ti)._value); - } - _tokens.clear(); -} - -/** - * Begins the initial download of the core API. This should be called after - * constructing the PPInstance. It is a separate method than the constructor, - * because it initiates some callbacks that might rely on the object having - * been fully constructed and its pointer stored. - */ -void PPInstance:: -begin() { - // On Windows and Linux, we must insist on having this call. OSX doesn't - // necessarily require it (which is lucky, since it appears that Safari - // doesn't necessarily provide it!) -#ifndef __APPLE__ - if (!has_plugin_thread_async_call) { - nout << "Browser version insufficient: we require at least NPAPI version 0.19.\n"; - set_failed(); - } -#else - // While Safari 5 on Mac claims to provide this function, it doesn't appear - // to work. (!) So we pretend we never have it on Mac. Fortunately, this - // hack does us no harm because the _request_timer hack works fine on OSX. - has_plugin_thread_async_call = false; -#endif // __APPLE__ - - string url = PANDA_PACKAGE_HOST_URL; - if (!url.empty() && url[url.length() - 1] != '/') { - url += '/'; - } - _download_url_prefix = url; - _standard_url_prefix = url; - nout << "Plugin is built with " << PANDA_PACKAGE_HOST_URL << "\n"; - - if (!is_plugin_loaded() && !_failed) { - // We need to read the contents.xml file. First, check to see if the - // version on disk is already current enough. - bool success = false; - - string contents_filename = _root_dir + "/contents.xml"; - if (read_contents_file(contents_filename, false)) { - if (time(nullptr) < _contents_expiration) { - // Got the file, and it's good. - get_core_api(); - success = true; - } - } - - if (!success) { - // Go download the latest contents.xml file. - _download_url_prefix = _standard_url_prefix; - _mirrors.clear(); - ostringstream strm; - strm << _download_url_prefix << "contents.xml"; - - // Append a uniquifying query string to the URL to force the download to - // go all the way through any caches. We use the time in seconds; - // that's unique enough. - strm << "?" << time(nullptr); - url = strm.str(); - - PPDownloadRequest *req = new PPDownloadRequest(PPDownloadRequest::RT_contents_file); - start_download(url, req); - } - } - - handle_request_loop(); -} - -/** - * Stores or updates the window parameters. - */ -void PPInstance:: -set_window(NPWindow *window) { - if (_got_window && - window->x == _window.x && - window->y == _window.y && - window->width == _window.width && - window->height == _window.height) { - // No changes. - return; - } - - if (_got_window) { - // We don't expect the browser to change the window's parent on-the-fly. - assert(_window.window == window->window); - } - - if (!_got_window) { -#ifdef _WIN32 - _orig_window_proc = nullptr; - if (window->type == NPWindowTypeWindow) { - // Save the window handle. - _hwnd = (HWND)window->window; - - // Now that we've got a window handle, we can go get the twirling icon - // images. - win_get_twirl_bitmaps(); - - _bg_brush = CreateSolidBrush(RGB(_bgcolor_r, _bgcolor_g, _bgcolor_b)); - - // Subclass the window to make it call our own window_proc instead of - // whatever window_proc it has already. This is mainly just a dopey - // trick to allow us to poll events in the main thread, but we also rely - // on this to paint the twirling icon into the browser window. - SetWindowLongPtr(_hwnd, GWLP_USERDATA, (LONG_PTR)this); - _orig_window_proc = SetWindowLongPtr(_hwnd, GWLP_WNDPROC, (LONG_PTR)st_window_proc); - - // Also set a timer to go off every once in a while, to update the - // twirling icon, and also to catch events in case something slips - // through. - _init_time = GetTickCount(); - SetTimer(_hwnd, 1, 100, nullptr); - } -#endif // _WIN32 -#ifdef MACOSX_HAS_EVENT_MODELS - osx_get_twirl_images(); -#endif // MACOSX_HAS_EVENT_MODELS - } - -#if defined(HAVE_GTK) && defined(HAVE_X11) - if (_use_xembed) { - if (!_got_window || _window.window != window->window) { - // The window has changed. Destroy the old GtkPlug. - if (_plug != nullptr) { - gtk_widget_destroy(_plug); - _plug = nullptr; - } - - // Create a new GtkPlug to bind to the XEmbed socket. - _plug = gtk_plug_new((GdkNativeWindow) reinterpret_cast(window->window)); - gtk_widget_show(_plug); - - nout << "original XID is " << window->window << ", created X11 window " - << GDK_DRAWABLE_XID(_plug->window) << "\n"; - } - } -#endif // HAVE_GTK && HAVE_X11 - - _window = *window; - _got_window = true; - -#ifdef HAVE_X11 - if (!_failed && !_started) { - x11_start_twirl_subprocess(); - } -#endif // HAVE_X11 - - if (!_failed) { - if (_p3d_inst == nullptr) { - create_instance(); - } else { - send_window(); - } - } -} - -/** - * Receives notification of a new stream object, e.g. a url request. - */ -NPError PPInstance:: -new_stream(NPMIMEType type, NPStream *stream, bool seekable, uint16_t *stype) { - assert(find(_streams.begin(), _streams.end(), stream) == _streams.end()); - if (_failed) { - return NPERR_GENERIC_ERROR; - } - - if (stream->notifyData == nullptr) { - // This is an unsolicited stream. Assume the first unsolicited stream we - // receive is the instance data; any other unsolicited stream is an error. - - if (!_got_instance_url && stream->url != nullptr) { - _got_instance_url = true; - _instance_url = stream->url; - stream->notifyData = new PPDownloadRequest(PPDownloadRequest::RT_instance_data); - - if (_p3d_inst != nullptr) { - // If we already have an instance by the time we get this stream, - // start sending the data to the instance (instead of having to mess - // around with a temporary file). - _p3d_instance_id = P3D_instance_start_stream_ptr(_p3d_inst, _instance_url.c_str()); - nout << "p3d instance to stream " << _p3d_instance_id << "\n"; - } - - *stype = NP_NORMAL; - _streams.push_back(stream); - return NPERR_NO_ERROR; - } - - // Don't finish downloading the unsolicited stream. - return NPERR_GENERIC_ERROR; - } - - PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData); - switch (req->_rtype) { - case PPDownloadRequest::RT_contents_file: - // This is the initial contents.xml file. We used to download this via - // NP_ASFILEONLY, but that option doesn't work on Windows within a Unicode - // user directory. So we use NP_NORMAL instead. - *stype = NP_NORMAL; - _streams.push_back(stream); - return NPERR_NO_ERROR; - - case PPDownloadRequest::RT_core_dll: - // This is the core API DLL (or dylib or whatever). Again, we have to use - // NP_NORMAL. - *stype = NP_NORMAL; - _streams.push_back(stream); - return NPERR_NO_ERROR; - - case PPDownloadRequest::RT_user: - // This is a request from the plugin. We'll receive this as a stream. - *stype = NP_NORMAL; - _streams.push_back(stream); - return NPERR_NO_ERROR; - - default: - // Don't know what this is. - nout << "Unexpected request " << (int)req->_rtype << "\n"; - } - - return NPERR_GENERIC_ERROR; -} - - -/** - * Stops any download streams that are currently active on the instance. It - * is necessary to call this explicitly before destroying the instance, at - * least for Safari. - */ -void PPInstance:: -stop_outstanding_streams() { - Streams::iterator si; - Streams streams; - streams.swap(_streams); - for (si = streams.begin(); si != streams.end(); ++si) { - NPStream *stream = (*si); - nout << "Stopping stream " << (void *)stream << "\n"; - browser->destroystream(_npp_instance, stream, NPRES_USER_BREAK); - destroy_stream(stream, NPRES_USER_BREAK); - } - - assert(_streams.empty()); - - // Also stop any currently pending _file_datas; these are locally- - // implemented streams. - FileDatas::iterator fi; - for (fi = _file_datas.begin(); fi != _file_datas.end(); ++fi) { - delete (*fi); - } - _file_datas.clear(); -} - -/** - * Called by the browser to ask how much data is ready to be received for the - * indicated stream. - */ -int32_t PPInstance:: -write_ready(NPStream *stream) { - // We're supposed to return the maximum amount of data the plugin is - // prepared to handle. Gee, I don't know. As much as you can give me, I - // guess. - return 0x7fffffff; -} - -/** - * Called by the browser to feed data read from a URL or whatever. - */ -int PPInstance:: -write_stream(NPStream *stream, int offset, int len, void *buffer) { - if (stream->notifyData == nullptr) { - nout << "Unexpected write_stream on " << stream->url << "\n"; - browser->destroystream(_npp_instance, stream, NPRES_USER_BREAK); - return 0; - } - - if (_failed) { - // We're done; stop this. - browser->destroystream(_npp_instance, stream, NPRES_USER_BREAK); - return 0; - } - - PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData); - switch (req->_rtype) { - case PPDownloadRequest::RT_user: - P3D_instance_feed_url_stream_ptr(_p3d_inst, req->_user_id, - P3D_RC_in_progress, 0, - stream->end, buffer, len); - return len; - - case PPDownloadRequest::RT_instance_data: - // There's a special case for the RT_instance_data stream. This is the - // first, unsolicited stream that indicates the p3d instance data. We - // have to send this stream into the instance, but we can only do this - // once the instance itself has been created. - - // We used to get away with returning 0 in write_ready until the instance - // was ready, but that turns out to fail under Safari Snow Leopard, which - // it seems will hold up every other download until the p3d file has been - // retrieved. Sigh. So we must start accepting the data even before the - // instance has been created, or we'll never get our contents.xml or any - // other important bits of data. - - // Nowadays we solve this problem by writing the data to a temporary file - // until the instance is ready for it. - if (_p3d_inst == nullptr) { - // The instance isn't ready, so stuff it in a temporary file. - if (!_p3d_temp_file.feed(stream->end, buffer, len)) { - set_failed(); - } - return len; - - } else { - // The instance has been created. Redirect the stream into the - // instance. - assert(!_p3d_temp_file._opened); - req->_rtype = PPDownloadRequest::RT_user; - req->_user_id = _p3d_instance_id; - P3D_instance_feed_url_stream_ptr(_p3d_inst, req->_user_id, - P3D_RC_in_progress, 0, - stream->end, buffer, len); - return len; - } - break; - - case PPDownloadRequest::RT_contents_file: - if (!_contents_temp_file.feed(stream->end, buffer, len)) { - set_failed(); - } - return len; - - case PPDownloadRequest::RT_core_dll: - if (!_core_dll_temp_file.feed(stream->end, buffer, len)) { - set_failed(); - } - return len; - - default: - nout << "Unexpected write_stream on " << stream->url << "\n"; - break; - } - - browser->destroystream(_npp_instance, stream, NPRES_USER_BREAK); - return 0; -} - -/** - * Called by the browser to mark the end of a stream; the file has either been - * successfully downloaded or failed. - */ -NPError PPInstance:: -destroy_stream(NPStream *stream, NPReason reason) { - Streams::iterator si = find(_streams.begin(), _streams.end(), stream); - if (si != _streams.end()) { - _streams.erase(si); - } - - if (stream->notifyData == nullptr) { - nout << "Unexpected destroy_stream on " << stream->url << "\n"; - return NPERR_NO_ERROR; - } - - PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData); - - P3D_result_code result_code = P3D_RC_done; - if (reason != NPRES_DONE) { - if (reason == NPRES_USER_BREAK) { - result_code = P3D_RC_shutdown; - } else { - result_code = P3D_RC_generic_error; - } - } - - switch (req->_rtype) { - case PPDownloadRequest::RT_user: - if (!req->_notified_done) { - P3D_instance_feed_url_stream_ptr(_p3d_inst, req->_user_id, - result_code, 0, stream->end, nullptr, 0); - req->_notified_done = true; - } - break; - - case PPDownloadRequest::RT_instance_data: - if (!req->_notified_done) { - if (_p3d_inst == nullptr) { - // The instance still isn't ready; just mark the data done. We'll - // send the entire file to the instance when it is ready. - _p3d_temp_file.finish(); - if (result_code != P3D_RC_done) { - set_failed(); - } - - } else { - // The instance has (only just) been created. Tell it we've sent it - // all the data it will get. - P3D_instance_feed_url_stream_ptr(_p3d_inst, _p3d_instance_id, - result_code, 0, stream->end, nullptr, 0); - } - req->_notified_done = true; - } - break; - - case PPDownloadRequest::RT_contents_file: - if (!req->_notified_done) { - _contents_temp_file.finish(); - if (result_code != P3D_RC_done) { - set_failed(); - } - req->_notified_done = true; - } - break; - - case PPDownloadRequest::RT_core_dll: - if (!req->_notified_done) { - _core_dll_temp_file.finish(); - if (result_code != P3D_RC_done) { - set_failed(); - } - req->_notified_done = true; - } - break; - - default: - nout << "Unexpected destroy_stream on " << stream->url << "\n"; - break; - } - - return NPERR_NO_ERROR; -} - -/** - * Called by the browser to announce the end of a stream. This normally - * follows destroy_stream(), unless the stream was never created in the first - * place. - */ -void PPInstance:: -url_notify(const char *url, NPReason reason, void *notifyData) { - if (notifyData == nullptr) { - return; - } - - PPDownloadRequest *req = (PPDownloadRequest *)notifyData; - if (_failed) { - // We're done; ignore this. - delete req; - return; - } - - switch (req->_rtype) { - case PPDownloadRequest::RT_user: - if (!req->_notified_done) { - // We shouldn't have gotten here without notifying the stream unless the - // stream never got started (and hence we never called destroy_stream(). - nout << "Failure starting stream\n"; - assert(reason != NPRES_DONE); - - P3D_instance_feed_url_stream_ptr(_p3d_inst, req->_user_id, - P3D_RC_generic_error, 0, 0, nullptr, 0); - req->_notified_done = true; - } - break; - - case PPDownloadRequest::RT_contents_file: - if (reason == NPRES_DONE) { - downloaded_contents_file(_contents_temp_file._filename); - } else { - nout << "Failure downloading " << url << "\n"; - - if (reason == NPRES_USER_BREAK) { - nout << "Failure due to user break\n"; - } else { - // Couldn't download a fresh contents.xml for some reason. If there's - // an outstanding contents.xml file on disk, try to load that one as a - // fallback. - string contents_filename = _root_dir + "/contents.xml"; - if (read_contents_file(contents_filename, false)) { - get_core_api(); - } else { - nout << "Unable to read contents file " << contents_filename << "\n"; - set_failed(); - } - } - } - break; - - case PPDownloadRequest::RT_core_dll: - if (reason == NPRES_DONE) { - downloaded_plugin(_core_dll_temp_file._filename); - - } else { - nout << "Failure downloading " << url << "\n"; - - if (reason == NPRES_USER_BREAK) { - nout << "Failure due to user break\n"; - } else { - // Couldn't download from this mirror. Try the next one. - if (!_core_urls.empty()) { - string url = _core_urls.back(); - _core_urls.pop_back(); - - PPDownloadRequest *req2 = new PPDownloadRequest(PPDownloadRequest::RT_core_dll); - start_download(url, req2); - } - } - } - break; - - default: - nout << "Unexpected url_notify on stream type " << req->_rtype << "\n"; - break; - } - - delete req; -} - -/** - * Called by the browser to report the filename that contains the fully- - * downloaded stream contents. - */ -void PPInstance:: -stream_as_file(NPStream *stream, const char *fname) { - if (stream->notifyData == nullptr) { - nout << "Unexpected stream_as_file on " << stream->url << "\n"; - return; - } - - string filename = fname; -#ifdef __APPLE__ - // Safari seems to want to report the filename in the old-style form - // "Macintosh HD:blah:blah:blah" instead of the new-style form - // "blahblahblah". How annoying. - - size_t colon = filename.find(':'); - size_t slash = filename.find('/'); - if (colon < slash) { - // This might be such a filename. - - string fname2 = "/Volumes/"; - for (size_t p = 0; p < filename.size(); ++p) { - if (filename[p] == ':') { - fname2 += '/'; - } else { - fname2 += filename[p]; - } - } - - if (access(fname2.c_str(), R_OK) == 0) { - // Looks like we've converted it successfully. - filename = fname2; - - // Here's another crazy hack. In addition to the weird filename format, - // the file that Safari tells us about appears to be a temporary file - // that Safari's about to delete. In order to protect ourselves from - // this, we need to temporarily copy the file somewhere else. - char *name = tempnam(nullptr, "p3d_"); - - // We prefer just making a hard link; it's quick and easy. - if (link(filename.c_str(), name) != 0) { - // But sometimes the hard link might fail, particularly if these are - // two different file systems. In this case we have to open the files - // and copy the data by hand. - copy_file(filename, name); - } - - filename = name; - free(name); - - // TODO: remove this temporary file when we're done with it. - } - } - -#endif // __APPLE__ - - PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData); - downloaded_file(req, filename); -} - -/** - * Handles a request from the Core API or the application, forwarding it to - * the browser as appropriate. Returns true if we should continue the request - * loop, or false to return (temporarily) to JavaScript. - */ -bool PPInstance:: -handle_request(P3D_request *request) { - if (_p3d_inst == nullptr || _failed) { - return false; - } - assert(request->_instance == _p3d_inst); - bool handled = false; - bool continue_loop = true; - - switch (request->_request_type) { - case P3D_RT_stop: - if (_p3d_inst != nullptr) { - P3D_instance_finish_ptr(_p3d_inst); - _p3d_inst = nullptr; - } - cleanup_window(); - // Guess the browser doesn't really care. - handled = true; - break; - - case P3D_RT_get_url: - { - PPDownloadRequest *req = - new PPDownloadRequest(PPDownloadRequest::RT_user, - request->_request._get_url._unique_id); - start_download(request->_request._get_url._url, req); - } - break; - - case P3D_RT_notify: - // We mostly ignore notifies, since these are handled by the core API. - // But we do check for the "onwindowopen" notify, at which point we start - // spamming the refresh requests. - if (strcmp(request->_request._notify._message, "onwindowopen") == 0) { - _python_window_open = true; - if (_got_window) { - NPRect rect = { 0, 0, (unsigned short)_window.height, (unsigned short)_window.width }; - browser->invalidaterect(_npp_instance, &rect); - } - } - break; - - case P3D_RT_refresh: - if (_got_window) { - NPRect rect = { 0, 0, (unsigned short)_window.height, (unsigned short)_window.width }; - browser->invalidaterect(_npp_instance, &rect); - } - break; - - case P3D_RT_callback: - // In the case of a callback, yield control temporarily to JavaScript. - continue_loop = false; - break; - - default: - // Some request types are not handled. - nout << "Unhandled request: " << request->_request_type << "\n"; - break; - }; - - P3D_request_finish_ptr(request, handled); - return continue_loop; -} - -/** - * This method is called from strategically-chosen browser callback functions. - * Its purpose is to provide another hook into the main thread callback, - * particularly if the PluginAsyncCall function isn't available. - */ -void PPInstance:: -generic_browser_call() { - /* - if (!has_plugin_thread_async_call) { - // If we can't ask Mozilla to call us back using - // NPN_PluginThreadAsyncCall(), then we'll do it explicitly now, since we - // know we're in the main thread here. - handle_request_loop(); - } - */ -} - -/** - * Called by the browser as new window events are generated. Returns true if - * the event is handled, false if ignored. - */ -bool PPInstance:: -handle_event(void *event) { - bool retval = false; - - P3D_event_data edata; - memset(&edata, 0, sizeof(edata)); - edata._event_type = _event_type; - EventAuxData aux_data; - if (_event_type == P3D_ET_osx_event_record) { -#ifdef __APPLE__ - edata._event._osx_event_record._event = (EventRecord *)event; -#endif // __APPLE__ - -#ifdef MACOSX_HAS_EVENT_MODELS - } else if (_event_type == P3D_ET_osx_cocoa) { - // Copy the NPCocoaEvent structure componentwise into a P3DCocoaEvent - // structure. - NPCocoaEvent *np_event = (NPCocoaEvent *)event; - P3DCocoaEvent *p3d_event = &edata._event._osx_cocoa._event; - copy_cocoa_event(p3d_event, np_event, aux_data); - if (_got_window) { - handle_cocoa_event(p3d_event); - } -#endif // MACOSX_HAS_EVENT_MODELS - } - - if (_p3d_inst != nullptr) { - if (P3D_instance_handle_event_ptr(_p3d_inst, &edata)) { - retval = true; - } - } - - return retval; -} - -/** - * Returns a toplevel object that JavaScript or whatever can read and/or - * modify to control the instance. - */ -NPObject *PPInstance:: -get_panda_script_object() { - if (_script_object != nullptr) { - // NPRuntime "steals" a reference to this object. - browser->retainobject(_script_object); - return _script_object; - } - - P3D_object *main = nullptr; - - if (_p3d_inst != nullptr) { - main = P3D_instance_get_panda_script_object_ptr(_p3d_inst); - } - nout << "get_panda_script_object, main = " << main << "\n"; - - _script_object = PPToplevelObject::make_new(this); - _script_object->set_main(main); - - browser->retainobject(_script_object); - return _script_object; -} - -/** - * Sets the use_xembed flag, telling the instance what kind of window object - * to expect from NPAPI. If this is true, the window object is an XID - * following the XEmbed specification; if false, it is a normal window handle. - */ -void PPInstance:: -set_xembed(bool use_xembed) { - _use_xembed = use_xembed; -} - -/** - * Converts the indicated P3D_object to the equivalent NPVariant, and stores - * it in result. - */ -void PPInstance:: -p3dobj_to_variant(NPVariant *result, P3D_object *object) { - switch (P3D_OBJECT_GET_TYPE(object)) { - case P3D_OT_undefined: - VOID_TO_NPVARIANT(*result); - break; - - case P3D_OT_none: - NULL_TO_NPVARIANT(*result); - break; - - case P3D_OT_bool: - BOOLEAN_TO_NPVARIANT(P3D_OBJECT_GET_BOOL(object), *result); - break; - - case P3D_OT_int: - INT32_TO_NPVARIANT(P3D_OBJECT_GET_INT(object), *result); - break; - - case P3D_OT_float: - DOUBLE_TO_NPVARIANT(P3D_OBJECT_GET_FLOAT(object), *result); - break; - - case P3D_OT_string: - { - int size = P3D_OBJECT_GET_STRING(object, nullptr, 0); - char *buffer = (char *)browser->memalloc(size); - P3D_OBJECT_GET_STRING(object, buffer, size); - STRINGN_TO_NPVARIANT(buffer, size, *result); - } - break; - - case P3D_OT_object: - { - PPPandaObject *ppobj = PPPandaObject::make_new(this, object); - OBJECT_TO_NPVARIANT(ppobj, *result); - } - break; - } -} - -/** - * Converts the indicated NPVariant to the equivalent P3D_object, and returns - * it (newly-allocated). The caller is responsible for freeing the returned - * object later. - */ -P3D_object *PPInstance:: -variant_to_p3dobj(const NPVariant *variant) { - if (NPVARIANT_IS_VOID(*variant)) { - return P3D_new_undefined_object_ptr(); - } else if (NPVARIANT_IS_NULL(*variant)) { - return P3D_new_none_object_ptr(); - } else if (NPVARIANT_IS_BOOLEAN(*variant)) { - return P3D_new_bool_object_ptr(NPVARIANT_TO_BOOLEAN(*variant)); - } else if (NPVARIANT_IS_INT32(*variant)) { - return P3D_new_int_object_ptr(NPVARIANT_TO_INT32(*variant)); - } else if (NPVARIANT_IS_DOUBLE(*variant)) { - return P3D_new_float_object_ptr(NPVARIANT_TO_DOUBLE(*variant)); - } else if (NPVARIANT_IS_STRING(*variant)) { - NPString str = NPVARIANT_TO_STRING(*variant); - const UC_NPString &uc_str = *(UC_NPString *)(&str); - return P3D_new_string_object_ptr(uc_str.UTF8Characters, uc_str.UTF8Length); - } else if (NPVARIANT_IS_OBJECT(*variant)) { - NPObject *object = NPVARIANT_TO_OBJECT(*variant); - if (object->_class == &PPPandaObject::_object_class) { - // This is really a PPPandaObject. - PPPandaObject *ppobject = (PPPandaObject *)object; - P3D_object *obj = ppobject->get_p3d_object(); - return obj; - } - - // It's a generic NPObject of some kind. - return new PPBrowserObject(this, object); - } - - // Hmm, none of the above? - return P3D_new_none_object_ptr(); -} - -/** - * Outputs the variant value. - */ -void PPInstance:: -output_np_variant(ostream &out, const NPVariant &result) { - if (NPVARIANT_IS_NULL(result)) { - out << "null"; - } else if (NPVARIANT_IS_VOID(result)) { - out << "void"; - } else if (NPVARIANT_IS_BOOLEAN(result)) { - out << "bool " << NPVARIANT_TO_BOOLEAN(result); - } else if (NPVARIANT_IS_INT32(result)) { - out << "int " << NPVARIANT_TO_INT32(result); - } else if (NPVARIANT_IS_DOUBLE(result)) { - out << "double " << NPVARIANT_TO_DOUBLE(result); - } else if (NPVARIANT_IS_STRING(result)) { - NPString str = NPVARIANT_TO_STRING(result); - const UC_NPString &uc_str = *(UC_NPString *)(&str); - out << "string " << string(uc_str.UTF8Characters, uc_str.UTF8Length); - } else if (NPVARIANT_IS_OBJECT(result)) { - NPObject *child = NPVARIANT_TO_OBJECT(result); - out << "object " << child; - } -} - -/** - * Scans the element for the matching element. - */ -void PPInstance:: -find_host(TiXmlElement *xcontents) { - string host_url = PANDA_PACKAGE_HOST_URL; - TiXmlElement *xhost = xcontents->FirstChildElement("host"); - if (xhost != nullptr) { - const char *url = xhost->Attribute("url"); - if (url != nullptr && host_url == string(url)) { - // We're the primary host. This is the normal case. - read_xhost(xhost); - return; - - } else { - // We're not the primary host; perhaps we're an alternate host. - TiXmlElement *xalthost = xhost->FirstChildElement("alt_host"); - while (xalthost != nullptr) { - const char *url = xalthost->Attribute("url"); - if (url != nullptr && host_url == string(url)) { - // Yep, we're this alternate host. - read_xhost(xhost); - return; - } - xalthost = xalthost->NextSiblingElement("alt_host"); - } - } - - // Hmm, didn't find the URL we used mentioned. Assume we're the primary - // host. - read_xhost(xhost); - } -} - -/** - * Reads the host data from the (or ) entry in the - * contents.xml file. - */ -void PPInstance:: -read_xhost(TiXmlElement *xhost) { - // Get the "download" URL, which is the source from which we download - // everything other than the contents.xml file. - const char *download_url = xhost->Attribute("download_url"); - if (download_url != nullptr) { - _download_url_prefix = download_url; - } else { - _download_url_prefix = PANDA_PACKAGE_HOST_URL; - } - if (!_download_url_prefix.empty()) { - if (_download_url_prefix[_download_url_prefix.size() - 1] != '/') { - _download_url_prefix += "/"; - } - } - - TiXmlElement *xmirror = xhost->FirstChildElement("mirror"); - while (xmirror != nullptr) { - const char *url = xmirror->Attribute("url"); - if (url != nullptr) { - add_mirror(url); - } - xmirror = xmirror->NextSiblingElement("mirror"); - } -} - -/** - * Adds a new URL to serve as a mirror for this host. The mirrors will be - * consulted first, before consulting the host directly. - */ -void PPInstance:: -add_mirror(string mirror_url) { - // Ensure the URL ends in a slash. - if (!mirror_url.empty() && mirror_url[mirror_url.size() - 1] != '/') { - mirror_url += '/'; - } - - // Add it to the _mirrors list, but only if it's not already there. - if (find(_mirrors.begin(), _mirrors.end(), mirror_url) == _mirrors.end()) { - _mirrors.push_back(mirror_url); - } -} - -/** - * Selects num_mirrors elements, chosen at random, from the _mirrors list. - * Adds the selected mirrors to result. If there are fewer than num_mirrors - * elements in the list, adds only as many mirrors as we can get. - */ -void PPInstance:: -choose_random_mirrors(vector &result, int num_mirrors) { - vector selected; - - size_t num_to_select = std::min(_mirrors.size(), (size_t)num_mirrors); - while (num_to_select > 0) { - size_t i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size()); - while (find(selected.begin(), selected.end(), i) != selected.end()) { - // Already found this i, find a new one. - i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size()); - } - selected.push_back(i); - result.push_back(_mirrors[i]); - --num_to_select; - } -} - -/** - * This function is attached as an asynchronous callback to each instance; it - * will be notified when the instance has a request ready. This function may - * be called in a sub-thread. - */ -void PPInstance:: -request_ready(P3D_instance *instance) { - PPInstance *inst = (PPInstance *)(instance->_user_data); - assert(inst != nullptr); - - if (has_plugin_thread_async_call) { -#ifdef HAS_PLUGIN_THREAD_ASYNC_CALL - // Since we are running at least Gecko 1.9, and we have this very useful - // function, let's use it to ask the browser to call us back in the main - // thread. - assert((void *)browser->pluginthreadasynccall != nullptr); - browser->pluginthreadasynccall(inst->_npp_instance, browser_sync_callback, nullptr); -#endif // HAS_PLUGIN_THREAD_ASYNC_CALL - - } else { - // If we're using an older version of Gecko, we have to do this some - // other, OS-dependent way. - -#ifdef _WIN32 - // Use a Windows message to forward this event to the main thread. - - // Get the window handle for the window associated with this instance. - const NPWindow *win = inst->get_window(); - if (win != nullptr && win->type == NPWindowTypeWindow) { - PostMessage((HWND)(win->window), WM_USER, 0, 0); - } -#endif // _WIN32 - -#ifdef __APPLE__ - // Use an OSX timer to forward this event to the main thread. - - // Only set a new timer if we don't have one already started. - ACQUIRE_LOCK(inst->_timer_lock); - if (inst->_request_timer == nullptr) { - CFRunLoopTimerContext timer_context; - memset(&timer_context, 0, sizeof(timer_context)); - timer_context.info = inst; - inst->_request_timer = CFRunLoopTimerCreate - (nullptr, 0, 0, 0, 0, timer_callback, &timer_context); - CFRunLoopAddTimer(inst->_run_loop_main, inst->_request_timer, kCFRunLoopCommonModes); - } - RELEASE_LOCK(inst->_timer_lock); -#endif // __APPLE__ - - // Doesn't appear to be a reliable way to simulate this in Linux. - } -} - -/** - * Initiates a download request. - */ -void PPInstance:: -start_download(const string &url, PPDownloadRequest *req) { - nout << "start_download: " << url << "\n"; - if (url.substr(0, 7) == "file://") { - // If we're "downloading" a file URL, just go read the file directly. - downloaded_file(req, get_filename_from_url(url)); - delete req; - } else { - // Otherwise, ask the browser to download it. - browser->geturlnotify(_npp_instance, url.c_str(), nullptr, req); - } -} - -/** - * The contents.xml file has been successfully downloaded; copy it into place. - */ -void PPInstance:: -downloaded_contents_file(const string &filename) { - // Now we have the contents.xml file. Read this to get the filename and md5 - // hash of our core API DLL. - if (read_contents_file(filename, true)) { - // Successfully downloaded and read, and it has been written into its - // normal place. - get_core_api(); - - } else { - // Error reading the contents.xml file, or in loading the core API that it - // references. - nout << "Unable to read contents file " << filename << "\n"; - - // If there's an outstanding contents.xml file on disk, try to load that - // one as a fallback. - string contents_filename = _root_dir + "/contents.xml"; - if (read_contents_file(contents_filename, false)) { - get_core_api(); - } else { - nout << "Unable to read contents file " << contents_filename << "\n"; - set_failed(); - } - } -} - -/** - * Attempts to open and read the contents.xml file on disk. Copies the file - * to its standard location on success. Returns true on success, false on - * failure. - */ -bool PPInstance:: -read_contents_file(const string &contents_filename, bool fresh_download) { - _download_url_prefix = _standard_url_prefix; - _mirrors.clear(); - - TiXmlDocument doc(contents_filename.c_str()); - if (!doc.LoadFile()) { - return false; - } - - bool found_core_package = false; - - TiXmlElement *xcontents = doc.FirstChildElement("contents"); - if (xcontents != nullptr) { - int max_age = P3D_CONTENTS_DEFAULT_MAX_AGE; - xcontents->Attribute("max_age", &max_age); - - // Get the latest possible expiration time, based on the max_age - // indication. Any expiration time later than this is in error. - time_t now = time(nullptr); - _contents_expiration = now + (time_t)max_age; - - if (fresh_download) { - // Update the XML with the new download information. - TiXmlElement *xorig = xcontents->FirstChildElement("orig"); - while (xorig != nullptr) { - xcontents->RemoveChild(xorig); - xorig = xcontents->FirstChildElement("orig"); - } - - xorig = new TiXmlElement("orig"); - xcontents->LinkEndChild(xorig); - - xorig->SetAttribute("expiration", (int)_contents_expiration); - - } else { - // Read the expiration time from the XML. - int expiration = 0; - TiXmlElement *xorig = xcontents->FirstChildElement("orig"); - if (xorig != nullptr) { - xorig->Attribute("expiration", &expiration); - } - - _contents_expiration = std::min(_contents_expiration, (time_t)expiration); - } - - nout << "read contents.xml, max_age = " << max_age - << ", expires in " << std::max(_contents_expiration, now) - now - << " s\n"; - - // Look for the entry; it might point us at a different download - // URL, and it might mention some mirrors. - find_host(xcontents); - - // Now look for the core API package. - _coreapi_set_ver = ""; - TiXmlElement *xpackage = xcontents->FirstChildElement("package"); - while (xpackage != nullptr) { - const char *name = xpackage->Attribute("name"); - if (name != nullptr && strcmp(name, "coreapi") == 0) { - const char *platform = xpackage->Attribute("platform"); - if (platform != nullptr && strcmp(platform, DTOOL_PLATFORM) == 0) { - _coreapi_dll.load_xml(xpackage); - const char *set_ver = xpackage->Attribute("set_ver"); - if (set_ver != nullptr) { - _coreapi_set_ver = set_ver; - } - found_core_package = true; - break; - } - } - - xpackage = xpackage->NextSiblingElement("package"); - } - } - - if (!found_core_package) { - // Couldn't find the coreapi package description. - nout << "No coreapi package defined in contents file for " - << DTOOL_PLATFORM << "\n"; - return false; - } - - // Check the coreapi_set_ver token. If it is given, it specifies a minimum - // Core API version number we expect to find. If we didn't find that - // number, perhaps our contents.xml is out of date. - string coreapi_set_ver = lookup_token("coreapi_set_ver"); - if (!coreapi_set_ver.empty()) { - nout << "Instance asked for Core API set_ver " << coreapi_set_ver - << ", we found " << _coreapi_set_ver << "\n"; - // But don't bother if we just freshly downloaded it. - if (!fresh_download) { - if (compare_seq(coreapi_set_ver, _coreapi_set_ver) > 0) { - // The requested set_ver value is higher than the one we have on file; - // our contents.xml file must be out of date after all. - nout << "expiring contents.xml\n"; - _contents_expiration = 0; - } - } - } - - // Success. Now save the file in its proper place. - string standard_filename = _root_dir + "/contents.xml"; - - mkfile_complete(standard_filename, nout); - if (!doc.SaveFile(standard_filename.c_str())) { - nout << "Couldn't rewrite " << standard_filename << "\n"; - return false; - } - - return true; -} - -/** - * Returns the actual filename referenced by a file: url. - */ -string PPInstance:: -get_filename_from_url(const string &url) { - string filename = url.substr(7); - - // Strip off a trailing query string. - size_t query = filename.find('?'); - if (query != string::npos) { - filename = filename.substr(0, query); - } - -#ifdef _WIN32 - // On Windows, we have to munge the filename specially, because it's been - // URL-munged. It might begin with a leading slash as well as a drive - // letter. Clean up that nonsense. - if (filename.length() >= 3 && - (filename[0] == '/' || filename[0] == '\\') && - isalpha(filename[1]) && filename[2] == ':') { - filename = filename.substr(1); - } -#endif // _WIN32 - - return filename; -} - -/** - * Called to receive the fully-downloaded contents of a URL. - */ -void PPInstance:: -downloaded_file(PPDownloadRequest *req, const string &filename) { - // Since we're no longer using NP_ASFILEONLY, none of these URL requests - // will normally come through this codepath (they'll go through url_notify() - // above, instead), unless we short-circuited the browser by "downloading" a - // file: url. - switch (req->_rtype) { - case PPDownloadRequest::RT_contents_file: - // The contents.xml file that gets things going. - downloaded_contents_file(filename); - break; - - case PPDownloadRequest::RT_core_dll: - // This is the core API DLL (or dylib or whatever). Now that we've - // downloaded it, we can load it. - downloaded_plugin(filename); - break; - - case PPDownloadRequest::RT_user: - // Here's the user-requested file. It needs to be streamed to the user, - // so we'll open the file and feed it to the user. - feed_file(req, filename); - break; - - default: - // Don't know what this is. - nout << "Unexpected downloaded file, type " << (int)req->_rtype << "\n"; - } -} - -/** - * Opens the named file (extracted from a file:// URL) and feeds its contents - * to the core API. - */ -void PPInstance:: -feed_file(PPDownloadRequest *req, const string &filename) { - StreamingFileData *file_data = new StreamingFileData(req, filename, _p3d_inst); - _file_datas.push_back(file_data); -} - -/** - * Once the instance has been created, sends it all of the data we have saved - * up for it while we were waiting. - */ -void PPInstance:: -send_p3d_temp_file_data() { - assert(_p3d_temp_file._opened); - - nout << "Sending " << _p3d_temp_file._current_size - << " preliminary bytes of " << _p3d_temp_file._total_size - << " total p3d data\n"; - - static const size_t buffer_size = 4096; - char buffer[buffer_size]; - - _p3d_temp_file.close(); - - ifstream in(_p3d_temp_file._filename.c_str(), ios::binary); - in.read(buffer, buffer_size); - size_t total = 0; - size_t count = in.gcount(); - while (count != 0) { - P3D_instance_feed_url_stream_ptr(_p3d_inst, _p3d_instance_id, - P3D_RC_in_progress, 0, - _p3d_temp_file._total_size, buffer, count); - total += count; - - in.read(buffer, buffer_size); - count = in.gcount(); - } - nout << "sent " << count << " bytes.\n"; - - in.close(); - - if (_p3d_temp_file._finished) { - // If we'd already finished the stream earlier, tell the plugin. - P3D_instance_feed_url_stream_ptr(_p3d_inst, _p3d_instance_id, - P3D_RC_done, 0, _p3d_temp_file._total_size, - nullptr, 0); - } - _p3d_temp_file.cleanup(); -} - -/** - * Checks the core API DLL file against the specification in the contents - * file, and downloads it if necessary. - */ -void PPInstance:: -get_core_api() { - if (_coreapi_dll.quick_verify(_root_dir)) { - // The DLL file is good. Just load it. - do_load_plugin(); - - } else { - // The DLL file needs to be downloaded. Build up our list of URL's to - // attempt to download it from, in reverse order. - string url; - - // Our last act of desperation: hit the original host, with a query - // uniquifier, to break through any caches. - ostringstream strm; - strm << _download_url_prefix << _coreapi_dll.get_filename() - << "?" << time(nullptr); - url = strm.str(); - _core_urls.push_back(url); - - // Before we try that, we'll hit the original host, without a uniquifier. - url = _download_url_prefix; - url += _coreapi_dll.get_filename(); - _core_urls.push_back(url); - - // And before we try that, we'll try two mirrors, at random. - vector mirrors; - choose_random_mirrors(mirrors, 2); - for (vector::iterator si = mirrors.begin(); - si != mirrors.end(); - ++si) { - url = (*si) + _coreapi_dll.get_filename(); - _core_urls.push_back(url); - } - - // Now pick the first URL off the list, and try it. - assert(!_core_urls.empty()); - url = _core_urls.back(); - _core_urls.pop_back(); - - PPDownloadRequest *req = new PPDownloadRequest(PPDownloadRequest::RT_core_dll); - start_download(url, req); - } -} - -/** - * The core API DLL has been successfully downloaded; copy it into place. - */ -void PPInstance:: -downloaded_plugin(const string &filename) { - // We could have been downloading this file as a stream, but that would - // cause problems with multiple instances downloading the plugin at the same - // time. Instead, we let them all download the file asfile, and then only - // one of them is allowed to copy it into place. - - if (is_plugin_loaded()) { - // Some other instance got there first. Just get started. - create_instance(); - return; - } - - // Make sure the DLL was correctly downloaded before continuing. - if (!_coreapi_dll.quick_verify_pathname(filename)) { - nout << "After download, " << _coreapi_dll.get_filename() << " is no good.\n"; - nout << "Expected:\n"; - _coreapi_dll.write(nout); - const FileSpec *actual = _coreapi_dll.force_get_actual_file(filename); - if (actual != nullptr) { - nout << "Found:\n"; - actual->write(nout); - } - - // That DLL came out wrong. Try the next URL. - if (!_core_urls.empty()) { - string url = _core_urls.back(); - _core_urls.pop_back(); - - _core_dll_temp_file.cleanup(); - PPDownloadRequest *req = new PPDownloadRequest(PPDownloadRequest::RT_core_dll); - start_download(url, req); - return; - } - - set_failed(); - return; - } - - // Copy the file onto the target. - string pathname = _coreapi_dll.get_pathname(_root_dir); - if (!copy_file(filename, pathname)) { - nout << "Couldn't copy " << pathname << "\n"; - set_failed(); - return; - } - - if (!_coreapi_dll.quick_verify(_root_dir)) { - nout << "After copying, " << pathname << " is no good.\n"; - set_failed(); - return; - } - - // We downloaded and installed it successfully. Now load it. - do_load_plugin(); - return; -} - - -/** - * Once the core API DLL has been downloaded, loads it into memory and starts - * the instance. - */ -void PPInstance:: -do_load_plugin() { - string pathname = _coreapi_dll.get_pathname(_root_dir); - -#ifdef P3D_PLUGIN_P3D_PLUGIN - // This is a convenience macro for development. If defined and nonempty, it - // indicates the name of the plugin DLL that we will actually run, even - // after downloading a possibly different (presumably older) version. Its - // purpose is to simplify iteration on the plugin DLL. - string override_filename = P3D_PLUGIN_P3D_PLUGIN; - if (!override_filename.empty()) { - pathname = override_filename; - } -#endif // P3D_PLUGIN_P3D_PLUGIN - - nout << "Attempting to load core API from " << pathname << "\n"; - string contents_filename = _root_dir + "/contents.xml"; - if (!load_plugin(pathname, contents_filename, PANDA_PACKAGE_HOST_URL, - P3D_VC_normal, "", "", "", false, false, - _root_dir, "", "", nout)) { - nout << "Unable to launch core API in " << pathname << "\n"; - set_failed(); - return; - } - - // Format the coreapi_timestamp as a string, for passing as a parameter. - ostringstream stream; - stream << _coreapi_dll.get_timestamp(); - string coreapi_timestamp = stream.str(); - -#ifdef PANDA_OFFICIAL_VERSION - static const bool official = true; -#else - static const bool official = false; -#endif - P3D_set_plugin_version_ptr(P3D_PLUGIN_MAJOR_VERSION, P3D_PLUGIN_MINOR_VERSION, - P3D_PLUGIN_SEQUENCE_VERSION, official, - PANDA_DISTRIBUTOR, - PANDA_PACKAGE_HOST_URL, coreapi_timestamp.c_str(), - _coreapi_set_ver.c_str()); - - create_instance(); -} - -/** - * Actually creates the internal P3D_instance object, if possible and needed. - */ -void PPInstance:: -create_instance() { - if (_started) { - // Already created. - return; - } - - if (!is_plugin_loaded()) { - // Plugin is not loaded yet. - return; - } - -#ifdef __APPLE__ - // We no longer need to twirl the icon. Stop the timer. - if (_twirl_timer != nullptr) { - CFRunLoopTimerInvalidate(_twirl_timer); - CFRelease(_twirl_timer); - _twirl_timer = nullptr; - } -#endif // __APPLE__ - -#ifdef HAVE_X11 - x11_stop_twirl_subprocess(); -#endif // HAVE_X11 - - // In the Windows case, we let the timer keep running, because it also - // checks for wayward messages. - - P3D_token *tokens = nullptr; - if (!_tokens.empty()) { - tokens = &_tokens[0]; - } - _started = true; - _p3d_inst = P3D_new_instance_ptr(request_ready, tokens, _tokens.size(), - 0, nullptr, this); - if (_p3d_inst == nullptr) { - set_failed(); - return; - } - - // Now get the browser's toplevel DOM object (called the "window" object in - // JavaScript), to pass to the plugin. - NPObject *window_object = nullptr; - if (browser->getvalue(_npp_instance, NPNVWindowNPObject, - &window_object) == NPERR_NO_ERROR) { - PPBrowserObject *pobj = new PPBrowserObject(this, window_object); - P3D_instance_set_browser_script_object_ptr(_p3d_inst, pobj); - browser->releaseobject(window_object); - } else { - nout << "Couldn't get window_object\n"; - } - - if (_script_object != nullptr) { - // Now that we have a true instance, initialize our script_object with the - // proper P3D_object pointer. - P3D_object *main = P3D_instance_get_panda_script_object_ptr(_p3d_inst); - nout << "new instance, setting main = " << main << "\n"; - _script_object->set_main(main); - } - - if (_got_instance_url) { - // Create the user_id for streaming the p3d data into the instance. - _p3d_instance_id = P3D_instance_start_stream_ptr(_p3d_inst, _instance_url.c_str()); - nout << "p3d instance to stream " << _p3d_instance_id << "\n"; - - // If we have already started to receive any instance data, send it to the - // plugin now. - if (_p3d_temp_file._opened) { - send_p3d_temp_file_data(); - } - } - - if (_got_window) { - send_window(); - } -} - - -/** - * Actually issues the window parameters to the internal P3D_instance object. - */ -void PPInstance:: -send_window() { - assert(_p3d_inst != nullptr); - - int x = _window.x; - int y = _window.y; - - P3D_window_handle parent_window; - memset(&parent_window, 0, sizeof(parent_window)); - parent_window._window_handle_type = P3D_WHT_none; - - if (_window.type == NPWindowTypeWindow) { - // We have a "windowed" plugin. Parent our window to the one we were - // given. In this case, we should also reset the offset to (0, 0), since - // the window we were given is already placed in the right spot. -#ifdef _WIN32 - assert(!_use_xembed); - parent_window._window_handle_type = P3D_WHT_win_hwnd; - parent_window._handle._win_hwnd._hwnd = (HWND)(_window.window); - x = 0; - y = 0; - -#elif defined(__APPLE__) - assert(!_use_xembed); - parent_window._window_handle_type = _window_handle_type; - if (_window_handle_type == P3D_WHT_osx_port) { -#if !__LP64__ - NP_Port *port = (NP_Port *)_window.window; - parent_window._handle._osx_port._port = port->port; -#endif - } else if (_window_handle_type == P3D_WHT_osx_cgcontext) { - NP_CGContext *context = (NP_CGContext *)_window.window; - if (context != nullptr) { - parent_window._handle._osx_cgcontext._context = context->context; - parent_window._handle._osx_cgcontext._window = (WindowRef)context->window; - } - } - -#elif defined(HAVE_X11) - if (_use_xembed) { - // If we're using the XEmbed model, we've actually received an XID for a - // GtkSocket. -#ifdef HAVE_GTK - // If we're using XEmbed, pass the X11 Window pointer of our plug down - // to Panda. (Hmm, it would be nice to pass the XID object and use this - // system in general within Panda, but that's for the future, I think.) - assert(_plug != nullptr); - parent_window._window_handle_type = P3D_WHT_x11_window; - parent_window._handle._x11_window._xwindow = GDK_DRAWABLE_XID(_plug->window); -#endif // HAVE_GTK - } else { - // If we're not using XEmbed, this is just a standard X11 Window - // pointer. - parent_window._window_handle_type = P3D_WHT_x11_window; - parent_window._handle._x11_window._xwindow = (X11_Window)(_window.window); - } - x = 0; - y = 0; -#endif - - } else { - // We have a "windowless" plugin. Parent our window directly to the - // browser window. -#ifdef _WIN32 - HWND hwnd; - if (browser->getvalue(_npp_instance, NPNVnetscapeWindow, - &hwnd) == NPERR_NO_ERROR) { - parent_window._window_handle_type = P3D_WHT_win_hwnd; - parent_window._handle._win_hwnd._hwnd = hwnd; - } - -#elif defined(__APPLE__) - parent_window._window_handle_type = _window_handle_type; - if (_window_handle_type == P3D_WHT_osx_port) { -#if !__LP64__ - NP_Port *port = (NP_Port *)_window.window; - parent_window._handle._osx_port._port = port->port; -#endif - } else if (_window_handle_type == P3D_WHT_osx_cgcontext) { - NP_CGContext *context = (NP_CGContext *)_window.window; - if (context != nullptr) { - parent_window._handle._osx_cgcontext._context = context->context; - parent_window._handle._osx_cgcontext._window = (WindowRef)context->window; - } - } - -#elif defined(HAVE_X11) - unsigned long win; - if (browser->getvalue(_npp_instance, NPNVnetscapeWindow, - &win) == NPERR_NO_ERROR) { - parent_window._window_handle_type = P3D_WHT_x11_window; - parent_window._handle._x11_window._xwindow = win; - } -#endif - } - -#ifdef HAVE_X11 - // In the case of X11, grab the display as well. - parent_window._handle._x11_window._xdisplay = 0; - void *disp; - if (browser->getvalue(_npp_instance, NPNVxDisplay, - &disp) == NPERR_NO_ERROR) { - parent_window._handle._x11_window._xdisplay = disp; - } -#endif - - P3D_window_type window_type = P3D_WT_embedded; - if (_window.window == nullptr && _event_type != P3D_ET_osx_cocoa) { - // No parent window: it must be a hidden window. - window_type = P3D_WT_hidden; - } else if (_window.width == 0 || _window.height == 0) { - // No size: hidden. - window_type = P3D_WT_hidden; - } - - P3D_instance_setup_window_ptr - (_p3d_inst, window_type, - x, y, _window.width, _window.height, - &parent_window); -} - -/** - * Called at instance shutdown, this restores the parent window to its - * original state. - */ -void PPInstance:: -cleanup_window() { - if (_got_window) { -#ifdef _WIN32 - // Restore the parent window to its own window handler. - HWND hwnd = (HWND)_window.window; - SetWindowLongPtr(hwnd, GWLP_WNDPROC, _orig_window_proc); - InvalidateRect(hwnd, nullptr, true); - - if (_bg_brush != nullptr) { - DeleteObject(_bg_brush); - _bg_brush = nullptr; - } - for (int step = 0; step < twirl_num_steps + 1; ++step) { - if (_twirl_bitmaps[step] != nullptr) { - DeleteObject(_twirl_bitmaps[step]); - _twirl_bitmaps[step] = nullptr; - } - } -#endif // _WIN32 - -#ifdef HAVE_X11 - x11_stop_twirl_subprocess(); - -#ifdef HAVE_GTK - if (_plug != nullptr) { - gtk_widget_destroy(_plug); - _plug = nullptr; - } -#endif // HAVE_GTK -#endif // HAVE_X11 - - _got_window = false; - } -} - -/** - * Copies the data in the file named by from_filename into the file named by - * to_filename. - */ -bool PPInstance:: -copy_file(const string &from_filename, const string &to_filename) { - mkfile_complete(to_filename, nout); - ifstream in(from_filename.c_str(), ios::in | ios::binary); - ofstream out(to_filename.c_str(), ios::out | ios::binary); - - static const size_t buffer_size = 4096; - char buffer[buffer_size]; - - in.read(buffer, buffer_size); - size_t count = in.gcount(); - while (count != 0) { - out.write(buffer, count); - if (out.fail()) { - return false; - } - in.read(buffer, buffer_size); - count = in.gcount(); - } - - if (!in.eof()) { - return false; - } - - return true; -} - -/** - * Returns the value associated with the first appearance of the named token, - * or empty string if the token does not appear. - */ -string PPInstance:: -lookup_token(const string &keyword) const { - Tokens::const_iterator ti; - for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) { - if (keyword == (*ti)._keyword) { - return (*ti)._value; - } - } - - return string(); -} - -/** - * Returns true if the named token appears in the list, false otherwise. - */ -bool PPInstance:: -has_token(const string &keyword) const { - Tokens::const_iterator ti; - for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) { - if ((*ti)._keyword == keyword) { - return true; - } - } - - return false; -} - - -/** - * Compares the two dotted-integer sequence values numerically. Returns -1 if - * seq_a sorts first, 1 if seq_b sorts first, 0 if they are equivalent. - */ -int PPInstance:: -compare_seq(const string &seq_a, const string &seq_b) { - const char *num_a = seq_a.c_str(); - const char *num_b = seq_b.c_str(); - int comp = compare_seq_int(num_a, num_b); - while (comp == 0) { - if (*num_a != '.') { - if (*num_b != '.') { - // Both strings ran out together. - return 0; - } - // a ran out first. - return -1; - } else if (*num_b != '.') { - // b ran out first. - return 1; - } - - // Increment past the dot. - ++num_a; - ++num_b; - comp = compare_seq(num_a, num_b); - } - - return comp; -} - -/** - * Numerically compares the formatted integer value at num_a with num_b. - * Increments both num_a and num_b to the next character following the valid - * integer. - */ -int PPInstance:: -compare_seq_int(const char *&num_a, const char *&num_b) { - long int a; - char *next_a; - long int b; - char *next_b; - - a = strtol((char *)num_a, &next_a, 10); - b = strtol((char *)num_b, &next_b, 10); - - num_a = next_a; - num_b = next_b; - - if (a < b) { - return -1; - } else if (b < a) { - return 1; - } else { - return 0; - } -} - -/** - * Called when something has gone wrong that prevents the plugin instance from - * running. Specifically, this means it failed to load the core API. - */ -void PPInstance:: -set_failed() { - if (!_failed) { - _failed = true; - - nout << "Plugin failed.\n"; - stop_outstanding_streams(); - - // Look for the "onpluginfail" token. - string expression = lookup_token("onpluginfail"); - - if (!expression.empty()) { - // Now attempt to evaluate the expression. - NPObject *window_object = nullptr; - if (browser->getvalue(_npp_instance, NPNVWindowNPObject, - &window_object) == NPERR_NO_ERROR) { - NPString npexpr = { expression.c_str(), (uint32_t)expression.length() }; - NPVariant result; - if (browser->evaluate(_npp_instance, window_object, - &npexpr, &result)) { - nout << "Eval " << expression << "\n"; - browser->releasevariantvalue(&result); - } else { - nout << "Unable to eval " << expression << "\n"; - } - - browser->releaseobject(window_object); - } - } - - if (_p3d_inst != nullptr) { - P3D_instance_finish_ptr(_p3d_inst); - _p3d_inst = nullptr; - } - cleanup_window(); - } -} - -/** - * Checks for any new requests from the plugin, and dispatches them to the - * appropriate PPInstance. This function is called only in the main thread. - */ -void PPInstance:: -handle_request_loop() { - if (!is_plugin_loaded()) { - return; - } - - P3D_instance *p3d_inst = P3D_check_request_ptr(0.0); - while (p3d_inst != nullptr) { - P3D_request *request = P3D_instance_get_request_ptr(p3d_inst); - if (request != nullptr) { - PPInstance *inst = (PPInstance *)(p3d_inst->_user_data); - assert(inst != nullptr); - if (!inst->handle_request(request)) { - // If handling the request is meant to yield control temporarily to - // JavaScript (e.g. P3D_RT_callback), then do so now. - return; - } - - if (!is_plugin_loaded()) { - // Oops, we may have unloaded the plugin as an indirect effect of - // handling the request. If so, get out of here. - return; - } - } - p3d_inst = P3D_check_request_ptr(0.0); - } - - // Also check to see if we have any file_data objects that have finished and - // may be deleted. - size_t num_file_datas = _file_datas.size(); - size_t i = 0; - while (i < num_file_datas) { - if (!_file_datas[i]->is_done()) { - // This one keeps going. - ++i; - } else { - // This one is done. - delete _file_datas[i]; - _file_datas.erase(_file_datas.begin() + i); - --num_file_datas; - } - } -} - -/** - * This callback hook is passed to NPN_PluginThreadAsyncCall() (if that - * function is available) to forward a request to the main thread. The - * callback is actually called in the main thread. - */ -void PPInstance:: -browser_sync_callback(void *) { - handle_request_loop(); -} - - -#ifdef _WIN32 -/** - * We bind this function to the parent window we were given in set_window, so - * we can spin the request_loop when needed. This is only in the Windows - * case; other platforms rely on explicit windows events. - */ -LONG PPInstance:: -st_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - LONG_PTR self = GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (self == nullptr) { - // We haven't assigned the pointer yet (!?) - return DefWindowProc(hwnd, msg, wparam, lparam); - } - - return ((PPInstance *)self)->window_proc(hwnd, msg, wparam, lparam); -} -#endif // _WIN32 - -#ifdef _WIN32 -/** - * The non-static window_proc() function. - * - * We bind this function to the parent window we were given in set_window, so - * we can spin the request_loop when needed. This is only in the Windows - * case; other platforms rely on explicit windows events. - */ -LONG PPInstance:: -window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - if (!has_plugin_thread_async_call) { - // Since we're here in the main thread, call handle_request_loop() to see - // if there are any new requests to be serviced by the main thread. - handle_request_loop(); - } - - switch (msg) { - case WM_ERASEBKGND: - // Eat the WM_ERASEBKGND message, so the browser's intervening window - // won't overdraw on top of our own window. - return true; - - case WM_PAINT: - if (!_started) { - // If we haven't yet loaded the instance, we can paint a twirling icon - // in the window. - PAINTSTRUCT ps; - HDC dc = BeginPaint(hwnd, &ps); - win_paint_twirl(hwnd, dc); - EndPaint(hwnd, &ps); - } - return true; - - case WM_TIMER: - if (!_started) { - InvalidateRect(_hwnd, nullptr, FALSE); - } - break; - - case WM_USER: - break; - } - - return DefWindowProc(hwnd, msg, wparam, lparam); -} -#endif // _WIN32 - - -#ifdef _WIN32 -/** - * Fills _twirl_bitmaps with an array of bitmaps for drawing the twirling icon - * while we're waiting for the instance to load. - */ -void PPInstance:: -win_get_twirl_bitmaps() { - BITMAPINFOHEADER bmih; - bmih.biSize = sizeof(bmih); - bmih.biWidth = twirl_width; - bmih.biHeight = -twirl_height; - bmih.biPlanes = 1; - bmih.biBitCount = 32; - bmih.biCompression = BI_RGB; - bmih.biSizeImage = 0; - bmih.biXPelsPerMeter = 0; - bmih.biYPelsPerMeter = 0; - bmih.biClrUsed = 0; - bmih.biClrImportant = 0; - - BITMAPINFO bmi; - memcpy(&bmi, &bmih, sizeof(bmih)); - - HDC dc = GetDC(_hwnd); - - static const size_t twirl_size = twirl_width * twirl_height; - unsigned char twirl_data[twirl_size * 3]; - unsigned char new_data[twirl_size * 4]; - - for (int step = 0; step < twirl_num_steps + 1; ++step) { - get_twirl_data(twirl_data, twirl_size, step, - _fgcolor_r, _fgcolor_g, _fgcolor_b, - _bgcolor_r, _bgcolor_g, _bgcolor_b); - - // Expand out the RGB channels into RGBA. - for (int yi = 0; yi < twirl_height; ++yi) { - const unsigned char *sp = twirl_data + yi * twirl_width * 3; - unsigned char *dp = new_data + yi * twirl_width * 4; - for (int xi = 0; xi < twirl_width; ++xi) { - // RGB <= BGR. - dp[0] = sp[2]; - dp[1] = sp[1]; - dp[2] = sp[0]; - dp[3] = (unsigned char)0xff; - sp += 3; - dp += 4; - } - } - - // Now load the image. - _twirl_bitmaps[step] = CreateDIBitmap(dc, &bmih, CBM_INIT, (const char *)new_data, &bmi, 0); - } - - ReleaseDC(_hwnd, dc); -} -#endif // _WIN32 - -#ifdef _WIN32 -/** - * Paints the twirling icon into the browser window before the instance has - * started. - */ -void PPInstance:: -win_paint_twirl(HWND hwnd, HDC dc) { - RECT rect; - GetClientRect(_hwnd, &rect); - int width = rect.right - rect.left; - int height = rect.bottom - rect.top; - - // Double-buffer with an offscreen bitmap first. - HDC bdc = CreateCompatibleDC(dc); - HBITMAP buffer = CreateCompatibleBitmap(dc, width, height); - SelectObject(bdc, buffer); - - // Start by painting the background color. - FillRect(bdc, &rect, _bg_brush); - - if (!_started) { - DWORD now = GetTickCount(); - // Don't draw the twirling icon until at least half a second has passed, - // so we don't distract people by drawing it unnecessarily. - if (_failed || (now - _init_time) >= 500) { - // Which frame are we drawing? - int step = (now / 100) % twirl_num_steps; - if (_failed) { - step = twirl_num_steps; - } - - HBITMAP twirl = _twirl_bitmaps[step]; - - int left = rect.left + (width - twirl_width) / 2; - int top = rect.top + (height - twirl_height) / 2; - - HDC mem_dc = CreateCompatibleDC(bdc); - SelectObject(mem_dc, twirl); - - BitBlt(bdc, left, top, twirl_width, twirl_height, - mem_dc, 0, 0, SRCCOPY); - - SelectObject(mem_dc, nullptr); - DeleteDC(mem_dc); - } - } - - // Now blit the buffer to the window. - BitBlt(dc, 0, 0, width, height, bdc, 0, 0, SRCCOPY); -} -#endif // _WIN32 - -#ifdef MACOSX_HAS_EVENT_MODELS -/** - * Copies the NPCocoaEvent structure componentwise into a P3DCocoaEvent - * structure, for passing into the core API. - * - * The aux_data object is used to manage temporary storage on the strings - * created for the event. - */ -void PPInstance:: -copy_cocoa_event(P3DCocoaEvent *p3d_event, NPCocoaEvent *np_event, - EventAuxData &aux_data) { - p3d_event->version = np_event->version; - - switch (np_event->type) { - case NPCocoaEventDrawRect: - p3d_event->type = P3DCocoaEventDrawRect; - break; - case NPCocoaEventMouseDown: - p3d_event->type = P3DCocoaEventMouseDown; - break; - case NPCocoaEventMouseUp: - p3d_event->type = P3DCocoaEventMouseUp; - break; - case NPCocoaEventMouseMoved: - p3d_event->type = P3DCocoaEventMouseMoved; - break; - case NPCocoaEventMouseEntered: - p3d_event->type = P3DCocoaEventMouseEntered; - break; - case NPCocoaEventMouseExited: - p3d_event->type = P3DCocoaEventMouseExited; - break; - case NPCocoaEventMouseDragged: - p3d_event->type = P3DCocoaEventMouseDragged; - break; - case NPCocoaEventKeyDown: - p3d_event->type = P3DCocoaEventKeyDown; - break; - case NPCocoaEventKeyUp: - p3d_event->type = P3DCocoaEventKeyUp; - break; - case NPCocoaEventFlagsChanged: - p3d_event->type = P3DCocoaEventFlagsChanged; - break; - case NPCocoaEventFocusChanged: - p3d_event->type = P3DCocoaEventFocusChanged; - break; - case NPCocoaEventWindowFocusChanged: - p3d_event->type = P3DCocoaEventWindowFocusChanged; - break; - case NPCocoaEventScrollWheel: - p3d_event->type = P3DCocoaEventScrollWheel; - break; - case NPCocoaEventTextInput: - p3d_event->type = P3DCocoaEventTextInput; - break; - } - - switch (np_event->type) { - case NPCocoaEventDrawRect: - p3d_event->data.draw.context = np_event->data.draw.context; - p3d_event->data.draw.x = np_event->data.draw.x; - p3d_event->data.draw.y = np_event->data.draw.y; - p3d_event->data.draw.width = np_event->data.draw.width; - p3d_event->data.draw.height = np_event->data.draw.height; - break; - - case NPCocoaEventMouseDown: - case NPCocoaEventMouseUp: - case NPCocoaEventMouseMoved: - case NPCocoaEventMouseEntered: - case NPCocoaEventMouseExited: - case NPCocoaEventMouseDragged: - case NPCocoaEventScrollWheel: - p3d_event->data.mouse.modifierFlags = np_event->data.mouse.modifierFlags; - p3d_event->data.mouse.pluginX = np_event->data.mouse.pluginX; - p3d_event->data.mouse.pluginY = np_event->data.mouse.pluginY; - p3d_event->data.mouse.buttonNumber = np_event->data.mouse.buttonNumber; - p3d_event->data.mouse.clickCount = np_event->data.mouse.clickCount; - p3d_event->data.mouse.deltaX = np_event->data.mouse.deltaX; - p3d_event->data.mouse.deltaY = np_event->data.mouse.deltaY; - p3d_event->data.mouse.deltaZ = np_event->data.mouse.deltaZ; - break; - - case NPCocoaEventKeyDown: - case NPCocoaEventKeyUp: - case NPCocoaEventFlagsChanged: - p3d_event->data.key.modifierFlags = np_event->data.key.modifierFlags; - p3d_event->data.key.characters = - make_ansi_string(aux_data._characters, np_event->data.key.characters); - p3d_event->data.key.charactersIgnoringModifiers = - make_ansi_string(aux_data._characters_im, np_event->data.key.charactersIgnoringModifiers); - p3d_event->data.key.isARepeat = np_event->data.key.isARepeat; - p3d_event->data.key.keyCode = np_event->data.key.keyCode; - break; - - case NPCocoaEventFocusChanged: - case NPCocoaEventWindowFocusChanged: - p3d_event->data.focus.hasFocus = np_event->data.focus.hasFocus; - break; - - case NPCocoaEventTextInput: - p3d_event->data.text.text = - make_ansi_string(aux_data._text, np_event->data.text.text); - break; - } -} -#endif // MACOSX_HAS_EVENT_MODELS - -#ifdef MACOSX_HAS_EVENT_MODELS -/** - * OSX only: Fills result with the unicode characters in the NPNSString. Also - * returns result.c_str(). - */ -const wchar_t *PPInstance:: -make_ansi_string(std::wstring &result, NPNSString *ns_string) { - result.clear(); - - if (ns_string != nullptr) { - // An NPNSString is really just an NSString, which is itself just a - // CFString. - CFStringRef cfstr = (CFStringRef)ns_string; - CFIndex length = CFStringGetLength(cfstr); - - for (CFIndex i = 0; i < length; ++i) { - result += (wchar_t)CFStringGetCharacterAtIndex(cfstr, i); - } - } - - return result.c_str(); -} -#endif // MACOSX_HAS_EVENT_MODELS - -#ifdef MACOSX_HAS_EVENT_MODELS -/** - * Locally processes a Cocoa event for the window before sending it down to - * the Core API. This is used for drawing a twirling icon in the window while - * the Core API is downloading. - */ -void PPInstance:: -handle_cocoa_event(const P3DCocoaEvent *p3d_event) { - switch (p3d_event->type) { - case P3DCocoaEventDrawRect: - if (!_started) { - CGContextRef context = p3d_event->data.draw.context; - paint_twirl_osx_cgcontext(context); - } - break; - - default: - break; - } -} -#endif // MACOSX_HAS_EVENT_MODELS - -#ifdef MACOSX_HAS_EVENT_MODELS -/** - * Fills _twirl_images with an array of images for drawing the twirling icon - * while we're waiting for the instance to load. - */ -void PPInstance:: -osx_get_twirl_images() { - if (_got_twirl_images) { - return; - } - _got_twirl_images = true; - - static const size_t twirl_size = twirl_width * twirl_height; - unsigned char twirl_data[twirl_size * 3]; - - for (int step = 0; step < twirl_num_steps + 1; ++step) { - get_twirl_data(twirl_data, twirl_size, step, - _fgcolor_r, _fgcolor_g, _fgcolor_b, - _bgcolor_r, _bgcolor_g, _bgcolor_b); - - unsigned char *new_data = new unsigned char[twirl_size * 4]; - - // Expand the RGB channels into RGBA. Flip upside-down too. - for (int yi = 0; yi < twirl_height; ++yi) { - const unsigned char *sp = twirl_data + (twirl_height - 1 - yi) * twirl_width * 3; - unsigned char *dp = new_data + yi * twirl_width * 4; - for (int xi = 0; xi < twirl_width; ++xi) { - // RGB <= BGR. - dp[0] = sp[2]; - dp[1] = sp[1]; - dp[2] = sp[0]; - dp[3] = (unsigned char)0xff; - sp += 3; - dp += 4; - } - } - - OsxImageData &image = _twirl_images[step]; - image._raw_data = new_data; - - image._data = - CFDataCreateWithBytesNoCopy(nullptr, (const UInt8 *)image._raw_data, - twirl_size * 4, kCFAllocatorNull); - image._provider = CGDataProviderCreateWithCFData(image._data); - image._color_space = CGColorSpaceCreateDeviceRGB(); - - image._image = - CGImageCreate(twirl_width, twirl_height, 8, 32, - twirl_width * 4, image._color_space, - kCGImageAlphaFirst | kCGBitmapByteOrder32Little, - image._provider, nullptr, false, kCGRenderingIntentDefault); - } -} -#endif // MACOSX_HAS_EVENT_MODELS - -#ifdef MACOSX_HAS_EVENT_MODELS -/** - * Frees the twirl_images array. - */ -void PPInstance:: -osx_release_twirl_images() { - if (!_got_twirl_images) { - return; - } - _got_twirl_images = false; - - for (int step = 0; step < twirl_num_steps + 1; ++step) { - OsxImageData &image = _twirl_images[step]; - - if (image._image != nullptr) { - CGImageRelease(image._image); - image._image = nullptr; - } - if (image._color_space != nullptr) { - CGColorSpaceRelease(image._color_space); - image._color_space = nullptr; - } - if (image._provider != nullptr) { - CGDataProviderRelease(image._provider); - image._provider = nullptr; - } - if (image._data != nullptr) { - CFRelease(image._data); - image._data = nullptr; - } - if (image._raw_data != nullptr) { - delete[] image._raw_data; - image._raw_data = nullptr; - } - } -} -#endif // MACOSX_HAS_EVENT_MODELS - -#ifdef MACOSX_HAS_EVENT_MODELS -/** - * Actually paints the twirling icon in the OSX window, using Core Graphics. - * (We don't bother painting it in the older Cocoa interface.) - */ -void PPInstance:: -paint_twirl_osx_cgcontext(CGContextRef context) { - // Clear the whole region to the bgcolor before beginning. - float bg_components[] = { _bgcolor_r / 255.0f, _bgcolor_g / 255.0f, _bgcolor_b / 255.0f, 1 }; - CGColorSpaceRef rgb_space = CGColorSpaceCreateDeviceRGB(); - CGColorRef bg = CGColorCreate(rgb_space, bg_components); - - CGRect region = { { 0, 0 }, { _window.width, _window.height } }; - CGContextBeginPath(context); - CGContextSetFillColorWithColor(context, bg); - CGContextAddRect(context, region); - CGContextFillPath(context); - - CGColorRelease(bg); - CGColorSpaceRelease(rgb_space); - - if (_failed) { - // Draw the failed icon if something went wrong. - osx_paint_image(context, _twirl_images[twirl_num_steps]); - - } else { - struct timeval tv; - gettimeofday(&tv, nullptr); - double now = (double)(tv.tv_sec - _init_sec) + (double)(tv.tv_usec - _init_usec) / 1000000.0; - - // Don't draw the twirling icon until at least half a second has passed, - // so we don't distract people by drawing it unnecessarily. - if (now >= 0.5) { - int step = ((int)(now * 10.0)) % twirl_num_steps; - osx_paint_image(context, _twirl_images[step]); - } - } -} -#endif // MACOSX_HAS_EVENT_MODELS - -#ifdef MACOSX_HAS_EVENT_MODELS -/** - * Draws the indicated image, centered within the window. Returns true on - * success, false if the image is not defined. - */ -bool PPInstance:: -osx_paint_image(CGContextRef context, const OsxImageData &image) { - if (image._image == nullptr) { - return false; - } - - // Determine the relative size of image and window. - int win_cx = _window.width / 2; - int win_cy = _window.height / 2; - - CGRect rect = { { 0, 0 }, { 0, 0 } }; - - // The bitmap fits within the window; center it. - - // This is the top-left corner of the bitmap in window coordinates. - int p_x = win_cx - twirl_width / 2; - int p_y = win_cy - twirl_height / 2; - - rect.origin.x += p_x; - rect.origin.y += p_y; - rect.size.width = twirl_width; - rect.size.height = twirl_height; - - CGContextDrawImage(context, rect, image._image); - - return true; -} -#endif // MACOSX_HAS_EVENT_MODELS - -#ifdef __APPLE__ -/** - * OSX only: this callback is associated with a CFRunLoopTimer; it's used to - * forward request messages to the main thread. - */ -void PPInstance:: -timer_callback(CFRunLoopTimerRef timer, void *info) { - PPInstance *self = (PPInstance *)info; - ACQUIRE_LOCK(self->_timer_lock); - if (self->_request_timer != nullptr) { - CFRunLoopTimerInvalidate(self->_request_timer); - CFRelease(self->_request_timer); - self->_request_timer = nullptr; - } - RELEASE_LOCK(self->_timer_lock); - - self->handle_request_loop(); -} -#endif // __APPLE__ - -#ifdef __APPLE__ -/** - * OSX only: this callback is used to twirl the icon before the instance - * loads. - */ -void PPInstance:: -st_twirl_timer_callback(CFRunLoopTimerRef timer, void *info) { - PPInstance *self = (PPInstance *)info; - self->twirl_timer_callback(); -} -#endif // __APPLE__ - -#ifdef __APPLE__ -/** - * OSX only: this callback is used to twirl the icon before the instance - * loads. - */ -void PPInstance:: -twirl_timer_callback() { - if (_got_window) { - NPRect rect = { 0, 0, (unsigned short)_window.height, (unsigned short)_window.width }; - browser->invalidaterect(_npp_instance, &rect); - } -} -#endif // __APPLE__ - -#ifdef HAVE_X11 -/** - * Spawns a separate process to twirl the loading icon in the X11 browser - * window. - */ -void PPInstance:: -x11_start_twirl_subprocess() { - if (_twirl_subprocess_pid != -1) { - // Already started. - return; - } - - // Fork and exec. - pid_t child = fork(); - if (child < 0) { - perror("fork"); - return; - } - - if (child == 0) { - // Here we are in the child process. - x11_twirl_subprocess_run(); - _exit(99); - } - - // In the parent process. - _twirl_subprocess_pid = child; - nout << "Started twirl subprocess, pid " << _twirl_subprocess_pid - << "\n"; -} -#endif // HAVE_X11 - -#ifdef HAVE_X11 -/** - * Kills the twirl process that was started earlier. - */ -void PPInstance:: -x11_stop_twirl_subprocess() { - if (_twirl_subprocess_pid == -1) { - // Already stopped. - return; - } - - kill(_twirl_subprocess_pid, SIGKILL); - - int status; - pid_t result = waitpid(_twirl_subprocess_pid, &status, 0); - - nout << "Twirl window process has successfully stopped.\n"; - if (WIFEXITED(status)) { - nout << " exited normally, status = " - << WEXITSTATUS(status) << "\n"; - } else if (WIFSIGNALED(status)) { - nout << " signalled by " << WTERMSIG(status) << ", core = " - << WCOREDUMP(status) << "\n"; - } else if (WIFSTOPPED(status)) { - nout << " stopped by " << WSTOPSIG(status) << "\n"; - } - _twirl_subprocess_pid = -1; -} -#endif // HAVE_X11 - -#ifdef HAVE_X11 -/** - * The code that is run within a subprocess. This code is responsible for - * twirling the loading icon endlessly. - */ -void PPInstance:: -x11_twirl_subprocess_run() { - // Since everything within this function happens within a subprocess that - // will just exit, we can be a little sloppy with our resource allocation. - // It is all done directly within this function, and we don't need to worry - // about freeing stuff. - - // First, sleep for 0.5 seconds, so we don't start twirling right away (to - // avoid distracting the user unnecessarily). - - struct timespec req; - req.tv_sec = 0; - req.tv_nsec = 500000000; // 500 ms - nanosleep(&req, nullptr); - - // We haven't been killed yet, so the plugin is still loading. Start - // twirling. - - // First, embed a window. - X11_Display *display = XOpenDisplay(nullptr); - assert(display != nullptr); - int screen = DefaultScreen(display); - - int depth = DefaultDepth(display, screen); - Visual *dvisual = DefaultVisual(display, screen); - - long event_mask = ExposureMask; - - // Allocate the foreground and background colors. - Colormap colormap = DefaultColormap(display, screen); - - XColor fg; - fg.red = _fgcolor_r * 0x101; - fg.green = _fgcolor_g * 0x101; - fg.blue = _fgcolor_b * 0x101; - fg.flags = DoRed | DoGreen | DoBlue; - unsigned long fg_pixel = -1; - if (XAllocColor(display, colormap, &fg)) { - fg_pixel = fg.pixel; - } - - XColor bg; - bg.red = _bgcolor_r * 0x101; - bg.green = _bgcolor_g * 0x101; - bg.blue = _bgcolor_b * 0x101; - bg.flags = DoRed | DoGreen | DoBlue; - unsigned long bg_pixel = -1; - if (XAllocColor(display, colormap, &bg)) { - bg_pixel = bg.pixel; - } - - // Initialize window attributes - XSetWindowAttributes wa; - wa.background_pixel = XWhitePixel(display, screen); - if (bg_pixel != -1) { - wa.background_pixel = bg_pixel; - } - wa.border_pixel = 0; - wa.event_mask = event_mask; - - unsigned long attrib_mask = CWBackPixel | CWBorderPixel | CWEventMask; - - X11_Window parent = 0; - if (_use_xembed) { -#ifdef HAVE_GTK - assert(_plug != nullptr); - parent = GDK_DRAWABLE_XID(_plug->window); -#endif // HAVE_GTK - } else { - parent = (X11_Window)(_window.window); - } - - X11_Window window = XCreateWindow - (display, parent, 0, 0, _window.width, _window.height, - 0, depth, InputOutput, dvisual, attrib_mask, &wa); - XMapWindow(display, window); - - // Create a graphics context. - XGCValues gcval; - gcval.function = GXcopy; - gcval.plane_mask = AllPlanes; - gcval.foreground = BlackPixel(display, screen); - if (fg_pixel != -1) { - gcval.foreground = fg_pixel; - } - gcval.background = WhitePixel(display, screen); - if (bg_pixel != -1) { - gcval.background = bg_pixel; - } - GC graphics_context = XCreateGC(display, window, - GCFunction | GCPlaneMask | GCForeground | GCBackground, &gcval); - - // Load up the twirling images. - XImage *images[twirl_num_steps]; - double r_ratio = dvisual->red_mask / 255.0; - double g_ratio = dvisual->green_mask / 255.0; - double b_ratio = dvisual->blue_mask / 255.0; - - static const size_t twirl_size = twirl_width * twirl_height; - unsigned char twirl_data[twirl_size * 3]; - - for (int step = 0; step < twirl_num_steps; ++step) { - get_twirl_data(twirl_data, twirl_size, step, - _fgcolor_r, _fgcolor_g, _fgcolor_b, - _bgcolor_r, _bgcolor_g, _bgcolor_b); - uint32_t *new_data = new uint32_t[twirl_size]; - int j = 0; - for (int i = 0; i < twirl_size * 3; i += 3) { - unsigned int r, g, b; - r = (unsigned int)(twirl_data[i+0] * r_ratio); - g = (unsigned int)(twirl_data[i+1] * g_ratio); - b = (unsigned int)(twirl_data[i+2] * b_ratio); - new_data[j++] = ((r & dvisual->red_mask) | - (g & dvisual->green_mask) | - (b & dvisual->blue_mask)); - } - - // Now load the image. - images[step] = XCreateImage(display, CopyFromParent, DefaultDepth(display, screen), - ZPixmap, 0, (char *)new_data, twirl_width, twirl_height, 32, 0); - } - - // Now twirl indefinitely, until our parent process kills us. - bool needs_redraw = true; - int last_step = -1; - while (true) { - // First, scan for X events. - XEvent event; - while (XCheckWindowEvent(display, window, ~0, &event)) { - switch (event.type) { - case Expose: - case GraphicsExpose: - needs_redraw = true; - break; - } - - // We should probably track the resize event, but this window will be - // short-lived (and probably won't have an opportunity to resize anyway) - // so we don't bother. - } - - // What step are we on now? - struct timeval tv; - gettimeofday(&tv, nullptr); - double now = (double)(tv.tv_sec - _init_sec) + (double)(tv.tv_usec - _init_usec) / 1000000.0; - int step = ((int)(now * 10.0)) % twirl_num_steps; - if (step != last_step) { - needs_redraw = true; - } - - if (needs_redraw) { - XClearWindow(display, window); - int xo = (_window.width - twirl_width) / 2; - int yo = (_window.height - twirl_height) / 2; - XPutImage(display, window, graphics_context, images[step], 0, 0, - xo, yo, twirl_width, twirl_height); - - XFlush(display); - needs_redraw = false; - last_step = step; - } - - struct timespec req; - req.tv_sec = 0; - req.tv_nsec = 100000000; // 100 ms - nanosleep(&req, nullptr); - } -} -#endif // HAVE_X11 - -/** - * - */ -PPInstance::StreamingFileData:: -StreamingFileData(PPDownloadRequest *req, const string &filename, - P3D_instance *p3d_inst) : - _p3d_inst(p3d_inst), - _user_id(req->_user_id), - _filename(filename), - _file(filename.c_str(), ios::in | ios::binary) -{ - // First, seek to the end to get the file size. - _file.seekg(0, ios::end); - _file_size = _file.tellg(); - _total_count = 0; - - // Then return to the beginning to read the data. - _file.seekg(0, ios::beg); - - // Now start up the thread. - _thread_done = false; - _thread_continue = true; - INIT_THREAD(_thread); - - SPAWN_THREAD(_thread, thread_run, this); -} - -/** - * - */ -PPInstance::StreamingFileData:: -~StreamingFileData() { - // Time to stop. - _thread_continue = false; - - JOIN_THREAD(_thread); -} - -/** - * Returns true if the file has been fully read and this object is ready to be - * deleted, or false if there is more work to do. - */ -bool PPInstance::StreamingFileData:: -is_done() const { - return _thread_done; -} - -/** - * The main function of the file thread. This reads the file contents and - * feeds it to the core API. - */ -void PPInstance::StreamingFileData:: -thread_run() { - static const size_t buffer_size = 81920; - // static const size_t buffer_size = 512; - char buffer[buffer_size]; - - _file.read(buffer, buffer_size); - size_t count = _file.gcount(); - while (count != 0) { - _total_count += count; - bool download_ok = P3D_instance_feed_url_stream_ptr - (_p3d_inst, _user_id, P3D_RC_in_progress, - 0, _file_size, (const unsigned char *)buffer, count); - - if (!download_ok) { - // Never mind. - _thread_done = true; - return; - } - - if (!_thread_continue) { - // Interrupted by the main thread. Presumably we're being shut down. - _thread_done = true; - return; - } - - // So far, so good. Read some more. - _file.read(buffer, buffer_size); - count = _file.gcount(); - - // This is useful for development, to slow things down enough to see the - // progress bar move. -#ifdef _WIN32 - Sleep(10); -#else - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 10000; - select(0, nullptr, nullptr, nullptr, &tv); -#endif - } - - // End of file. - P3D_result_code result = P3D_RC_done; - if (_file.fail() && !_file.eof()) { - // Got an error while reading. - result = P3D_RC_generic_error; - } - - P3D_instance_feed_url_stream_ptr - (_p3d_inst, _user_id, result, 0, _total_count, nullptr, 0); - - // All done. - _thread_done = true; -} - - -/** - * - */ -PPInstance::StreamTempFile:: -StreamTempFile() { - _opened = false; - _finished = false; - _current_size = 0; - _total_size = 0; -} - -/** - * - */ -PPInstance::StreamTempFile:: -~StreamTempFile() { - cleanup(); -} - -/** - * Creates the temp file and prepares to write to it. It is not normally - * necessary to call this explicitly; it will be called automatically on the - * first call to feed(). - */ -void PPInstance::StreamTempFile:: -open() { - assert(!_opened); - _opened = true; - _finished = false; - _current_size = 0; - _total_size = 0; - - char *name = tempnam(nullptr, "p3d_"); - _filename = name; - free(name); - - _stream.clear(); - _stream.open(_filename.c_str(), ios::binary); - if (!_stream) { - nout << "Unable to open temp file " << _filename << "\n"; - } else { - nout << "Opening " << _filename << "\n"; - } -} - -/** - * Receives new data from the URL and writes it to the temp file. Returns - * true on success, false on failure. - */ -bool PPInstance::StreamTempFile:: -feed(size_t total_expected_data, const void *this_data, - size_t this_data_size) { - if (_finished) { - nout << "feed(" << total_expected_data << ", " << (void *)this_data - << ", " << this_data_size << ") to " << _filename - << ", but already finished at " << _current_size << "\n"; - return false; - } - - if (!_opened) { - open(); - } - - _stream.write((const char *)this_data, this_data_size); - _current_size += this_data_size; - _total_size = total_expected_data; - - if (!_stream) { - return false; - } - return true; -} - -/** - * Marks the end of the data received from the URL. The file is closed but - * not yet deleted; it remains on disk and may be read at leisure. - */ -void PPInstance::StreamTempFile:: -finish() { - if (!_finished) { - _finished = true; - _total_size = _current_size; - } - - _stream.close(); -} - -/** - * Closes the stream for more data. The file is not yet deleted; it remains - * on disk and may be read at leisure. - */ -void PPInstance::StreamTempFile:: -close() { - _stream.close(); -} - -/** - * Closes all open processes and removes the temp file from disk. - */ -void PPInstance::StreamTempFile:: -cleanup() { - finish(); - - if (!_filename.empty()) { - nout << "Deleting " << _filename << "\n"; - unlink(_filename.c_str()); - _filename.clear(); - } - - _opened = false; - _finished = false; -} diff --git a/direct/src/plugin_npapi/ppInstance.h b/direct/src/plugin_npapi/ppInstance.h deleted file mode 100644 index f24ebb6443..0000000000 --- a/direct/src/plugin_npapi/ppInstance.h +++ /dev/null @@ -1,302 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppInstance.h - * @author drose - * @date 2009-06-19 - */ - -#ifndef PPINSTANCE_H -#define PPINSTANCE_H - -#include "nppanda3d_common.h" -#include "fileSpec.h" -#include "get_tinyxml.h" -#include "p3d_lock.h" -#include "get_twirl_data.h" -#include "p3d_plugin_config.h" -#include "plugin_get_x11.h" - -#if defined(HAVE_X11) && defined(HAVE_GTK) -#include "pre_x11_include.h" -#include -#include -#include "post_x11_include.h" -#endif // HAVE_X11 && HAVE_GTK - -#include - -class PPToplevelObject; -class PPDownloadRequest; - -/** - * This represents a single instance of the Panda3D plugin, via the NPAPI - * interface. This instance brokers the communication with the P3D Core API, - * as defined in the plugin directory. - */ -class PPInstance { -public: - PPInstance(NPMIMEType pluginType, NPP instance, uint16_t mode, - int16_t argc, char *argn[], char *argv[], NPSavedData *saved, - P3D_window_handle_type window_handle_type, - P3D_event_type event_type); - ~PPInstance(); - - void begin(); - - inline NPP get_npp_instance() const; - - inline const NPWindow *get_window() const; - void set_window(NPWindow *window); - NPError new_stream(NPMIMEType type, NPStream *stream, - bool seekable, uint16_t *stype); - void stop_outstanding_streams(); - - int32_t write_ready(NPStream *stream); - int write_stream(NPStream *stream, int offset, int len, void *buffer); - NPError destroy_stream(NPStream *stream, NPReason reason); - void url_notify(const char *url, NPReason reason, void *notifyData); - void stream_as_file(NPStream *stream, const char *fname); - - bool handle_request(P3D_request *request); - static void generic_browser_call(); - - bool handle_event(void *event); - - NPObject *get_panda_script_object(); - void set_xembed(bool use_xembed); - - void p3dobj_to_variant(NPVariant *result, P3D_object *object); - P3D_object *variant_to_p3dobj(const NPVariant *variant); - - static void output_np_variant(std::ostream &out, const NPVariant &result); - -private: - void find_host(TiXmlElement *xcontents); - void read_xhost(TiXmlElement *xhost); - void add_mirror(std::string mirror_url); - void choose_random_mirrors(std::vector &result, int num_mirrors); - - static void request_ready(P3D_instance *instance); - - void start_download(const std::string &url, PPDownloadRequest *req); - void downloaded_file(PPDownloadRequest *req, const std::string &filename); - static std::string get_filename_from_url(const std::string &url); - void feed_file(PPDownloadRequest *req, const std::string &filename); - - void open_p3d_temp_file(); - void send_p3d_temp_file_data(); - - void downloaded_contents_file(const std::string &filename); - bool read_contents_file(const std::string &contents_filename, bool fresh_download); - void get_core_api(); - void downloaded_plugin(const std::string &filename); - void do_load_plugin(); - - void create_instance(); - void send_window(); - void cleanup_window(); - bool copy_file(const std::string &from_filename, const std::string &to_filename); - - std::string lookup_token(const std::string &keyword) const; - bool has_token(const std::string &keyword) const; - static int compare_seq(const std::string &seq_a, const std::string &seq_b); - static int compare_seq_int(const char *&num_a, const char *&num_b); - - void set_failed(); - - static void handle_request_loop(); - static void browser_sync_callback(void *); - -#ifdef _WIN32 - static LONG - st_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); - LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); - void win_get_twirl_bitmaps(); - void win_paint_twirl(HWND hwnd, HDC dc); -#endif // _WIN32 - - class EventAuxData { - public: - std::wstring _characters; - std::wstring _characters_im; - std::wstring _text; - }; -#ifdef MACOSX_HAS_EVENT_MODELS - static void copy_cocoa_event(P3DCocoaEvent *p3d_event, - NPCocoaEvent *np_event, - EventAuxData &aux_data); - static const wchar_t *make_ansi_string(std::wstring &result, NPNSString *ns_string); - void handle_cocoa_event(const P3DCocoaEvent *p3d_event); - void osx_get_twirl_images(); - void osx_release_twirl_images(); - void paint_twirl_osx_cgcontext(CGContextRef context); - class OsxImageData; - bool osx_paint_image(CGContextRef context, const OsxImageData &image); -#endif // MACOSX_HAS_EVENT_MODELS - -#ifdef __APPLE__ - static void timer_callback(CFRunLoopTimerRef timer, void *info); - static void st_twirl_timer_callback(CFRunLoopTimerRef timer, void *info); - void twirl_timer_callback(); -#endif // __APPLE__ - -#ifdef HAVE_X11 - void x11_start_twirl_subprocess(); - void x11_stop_twirl_subprocess(); - void x11_twirl_subprocess_run(); -#endif // HAVE_X11 - -private: - NPP _npp_instance; - unsigned int _npp_mode; - P3D_window_handle_type _window_handle_type; - P3D_event_type _event_type; - typedef std::vector Tokens; - Tokens _tokens; - - // Set from fgcolor & bgcolor. - int _fgcolor_r, _fgcolor_b, _fgcolor_g; - int _bgcolor_r, _bgcolor_b, _bgcolor_g; - - std::string _root_dir; - std::string _standard_url_prefix; - std::string _download_url_prefix; - typedef std::vector Mirrors; - Mirrors _mirrors; - - // A list of URL's that we will attempt to download the core API from. - typedef std::vector CoreUrls; - CoreUrls _core_urls; - - std::string _coreapi_set_ver; - FileSpec _coreapi_dll; - time_t _contents_expiration; - bool _failed; - bool _started; - - // This class is used to stream data from some URL into a temporary local - // file. - class StreamTempFile { - public: - StreamTempFile(); - ~StreamTempFile(); - - void open(); - bool feed(size_t total_expected_data, const void *this_data, - size_t this_data_size); - void finish(); - void close(); - void cleanup(); - - bool _opened; - bool _finished; - size_t _current_size; - size_t _total_size; - ofstream _stream; - std::string _filename; - }; - - bool _got_instance_url; - std::string _instance_url; - int _p3d_instance_id; - StreamTempFile _p3d_temp_file; - StreamTempFile _contents_temp_file; - StreamTempFile _core_dll_temp_file; - - // We need to keep a list of the NPStream objects that the instance owns, - // because Safari (at least) won't automatically delete all of the - // outstanding streams when the instance is destroyed. - typedef std::vector Streams; - Streams _streams; - - // This class is used for feeding local files (accessed via a "file:" url) - // into the core API. - class StreamingFileData { - public: - StreamingFileData(PPDownloadRequest *req, const std::string &filename, - P3D_instance *p3d_inst); - ~StreamingFileData(); - - bool is_done() const; - - private: - void thread_run(); - THREAD_CALLBACK_DECLARATION(PPInstance::StreamingFileData, thread_run); - - private: - bool _thread_done; - bool _thread_continue; - - P3D_instance *_p3d_inst; - int _user_id; - std::string _filename; - ifstream _file; - size_t _file_size; - size_t _total_count; - - THREAD _thread; - }; - - typedef std::vector FileDatas; - static FileDatas _file_datas; - - bool _use_xembed; - bool _got_window; - NPWindow _window; -#if defined(HAVE_X11) && defined(HAVE_GTK) - GtkWidget *_plug; -#endif // HAVE_X11 && HAVE_GTK - -#ifdef _WIN32 - LONG_PTR _orig_window_proc; - HWND _hwnd; - HBITMAP _twirl_bitmaps[twirl_num_steps + 1]; - HBRUSH _bg_brush; - DWORD _init_time; -#endif // _WIN32 - -#ifdef __APPLE__ - CFRunLoopRef _run_loop_main; - CFRunLoopTimerRef _request_timer; - LOCK _timer_lock; - CFRunLoopTimerRef _twirl_timer; -#endif // __APPLE__ - -#ifdef MACOSX_HAS_EVENT_MODELS - class OsxImageData { - public: - unsigned char *_raw_data; - CFDataRef _data; - CGDataProviderRef _provider; - CGColorSpaceRef _color_space; - CGImageRef _image; - }; - OsxImageData _twirl_images[twirl_num_steps + 1]; - bool _got_twirl_images; -#endif // MACOSX_HAS_EVENT_MODELS - -#ifdef HAVE_X11 - pid_t _twirl_subprocess_pid; -#endif // HAVE_X11 - -#ifndef _WIN32 - long _init_sec; - long _init_usec; -#endif // _WIN32 - - bool _python_window_open; - - PPToplevelObject *_script_object; - - P3D_instance *_p3d_inst; -}; - -#include "ppInstance.I" - -#endif diff --git a/direct/src/plugin_npapi/ppPandaObject.I b/direct/src/plugin_npapi/ppPandaObject.I deleted file mode 100644 index eafcfbd83c..0000000000 --- a/direct/src/plugin_npapi/ppPandaObject.I +++ /dev/null @@ -1,25 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppPandaObject.I - * @author drose - * @date 2009-07-03 - */ - -/** - * Returns the p3d_object this PPPandaObject maps to. This may be NULL if the - * object is not fully initialized. If not NULL, this returns a new - * reference. - */ -inline P3D_object *PPPandaObject:: -get_p3d_object() const { - if (_p3d_object != nullptr) { - P3D_OBJECT_INCREF(_p3d_object); - } - return _p3d_object; -} diff --git a/direct/src/plugin_npapi/ppPandaObject.cxx b/direct/src/plugin_npapi/ppPandaObject.cxx deleted file mode 100644 index e4e4854c3f..0000000000 --- a/direct/src/plugin_npapi/ppPandaObject.cxx +++ /dev/null @@ -1,428 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppPandaObject.cxx - * @author drose - * @date 2009-07-03 - */ - -#include "ppPandaObject.h" - -using std::string; - -NPClass PPPandaObject::_object_class = { - NP_CLASS_STRUCT_VERSION, - &PPPandaObject::NPAllocate, - &PPPandaObject::NPDeallocate, - &PPPandaObject::NPInvalidate, - &PPPandaObject::NPHasMethod, - &PPPandaObject::NPInvoke, - &PPPandaObject::NPInvokeDefault, - &PPPandaObject::NPHasProperty, - &PPPandaObject::NPGetProperty, - &PPPandaObject::NPSetProperty, - &PPPandaObject::NPRemoveProperty, -#if defined(NP_CLASS_STRUCT_VERSION_ENUM) && NP_CLASS_STRUCT_VERSION >= NP_CLASS_STRUCT_VERSION_ENUM - &PPPandaObject::NPEnumerate, -#endif -#if defined(NP_CLASS_STRUCT_VERSION_CTOR) && NP_CLASS_STRUCT_VERSION >= NP_CLASS_STRUCT_VERSION_CTOR - &PPPandaObject::NPConstruct, -#endif -}; - - -/** - * Use this call to construct a new PPPandaObject. - */ -PPPandaObject *PPPandaObject:: -make_new(PPInstance *inst, P3D_object *p3d_object) { - NPObject *npobj = - browser->createobject(inst->get_npp_instance(), &_object_class); - PPPandaObject *ppobj = (PPPandaObject *)npobj; - ppobj->construct(inst, p3d_object); - return ppobj; -} - -/** - * Changes the p3d_object this PPPandaObject maps to. The new object's - * reference count is incremented, and the previous object's is decremented. - */ -void PPPandaObject:: -set_p3d_object(P3D_object *p3d_object) { - if (p3d_object != nullptr) { - P3D_OBJECT_INCREF(p3d_object); - } - P3D_OBJECT_XDECREF(_p3d_object); - _p3d_object = p3d_object; -} - -/** - * Stands in for the C++ constructor. We can't have a true constructor - * because of the C-style interface in NPN_CreateObject(). This must be - * called explicitly following NPN_CreateObject(). - */ -void PPPandaObject:: -construct(PPInstance *inst, P3D_object *p3d_object) { - _instance = inst; - _p3d_object = nullptr; - set_p3d_object(p3d_object); -} - -/** - * This "destructor" is called by NPInvalidate(). - */ -void PPPandaObject:: -invalidate() { - _instance = nullptr; - set_p3d_object(nullptr); -} - -/** - * Returns true if the object has the named method, false otherwise. - */ -bool PPPandaObject:: -has_method(NPIdentifier name) { - string method_name = identifier_to_string(name); - // nout << this << ".has_method(" << method_name << ")\n"; - if (_p3d_object == nullptr) { - // Not powered up yet. - return false; - } - - // Unlike has_property(), below, it turns out that we really do need to - // honestly answer whether there is a method by this name, because if there - // is, then Firefox won't query the property in a meaningful fashion. - - // Of course, in Python the distinction between property and method is a - // little looser than Firefox seems to want to make it, and sometimes you - // have an object which is both. This could become problematic in obscure - // situations. Too bad, say I. Mozilla's bug, not mine. - - bool result = P3D_OBJECT_HAS_METHOD(_p3d_object, method_name.c_str()); - return result; -} - -/** - * Calls the named method on the object, storing the return value into result. - * Returns true on success, false on failure. - */ -bool PPPandaObject:: -invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, - NPVariant *result) { - string method_name = identifier_to_string(name); - // nout << this << ".invoke(" << method_name << ")\n"; - if (_p3d_object == nullptr) { - // Not powered up yet. - return false; - } - - P3D_object **p3dargs = new P3D_object *[argCount]; - unsigned int i; - for (i = 0; i < argCount; ++i) { - p3dargs[i] = _instance->variant_to_p3dobj(&args[i]); - } - - P3D_object *value = P3D_OBJECT_CALL(_p3d_object, method_name.c_str(), - true, p3dargs, argCount); - for (i = 0; i < argCount; ++i) { - P3D_OBJECT_DECREF(p3dargs[i]); - } - delete[] p3dargs; - - if (value == nullptr) { - // No such method, or some problem with the parameters. - return false; - } - - // We have the return value, and its value is stored in value. - _instance->p3dobj_to_variant(result, value); - P3D_OBJECT_DECREF(value); - return true; -} - -/** - * Calls the default method on the object, storing the return value into - * result. Returns true on success, false on failure. - */ -bool PPPandaObject:: -invoke_default(const NPVariant *args, uint32_t argCount, - NPVariant *result) { - // nout << this << ".invoke_default()\n"; - if (_p3d_object == nullptr) { - // Not powered up yet. - return false; - } - - P3D_object **p3dargs = new P3D_object *[argCount]; - unsigned int i; - for (i = 0; i < argCount; ++i) { - p3dargs[i] = _instance->variant_to_p3dobj(&args[i]); - } - - P3D_object *value = P3D_OBJECT_CALL(_p3d_object, "", true, - p3dargs, argCount); - for (i = 0; i < argCount; ++i) { - P3D_OBJECT_DECREF(p3dargs[i]); - } - delete[] p3dargs; - - if (value == nullptr) { - // No such method, or some problem with the parameters. - return false; - } - - // We have the return value, and its value is stored in value. - _instance->p3dobj_to_variant(result, value); - P3D_OBJECT_DECREF(value); - return true; -} - -/** - * Returns true if the object has the named property, false otherwise. - */ -bool PPPandaObject:: -has_property(NPIdentifier name) { - string property_name = identifier_to_string(name); - // nout << this << ".has_property(" << property_name << ")\n"; - if (_p3d_object == nullptr) { - // Not powered up yet. - return false; - } - - // If we say we don't have a given property, then set_property() will never - // be called. So we always say we *do* have any particular property, - // whether we currently have it right now or not (since we *could* have it - // if you call set_property()). - - // On the other hand, Firefox gets confused about methods that are also - // properties. So you have to say there's *no* property if there is in fact - // a callable method by that name, or Firefox will never call the method. - bool result = P3D_OBJECT_HAS_METHOD(_p3d_object, property_name.c_str()); - return !result; -} - -/** - * Retrieves the named property value from the object and stores it in result. - * Returns true on success, false on failure. - */ -bool PPPandaObject:: -get_property(NPIdentifier name, NPVariant *result) { - // Actually, we never return false. If the property doesn't exist, we - // return undefined, to be consistent with JavaScript (and with IE). - - string property_name = identifier_to_string(name); - // nout << this << ".get_property(" << property_name << ")\n"; - if (_p3d_object == nullptr) { - // Not powered up yet. - VOID_TO_NPVARIANT(*result); - return true; - } - - P3D_object *value = P3D_OBJECT_GET_PROPERTY(_p3d_object, property_name.c_str()); - if (value == nullptr) { - // No such property. - VOID_TO_NPVARIANT(*result); - return true; - } - - // We have the property, and its value is stored in value. - _instance->p3dobj_to_variant(result, value); - P3D_OBJECT_DECREF(value); - return true; -} - -/** - * Replaces the named property value on the object. Returns true on success, - * false on failure. - */ -bool PPPandaObject:: -set_property(NPIdentifier name, const NPVariant *value) { - string property_name = identifier_to_string(name); - // nout << this << ".set_property(" << property_name << ")\n"; - if (_p3d_object == nullptr) { - // Not powered up yet. - return false; - } - - P3D_object *object = _instance->variant_to_p3dobj(value); - bool result = P3D_OBJECT_SET_PROPERTY(_p3d_object, property_name.c_str(), - true, object); - P3D_OBJECT_DECREF(object); - return result; -} - -/** - * Deletes the named property value from the object. Returns true on success, - * false on failure. - */ -bool PPPandaObject:: -remove_property(NPIdentifier name) { - string property_name = identifier_to_string(name); - // nout << this << ".remove_property(" << property_name << ")\n"; - if (_p3d_object == nullptr) { - // Not powered up yet. - return false; - } - - bool result = P3D_OBJECT_SET_PROPERTY(_p3d_object, property_name.c_str(), - true, nullptr); - return result; -} - -/** - * Constructs a list of available properties on this object. Returns true on - * success, false on failure. - */ -bool PPPandaObject:: -enumerate(NPIdentifier **value, uint32_t *count) { - // nout << this << ".enumerate()\n"; TODO: Not implemented yet. - - // Note that the array of values must be allocated here with NPN_MemAlloc(). - *value = nullptr; - *count = 0; - return false; -} - -/** - * Gets the string equivalent of the indicated identifier, whether it is an - * integer identifier or a string identifier. - */ -string PPPandaObject:: -identifier_to_string(NPIdentifier ident) { - if (browser->identifierisstring(ident)) { - NPUTF8 *result = browser->utf8fromidentifier(ident); - if (result != nullptr) { - string strval(result); - browser->memfree(result); - return strval; - } - } else { - // An integer identifier. We could treat this as a special case, like - // Firefox does, but Safari doesn't appear to use integer identifiers and - // just sends everything as a string identifier. So to make things - // consistent internally, we also send everything as a string. - std::ostringstream strm; - strm << browser->intfromidentifier(ident); - return strm.str(); - } - - return string(); -} - - -// The remaining function bodies are the C-style function wrappers that are -// called directly by NPAPI, and which redirect into the above C++-style -// methods. - -/** - * Called by NPN_CreateObject() to allocate space for this object. - */ -NPObject *PPPandaObject:: -NPAllocate(NPP npp, NPClass *aClass) { - assert(aClass == &_object_class); - return (PPPandaObject *)malloc(sizeof(PPPandaObject)); -} - -/** - * Called to delete the space allocated by NPAllocate, above. - */ -void PPPandaObject:: -NPDeallocate(NPObject *npobj) { - ((PPPandaObject *)npobj)->invalidate(); - free(npobj); -} - -/** - * Called to destruct the object. - */ -void PPPandaObject:: -NPInvalidate(NPObject *npobj) { - // It turns out that this method isn't actually called by Safari's - // implementation of NPAPI, so we'll move the actual destructor call into - // NPDeallocate, above. -} - -/** - * - */ -bool PPPandaObject:: -NPHasMethod(NPObject *npobj, NPIdentifier name) { - return ((PPPandaObject *)npobj)->has_method(name); -} - -/** - * - */ -bool PPPandaObject:: -NPInvoke(NPObject *npobj, NPIdentifier name, - const NPVariant *args, uint32_t argCount, - NPVariant *result) { - return ((PPPandaObject *)npobj)->invoke(name, args, argCount, result); -} - -/** - * - */ -bool PPPandaObject:: -NPInvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, - NPVariant *result) { - return ((PPPandaObject *)npobj)->invoke_default(args, argCount, result); -} - -/** - * - */ -bool PPPandaObject:: -NPHasProperty(NPObject *npobj, NPIdentifier name) { - return ((PPPandaObject *)npobj)->has_property(name); -} - -/** - * - */ -bool PPPandaObject:: -NPGetProperty(NPObject *npobj, NPIdentifier name, NPVariant *result) { - return ((PPPandaObject *)npobj)->get_property(name, result); -} - -/** - * - */ -bool PPPandaObject:: -NPSetProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value) { - return ((PPPandaObject *)npobj)->set_property(name, value); -} - -/** - * - */ -bool PPPandaObject:: -NPRemoveProperty(NPObject *npobj, NPIdentifier name) { - return ((PPPandaObject *)npobj)->remove_property(name); -} - -/** - * - */ -bool PPPandaObject:: -NPEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) { - return ((PPPandaObject *)npobj)->enumerate(value, count); -} - -/** - * - */ -bool PPPandaObject:: -NPConstruct(NPObject *npobj, const NPVariant *args, - uint32_t argCount, NPVariant *result) { - // Not implemented. We don't use this constructor mechanism because it - // wasn't supported on earlier versions of Gecko. Instead, we use - // make_new() to construct PPPandaObjects via an explicit call to - // construct(). - return true; -} diff --git a/direct/src/plugin_npapi/ppPandaObject.h b/direct/src/plugin_npapi/ppPandaObject.h deleted file mode 100644 index 89a87d02af..0000000000 --- a/direct/src/plugin_npapi/ppPandaObject.h +++ /dev/null @@ -1,91 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppPandaObject.h - * @author drose - * @date 2009-07-03 - */ - -#ifndef PPPANDAOBJECT_H -#define PPPANDAOBJECT_H - -#include "nppanda3d_common.h" - -/** - * This is the interface layer between an NPObject and a P3D_object. It maps - * calls from NPAPI into the P3D_object system, thus allowing the browser to - * view and operate on a Panda object. - * - * Also see PPBrowserObject, which maps calls the other way. - */ -class PPPandaObject : public NPObject { -public: - static PPPandaObject *make_new(PPInstance *inst, P3D_object *p3d_object); - - inline P3D_object *get_p3d_object() const; - void set_p3d_object(P3D_object *p3d_object); - -private: - void construct(PPInstance *inst, P3D_object *p3d_object); - void invalidate(); - - bool has_method(NPIdentifier name); - bool invoke(NPIdentifier name, - const NPVariant *args, uint32_t argCount, - NPVariant *result); - bool invoke_default(const NPVariant *args, uint32_t argCount, - NPVariant *result); - bool has_property(NPIdentifier name); - bool get_property(NPIdentifier name, - NPVariant *result); - bool set_property(NPIdentifier name, - const NPVariant *value); - bool remove_property(NPIdentifier name); - bool enumerate(NPIdentifier **value, uint32_t *count); - -private: - static std::string identifier_to_string(NPIdentifier ident); - - -private: - static NPObject *NPAllocate(NPP npp, NPClass *aClass); - static void NPDeallocate(NPObject *npobj); - static void NPInvalidate(NPObject *npobj); - static bool NPHasMethod(NPObject *npobj, NPIdentifier name); - static bool NPInvoke(NPObject *npobj, NPIdentifier name, - const NPVariant *args, uint32_t argCount, - NPVariant *result); - static bool NPInvokeDefault(NPObject *npobj, - const NPVariant *args, - uint32_t argCount, - NPVariant *result); - static bool NPHasProperty(NPObject *npobj, NPIdentifier name); - static bool NPGetProperty(NPObject *npobj, NPIdentifier name, - NPVariant *result); - static bool NPSetProperty(NPObject *npobj, NPIdentifier name, - const NPVariant *value); - static bool NPRemoveProperty(NPObject *npobj, - NPIdentifier name); - static bool NPEnumerate(NPObject *npobj, NPIdentifier **value, - uint32_t *count); - static bool NPConstruct(NPObject *npobj, - const NPVariant *args, - uint32_t argCount, - NPVariant *result); - -private: - PPInstance *_instance; - P3D_object *_p3d_object; - -public: - static NPClass _object_class; -}; - -#include "ppPandaObject.I" - -#endif diff --git a/direct/src/plugin_npapi/ppToplevelObject.I b/direct/src/plugin_npapi/ppToplevelObject.I deleted file mode 100644 index f9972f68dd..0000000000 --- a/direct/src/plugin_npapi/ppToplevelObject.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppToplevelObject.I - * @author drose - * @date 2009-08-21 - */ diff --git a/direct/src/plugin_npapi/ppToplevelObject.cxx b/direct/src/plugin_npapi/ppToplevelObject.cxx deleted file mode 100644 index a3790d5039..0000000000 --- a/direct/src/plugin_npapi/ppToplevelObject.cxx +++ /dev/null @@ -1,232 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppToplevelObject.cxx - * @author drose - * @date 2009-08-21 - */ - -#include "ppToplevelObject.h" - -NPClass PPToplevelObject::_object_class = { - NP_CLASS_STRUCT_VERSION, - &PPToplevelObject::NPAllocate, - &PPToplevelObject::NPDeallocate, - &PPToplevelObject::NPInvalidate, - &PPToplevelObject::NPHasMethod, - &PPToplevelObject::NPInvoke, - &PPToplevelObject::NPInvokeDefault, - &PPToplevelObject::NPHasProperty, - &PPToplevelObject::NPGetProperty, - &PPToplevelObject::NPSetProperty, - &PPToplevelObject::NPRemoveProperty, -#if defined(NP_CLASS_STRUCT_VERSION_ENUM) && NP_CLASS_STRUCT_VERSION >= NP_CLASS_STRUCT_VERSION_ENUM - &PPToplevelObject::NPEnumerate, -#endif -#if defined(NP_CLASS_STRUCT_VERSION_CTOR) && NP_CLASS_STRUCT_VERSION >= NP_CLASS_STRUCT_VERSION_CTOR - &PPToplevelObject::NPConstruct, -#endif -}; - - -/** - * Use this call to construct a new PPToplevelObject. - */ -PPToplevelObject *PPToplevelObject:: -make_new(PPInstance *inst) { - NPObject *npobj = - browser->createobject(inst->get_npp_instance(), &_object_class); - PPToplevelObject *ppobj = (PPToplevelObject *)npobj; - ppobj->construct(inst); - return ppobj; -} - -/** - * Changes the "main" object this PPToplevelObject maps to. The new object's - * reference count is incremented, and the previous object's is decremented. - */ -void PPToplevelObject:: -set_main(P3D_object *p3d_object) { - if (p3d_object != nullptr) { - P3D_OBJECT_INCREF(p3d_object); - } - P3D_OBJECT_XDECREF(_main); - _main = p3d_object; -} - -/** - * Stands in for the C++ constructor. We can't have a true constructor - * because of the C-style interface in NPN_CreateObject(). This must be - * called explicitly following NPN_CreateObject(). - */ -void PPToplevelObject:: -construct(PPInstance *inst) { - _instance = inst; - _main = nullptr; - - // Get our one property name as an identifier, so we can look for it. - _main_id = browser->getstringidentifier("main"); -} - -/** - * This "destructor" is called by NPInvalidate(). - */ -void PPToplevelObject:: -invalidate() { - _instance = nullptr; - set_main(nullptr); -} - -/** - * Returns true if the object has the named property, false otherwise. - */ -bool PPToplevelObject:: -has_property(NPIdentifier name) { - if (_main == nullptr) { - // Not powered up yet. - return false; - } - - if (name == _main_id) { - return true; - } - - return false; -} - -/** - * Retrieves the named property value from the object and stores it in result. - * Returns true on success, false on failure. - */ -bool PPToplevelObject:: -get_property(NPIdentifier name, NPVariant *result) { - if (_main == nullptr) { - // Not powered up yet. - return false; - } - - if (name == _main_id) { - _instance->p3dobj_to_variant(result, _main); - return true; - } - - return false; -} - - -// The remaining function bodies are the C-style function wrappers that are -// called directly by NPAPI, and which redirect into the above C++-style -// methods. - -/** - * Called by NPN_CreateObject() to allocate space for this object. - */ -NPObject *PPToplevelObject:: -NPAllocate(NPP npp, NPClass *aClass) { - assert(aClass == &_object_class); - return (PPToplevelObject *)malloc(sizeof(PPToplevelObject)); -} - -/** - * Called to delete the space allocated by NPAllocate, above. - */ -void PPToplevelObject:: -NPDeallocate(NPObject *npobj) { - ((PPToplevelObject *)npobj)->invalidate(); - free(npobj); -} - -/** - * Called to destruct the object. - */ -void PPToplevelObject:: -NPInvalidate(NPObject *npobj) { - // It turns out that this method isn't actually called by Safari's - // implementation of NPAPI, so we'll move the actual destructor call into - // NPDeallocate, above. -} - -/** - * - */ -bool PPToplevelObject:: -NPHasMethod(NPObject *npobj, NPIdentifier name) { - return false; -} - -/** - * - */ -bool PPToplevelObject:: -NPInvoke(NPObject *npobj, NPIdentifier name, - const NPVariant *args, uint32_t argCount, - NPVariant *result) { - return false; -} - -/** - * - */ -bool PPToplevelObject:: -NPInvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, - NPVariant *result) { - return false; -} - -/** - * - */ -bool PPToplevelObject:: -NPHasProperty(NPObject *npobj, NPIdentifier name) { - return ((PPToplevelObject *)npobj)->has_property(name); -} - -/** - * - */ -bool PPToplevelObject:: -NPGetProperty(NPObject *npobj, NPIdentifier name, NPVariant *result) { - return ((PPToplevelObject *)npobj)->get_property(name, result); -} - -/** - * - */ -bool PPToplevelObject:: -NPSetProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value) { - return false; -} - -/** - * - */ -bool PPToplevelObject:: -NPRemoveProperty(NPObject *npobj, NPIdentifier name) { - return false; -} - -/** - * - */ -bool PPToplevelObject:: -NPEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) { - return false; -} - -/** - * - */ -bool PPToplevelObject:: -NPConstruct(NPObject *npobj, const NPVariant *args, - uint32_t argCount, NPVariant *result) { - // Not implemented. We don't use this constructor mechanism because it - // wasn't supported on earlier versions of Gecko. Instead, we use - // make_new() to construct PPToplevelObjects via an explicit call to - // construct(). - return true; -} diff --git a/direct/src/plugin_npapi/ppToplevelObject.h b/direct/src/plugin_npapi/ppToplevelObject.h deleted file mode 100644 index ad9130c9d1..0000000000 --- a/direct/src/plugin_npapi/ppToplevelObject.h +++ /dev/null @@ -1,76 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file ppToplevelObject.h - * @author drose - * @date 2009-08-21 - */ - -#ifndef PPTOPLEVELOBJECT_H -#define PPTOPLEVELOBJECT_H - -#include "nppanda3d_common.h" - -/** - * This is a special object fed to Mozilla as the toplevel scripting object - * for the instance. It has only one property, "main", which corresponds to - * the appRunner.main object from Python. - */ -class PPToplevelObject : public NPObject { -public: - static PPToplevelObject *make_new(PPInstance *inst); - - inline P3D_object *get_main() const; - void set_main(P3D_object *main); - -private: - void construct(PPInstance *inst); - void invalidate(); - - bool has_property(NPIdentifier name); - bool get_property(NPIdentifier name, - NPVariant *result); - -private: - static NPObject *NPAllocate(NPP npp, NPClass *aClass); - static void NPDeallocate(NPObject *npobj); - static void NPInvalidate(NPObject *npobj); - static bool NPHasMethod(NPObject *npobj, NPIdentifier name); - static bool NPInvoke(NPObject *npobj, NPIdentifier name, - const NPVariant *args, uint32_t argCount, - NPVariant *result); - static bool NPInvokeDefault(NPObject *npobj, - const NPVariant *args, - uint32_t argCount, - NPVariant *result); - static bool NPHasProperty(NPObject *npobj, NPIdentifier name); - static bool NPGetProperty(NPObject *npobj, NPIdentifier name, - NPVariant *result); - static bool NPSetProperty(NPObject *npobj, NPIdentifier name, - const NPVariant *value); - static bool NPRemoveProperty(NPObject *npobj, - NPIdentifier name); - static bool NPEnumerate(NPObject *npobj, NPIdentifier **value, - uint32_t *count); - static bool NPConstruct(NPObject *npobj, - const NPVariant *args, - uint32_t argCount, - NPVariant *result); - -private: - PPInstance *_instance; - P3D_object *_main; - NPIdentifier _main_id; - -public: - static NPClass _object_class; -}; - -#include "ppToplevelObject.I" - -#endif diff --git a/direct/src/plugin_npapi/startup.cxx b/direct/src/plugin_npapi/startup.cxx deleted file mode 100644 index 315a21f8cc..0000000000 --- a/direct/src/plugin_npapi/startup.cxx +++ /dev/null @@ -1,590 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file startup.cxx - * @author drose - * @date 2009-06-17 - */ - -#include "startup.h" -#include "p3d_plugin_config.h" -#include "p3d_lock.h" -#include "ppBrowserObject.h" -#include "wstring_encode.h" -#include "find_root_dir.h" - -#ifdef _WIN32 -#include -#include -#endif - -using std::string; - -static ofstream logfile; -std::ostream *nout_stream = &logfile; - -string global_root_dir; -bool has_plugin_thread_async_call; - -NPNetscapeFuncs *browser; - -// These function prototypes changed slightly (but insignificantly) between -// releases of NPAPI. To avoid compilation errors, we use our own name, and -// cast it to the correct type. -static int32_t -NPP_WriteReady_x(NPP instance, NPStream *stream); -static int32_t -NPP_Write_x(NPP instance, NPStream *stream, int32_t offset, - int32_t len, void *buffer); - -// open_logfile() also assigns global_root_dir. -static bool logfile_is_open = false; -static void -open_logfile() { - if (!logfile_is_open) { - global_root_dir = find_root_dir(); - - // Note that this logfile name may not be specified at runtime. It must - // be compiled in if it is specified at all. - - string log_directory; - // Allow the developer to compile in the log directory. -#ifdef P3D_PLUGIN_LOG_DIRECTORY - if (log_directory.empty()) { - log_directory = P3D_PLUGIN_LOG_DIRECTORY; - } -#endif - - // Failing that, we write logfiles to Panda3Dlog. - if (log_directory.empty()) { - log_directory = global_root_dir + "/log"; - } - mkdir_complete(log_directory, std::cerr); - - // Ensure that the log directory ends with a slash. - if (!log_directory.empty() && log_directory[log_directory.size() - 1] != '/') { -#ifdef _WIN32 - if (log_directory[log_directory.size() - 1] != '\\') -#endif - log_directory += "/"; - } - - // Construct the logfile pathname. - - string log_basename; -#ifdef P3D_PLUGIN_LOG_BASENAME1 - if (log_basename.empty()) { - log_basename = P3D_PLUGIN_LOG_BASENAME1; - } -#endif - if (log_basename.empty()) { - log_basename = "p3dplugin"; - } - - if (!log_basename.empty()) { - string log_pathname = log_directory; - log_pathname += log_basename; - log_pathname += ".log"; - - logfile.close(); - logfile.clear(); -#ifdef _WIN32 - std::wstring log_pathname_w; - string_to_wstring(log_pathname_w, log_pathname); - logfile.open(log_pathname_w.c_str(), std::ios::out | std::ios::trunc); -#else - logfile.open(log_pathname.c_str(), std::ios::out | std::ios::trunc); -#endif // _WIN32 - logfile.setf(std::ios::unitbuf); - } - - // If we didn't have a logfile name compiled in, we throw away log output - // by the simple expedient of never actually opening the ofstream. - logfile_is_open = true; - } -} - - -/** - * On Unix, this function is called by the browser to get the mimetypes and - * extensions this plugin is supposed to handle. - */ -#if NP_VERSION_MAJOR == 0 && NP_VERSION_MINOR <= 22 -char * -#else -const char * -#endif -NP_GetMIMEDescription(void) { - return "application/x-panda3d:p3d:Panda3D applet;"; -} - -/** - * On Unix, this function is called by the browser to get some information - * like the name and description. - */ -NPError -NP_GetValue(void*, NPPVariable variable, void* value) { - if (value == nullptr) { - return NPERR_INVALID_PARAM; - } - - switch (variable) { - case NPPVpluginNameString: - *(const char **)value = "Panda3D Game Engine Plug-In"; - break; - case NPPVpluginDescriptionString: - *(const char **)value = "Runs 3-D games and interactive applets"; - break; - default: - nout << "Ignoring GetValue request " << variable << "\n"; - return NPERR_INVALID_PARAM; - } - - return NPERR_NO_ERROR; -} - -/** - * This function is called (almost) before any other function, to ask the - * plugin to initialize itself and to send the pointers to the browser control - * functions. Also see NP_GetEntryPoints. - */ -#ifdef _WIN32 -NPError OSCALL -NP_Initialize(NPNetscapeFuncs *browserFuncs) -#else -// On Mac, the API specifies this second parameter is included, but it lies. -// We actually don't get a second parameter there, but we have to put it here -// to make the compiler happy. -NPError OSCALL -NP_Initialize(NPNetscapeFuncs *browserFuncs, - NPPluginFuncs *pluginFuncs) -#endif -{ - // save away browser functions - browser = browserFuncs; - - // open_logfile() also assigns global_root_dir. - open_logfile(); - nout << "Initializing Panda3D plugin version " << P3D_PLUGIN_VERSION_STR << "\n"; - - nout << "browserFuncs = " << browserFuncs << "\n"; - - // On Unix, we have to use the pluginFuncs argument to pass our entry - // points. -#if !defined(_WIN32) && !defined(__APPLE__) - if (pluginFuncs != nullptr) { - NP_GetEntryPoints(pluginFuncs); - } -#endif - - int browser_major = (browser->version >> 8) & 0xff; - int browser_minor = browser->version & 0xff; - nout << "Browser NPAPI version " << browser_major << "." << browser_minor << "\n"; - - int expected_major = NP_VERSION_MAJOR; - int expected_minor = NP_VERSION_MINOR; - - nout << "Plugin compiled with NPAPI version " - << expected_major << "." << expected_minor << "\n"; - - has_plugin_thread_async_call = false; -#ifdef HAS_PLUGIN_THREAD_ASYNC_CALL - // Check if the browser offers this very useful call. - if (browser_major > 0 || browser_minor >= NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL) { - if ((void *)browser->pluginthreadasynccall == nullptr) { - nout << "Browser should have PLUGIN_THREAD_ASYNC_CALL, but the pointer is NULL.\n"; - has_plugin_thread_async_call = false; - } else { - has_plugin_thread_async_call = true; - } - } -#endif - - // Seed the lame random number generator in rand(); we use it to select a - // mirror for downloading. - srand((unsigned int)time(nullptr)); - - return NPERR_NO_ERROR; -} - -/** - * This method is extracted directly from the DLL and called at initialization - * time by the browser, either before or after NP_Initialize, to retrieve the - * pointers to the rest of the plugin functions that are not exported from the - * DLL. - */ -NPError OSCALL -NP_GetEntryPoints(NPPluginFuncs *pluginFuncs) { - // open_logfile() also assigns global_root_dir. - open_logfile(); - nout << "NP_GetEntryPoints, pluginFuncs = " << pluginFuncs << "\n"; - if (pluginFuncs->size == 0) { - pluginFuncs->size = sizeof(*pluginFuncs); - } - if (pluginFuncs->size < offsetof(NPPluginFuncs, gotfocus)) { - nout << "Invalid NPPPluginFuncs size\n"; - return NPERR_INVALID_FUNCTABLE_ERROR; - } - - // Not entirely sure what version number we should send back here. Sending - // the verion number of the NPAPI library we're compiled against doesn't - // seem 100% right, because there's no reason to think that *this* code - // knows about all of the functions provided by the particular library - // version it's compiled against. - pluginFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; - - pluginFuncs->newp = NPP_New; - pluginFuncs->destroy = NPP_Destroy; - pluginFuncs->setwindow = NPP_SetWindow; - pluginFuncs->newstream = NPP_NewStream; - pluginFuncs->destroystream = NPP_DestroyStream; - pluginFuncs->asfile = NPP_StreamAsFile; - - // WebKit's NPAPI defines the wrong prototype for these functions, so we - // have to cast them. But the casting typename isn't consistent between - // WebKit and Mozilla's NPAPI headers. -#ifdef NewNPP_WriteProc - pluginFuncs->writeready = NewNPP_WriteReadyProc(NPP_WriteReady_x); - pluginFuncs->write = NewNPP_WriteProc(NPP_Write_x); -#else - pluginFuncs->writeready = (NPP_WriteReadyProcPtr)NPP_WriteReady_x; - pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write_x; -#endif - - pluginFuncs->print = NPP_Print; - pluginFuncs->event = NPP_HandleEvent; - pluginFuncs->urlnotify = NPP_URLNotify; - pluginFuncs->getvalue = NPP_GetValue; - pluginFuncs->setvalue = NPP_SetValue; - - return NPERR_NO_ERROR; -} - -/** - * This function is called when the browser is done with the plugin; it asks - * the plugin to unload itself and free all used resources. - */ -NPError OSCALL -NP_Shutdown(void) { - nout << "shutdown\n"; - unload_plugin(nout); - PPBrowserObject::clear_class_definition(); - - // Not clear whether there's a return value or not. Some versions of the - // API have different opinions on this. - return NPERR_NO_ERROR; -} - -/** - * Called by the browser to create a new instance of the plugin. - */ -NPError -NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, - int16_t argc, char *argn[], char *argv[], NPSavedData *saved) { - nout << "new instance " << instance << "\n"; - - P3D_window_handle_type window_handle_type = P3D_WHT_none; - P3D_event_type event_type = P3D_ET_none; - -#ifdef __APPLE__ - // The default drawing model for Apple is via the deprecated QuickDraw - // GrafPtr, and the default event model is via the deprecated Carbon - // EventRecord. - window_handle_type = P3D_WHT_osx_port; - event_type = P3D_ET_osx_event_record; - - // But we have to request the CoreGraphics drawing model to be compatible - // with Snow Leopard. - NPBool supports_core_graphics = false; -#ifdef MACOSX_HAS_COREGRAPHICS_DRAWING_MODEL - NPError err = browser->getvalue(instance, - NPNVsupportsCoreGraphicsBool, - &supports_core_graphics); - if (err != NPERR_NO_ERROR) { - supports_core_graphics = false; - } - - if (supports_core_graphics) { - // Set the drawing model - err = browser->setvalue(instance, - (NPPVariable)NPNVpluginDrawingModel, - (void *)NPDrawingModelCoreGraphics); - if (err == NPERR_NO_ERROR) { - window_handle_type = P3D_WHT_osx_cgcontext; - } - } -#endif // MACOSX_HAS_COREGRAPHICS_DRAWING_MODEL - nout << "supports_core_graphics = " << (bool)supports_core_graphics - << " window_handle_type = " << window_handle_type << "\n"; - - // And Snow Leopard also wants the new Cocoa event model. - NPBool supports_cocoa = false; -#ifdef MACOSX_HAS_EVENT_MODELS - err = browser->getvalue(instance, NPNVsupportsCocoaBool, &supports_cocoa); - if (err != NPERR_NO_ERROR) { - supports_cocoa = false; - } - - if (supports_cocoa) { - // Set the event model - err = browser->setvalue(instance, - (NPPVariable)NPPVpluginEventModel, - (void *)NPEventModelCocoa); - if (err == NPERR_NO_ERROR) { - event_type = P3D_ET_osx_cocoa; - } - } -#endif // MACOSX_HAS_EVENT_MODELS - nout << "supports_cocoa = " << (bool)supports_cocoa - << " event_type = " << event_type << "\n"; -#endif // __APPLE__ - - PPInstance *inst = new PPInstance(pluginType, instance, mode, - argc, argn, argv, saved, - window_handle_type, event_type); - instance->pdata = inst; - nout << "new instance->pdata = " << inst << "\n"; - - // To experiment with a "windowless" plugin, which really means we create - // our own window without an intervening window, try this. - // browser->setvalue(instance, NPPVpluginWindowBool, (void *)false); - - // Now that we have stored the pointer, we can call begin(), which starts to - // initiate downloads. - inst->begin(); - - return NPERR_NO_ERROR; -} - -/** - * Called by the browser to destroy an instance of the plugin previously - * created with NPP_New. - */ -NPError -NPP_Destroy(NPP instance, NPSavedData **save) { - nout << "destroy instance " << instance << ", " - << (PPInstance *)instance->pdata << "\n"; - nout << "save = " << (void *)save << "\n"; - // (*save) = NULL; - PPInstance *inst = (PPInstance *)(instance->pdata); - assert(inst != nullptr); - inst->stop_outstanding_streams(); - - delete inst; - instance->pdata = nullptr; - - return NPERR_NO_ERROR; -} - -/** - * Called by the browser to inform the instance of its window size and - * placement. This is called initially to create the window, and may be - * called subsequently when the window needs to be moved. It may be called - * redundantly. - */ -NPError -NPP_SetWindow(NPP instance, NPWindow *window) { - nout << "SetWindow " << window->x << ", " << window->y - << ", " << window->width << ", " << window->height - << "\n"; - - PPInstance *inst = (PPInstance *)(instance->pdata); - assert(inst != nullptr); - inst->set_window(window); - - return NPERR_NO_ERROR; -} - -/** - * Called by the browser when a new data stream is created, usually in - * response to a geturl request; but it is also called initially to supply the - * data in the data or src element. The plugin must specify how it can - * receive the stream. - */ -NPError -NPP_NewStream(NPP instance, NPMIMEType type, NPStream *stream, - NPBool seekable, uint16_t *stype) { - nout << "NewStream " << type << ": " << (void *)stream - << ", " << stream->url << ", size = " << stream->end - << ", notifyData = " << stream->notifyData - << ", for " << instance - << ", " << (PPInstance *)(instance->pdata) << "\n"; - PPInstance::generic_browser_call(); - PPInstance *inst = (PPInstance *)(instance->pdata); - assert(inst != nullptr); - - return inst->new_stream(type, stream, seekable != 0, stype); -} - -/** - * Called by the browser to mark the end of a stream created with NewStream. - */ -NPError -NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason) { - nout << "DestroyStream: " << (void *)stream << ", " << stream->url - << ", notifyData = " << stream->notifyData - << ", reason = " << reason - << ", for " << instance - << ", " << (PPInstance *)(instance->pdata) << "\n"; - - PPInstance::generic_browser_call(); - PPInstance *inst = (PPInstance *)(instance->pdata); - assert(inst != nullptr); - - return inst->destroy_stream(stream, reason); -} - -/** - * Called by the browser to ask how many bytes it can deliver for a stream. - */ -int32_t -NPP_WriteReady_x(NPP instance, NPStream *stream) { - // nout << "WriteReady " << stream->url << " for " << instance << ", " << - // (PPInstance *)(instance->pdata) << "\n"; - PPInstance::generic_browser_call(); - PPInstance *inst = (PPInstance *)(instance->pdata); - assert(inst != nullptr); - - return inst->write_ready(stream); -} - -/** - * Called by the browser to deliver bytes for the stream; the plugin should - * return the number of bytes consumed. - */ -int32_t -NPP_Write_x(NPP instance, NPStream *stream, int32_t offset, - int32_t len, void *buffer) { - // nout << "Write " << stream->url << ", " << offset << ", " << len << " for - // " << instance << ", " << (PPInstance *)(instance->pdata) << "\n"; - PPInstance::generic_browser_call(); - PPInstance *inst = (PPInstance *)(instance->pdata); - assert(inst != nullptr); - - return inst->write_stream(stream, offset, len, buffer); -} - -/** - * Called by the browser to report the filename that contains the fully- - * downloaded stream, if NP_ASFILEONLY was specified by the plugin in - * NPP_NewStream. - */ -void -NPP_StreamAsFile(NPP instance, NPStream *stream, const char *fname) { - nout << "StreamAsFile " << stream->url - << ", " << stream->end - << ", notifyData = " << stream->notifyData - << "\n"; - - PPInstance::generic_browser_call(); - PPInstance *inst = (PPInstance *)(instance->pdata); - assert(inst != nullptr); - - inst->stream_as_file(stream, fname); -} - -/** - * Called by the browser when the user attempts to print the page containing - * the plugin instance. - */ -void -NPP_Print(NPP instance, NPPrint *platformPrint) { - nout << "Print\n"; -} - -/** - * Called by the browser to inform the plugin of OS window events. - */ -int16_t -NPP_HandleEvent(NPP instance, void *event) { - // nout << "HandleEvent\n"; - PPInstance::generic_browser_call(); - - PPInstance *inst = (PPInstance *)(instance->pdata); - assert(inst != nullptr); - - return inst->handle_event(event); -} - -/** - * Called by the browser to inform the plugin of a completed URL request. - */ -void -NPP_URLNotify(NPP instance, const char *url, - NPReason reason, void *notifyData) { - nout << "URLNotify: " << url - << ", notifyData = " << notifyData - << ", reason = " << reason - << "\n"; - - PPInstance::generic_browser_call(); - PPInstance *inst = (PPInstance *)(instance->pdata); - assert(inst != nullptr); - - inst->url_notify(url, reason, notifyData); -} - -/** - * Called by the browser to query specific information from the plugin. - */ -NPError -NPP_GetValue(NPP instance, NPPVariable variable, void *value) { - nout << "GetValue " << variable << "\n"; - PPInstance::generic_browser_call(); - PPInstance *inst = (PPInstance *)(instance->pdata); - assert(inst != nullptr); - - if (variable == NPPVpluginScriptableNPObject) { - NPObject *obj = inst->get_panda_script_object(); - if (obj != nullptr) { - *(NPObject **)value = obj; - return NPERR_NO_ERROR; - } - - } else if (variable == NPPVpluginNeedsXEmbed) { - // If we have Gtk2 available, we can use it to support the XEmbed - // protocol, which Chromium (at least) requires. - - // In this case, we'll say we can do it, if the browser supports it. - // (Though probably the browser wouldn't be asking if it couldn't.) - - NPBool supports_xembed = false; - NPError err = browser->getvalue(instance, NPNVSupportsXEmbedBool, &supports_xembed); - if (err != NPERR_NO_ERROR) { - supports_xembed = false; - } - nout << "browser supports_xembed: " << (supports_xembed != 0) << "\n"; -#ifdef HAVE_GTK - bool plugin_supports = true; -#else - bool plugin_supports = false; - supports_xembed = false; -#endif // HAVE_GTK - nout << "plugin supports_xembed: " << plugin_supports << "\n"; - - inst->set_xembed(supports_xembed != 0); - *(NPBool *)value = supports_xembed; - - return NPERR_NO_ERROR; - - } else { - return NP_GetValue(nullptr, variable, value); - } - - return NPERR_GENERIC_ERROR; -} - -/** - * Called by the browser to update a scriptable value. - */ -NPError -NPP_SetValue(NPP instance, NPNVariable variable, void *value) { - nout << "SetValue " << variable << "\n"; - PPInstance::generic_browser_call(); - return NPERR_GENERIC_ERROR; -} diff --git a/direct/src/plugin_npapi/startup.h b/direct/src/plugin_npapi/startup.h deleted file mode 100644 index e996f58084..0000000000 --- a/direct/src/plugin_npapi/startup.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file startup.h - * @author drose - * @date 2009-06-19 - */ - -#ifndef STARTUP_H -#define STARTUP_H - -#include "nppanda3d_common.h" - -#ifndef OSCALL -#define OSCALL -#endif - -extern "C" { -#ifdef _WIN32 - NPError OSCALL NP_Initialize(NPNetscapeFuncs *browserFuncs); -#else - NPError OSCALL NP_Initialize(NPNetscapeFuncs *browserFuncs, - NPPluginFuncs *pluginFuncs); -#endif - -#if NP_VERSION_MAJOR == 0 && NP_VERSION_MINOR <= 22 - // Until at least 0.22, this function was declared to return a char *. - char *NP_GetMIMEDescription(void); -#else - // At some point after 0.22, it was corrected to return a const char *. - const char *NP_GetMIMEDescription(void); -#endif - NPError NP_GetValue(void*, NPPVariable variable, void* value); - NPError OSCALL NP_GetEntryPoints(NPPluginFuncs *pluginFuncs); - NPError OSCALL NP_Shutdown(void); -} - -void request_ready(P3D_instance *instance); - -#endif diff --git a/direct/src/plugin_standalone/make_osx_bundle.py b/direct/src/plugin_standalone/make_osx_bundle.py deleted file mode 100755 index c88ce5ecf7..0000000000 --- a/direct/src/plugin_standalone/make_osx_bundle.py +++ /dev/null @@ -1,97 +0,0 @@ -#! /usr/bin/env python - -""" - -This script constructs the bundle directory structure for the OSX -program panda3d_mac, which is built by the code in this directory. It -takes no parameters, and produces the app bundle in the same place. - -""" - -import getopt -import sys -import os -import glob -import shutil - -import direct -from panda3d.core import Filename, DSearchPath, getModelPath, ExecutionEnvironment - -def usage(code, msg = ''): - sys.stderr.write(__doc__) - sys.stderr.write(msg + '\n') - sys.exit(code) - -def makeBundle(startDir): - fstartDir = Filename.fromOsSpecific(startDir) - - # Search for panda3d_mac along $PATH. - path = DSearchPath() - if 'PATH' in os.environ: - path.appendPath(os.environ['PATH']) - path.appendPath(os.defpath) - panda3d_mac = path.findFile('panda3d_mac') - if not panda3d_mac: - raise Exception("Couldn't find panda3d_mac on path.") - - # Construct a search path to look for the images. - search = DSearchPath() - - # First on the path: an explicit $PLUGIN_IMAGES env var. - if ExecutionEnvironment.hasEnvironmentVariable('PLUGIN_IMAGES'): - search.appendDirectory(Filename.expandFrom('$PLUGIN_IMAGES')) - - # Next on the path: the models/plugin_images directory within the - # current directory. - search.appendDirectory('models/plugin_images') - - # Finally on the path: models/plugin_images within the model - # search path. - for dir in getModelPath().getDirectories(): - search.appendDirectory(Filename(dir, 'plugin_images')) - - # Now find the icon file on the above search path. - icons = search.findFile('panda3d.icns') - if not icons: - raise Exception("Couldn't find panda3d.icns on model-path.") - - # Generate the bundle directory structure - rootFilename = Filename(fstartDir) - bundleFilename = Filename(rootFilename, 'Panda3D.app') - if os.path.exists(bundleFilename.toOsSpecific()): - shutil.rmtree(bundleFilename.toOsSpecific()) - - plistFilename = Filename(bundleFilename, 'Contents/Info.plist') - plistFilename.makeDir() - exeFilename = Filename(bundleFilename, 'Contents/MacOS/panda3d_mac') - exeFilename.makeDir() - iconFilename = Filename(bundleFilename, 'Contents/Resources/panda3d.icns') - iconFilename.makeDir() - - # Copy in Info.plist, the icon file, and the compiled executable. - shutil.copyfile(Filename(fstartDir, "panda3d_mac.plist").toOsSpecific(), plistFilename.toOsSpecific()) - shutil.copyfile(icons.toOsSpecific(), iconFilename.toOsSpecific()) - print('%s %s' % (panda3d_mac, exeFilename)) - shutil.copyfile(panda3d_mac.toOsSpecific(), exeFilename.toOsSpecific()) - os.chmod(exeFilename.toOsSpecific(), 0o755) - - # All done! - bundleFilename.touch() - print(bundleFilename.toOsSpecific()) - -if __name__ == '__main__': - try: - opts, args = getopt.getopt(sys.argv[1:], 'h') - except getopt.error as msg: - usage(1, msg) - - for opt, arg in opts: - if opt == '-h': - usage(0) - - if args: - usage(1, 'No arguments are expected.') - - startDir = os.path.split(sys.argv[0])[0] - makeBundle(startDir) - diff --git a/direct/src/plugin_standalone/p3dEmbed.cxx b/direct/src/plugin_standalone/p3dEmbed.cxx deleted file mode 100644 index 1d191e9c22..0000000000 --- a/direct/src/plugin_standalone/p3dEmbed.cxx +++ /dev/null @@ -1,261 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dEmbed.cxx - * @author rdb - * @date 2009-12-07 - */ - -#define P3D_FUNCTION_PROTOTYPES - -#include "p3dEmbed.h" -#include "load_plugin.h" -#include "find_root_dir.h" - -using std::cerr; -using std::string; - -/** - * - */ -P3DEmbed:: -P3DEmbed(bool console_environment) : Panda3DBase(console_environment) { - // Since the Panda3DBase constructor no longer assigns _root_dir, we have to - // do it here. - _root_dir = find_root_dir(); - - // We should leave the arguments intact, just pass them 1:1 as we've - // received them. - _prepend_filename_to_args = false; -} - -/** - * Runs with the data embedded in the current executable, at the specified - * offset. - */ -int P3DEmbed:: -run_embedded(std::streampos read_offset, int argc, char *argv[]) { - // Check to see if we've actually got an application embedded. If we do, - // read_offset will have been modified to contain a different value than the - // one we compiled in, above. We test against read_offset + 1, because any - // appearances of this exact number within the binary will be replaced - // (including this one). - - // We also have to store this computation in a member variable, to work - // around a compiler optimization that might otherwise remove the + 1 from - // the test. - _read_offset_check = read_offset + (std::streampos)1; - if (_read_offset_check == (std::streampos)0xFF3D3D01) { - cerr << "This program is not intended to be run directly.\nIt is used " - "by pdeploy to construct an embedded Panda3D application.\n"; - return 1; - } - - // Make sure the splash window will be put in the center of the screen - _win_x = -2; - _win_y = -2; - - // Read out some parameters from the binary - pifstream read; - Filename f = ExecutionEnvironment::get_binary_name(); - f.make_absolute(); - f.set_binary(); - if (!f.open_read(read)) { - cerr << "Failed to read from stream. Maybe the binary is corrupt?\n"; - return 1; - } - read.seekg(read_offset); - int curchr = read.get(); - if (curchr == EOF) { - cerr << "Couldn't seek to " << read_offset << "\n"; - return 1; - } - - string curstr; - bool havenull = false; - P3D_token token; - token._keyword = nullptr; - token._value = nullptr; - string keyword; - string value; - string root_dir; - string host_dir; - string start_dir; - - while (true) { - if (curchr == EOF) { - cerr << "Truncated stream\n"; - return 1; - - } else if (curchr == 0) { - // Two null bytes in a row means we've reached the end of the data. - if (havenull) { - break; - } - - // This means we haven't seen an '=' character yet. - if (keyword == "") { - if (curstr != "") { - cerr << "Ignoring token '" << curstr << "' without value\n"; - } - - } else if (keyword == "start_dir") { - // Don't pass this on as a token, since it has slightly different - // semantics when used as an HTML token. - start_dir = curstr; - - } else { - value.assign(curstr); - P3D_token token; - token._keyword = strdup(keyword.c_str()); - token._value = strdup(value.c_str()); - _tokens.push_back(token); - - // Read out the tokens that may interest us - if (keyword == "width") { - _win_width = atoi(value.c_str()); - _got_win_size = true; - } else if (keyword == "height") { - _win_height = atoi(value.c_str()); - _got_win_size = true; - } else if (keyword == "log_basename") { - _log_basename = value; - } else if (keyword == "log_directory") { - _log_dirname = value; - } else if (keyword == "root_dir") { - root_dir = value; - } else if (keyword == "host_dir") { - host_dir = value; - } else if (keyword == "verify_contents") { - if (value == "never") { - _verify_contents = P3D_VC_never; - } else if (value == "force") { - _verify_contents = P3D_VC_force; - } else if (value == "normal") { - _verify_contents = P3D_VC_normal; - } else if (value == "none") { - _verify_contents = P3D_VC_none; - } else { - _verify_contents = (P3D_verify_contents)atoi(value.c_str()); - } - } - } - curstr = ""; - havenull = true; - } else if (curchr == '=') { - keyword.assign(curstr); - curstr = ""; - havenull = false; - } else { - curstr += curchr; - havenull = false; - } - curchr = read.get(); - } - - // Update the offset to the current read pointer. This is where the - // multifile really starts. - read_offset = read.tellg(); - read.close(); - - // Make the root directory absolute - if (!root_dir.empty()) { - Filename root_dir_f(root_dir); - root_dir_f.make_absolute(f.get_dirname()); - _root_dir = root_dir_f.to_os_specific(); - } - - // Make the host directory absolute - if (!host_dir.empty()) { - Filename host_dir_f(host_dir); - host_dir_f.make_absolute(f.get_dirname()); - _host_dir = host_dir_f.to_os_specific(); - } - - // Make the start directory absolute - if (!start_dir.empty()) { - Filename start_dir_f(start_dir); - start_dir_f.make_absolute(f.get_dirname()); - _start_dir = start_dir_f.to_os_specific(); - } - - // Initialize the core API by directly assigning all of the function - // pointers. - P3D_initialize_ptr = &P3D_initialize; - P3D_finalize_ptr = &P3D_finalize; - P3D_set_plugin_version_ptr = &P3D_set_plugin_version; - P3D_set_super_mirror_ptr = &P3D_set_super_mirror; - P3D_new_instance_ptr = &P3D_new_instance; - P3D_instance_start_ptr = &P3D_instance_start; - P3D_instance_start_stream_ptr = &P3D_instance_start_stream; - P3D_instance_finish_ptr = &P3D_instance_finish; - P3D_instance_setup_window_ptr = &P3D_instance_setup_window; - - P3D_object_get_type_ptr = &P3D_object_get_type; - P3D_object_get_bool_ptr = &P3D_object_get_bool; - P3D_object_get_int_ptr = &P3D_object_get_int; - P3D_object_get_float_ptr = &P3D_object_get_float; - P3D_object_get_string_ptr = &P3D_object_get_string; - P3D_object_get_repr_ptr = &P3D_object_get_repr; - P3D_object_get_property_ptr = &P3D_object_get_property; - P3D_object_set_property_ptr = &P3D_object_set_property; - P3D_object_has_method_ptr = &P3D_object_has_method; - P3D_object_call_ptr = &P3D_object_call; - P3D_object_eval_ptr = &P3D_object_eval; - P3D_object_incref_ptr = &P3D_object_incref; - P3D_object_decref_ptr = &P3D_object_decref; - - P3D_make_class_definition_ptr = &P3D_make_class_definition; - P3D_new_undefined_object_ptr = &P3D_new_undefined_object; - P3D_new_none_object_ptr = &P3D_new_none_object; - P3D_new_bool_object_ptr = &P3D_new_bool_object; - P3D_new_int_object_ptr = &P3D_new_int_object; - P3D_new_float_object_ptr = &P3D_new_float_object; - P3D_new_string_object_ptr = &P3D_new_string_object; - P3D_instance_get_panda_script_object_ptr = &P3D_instance_get_panda_script_object; - P3D_instance_set_browser_script_object_ptr = &P3D_instance_set_browser_script_object; - - P3D_instance_get_request_ptr = &P3D_instance_get_request; - P3D_check_request_ptr = &P3D_check_request; - P3D_request_finish_ptr = &P3D_request_finish; - P3D_instance_feed_url_stream_ptr = &P3D_instance_feed_url_stream; - P3D_instance_handle_event_ptr = &P3D_instance_handle_event; - - // Calling the executable with --prep just prepares the directory structure, - // this is usually invoked in the installer. - if (argc == 2 && strcmp(argv[1], "--prep") == 0) { - cerr << "Invoking the prepare step is deprecated, please rebuild the application using a more recent version of pdeploy\n"; - _window_type = P3D_WT_hidden; - _log_basename = "prep"; - P3D_token token; - token._keyword = "stop_on_ready"; - token._value = "1"; - _tokens.push_back(token); - token._keyword = "hidden"; - token._value = "1"; - _tokens.push_back(token); - } - - // Now call init_plugin() to verify that we got all of the required function - // pointers. This will also call P3D_initialize(). - if (!init_plugin("", _host_url, _verify_contents, _this_platform, - _log_dirname, _log_basename, true, _console_environment, - _root_dir, _host_dir, _start_dir, cerr)) { - cerr << "Unable to launch core API\n"; - return 1; - } - - // Create a plugin instance and run the program - P3D_instance *inst = create_instance(f, true, argv, argc, read_offset); - _instances.insert(inst); - - run_main_loop(); - - unload_plugin(cerr); - return 0; -} diff --git a/direct/src/plugin_standalone/p3dEmbed.h b/direct/src/plugin_standalone/p3dEmbed.h deleted file mode 100644 index 7d1da92883..0000000000 --- a/direct/src/plugin_standalone/p3dEmbed.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dEmbed.h - * @author drose - * @date 2010-01-04 - */ - -#ifndef P3DEMBED_H -#define P3DEMBED_H - -#include "dtoolbase.h" -#ifdef _WIN32 -#include -#endif - -#include "panda3dBase.h" -#include "p3d_plugin.h" -#include "httpChannel.h" -#include "ramfile.h" -#include "fileSpec.h" -#include "pset.h" -#include "vector_string.h" - -/** - * This program is constructed to self-embed a p3d file and execute it - * directly. - */ -class P3DEmbed : public Panda3DBase { -public: - P3DEmbed(bool console_environment); - - int run_embedded(std::streampos read_offset, int argc, char *argv[]); - - std::streampos _read_offset_check; -}; - -#endif diff --git a/direct/src/plugin_standalone/p3dEmbedMain.cxx b/direct/src/plugin_standalone/p3dEmbedMain.cxx deleted file mode 100644 index 0aa3d2eb35..0000000000 --- a/direct/src/plugin_standalone/p3dEmbedMain.cxx +++ /dev/null @@ -1,35 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file p3dEmbedMain.cxx - * @author drose - * @date 2010-01-04 - */ - -#include "p3dEmbed.h" - -#ifdef P3DEMBEDW -#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup") -#endif - -#ifdef _WIN32 -volatile unsigned __int32 p3d_offset = 0xFF3D3D00; -#else -#include -volatile uint32_t p3d_offset = 0xFF3D3D00; -#endif - -int -main(int argc, char *argv[]) { -#ifdef P3DEMBEDW - P3DEmbed program(false); -#else - P3DEmbed program(true); -#endif - return program.run_embedded(p3d_offset, argc, argv); -} diff --git a/direct/src/plugin_standalone/panda3d.I b/direct/src/plugin_standalone/panda3d.I deleted file mode 100644 index ba15732f4d..0000000000 --- a/direct/src/plugin_standalone/panda3d.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file panda3d.I - * @author drose - * @date 2009-06-30 - */ diff --git a/direct/src/plugin_standalone/panda3d.cxx b/direct/src/plugin_standalone/panda3d.cxx deleted file mode 100644 index d21933a441..0000000000 --- a/direct/src/plugin_standalone/panda3d.cxx +++ /dev/null @@ -1,942 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file panda3d.cxx - * @author drose - * @date 2009-06-03 - */ - -#include "panda3d.h" -#include "load_plugin.h" -#include "p3d_plugin_config.h" -#include "find_root_dir.h" -#include "pandaSystem.h" -#include "dtool_platform.h" -#include "pandaVersion.h" -#include "panda_getopt.h" - -#include -#include -#include -#ifdef _WIN32 -#include -#else -#include -#endif - -using std::cerr; -using std::cout; -using std::string; - -/** - * - */ -Panda3D:: -Panda3D(bool console_environment) : Panda3DBase(console_environment) { - // We use the runtime PandaSystem setting for this value, rather than the - // hard-compiled-in setting, to allow users to override this with a - // Config.prc variable if needed. - _host_url = PandaSystem::get_package_host_url(); -} - -/** - * Starts the program going, with command-line arguments. Returns 0 on - * success, nonzero on failure. - */ -int Panda3D:: -run_command_line(int argc, char *argv[]) { - extern char *optarg; - extern int optind; - - // We prefix a "+" sign to tell getopt not to parse options following the - // first not-option parameter. (These will be passed into the sub-process.) - const char *optstr = "+mu:M:Sp:nfw:t:s:o:l:iVUPh"; - - bool allow_multiple = false; - - int flag = getopt(argc, argv, optstr); - - while (flag != EOF) { - switch (flag) { - case 'm': - allow_multiple = true; - break; - - case 'u': - _host_url = optarg; - break; - - case 'M': - _super_mirror_url = optarg; - break; - - case 'S': - _enable_security = true; - break; - - case 'p': - _this_platform = optarg; - _coreapi_platform = optarg; - break; - - case 'n': - _verify_contents = P3D_VC_normal; - break; - - case 'f': - _verify_contents = P3D_VC_force; - break; - - case 'w': - if (strcmp(optarg, "toplevel") == 0) { - _window_type = P3D_WT_toplevel; - } else if (strcmp(optarg, "embedded") == 0) { - _window_type = P3D_WT_embedded; - } else if (strcmp(optarg, "fullscreen") == 0) { - _window_type = P3D_WT_fullscreen; - } else if (strcmp(optarg, "hidden") == 0) { - _window_type = P3D_WT_hidden; - } else { - cerr << "Invalid value for -w: " << optarg << "\n"; - return 1; - } - break; - - case 't': - if (!parse_token(optarg)) { - cerr << "Web tokens (-t) must be of the form token=value: " << optarg << "\n"; - return 1; - } - break; - - case 's': - if (!parse_int_pair(optarg, _win_width, _win_height)) { - cerr << "Invalid value for -s: " << optarg << "\n"; - return 1; - } - _got_win_size = true; - break; - - case 'o': - if (!parse_int_pair(optarg, _win_x, _win_y)) { - cerr << "Invalid value for -o: " << optarg << "\n"; - return 1; - } - break; - - case 'l': - _log_dirname = Filename::from_os_specific(optarg).to_os_specific(); - _log_basename = "p3dcore"; - break; - - case 'i': - { - P3D_token token; - token._keyword = "keep_pythonpath"; - token._value = "1"; - _tokens.push_back(token); - token._keyword = "interactive_console"; - token._value = "1"; - _tokens.push_back(token); - - // We should also ignore control-C in this case, so that an interrupt - // will be delivered to the subordinate Python process and return to a - // command shell, and won't just kill the panda3d process. -#ifdef _WIN32 - SetConsoleCtrlHandler(nullptr, true); -#else - struct sigaction ignore; - memset(&ignore, 0, sizeof(ignore)); - ignore.sa_handler = SIG_IGN; - sigaction(SIGINT, &ignore, nullptr); -#endif // _WIN32 - } - break; - - case 'V': - cout << P3D_PLUGIN_MAJOR_VERSION << "." - << P3D_PLUGIN_MINOR_VERSION << "." - << P3D_PLUGIN_SEQUENCE_VERSION; -#ifndef PANDA_OFFICIAL_VERSION - cout << "c"; -#endif - cout << "\n"; - exit(0); - - case 'U': - cout << _host_url << "\n"; - exit(0); - - case 'P': - cout << DTOOL_PLATFORM << "\n"; - exit(0); - - case 'h': - case '?': - case '+': - default: - usage(); - return 1; - } - flag = getopt(argc, argv, optstr); - } - - argc -= (optind-1); - argv += (optind-1); - - if (argc < 2 && _exit_with_last_instance) { - // No instances on the command line--that *might* be an error. - usage(); - return 1; - } - - if (!post_arg_processing()) { - return 1; - } - - int num_instance_filenames, num_instance_args; - char **instance_filenames, **instance_args; - - if (argc > 1) { - if (allow_multiple) { - // With -m, the remaining arguments are all instance filenames. - num_instance_filenames = argc - 1; - instance_filenames = argv + 1; - num_instance_args = 0; - instance_args = argv + argc; - - } else { - // Without -m, there is one instance filename, and everything else gets - // delivered to that instance. - num_instance_filenames = 1; - instance_filenames = argv + 1; - num_instance_args = argc - 2; - instance_args = argv + 2; - } - - if (_window_type == P3D_WT_embedded) { - // The user asked for an embedded window. Create a toplevel window to - // be its parent, of the requested size. - if (_win_width == 0 && _win_height == 0) { - _win_width = 800; - _win_height = 600; - } - - make_parent_window(); - - // Center the child window(s) within the parent window. -#ifdef _WIN32 - assert(_parent_window._window_handle_type == P3D_WHT_win_hwnd); - HWND parent_hwnd = _parent_window._handle._win_hwnd._hwnd; - - RECT rect; - GetClientRect(parent_hwnd, &rect); - - _win_x = (int)(rect.right * 0.1); - _win_y = (int)(rect.bottom * 0.1); - _win_width = (int)(rect.right * 0.8); - _win_height = (int)(rect.bottom * 0.8); -#endif - - // Subdivide the window into num_x_spans * num_y_spans sub-windows. - int num_y_spans = int(sqrt((double)num_instance_filenames)); - int num_x_spans = (num_instance_filenames + num_y_spans - 1) / num_y_spans; - - int origin_x = _win_x; - int origin_y = _win_y; - _win_width = _win_width / num_x_spans; - _win_height = _win_height / num_y_spans; - _got_win_size = true; - - for (int yi = 0; yi < num_y_spans; ++yi) { - for (int xi = 0; xi < num_x_spans; ++xi) { - int i = yi * num_x_spans + xi; - if (i >= num_instance_filenames) { - continue; - } - - // Create instance i at window slot (xi, yi). - _win_x = origin_x + xi * _win_width; - _win_y = origin_y + yi * _win_height; - - P3D_instance *inst = create_instance - (instance_filenames[i], true, - instance_args, num_instance_args); - _instances.insert(inst); - } - } - - } else { - // Not an embedded window. Create each window with the same parameters. - for (int i = 0; i < num_instance_filenames; ++i) { - P3D_instance *inst = create_instance - (instance_filenames[i], true, - instance_args, num_instance_args); - _instances.insert(inst); - } - } - } - - run_main_loop(); - - // All instances have finished; we can exit. - unload_plugin(cerr); - return 0; -} - -/** - * Sets up some internal state after processing the command-line arguments. - * Returns true on success, false on failure. - */ -bool Panda3D:: -post_arg_processing() { - // Now is a good time to assign _root_dir. - _root_dir = find_root_dir(); - - // Set host_url_prefix to end with a slash. - _host_url_prefix = _host_url; - if (!_host_url_prefix.empty() && _host_url_prefix[_host_url_prefix.length() - 1] != '/') { - _host_url_prefix += '/'; - } - _download_url_prefix = _host_url_prefix; - - // If the "super mirror" URL is a filename, convert it to a file: url. - if (!_super_mirror_url.empty()) { - if (!is_url(_super_mirror_url)) { - Filename filename = Filename::from_os_specific(_super_mirror_url); - filename.make_absolute(); - string path = filename.to_os_generic(); - if (!path.empty() && path[0] != '/') { - // On Windows, a leading drive letter must be preceded by an - // additional slash. - path = "/" + path; - } - _super_mirror_url = "file://" + path; - } - - // And make sure the super_mirror_url_prefix ends with a slash. - _super_mirror_url_prefix = _super_mirror_url; - if (!_super_mirror_url_prefix.empty() && _super_mirror_url_prefix[_super_mirror_url_prefix.length() - 1] != '/') { - _super_mirror_url_prefix += '/'; - } - } - - if (!get_plugin()) { - cerr << "Unable to load Panda3D plugin.\n"; - return false; - } - - // Set up the "super mirror" URL, if specified. - if (!_super_mirror_url.empty()) { - P3D_set_super_mirror_ptr(_super_mirror_url.c_str()); - } - - return true; -} - -/** - * Downloads the contents.xml file from the named URL and attempts to use it - * to load the core API. Returns true on success, false on failure. - */ -bool Panda3D:: -get_plugin() { - // First, look for the existing contents.xml file. - bool success = false; - bool is_fresh = false; - - Filename contents_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml"); - if (_verify_contents != P3D_VC_force) { - if (read_contents_file(contents_filename, false)) { - if (_verify_contents == P3D_VC_none || time(nullptr) < _contents_expiration) { - // Got the file, and it's good. - success = true; - } - } - } - - if (!success) { - // Couldn't read it (or it wasn't current enough), so go get a new one. - if (!download_contents_file(contents_filename)) { - // We don't have a usable contents.xml file. - return false; - } - is_fresh = true; - } - - // Now that we've downloaded the contents file successfully, start the Core - // API. - if (!get_core_api()) { - // We failed. Make sure contents.xml is up-to-date and try again. - if (!is_fresh && download_contents_file(contents_filename) && get_core_api()) { - return true; - } else { - return false; - } - } - - return true; -} - -/** - * Redownloads the contents.xml file from the named URL without first checking - * if it is up to date. Returns true if we have a contents.xml file that - * might be usable, false otherwise. - */ -bool Panda3D:: -download_contents_file(const Filename &contents_filename) { - bool success = false; - HTTPClient *http = HTTPClient::get_global_ptr(); - - // Try the super_mirror first. - if (!_super_mirror_url_prefix.empty()) { - // We don't bother putting a uniquifying query string when we're - // downloading this file from the super_mirror. The super_mirror is by - // definition a cache, so it doesn't make sense to bust caches here. - string url = _super_mirror_url_prefix + "contents.xml"; - PT(HTTPChannel) channel = http->make_channel(false); - channel->get_document(url); - - Filename tempfile = Filename::temporary("", "p3d_"); - if (!channel->download_to_file(tempfile)) { - cerr << "Unable to download " << url << "\n"; - tempfile.unlink(); - } else { - // Successfully downloaded from the super_mirror; try to read it. - success = read_contents_file(tempfile, true); - tempfile.unlink(); - } - } - - if (!success) { - // Go download contents.xml from the actual host. - std::ostringstream strm; - strm << _host_url_prefix << "contents.xml"; - // Append a uniquifying query string to the URL to force the download to - // go all the way through any caches. We use the time in seconds; that's - // unique enough. - strm << "?" << time(nullptr); - string url = strm.str(); - - // We might as well explicitly request the cache to be disabled too, since - // we have an interface for that via HTTPChannel. - DocumentSpec request(url); - request.set_cache_control(DocumentSpec::CC_no_cache); - - PT(HTTPChannel) channel = http->make_channel(false); - channel->get_document(request); - - // Since we have to download some of it, might as well ask the core API to - // check all of it. - if (_verify_contents == P3D_VC_none) { - _verify_contents = P3D_VC_normal; - } - - // First, download it to a temporary file. - Filename tempfile = Filename::temporary("", "p3d_"); - if (!channel->download_to_file(tempfile)) { - cerr << "Unable to download " << url << "\n"; - - // Couldn't download, but try to read the existing contents.xml file - // anyway. Maybe it's good enough. - success = read_contents_file(contents_filename, false); - - } else { - // Successfully downloaded; read it and move it into place. - success = read_contents_file(tempfile, true); - } - - tempfile.unlink(); - } - - return success; -} - -/** - * Attempts to open and read the contents.xml file on disk. Copies the file - * to its standard location on success. Returns true on success, false on - * failure. - */ -bool Panda3D:: -read_contents_file(const Filename &contents_filename, bool fresh_download) { - string os_contents_filename = contents_filename.to_os_specific(); - TiXmlDocument doc(os_contents_filename.c_str()); - if (!doc.LoadFile()) { - return false; - } - - bool found_core_package = false; - - TiXmlElement *xcontents = doc.FirstChildElement("contents"); - if (xcontents != nullptr) { - int max_age = P3D_CONTENTS_DEFAULT_MAX_AGE; - xcontents->Attribute("max_age", &max_age); - - // Get the latest possible expiration time, based on the max_age - // indication. Any expiration time later than this is in error. - time_t now = time(nullptr); - _contents_expiration = now + (time_t)max_age; - - if (fresh_download) { - // Update the XML with the new download information. - TiXmlElement *xorig = xcontents->FirstChildElement("orig"); - while (xorig != nullptr) { - xcontents->RemoveChild(xorig); - xorig = xcontents->FirstChildElement("orig"); - } - - xorig = new TiXmlElement("orig"); - xcontents->LinkEndChild(xorig); - - xorig->SetAttribute("expiration", (int)_contents_expiration); - - } else { - // Read the expiration time from the XML. - int expiration = 0; - TiXmlElement *xorig = xcontents->FirstChildElement("orig"); - if (xorig != nullptr) { - xorig->Attribute("expiration", &expiration); - } - - _contents_expiration = std::min(_contents_expiration, (time_t)expiration); - } - - // Look for the entry; it might point us at a different download - // URL, and it might mention some mirrors. - find_host(xcontents); - - // Now look for the core API package. - _coreapi_set_ver = ""; - TiXmlElement *xpackage = xcontents->FirstChildElement("package"); - while (xpackage != nullptr) { - const char *name = xpackage->Attribute("name"); - if (name != nullptr && strcmp(name, "coreapi") == 0) { - const char *platform = xpackage->Attribute("platform"); - if (platform != nullptr && _coreapi_platform == string(platform)) { - _coreapi_dll.load_xml(xpackage); - const char *set_ver = xpackage->Attribute("set_ver"); - if (set_ver != nullptr) { - _coreapi_set_ver = set_ver; - } - found_core_package = true; - break; - } - } - - xpackage = xpackage->NextSiblingElement("package"); - } - } - - if (!found_core_package) { - // Couldn't find the coreapi package description. - nout << "No coreapi package defined in contents file for " - << _coreapi_platform << "\n"; - return false; - } - - // Check the coreapi_set_ver token. If it is given, it specifies a minimum - // Core API version number we expect to find. If we didn't find that - // number, perhaps our contents.xml is out of date. - string coreapi_set_ver = lookup_token("coreapi_set_ver"); - if (!coreapi_set_ver.empty()) { - nout << "Instance asked for Core API set_ver " << coreapi_set_ver - << ", we found " << _coreapi_set_ver << "\n"; - // But don't bother if we just freshly downloaded it. - if (!fresh_download) { - if (compare_seq(coreapi_set_ver, _coreapi_set_ver) > 0) { - // The requested set_ver value is higher than the one we have on file; - // our contents.xml file must be out of date after all. - nout << "expiring contents.xml\n"; - _contents_expiration = 0; - } - } - } - - // Success. Now copy the file into place. - Filename standard_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml"); - if (fresh_download) { - Filename tempfile = Filename::temporary("", "p3d_"); - string os_specific = tempfile.to_os_specific(); - if (!doc.SaveFile(os_specific.c_str())) { - nout << "Couldn't write to " << tempfile << "\n"; - tempfile.unlink(); - return false; - } - tempfile.rename_to(standard_filename); - - } else { - if (contents_filename != standard_filename) { - if (!contents_filename.rename_to(standard_filename)) { - nout << "Couldn't move contents.xml to " << standard_filename << "\n"; - contents_filename.unlink(); - return false; - } - } - } - - return true; -} - -/** - * Scans the element for the matching element. - */ -void Panda3D:: -find_host(TiXmlElement *xcontents) { - TiXmlElement *xhost = xcontents->FirstChildElement("host"); - if (xhost != nullptr) { - const char *url = xhost->Attribute("url"); - if (url != nullptr && _host_url == string(url)) { - // We're the primary host. This is the normal case. - read_xhost(xhost); - return; - - } else { - // We're not the primary host; perhaps we're an alternate host. - TiXmlElement *xalthost = xhost->FirstChildElement("alt_host"); - while (xalthost != nullptr) { - const char *url = xalthost->Attribute("url"); - if (url != nullptr && _host_url == string(url)) { - // Yep, we're this alternate host. - read_xhost(xhost); - return; - } - xalthost = xalthost->NextSiblingElement("alt_host"); - } - } - - // Hmm, didn't find the URL we used mentioned. Assume we're the primary - // host. - read_xhost(xhost); - } -} - -/** - * Reads the host data from the (or ) entry in the - * contents.xml file. - */ -void Panda3D:: -read_xhost(TiXmlElement *xhost) { - // Get the "download" URL, which is the source from which we download - // everything other than the contents.xml file. - const char *download_url = xhost->Attribute("download_url"); - if (download_url == nullptr) { - download_url = xhost->Attribute("url"); - } - - if (download_url != nullptr) { - _download_url_prefix = download_url; - } else { - _download_url_prefix = _host_url_prefix; - } - if (!_download_url_prefix.empty()) { - if (_download_url_prefix[_download_url_prefix.size() - 1] != '/') { - _download_url_prefix += "/"; - } - } - - TiXmlElement *xmirror = xhost->FirstChildElement("mirror"); - while (xmirror != nullptr) { - const char *url = xmirror->Attribute("url"); - if (url != nullptr) { - add_mirror(url); - } - xmirror = xmirror->NextSiblingElement("mirror"); - } -} - -/** - * Adds a new URL to serve as a mirror for this host. The mirrors will be - * consulted first, before consulting the host directly. - */ -void Panda3D:: -add_mirror(string mirror_url) { - // Ensure the URL ends in a slash. - if (!mirror_url.empty() && mirror_url[mirror_url.size() - 1] != '/') { - mirror_url += '/'; - } - - // Add it to the _mirrors list, but only if it's not already there. - if (find(_mirrors.begin(), _mirrors.end(), mirror_url) == _mirrors.end()) { - _mirrors.push_back(mirror_url); - } -} - -/** - * Selects num_mirrors elements, chosen at random, from the _mirrors list. - * Adds the selected mirrors to result. If there are fewer than num_mirrors - * elements in the list, adds only as many mirrors as we can get. - */ -void Panda3D:: -choose_random_mirrors(vector_string &result, int num_mirrors) { - pvector selected; - - size_t num_to_select = std::min(_mirrors.size(), (size_t)num_mirrors); - while (num_to_select > 0) { - size_t i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size()); - while (find(selected.begin(), selected.end(), i) != selected.end()) { - // Already found this i, find a new one. - i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size()); - } - selected.push_back(i); - result.push_back(_mirrors[i]); - --num_to_select; - } -} - -/** - * Checks the core API DLL file against the specification in the contents - * file, and downloads it if necessary. - */ -bool Panda3D:: -get_core_api() { - bool is_fresh = false; - if (!_coreapi_dll.quick_verify(_root_dir)) { - if (!download_core_api()) { - return false; - } - is_fresh = true; - } - - // Now we've got the DLL. Load it. - string pathname = _coreapi_dll.get_pathname(_root_dir); - -#ifdef P3D_PLUGIN_P3D_PLUGIN - // This is a convenience macro for development. If defined and nonempty, it - // indicates the name of the plugin DLL that we will actually run, even - // after downloading a possibly different (presumably older) version. Its - // purpose is to simplify iteration on the plugin DLL. - string override_filename = P3D_PLUGIN_P3D_PLUGIN; - if (!override_filename.empty()) { - pathname = override_filename; - } -#endif // P3D_PLUGIN_P3D_PLUGIN - - bool trusted_environment = !_enable_security; - - Filename contents_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml"); - if (!load_plugin(pathname, contents_filename.to_os_specific(), - _host_url, _verify_contents, _this_platform, _log_dirname, - _log_basename, trusted_environment, _console_environment, - _root_dir, _host_dir, _start_dir, cerr)) { - - // If we're not sure this is the latest version, make sure it is up-to- - // date, and then try again. - if (is_fresh || !download_core_api() || - !load_plugin(pathname, contents_filename.to_os_specific(), - _host_url, _verify_contents, _this_platform, _log_dirname, - _log_basename, trusted_environment, _console_environment, - _root_dir, _host_dir, _start_dir, cerr)) { - - cerr << "Unable to launch core API in " << pathname << "\n"; - return false; - } - } - - // Successfully loaded. -#ifdef PANDA_OFFICIAL_VERSION - static const bool official = true; -#else - static const bool official = false; -#endif - - // Format the coreapi_timestamp as a string, for passing as a parameter. - std::ostringstream stream; - stream << _coreapi_dll.get_timestamp(); - string coreapi_timestamp = stream.str(); - - P3D_set_plugin_version_ptr(P3D_PLUGIN_MAJOR_VERSION, P3D_PLUGIN_MINOR_VERSION, - P3D_PLUGIN_SEQUENCE_VERSION, official, - PANDA_DISTRIBUTOR, - _host_url.c_str(), coreapi_timestamp.c_str(), - _coreapi_set_ver.c_str()); - - return true; -} - -/** - * Downloads the latest version of the core API from the plug-in server. - */ -bool Panda3D:: -download_core_api() { - // The DLL file needs to be downloaded. Build up our list of URL's to - // attempt to download it from, in reverse order. - string url; - vector_string core_urls; - - // Our last act of desperation: hit the original host, with a query - // uniquifier, to break through any caches. - std::ostringstream strm; - strm << _download_url_prefix << _coreapi_dll.get_filename() - << "?" << time(nullptr); - url = strm.str(); - core_urls.push_back(url); - - // Before we try that, we'll hit the original host, without a uniquifier. - url = _download_url_prefix; - url += _coreapi_dll.get_filename(); - core_urls.push_back(url); - - // And before we try that, we'll try two mirrors, at random. - vector_string mirrors; - choose_random_mirrors(mirrors, 2); - for (vector_string::iterator si = mirrors.begin(); - si != mirrors.end(); - ++si) { - url = (*si) + _coreapi_dll.get_filename(); - core_urls.push_back(url); - } - - // The very first thing we'll try is the super_mirror, if we have one. - if (!_super_mirror_url_prefix.empty()) { - url = _super_mirror_url_prefix + _coreapi_dll.get_filename(); - core_urls.push_back(url); - } - - // Now pick URL's off the list, and try them, until we have success. - Filename pathname = Filename::from_os_specific(_coreapi_dll.get_pathname(_root_dir)); - pathname.make_dir(); - HTTPClient *http = HTTPClient::get_global_ptr(); - - bool success = false; - while (!core_urls.empty()) { - url = core_urls.back(); - core_urls.pop_back(); - - PT(HTTPChannel) channel = http->get_document(url); - if (!channel->download_to_file(pathname)) { - cerr << "Unable to download " << url << "\n"; - - } else if (!_coreapi_dll.full_verify(_root_dir)) { - cerr << "Mismatched download for " << url << "\n"; - - } else { - // successfully downloaded! - success = true; - break; - } - } - - if (!success) { - return false; - } - - // Since we had to download some of it, might as well ask the core API to - // check all of it. - if (_verify_contents == P3D_VC_none) { - _verify_contents = P3D_VC_normal; - } - return true; -} - -/** - * Reports the available command-line options. - */ -void Panda3D:: -usage() { - cerr - << "\nThis is panda3d version " - << P3D_PLUGIN_MAJOR_VERSION << "." - << P3D_PLUGIN_MINOR_VERSION << "." - << P3D_PLUGIN_SEQUENCE_VERSION; -#ifndef PANDA_OFFICIAL_VERSION - cerr << "c"; -#endif - - cerr - << "\n\nUsage:\n" - << " panda3d [opts] file.p3d [args]\n" - << " panda3d -m [opts] file_a.p3d file_b.p3d [file_c.p3d ...]\n\n" - - << "This program is used to execute a Panda3D application bundle stored\n" - << "in a .p3d file. In the first form, without the -m option, it\n" - << "executes one application; remaining arguments following the\n" - << "application name are passed into the application. In the second\n" - << "form, with the -m option, it can execute multiple applications\n" - << "simultaneously, though in this form arguments cannot be passed into\n" - << "the applications.\n\n" - - << "Options:\n\n" - - << " -m\n" - << " Indicates that multiple application filenames will be passed on\n" - << " the command line. All applications will be run at the same\n" - << " time, but additional arguments may not be passed to any of the\n" - << " applictions.\n\n" - - << " -t token=value\n" - << " Defines a web token or parameter to pass to the application(s).\n" - << " This simulates a entry in an tag.\n\n" - - << " -w [toplevel|embedded|fullscreen|hidden]\n" - << " Specify the type of graphic window to create. If you specify\n" - << " \"embedded\", a new window is created to be the parent.\n\n" - - << " -s width,height\n" - << " Specify the size of the graphic window.\n\n" - - << " -o x,y\n" - << " Specify the position (origin) of the graphic window on the\n" - << " screen, or on the parent window. If you specify -1,-1\n" - << " the default position will be used, and a value of -2,-2\n" - << " means that the window will be centered on the screen.\n\n" - - << " -l log_dirname\n" - << " Specify the full path to the directory in which log files are\n" - << " to be written. If this is not specified, the default is to send\n" - << " the application output to the console.\n\n" - - << " -n\n" - << " Allow a network connect to the Panda3D download server, to check\n" - << " if a new version is available (but only if the current version\n" - << " appears to be out-of-date). The default behavior, if both -n\n" - << " and -f are omitted, is not to contact the server at all, unless\n" - << " the local contents do not exist or cannot be read.\n\n" - - << " -f\n" - << " Force an initial contact of the Panda3D download server, even\n" - << " if the local contents appear to be current. This is mainly\n" - << " useful when testing local republishes.\n\n" - - << " -i\n" - << " Runs the application interactively. This requires that the application\n" - << " was built with -D on the packp3d command line. If so, this option will\n" - << " create an interactive Python prompt after the application has loaded.\n" - << " It will also retain the PYTHONPATH environment variable from the user's\n" - << " environment, allowing Python files on disk to shadow the same-named\n" - << " Python files within the p3d file, for rapid iteration on the Python\n" - << " code.\n\n" - - << " -S\n" - << " Runs the application with security enabled, as if it were embedded in\n" - << " a web page.\n\n" - - << " -u url\n" - - << " Specify the URL of the Panda3D download server. This is the host\n" - << " from which the plugin itself will be downloaded if necessary. The\n" - << " default is \"" << _host_url << "\" .\n\n" - - << " -M super_mirror_url\n" - << " Specifies the \"super mirror\" URL, the special URL that is consulted\n" - << " first before downloading any package file referenced by a p3d file.\n" - << " This is primarily intended to support pre-installing a downloadable\n" - << " Panda3D tree on the local machine, to allow p3d applications to\n" - << " execute without requiring an internet connection.\n\n" - - << " -p platform\n" - << " Specify the platform to masquerade as. The default is \"" - << DTOOL_PLATFORM << "\" .\n\n" - - << " -V\n" - << " Output only the plugin version string and exit immediately.\n\n" - - << " -U\n" - << " Output only the plugin host URL and exit immediately.\n\n" - - << " -P\n" - << " Output only the plugin platform string and exit immediately.\n\n"; -} diff --git a/direct/src/plugin_standalone/panda3d.h b/direct/src/plugin_standalone/panda3d.h deleted file mode 100644 index a9b647fc7d..0000000000 --- a/direct/src/plugin_standalone/panda3d.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file panda3d.h - * @author drose - * @date 2009-06-30 - */ - -#ifndef PANDA3D_H -#define PANDA3D_H - -#include "dtoolbase.h" -#ifdef _WIN32 -#include -#endif - -#include "panda3dBase.h" -#include "p3d_plugin.h" -#include "httpChannel.h" -#include "ramfile.h" -#include "fileSpec.h" -#include "pset.h" -#include "vector_string.h" - -/** - * A standalone program that invokes the Panda3D plugin to launch .p3d files. - */ -class Panda3D : public Panda3DBase { -public: - Panda3D(bool console_environment); - - int run_command_line(int argc, char *argv[]); - -protected: - bool post_arg_processing(); - bool get_plugin(); - bool download_contents_file(const Filename &contents_filename); - bool read_contents_file(const Filename &contents_filename, bool fresh_download); - void find_host(TiXmlElement *xcontents); - void read_xhost(TiXmlElement *xhost); - void add_mirror(std::string mirror_url); - void choose_random_mirrors(vector_string &result, int num_mirrors); - bool get_core_api(); - bool download_core_api(); - - void usage(); - -protected: - std::string _super_mirror_url; - std::string _host_url_prefix; - std::string _download_url_prefix; - std::string _super_mirror_url_prefix; - typedef pvector Mirrors; - Mirrors _mirrors; - - std::string _coreapi_set_ver; - FileSpec _coreapi_dll; -}; - -#include "panda3d.I" - -#endif diff --git a/direct/src/plugin_standalone/panda3d.ico b/direct/src/plugin_standalone/panda3d.ico deleted file mode 100644 index 3a2c1c33d4..0000000000 Binary files a/direct/src/plugin_standalone/panda3d.ico and /dev/null differ diff --git a/direct/src/plugin_standalone/panda3dBase.I b/direct/src/plugin_standalone/panda3dBase.I deleted file mode 100644 index f875229f2d..0000000000 --- a/direct/src/plugin_standalone/panda3dBase.I +++ /dev/null @@ -1,29 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file panda3dBase.I - * @author rdb - * @date 2009-12-07 - */ - -/** - * Returns true if it is time to exit because the last instance has exited, or - * false if we should continue running. - */ -bool Panda3DBase:: -time_to_exit() { - return _instances.empty() && _exit_with_last_instance; -} - -/** - * Returns the P3D_instance associated with this URLGetter. - */ -P3D_instance *Panda3DBase::URLGetter:: -get_instance() { - return _instance; -} diff --git a/direct/src/plugin_standalone/panda3dBase.cxx b/direct/src/plugin_standalone/panda3dBase.cxx deleted file mode 100644 index cfdaeb1350..0000000000 --- a/direct/src/plugin_standalone/panda3dBase.cxx +++ /dev/null @@ -1,766 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file panda3dBase.cxx - * @author rdb - * @date 2009-12-07 - */ - -#include "dtoolbase.h" -#ifdef _WIN32 -#include -#include -#else -#include -#endif -#include "panda3dBase.h" -#include "httpClient.h" -#include "find_root_dir.h" -#include "load_plugin.h" -#include "executionEnvironment.h" -#include "multifile.h" -#include "tinyxml.h" - -// We can include this header file to get the DTOOL_PLATFORM definition, even -// though we don't link with dtool. -#include "dtool_platform.h" -#include "pandaVersion.h" - -#include -#include -#include - -using std::cerr; -using std::string; - -// The amount of time in seconds to wait for new messages. -static const double wait_cycle = 0.2; - -/** - * - */ -Panda3DBase:: -Panda3DBase(bool console_environment) { - _console_environment = console_environment; - - // We can't assign _root_dir immediately, because it requires a low-level - // Mac call that will balk if we're not logged in to the console, which will - // prevent scripts from using "panda3d -P" to find the platform and whatnot. - // Instead, we'll assign it after we've processed the command line. - // _root_dir = find_root_dir(); - - _reporting_download = false; - _enable_security = false; - - _window_type = P3D_WT_toplevel; - - _win_x = -1; - _win_y = -1; - _win_width = 800; - _win_height = 600; - _got_win_size = false; - - _exit_with_last_instance = true; - _host_url = PANDA_PACKAGE_HOST_URL; - // Better to leave _this_platform set to the empty string until the user - // specifies otherwise; this allows the plugin to select a suitable platform - // at runtime. - _this_platform = ""; - _coreapi_platform = DTOOL_PLATFORM; - _verify_contents = P3D_VC_none; - _contents_expiration = 0; - - // Seed the lame random number generator in rand(); we use it to select a - // mirror for downloading. - srand((unsigned int)time(nullptr)); - - _prepend_filename_to_args = true; -} - -/** - * Gets lost in the application main loop, waiting for system events and - * notifications from the open instance(s). - */ -void Panda3DBase:: -run_main_loop() { -#ifdef _WIN32 - if (_window_type == P3D_WT_embedded) { - // Wait for new messages from Windows, and new requests from the plugin. - MSG msg; - int retval; - retval = GetMessage(&msg, nullptr, 0, 0); - while (retval != 0 && !time_to_exit()) { - if (retval == -1) { - cerr << "Error processing message queue.\n"; - return; - } - TranslateMessage(&msg); - DispatchMessage(&msg); - - // Check for new requests from the Panda3D plugin. - P3D_instance *inst = P3D_check_request_ptr(wait_cycle); - while (inst != nullptr) { - P3D_request *request = P3D_instance_get_request_ptr(inst); - if (request != nullptr) { - handle_request(request); - } - inst = P3D_check_request_ptr(wait_cycle); - } - - while (!_url_getters.empty() && - !PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE)) { - // If there are no Windows messages, check the download tasks. - run_getters(); - } - retval = GetMessage(&msg, nullptr, 0, 0); - } - - // WM_QUIT has been received. Terminate all instances, and fall through. - while (!_instances.empty()) { - P3D_instance *inst = *(_instances.begin()); - delete_instance(inst); - } - - } else { - // Not an embedded window, so we don't have our own window to generate - // Windows events. Instead, just wait for requests. - while (!time_to_exit()) { - P3D_instance *inst = P3D_check_request_ptr(wait_cycle); - if (inst != nullptr) { - P3D_request *request = P3D_instance_get_request_ptr(inst); - if (request != nullptr) { - handle_request(request); - } - } - run_getters(); - } - } - -#elif defined(__APPLE__) && !__LP64__ - // OSX really prefers to own the main loop, so we install a timer to call - // out to our instances and getters, rather than polling within the event - // loop as we do in the Windows case, above. - EventLoopRef main_loop = GetMainEventLoop(); - EventLoopTimerUPP timer_upp = NewEventLoopTimerUPP(st_timer_callback); - EventLoopTimerRef timer; - EventTimerInterval interval = wait_cycle * kEventDurationSecond; - InstallEventLoopTimer(main_loop, interval, interval, - timer_upp, this, &timer); - RunApplicationEventLoop(); - RemoveEventLoopTimer(timer); - - // Terminate all instances, and fall through. - while (!_instances.empty()) { - P3D_instance *inst = *(_instances.begin()); - delete_instance(inst); - } - -#else // _WIN32, __APPLE__ - - // Now wait while we process pending requests. - while (!time_to_exit()) { - P3D_instance *inst = P3D_check_request_ptr(wait_cycle); - if (inst != nullptr) { - P3D_request *request = P3D_instance_get_request_ptr(inst); - if (request != nullptr) { - handle_request(request); - } - } - run_getters(); - } - -#endif // _WIN32, __APPLE__ -} - -/** - * Polls all of the active URL requests. - */ -void Panda3DBase:: -run_getters() { - URLGetters::iterator gi; - gi = _url_getters.begin(); - while (gi != _url_getters.end()) { - URLGetter *getter = (*gi); - if (getter->run()) { - // This URLGetter is still working. Leave it. - ++gi; - } else { - // This URLGetter is done. Remove it and delete it. - URLGetters::iterator dgi = gi; - ++gi; - _url_getters.erase(dgi); - delete getter; - } - } -} - -/** - * Handles a single request received via the plugin API from a p3d instance. - */ -void Panda3DBase:: -handle_request(P3D_request *request) { - bool handled = false; - switch (request->_request_type) { - case P3D_RT_stop: - delete_instance(request->_instance); -#ifdef _WIN32 - // Post a silly message to spin the event loop. - PostMessage(nullptr, WM_USER, 0, 0); -#endif - handled = true; - break; - - case P3D_RT_get_url: - { - int unique_id = request->_request._get_url._unique_id; - const string &url = request->_request._get_url._url; - URLGetter *getter = new URLGetter - (request->_instance, unique_id, URLSpec(url), ""); - _url_getters.insert(getter); - handled = true; - } - break; - - case P3D_RT_notify: - { - // cerr << "Notify: " << request->_request._notify._message << "\n"; - if (strcmp(request->_request._notify._message, "ondownloadnext") == 0) { - // Tell the user we're downloading a package. - report_downloading_package(request->_instance); - } else if (strcmp(request->_request._notify._message, "ondownloadcomplete") == 0) { - // Tell the user we're done downloading. - report_download_complete(request->_instance); - } else if (strcmp(request->_request._notify._message, "onfail") == 0) { - // Failure! What else can we do? - cerr << "Failed to execute.\n"; - exit(1); - } - } - break; - - default: - break; - }; - - P3D_request_finish_ptr(request, handled); -} - -#ifdef _WIN32 -LONG WINAPI -window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - switch (msg) { - case WM_DESTROY: - PostQuitMessage(0); - break; - }; - - return DefWindowProc(hwnd, msg, wparam, lparam); -} - -/** - * Creates a toplevel window to contain the embedded instances. Windows - * implementation. - */ -void Panda3DBase:: -make_parent_window() { - WNDCLASS wc; - - HINSTANCE application = GetModuleHandle(nullptr); - ZeroMemory(&wc, sizeof(WNDCLASS)); - wc.lpfnWndProc = (WNDPROC)window_proc; - wc.hInstance = application; - wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); - wc.lpszClassName = "panda3d"; - - if (!RegisterClass(&wc)) { - cerr << "Could not register window class!\n"; - exit(1); - } - - DWORD window_style = - WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | - WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | - WS_SIZEBOX | WS_MAXIMIZEBOX; - - HWND toplevel_window = - CreateWindow("panda3d", "Panda3D", window_style, - CW_USEDEFAULT, CW_USEDEFAULT, _win_width, _win_height, - nullptr, nullptr, application, 0); - if (!toplevel_window) { - cerr << "Could not create toplevel window!\n"; - exit(1); - } - - ShowWindow(toplevel_window, SW_SHOWNORMAL); - - memset(&_parent_window, 0, sizeof(_parent_window)); - _parent_window._window_handle_type = P3D_WHT_win_hwnd; - _parent_window._handle._win_hwnd._hwnd = toplevel_window; -} - -#else - -/** - * Creates a toplevel window to contain the embedded instances. - */ -void Panda3DBase:: -make_parent_window() { - // TODO. - assert(false); -} - -#endif - -/** - * Uses the plugin API to create a new P3D instance to play a particular .p3d - * file. This instance is also started if start_instance is true (which - * requires that the named p3d file exists). - */ -P3D_instance *Panda3DBase:: -create_instance(const string &p3d, bool start_instance, - char **args, int num_args, int p3d_offset) { - // Check to see if the p3d filename we were given is a URL, or a local file. - Filename p3d_filename = Filename::from_os_specific(p3d); - string os_p3d_filename = p3d; - bool is_local = !is_url(p3d); - if (is_local && start_instance) { - if (!p3d_filename.exists()) { - cerr << "No such file: " << p3d_filename << "\n"; - exit(1); - } - - p3d_filename.make_absolute(); - os_p3d_filename = p3d_filename.to_os_specific(); - } - if (is_local) { - read_p3d_info(p3d_filename, p3d_offset); - } - - // Build up the token list. - Tokens tokens = _tokens; - P3D_token token; - - string log_basename; - if (!_log_dirname.empty()) { - // Generate output to a logfile. - log_basename = p3d_filename.get_basename_wo_extension(); - token._keyword = "log_basename"; - token._value = log_basename.c_str(); - tokens.push_back(token); - } else { - // Send output to the console. - token._keyword = "console_output"; - if (_console_environment) { - token._value = "1"; - } else { - token._value = "0"; - } - tokens.push_back(token); - } - - token._keyword = "auto_start"; - token._value = "1"; - tokens.push_back(token); - - P3D_token *tokens_p = nullptr; - size_t num_tokens = tokens.size(); - if (!tokens.empty()) { - tokens_p = &tokens[0]; - } - - // Build up the argument list, beginning with the p3d_filename. - pvector argv; - if (_prepend_filename_to_args) { - argv.push_back(os_p3d_filename.c_str()); - } - for (int i = 0; i < num_args; ++i) { - argv.push_back(args[i]); - } - - P3D_instance *inst = P3D_new_instance_ptr(nullptr, tokens_p, num_tokens, - argv.size(), &argv[0], nullptr); - - if (inst != nullptr) { - if (start_instance) { - // We call start() first, to give the core API a chance to notice the - // "hidden" attrib before we set the window parameters. - P3D_instance_start_ptr(inst, is_local, os_p3d_filename.c_str(), p3d_offset); - } - - P3D_instance_setup_window_ptr - (inst, _window_type, _win_x, _win_y, _win_width, _win_height, &_parent_window); - } - - return inst; -} - -/** - * Deletes the indicated instance and removes it from the internal structures. - */ -void Panda3DBase:: -delete_instance(P3D_instance *inst) { - P3D_instance_finish_ptr(inst); - _instances.erase(inst); - - // Make sure we also terminate any pending URLGetters associated with this - // instance. - URLGetters::iterator gi; - gi = _url_getters.begin(); - while (gi != _url_getters.end()) { - URLGetter *getter = (*gi); - if (getter->get_instance() == inst) { - URLGetters::iterator dgi = gi; - ++gi; - _url_getters.erase(dgi); - delete getter; - } - } -} - -/** - * Opens the p3d file to read the p3d_info.xml file within it, looking for any - * locally-relevant parameters (like width and height). - */ -bool Panda3DBase:: -read_p3d_info(const Filename &p3d_filename, int p3d_offset) { - PT(Multifile) mf = new Multifile; - if (!mf->open_read(p3d_filename, p3d_offset)) { - return false; - } - int si = mf->find_subfile("p3d_info.xml"); - if (si == -1) { - return false; - } - - string p3d_info; - mf->read_subfile(si, p3d_info); - std::istringstream strm(p3d_info); - TiXmlDocument doc; - strm >> doc; - if (strm.fail() && !strm.eof()) { - return false; - } - TiXmlElement *xpackage = doc.FirstChildElement("package"); - if (xpackage == nullptr) { - return false; - } - TiXmlElement *xconfig = xpackage->FirstChildElement("config"); - if (xconfig == nullptr) { - return false; - } - - // Successfully read the p3d_info.xml file. - if (!_got_win_size) { - // If the user didn't override the size on the command line, allow the p3d - // file to request a preferred size. - if (xconfig->Attribute("width", &_win_width)) { - _got_win_size = true; - } - if (xconfig->Attribute("height", &_win_height)) { - _got_win_size = true; - } - } - - return true; -} - -/** - * Parses a web token of the form token=value, and stores it in _tokens. - * Returns true on success, false on failure. - */ -bool Panda3DBase:: -parse_token(const char *arg) { - const char *equals = strchr(arg, '='); - if (equals == nullptr) { - return false; - } - - // By convention, the keyword is always lowercase. - string keyword; - for (const char *p = arg; p < equals; ++p) { - keyword += tolower(*p); - } - - P3D_token token; - token._keyword = strdup(keyword.c_str()); - token._value = strdup(equals + 1); - - _tokens.push_back(token); - - return true; -} - -/** - * Parses a string into an x,y pair of integers. Returns true on success, - * false on failure. - */ -bool Panda3DBase:: -parse_int_pair(const char *arg, int &x, int &y) { - char *endptr; - x = strtol(arg, &endptr, 10); - if (*endptr == ',') { - y = strtol(endptr + 1, &endptr, 10); - if (*endptr == '\0') { - return true; - } - } - - // Some parse error on the string. - return false; -} - -/** - * Returns the value associated with the first appearance of the named token, - * or empty string if the token does not appear. - */ -string Panda3DBase:: -lookup_token(const string &keyword) const { - Tokens::const_iterator ti; - for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) { - if (keyword == (*ti)._keyword) { - return (*ti)._value; - } - } - - return string(); -} - -/** - * Compares the two dotted-integer sequence values numerically. Returns -1 if - * seq_a sorts first, 1 if seq_b sorts first, 0 if they are equivalent. - */ -int Panda3DBase:: -compare_seq(const string &seq_a, const string &seq_b) { - const char *num_a = seq_a.c_str(); - const char *num_b = seq_b.c_str(); - int comp = compare_seq_int(num_a, num_b); - while (comp == 0) { - if (*num_a != '.') { - if (*num_b != '.') { - // Both strings ran out together. - return 0; - } - // a ran out first. - return -1; - } else if (*num_b != '.') { - // b ran out first. - return 1; - } - - // Increment past the dot. - ++num_a; - ++num_b; - comp = compare_seq(num_a, num_b); - } - - return comp; -} - -/** - * Numerically compares the formatted integer value at num_a with num_b. - * Increments both num_a and num_b to the next character following the valid - * integer. - */ -int Panda3DBase:: -compare_seq_int(const char *&num_a, const char *&num_b) { - long int a; - char *next_a; - long int b; - char *next_b; - - a = strtol((char *)num_a, &next_a, 10); - b = strtol((char *)num_b, &next_b, 10); - - num_a = next_a; - num_b = next_b; - - if (a < b) { - return -1; - } else if (b < a) { - return 1; - } else { - return 0; - } -} - -/** - * Returns true if the indicated string appears to be a URL, with a leading - * http:// or file:// or whatever, or false if it must be a local filename - * instead. - */ -bool Panda3DBase:: -is_url(const string ¶m) { - // We define a URL prefix as a sequence of at least two letters, followed by - // a colon, followed by at least one slash. - size_t p = 0; - while (p < param.size() && isalpha(param[p])) { - ++p; - } - if (p < 2) { - // Not enough letters. - return false; - } - if (p >= param.size() || param[p] != ':') { - // No colon. - return false; - } - ++p; - if (p >= param.size() || param[p] != '/') { - // No slash. - return false; - } - - // It matches the rules. - return true; -} - -/** - * Tells the user we have to download a package. - */ -void Panda3DBase:: -report_downloading_package(P3D_instance *instance) { - P3D_object *obj = P3D_instance_get_panda_script_object_ptr(instance); - - P3D_object *display_name = P3D_object_get_property_ptr(obj, "downloadPackageDisplayName"); - if (display_name == nullptr) { - cerr << "Installing package.\n"; - return; - } - - int name_length = P3D_object_get_string_ptr(display_name, nullptr, 0); - char *name = new char[name_length + 1]; - P3D_object_get_string_ptr(display_name, name, name_length + 1); - - cerr << "Installing " << name << "\n"; - - delete[] name; - P3D_object_decref_ptr(display_name); - _reporting_download = true; -} - -/** - * Tells the user we're done downloading packages - */ -void Panda3DBase:: -report_download_complete(P3D_instance *instance) { - if (_reporting_download) { - cerr << "Install complete.\n"; - } -} - -#if defined(__APPLE__) && !__LP64__ -/** - * Installed as a timer on the event loop, so we can process local events, in - * the Apple implementation. - */ -pascal void Panda3DBase:: -st_timer_callback(EventLoopTimerRef timer, void *user_data) { - ((Panda3DBase *)user_data)->timer_callback(timer); -} -#endif // __APPLE__ - -#if defined(__APPLE__) && !__LP64__ -/** - * Installed as a timer on the event loop, so we can process local events, in - * the Apple implementation. - */ -void Panda3DBase:: -timer_callback(EventLoopTimerRef timer) { - // Check for new requests from the Panda3D plugin. - P3D_instance *inst = P3D_check_request_ptr(0.0); - while (inst != nullptr) { - P3D_request *request = P3D_instance_get_request_ptr(inst); - if (request != nullptr) { - handle_request(request); - } - inst = P3D_check_request_ptr(0.0); - } - - // Check the download tasks. - run_getters(); - - // If we're out of instances, exit the application. - if (time_to_exit()) { - QuitApplicationEventLoop(); - } -} -#endif // __APPLE__ - -/** - * - */ -Panda3DBase::URLGetter:: -URLGetter(P3D_instance *instance, int unique_id, - const URLSpec &url, const string &post_data) : - _instance(instance), - _unique_id(unique_id), - _url(url), - _post_data(post_data) -{ - HTTPClient *http = HTTPClient::get_global_ptr(); - - _channel = http->make_channel(false); - // _channel->set_download_throttle(true); - if (_post_data.empty()) { - _channel->begin_get_document(_url); - } else { - _channel->begin_post_form(_url, _post_data); - } - - _channel->download_to_ram(&_rf); - _bytes_sent = 0; -} - -/** - * Polls the URLGetter for new results. Returns true if the URL request is - * still in progress and run() should be called again later, or false if the - * URL request has been completed and run() should not be called again. - */ -bool Panda3DBase::URLGetter:: -run() { - if (_channel->run() || _rf.get_data_size() != 0) { - if (_rf.get_data_size() != 0) { - // Got some new data. - bool download_ok = P3D_instance_feed_url_stream_ptr - (_instance, _unique_id, P3D_RC_in_progress, - _channel->get_status_code(), - _channel->get_file_size(), - (const unsigned char *)_rf.get_data().data(), _rf.get_data_size()); - _bytes_sent += _rf.get_data_size(); - _rf.clear(); - - if (!download_ok) { - // The plugin doesn't care any more. Interrupt the download. - cerr << "Download interrupted: " << _url - << ", after " << _bytes_sent << " of " << _channel->get_file_size() - << " bytes.\n"; - return false; - } - } - - // Still more to come; call this method again later. - return true; - } - - // All done. - P3D_result_code status = P3D_RC_done; - if (!_channel->is_valid()) { - if (_channel->get_status_code() != 0) { - status = P3D_RC_http_error; - } else { - status = P3D_RC_generic_error; - } - cerr << "Error getting URL " << _url << "\n"; - } - - P3D_instance_feed_url_stream_ptr - (_instance, _unique_id, status, - _channel->get_status_code(), - _bytes_sent, nullptr, 0); - return false; -} diff --git a/direct/src/plugin_standalone/panda3dBase.h b/direct/src/plugin_standalone/panda3dBase.h deleted file mode 100644 index 3b41aab07c..0000000000 --- a/direct/src/plugin_standalone/panda3dBase.h +++ /dev/null @@ -1,126 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file panda3dBase.h - * @author rdb - * @date 2009-12-07 - */ - -#ifndef PANDA3DBASE_H -#define PANDA3DBASE_H - -// This program must link with Panda for HTTPClient support. This means it -// probably should be built with LINK_ALL_STATIC defined, so we won't have to -// deal with confusing .dll or .so files that might compete on the disk with -// the dynamically-loaded versions. There's no competition in memory address -// space, though, because p3d_plugin--the only file we dynamically link in-- -// doesn't itself link with Panda. - -#include "pandabase.h" -#include "p3d_plugin.h" -#include "httpChannel.h" -#include "ramfile.h" -#include "fileSpec.h" -#include "pset.h" -#include "vector_string.h" - -/** - * Base for creating a standalone application that invokes the panda3d plugin - * to launch .p3d files. - */ -class Panda3DBase { -public: - Panda3DBase(bool console_environment); - - void run_main_loop(); - -protected: - void run_getters(); - void handle_request(P3D_request *request); - void make_parent_window(); - - P3D_instance * - create_instance(const std::string &p3d, bool start_instance, - char **args, int num_args, int p3d_offset = 0); - void delete_instance(P3D_instance *instance); - - bool read_p3d_info(const Filename &p3d_filename, int p3d_offset = 0); - bool parse_token(const char *arg); - bool parse_int_pair(const char *arg, int &x, int &y); - std::string lookup_token(const std::string &keyword) const; - static int compare_seq(const std::string &seq_a, const std::string &seq_b); - static int compare_seq_int(const char *&num_a, const char *&num_b); - static bool is_url(const std::string ¶m); - - void report_downloading_package(P3D_instance *instance); - void report_download_complete(P3D_instance *instance); - - inline bool time_to_exit(); - -#ifdef __APPLE__ - static pascal void st_timer_callback(EventLoopTimerRef timer, void *user_data); - void timer_callback(EventLoopTimerRef timer); -#endif - -protected: - std::string _host_url; - std::string _root_dir; - std::string _host_dir; - std::string _start_dir; - std::string _log_dirname; - std::string _log_basename; - std::string _this_platform; - std::string _coreapi_platform; - P3D_verify_contents _verify_contents; - time_t _contents_expiration; - - P3D_window_type _window_type; - P3D_window_handle _parent_window; - int _win_x, _win_y; - int _win_width, _win_height; - bool _got_win_size; - bool _exit_with_last_instance; - - bool _reporting_download; - bool _enable_security; - bool _console_environment; - bool _prepend_filename_to_args; - - typedef pset Instances; - Instances _instances; - - typedef pvector Tokens; - Tokens _tokens; - - // This nested class keeps track of active URL requests. - class URLGetter { - public: - URLGetter(P3D_instance *instance, int unique_id, - const URLSpec &url, const std::string &post_data); - - bool run(); - inline P3D_instance *get_instance(); - - private: - P3D_instance *_instance; - int _unique_id; - URLSpec _url; - std::string _post_data; - - PT(HTTPChannel) _channel; - Ramfile _rf; - size_t _bytes_sent; - }; - - typedef pset URLGetters; - URLGetters _url_getters; -}; - -#include "panda3dBase.I" - -#endif diff --git a/direct/src/plugin_standalone/panda3dMac.I b/direct/src/plugin_standalone/panda3dMac.I deleted file mode 100644 index d70e879a92..0000000000 --- a/direct/src/plugin_standalone/panda3dMac.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file panda3dMac.I - * @author drose - * @date 2009-10-23 - */ diff --git a/direct/src/plugin_standalone/panda3dMac.cxx b/direct/src/plugin_standalone/panda3dMac.cxx deleted file mode 100644 index 5a87c60d3e..0000000000 --- a/direct/src/plugin_standalone/panda3dMac.cxx +++ /dev/null @@ -1,107 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file panda3dMac.cxx - * @author drose - * @date 2009-10-23 - */ - -#include "panda3dMac.h" -#include "load_plugin.h" - -#include -#include - -// Having a global Panda3DMac object just makes things easier. -static Panda3DMac *this_prog; - -/** - * - */ -Panda3DMac:: -Panda3DMac() : Panda3D(false) { - // Mac applications traditionally keep running even when all windows are - // closed. - _exit_with_last_instance = false; - - // No command-line arguments, so just run. - if (!post_arg_processing()) { - exit(1); - } -} - -/** - * Opens a p3d file received via the "open documents" event as its own - * instance. - */ -void Panda3DMac:: -open_p3d_file(FSRef *ref) { - OSErr err; - - static const size_t buffer_size = 4096; - UInt8 filename[buffer_size]; - err = FSRefMakePath(ref, filename, buffer_size); - if (err) { - std::cerr << "Couldn't get filename\n"; - return; - } - - // Create an instance. - create_instance((char *)filename, true, nullptr, 0); -} - -static pascal OSErr -open_documents_handler(const AppleEvent *theAppleEvent, AppleEvent *reply, - SRefCon handlerRefcon) { - AEDescList docList; - FSRef theFSRef; - long index; - long count = 0; - - // Get the list of file aliases from the event. - OSErr err = AEGetParamDesc(theAppleEvent, - keyDirectObject, typeAEList, &docList); - require_noerr(err, CantGetDocList); - - err = AECountItems(&docList, &count); - require_noerr(err, CantGetCount); - - for (index = 1; index <= count; index++) { - err = AEGetNthPtr(&docList, index, typeFSRef, - nullptr, nullptr, &theFSRef, sizeof(FSRef), nullptr);// 5 - require_noerr(err, CantGetDocDescPtr); - - // Here's the file, do something with it. - this_prog->open_p3d_file(&theFSRef); - } - - // Release list of files - AEDisposeDesc(&docList); - - // Error handlers. - CantGetDocList: - CantGetCount: - CantGetDocDescPtr: - return (err); -} - -int -main(int argc, char *argv[]) { - OSErr err; - AEEventHandlerUPP handler; - - this_prog = new Panda3DMac; - handler = NewAEEventHandlerUPP(open_documents_handler); - err = AEInstallEventHandler - (kCoreEventClass, kAEOpenDocuments, handler, 0, false); - - // The command-line options are weird when we start from the Launcher. Just - // ignore them. - this_prog->run_main_loop(); - return 0; -} diff --git a/direct/src/plugin_standalone/panda3dMac.h b/direct/src/plugin_standalone/panda3dMac.h deleted file mode 100644 index 8d6899dba0..0000000000 --- a/direct/src/plugin_standalone/panda3dMac.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file panda3dMac.h - * @author drose - * @date 2009-10-23 - */ - -#ifndef PANDA3DMAC_H -#define PANDA3DMAC_H - -#include "panda3d.h" - -#include - -/** - * A specialization of Panda3D for running as a Carbon application on OS X. - * Instead of taking input from the command line, this program waits quietly - * for an "open documents" Apple event. - */ -class Panda3DMac : public Panda3D { -public: - Panda3DMac(); - - void open_p3d_file(FSRef *ref); -}; - -#endif diff --git a/direct/src/plugin_standalone/panda3dMain.cxx b/direct/src/plugin_standalone/panda3dMain.cxx deleted file mode 100644 index 51420eb39a..0000000000 --- a/direct/src/plugin_standalone/panda3dMain.cxx +++ /dev/null @@ -1,21 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file panda3dMain.cxx - * @author drose - * @date 2009-10-23 - */ - -#include "panda3d.h" - -// The normal, "console" program. -int -main(int argc, char *argv[]) { - Panda3D program(true); - return program.run_command_line(argc, argv); -} diff --git a/direct/src/plugin_standalone/panda3dWinMain.cxx b/direct/src/plugin_standalone/panda3dWinMain.cxx deleted file mode 100644 index ec7b8e630c..0000000000 --- a/direct/src/plugin_standalone/panda3dWinMain.cxx +++ /dev/null @@ -1,76 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file panda3dWinMain.cxx - * @author drose - * @date 2009-10-23 - */ - -#include "panda3d.h" - -// On Windows, we may need to build panda3dw.exe, a non-console version of -// this program. - -// Returns a newly-allocated string representing the quoted argument beginning -// at p. Advances p to the first character following the close quote. -static char * -parse_quoted_arg(char *&p) { - char quote = *p; - ++p; - std::string result; - - while (*p != '\0' && *p != quote) { - // TODO: handle escape characters? Not sure if we need to. - result += *p; - ++p; - } - if (*p == quote) { - ++p; - } - return strdup(result.c_str()); -} - -// Returns a newly-allocated string representing the unquoted argument -// beginning at p. Advances p to the first whitespace following the argument. -static char * -parse_unquoted_arg(char *&p) { - std::string result; - while (*p != '\0' && !isspace(*p)) { - result += *p; - ++p; - } - return strdup(result.c_str()); -} - -int WINAPI -WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { - char *command_line = GetCommandLine(); - - std::vector argv; - - char *p = command_line; - while (*p != '\0') { - if (*p == '"') { - char *arg = parse_quoted_arg(p); - argv.push_back(arg); - } else { - char *arg = parse_unquoted_arg(p); - argv.push_back(arg); - } - - // Skip whitespace. - while (*p != '\0' && isspace(*p)) { - ++p; - } - } - - assert(!argv.empty()); - - Panda3D program(false); - return program.run_command_line(argv.size(), &argv[0]); -} diff --git a/direct/src/plugin_standalone/panda3d_mac.plist b/direct/src/plugin_standalone/panda3d_mac.plist deleted file mode 100644 index eb122e23d8..0000000000 --- a/direct/src/plugin_standalone/panda3d_mac.plist +++ /dev/null @@ -1,55 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleDisplayName - Panda3D Runtime - CFBundleDocumentTypes - - - CFBundleTypeName - Panda3D applet - CFBundleTypeIconFile - panda3d.icns - CFBundleTypeExtensions - - p3d - - CFBundleTypeMIMETypes - - application/x-panda3d - - CFBundleTypeRole - Viewer - LSTypeIsPackage - - - - CFBundleExecutable - panda3d_mac - CFBundleIconFile - panda3d.icns - CFBundleIdentifier - org.panda3d.runtime.panda3d_mac - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Panda3D - CFBundlePackageType - APPL - CFBundleShortVersionString - 0.9.3 - CFBundleSignature - ???? - CFBundleVersion - 0.9.3 - LSHasLocalizedDisplayName - - NSAppleScriptEnabled - - NSPrincipalClass - NSApplication - - diff --git a/direct/src/showbase/AppRunnerGlobal.py b/direct/src/showbase/AppRunnerGlobal.py index 2ab5c0ccf9..f5663b5ef5 100644 --- a/direct/src/showbase/AppRunnerGlobal.py +++ b/direct/src/showbase/AppRunnerGlobal.py @@ -6,6 +6,9 @@ This is needed for apps that start themselves by importing DirectStart; it provides a place for these apps to look for the AppRunner at startup. """ +if __debug__: + print('AppRunner has been removed and AppRunnerGlobal has been deprecated') + #: Contains the global AppRunner instance, or None if this application #: was not run from the runtime environment. appRunner = None diff --git a/direct/src/showbase/Audio3DManager.py b/direct/src/showbase/Audio3DManager.py index 4a67c9bc27..e0f38c1d2a 100644 --- a/direct/src/showbase/Audio3DManager.py +++ b/direct/src/showbase/Audio3DManager.py @@ -2,7 +2,7 @@ __all__ = ['Audio3DManager'] -from panda3d.core import Vec3, VBase3, WeakNodePath +from panda3d.core import Vec3, VBase3, WeakNodePath, ClockObject from direct.task.TaskManagerGlobal import Task, taskMgr # class Audio3DManager: @@ -122,9 +122,11 @@ class Audio3DManager: This is relative to the sound root (probably render). Default: VBase3(0, 0, 0) """ + if isinstance(velocity, tuple) and len(velocity) == 3: + velocity = VBase3(*velocity) if not isinstance(velocity, VBase3): raise TypeError("Invalid argument 1, expected ") - self.vel_dict[sound]=velocity + self.vel_dict[sound] = velocity def setSoundVelocityAuto(self, sound): """ @@ -139,14 +141,22 @@ class Audio3DManager: """ Get the velocity of the sound. """ - if (sound in self.vel_dict): + if sound in self.vel_dict: vel = self.vel_dict[sound] - if (vel!=None): + if vel is not None: return vel - else: - for known_object in list(self.sound_dict.keys()): - if self.sound_dict[known_object].count(sound): - return known_object.getPosDelta(self.root)/globalClock.getDt() + + for known_object in list(self.sound_dict.keys()): + if self.sound_dict[known_object].count(sound): + node_path = known_object.getNodePath() + if not node_path: + # The node has been deleted. + del self.sound_dict[known_object] + continue + + clock = ClockObject.getGlobalClock() + return node_path.getPosDelta(self.root) / clock.getDt() + return VBase3(0, 0, 0) def setListenerVelocity(self, velocity): @@ -155,9 +165,11 @@ class Audio3DManager: This is relative to the sound root (probably render). Default: VBase3(0, 0, 0) """ + if isinstance(velocity, tuple) and len(velocity) == 3: + velocity = VBase3(*velocity) if not isinstance(velocity, VBase3): raise TypeError("Invalid argument 0, expected ") - self.listener_vel=velocity + self.listener_vel = velocity def setListenerVelocityAuto(self): """ @@ -172,10 +184,11 @@ class Audio3DManager: """ Get the velocity of the listener. """ - if (self.listener_vel!=None): + if self.listener_vel is not None: return self.listener_vel - elif (self.listener_target!=None): - return self.listener_target.getPosDelta(self.root)/globalClock.getDt() + elif self.listener_target is not None: + clock = ClockObject.getGlobalClock() + return self.listener_target.getPosDelta(self.root) / clock.getDt() else: return VBase3(0, 0, 0) diff --git a/direct/src/showbase/BufferViewer.py b/direct/src/showbase/BufferViewer.py index 48dff3a1ea..aced71061e 100644 --- a/direct/src/showbase/BufferViewer.py +++ b/direct/src/showbase/BufferViewer.py @@ -442,3 +442,21 @@ class BufferViewer(DirectObject): cards[index] = placer return Task.cont + + # Snake-case aliases, for people who prefer these. + advance_card = advanceCard + analyze_texture_set = analyzeTextureSet + is_enabled = isEnabled + is_valid_texture_set = isValidTextureSet + maintain_readout = maintainReadout + make_frame = makeFrame + refresh_readout = refreshReadout + select_card = selectCard + set_card_size = setCardSize + set_exclude = setExclude + set_include = setInclude + set_layout = setLayout + set_position = setPosition + set_render_parent = setRenderParent + set_sort = setSort + toggle_enable = toggleEnable diff --git a/direct/src/showbase/EventManager.py b/direct/src/showbase/EventManager.py index af8bd1c91a..319503b414 100644 --- a/direct/src/showbase/EventManager.py +++ b/direct/src/showbase/EventManager.py @@ -19,7 +19,7 @@ class EventManager: Create a C++ event queue and handler """ # Make a notify category for this class (unless there already is one) - if (EventManager.notify == None): + if EventManager.notify is None: EventManager.notify = directNotify.newCategory("EventManager") self.eventQueue = eventQueue @@ -37,8 +37,10 @@ class EventManager: processFunc = self.processEventPstats else: processFunc = self.processEvent - while (not self.eventQueue.isQueueEmpty()): - processFunc(self.eventQueue.dequeueEvent()) + isEmptyFunc = self.eventQueue.isQueueEmpty + dequeueFunc = self.eventQueue.dequeueEvent + while not isEmptyFunc(): + processFunc(dequeueFunc()) def eventLoopTask(self, task): """ @@ -78,13 +80,13 @@ class EventManager: # ******** Duplicate any changes in processEventPstats ********* # ************************************************************** # Get the event name - eventName = event.getName() + eventName = event.name if eventName: paramList = [] - for i in range(event.getNumParameters()): - eventParameter = event.getParameter(i) + for eventParameter in event.parameters: eventParameterData = self.parseEventParameter(eventParameter) paramList.append(eventParameterData) + # Do not print the new frame debug, it is too noisy! if (EventManager.notify.getDebug() and eventName != 'NewFrame'): EventManager.notify.debug('received C++ event named: ' + eventName + @@ -94,13 +96,12 @@ class EventManager: # ************************************************************** # Send the event, we used to send it with the event # name as a parameter, but now you can use extraArgs for that - if paramList: - messenger.send(eventName, paramList) - else: - messenger.send(eventName) + messenger.send(eventName, paramList) + # Also send the event down into C++ land - if self.eventHandler: - self.eventHandler.dispatchEvent(event) + handler = self.eventHandler + if handler: + handler.dispatchEvent(event) else: # An unnamed event from C++ is probably a bad thing @@ -115,13 +116,13 @@ class EventManager: # ******** Duplicate any changes in processEvent ********* # ******************************************************** # Get the event name - eventName = event.getName() + eventName = event.name if eventName: paramList = [] - for i in range(event.getNumParameters()): - eventParameter = event.getParameter(i) + for eventParameter in event.parameters: eventParameterData = self.parseEventParameter(eventParameter) paramList.append(eventParameterData) + # Do not print the new frame debug, it is too noisy! if (EventManager.notify.getDebug() and eventName != 'NewFrame'): EventManager.notify.debug('received C++ event named: ' + eventName + @@ -131,45 +132,36 @@ class EventManager: # ******************************************************** # ******** Duplicate any changes in processEvent ********* # ******************************************************** - if self._wantPstats: - name = eventName - hyphen = name.find('-') - if hyphen >= 0: - name = name[0:hyphen] - pstatCollector = PStatCollector('App:Show code:eventManager:' + name) - pstatCollector.start() - if self.eventHandler: - cppPstatCollector = PStatCollector( - 'App:Show code:eventManager:' + name + ':C++') - - if paramList: - messenger.send(eventName, paramList) - else: - messenger.send(eventName) - # Also send the event down into C++ land + name = eventName + hyphen = name.find('-') + if hyphen >= 0: + name = name[0:hyphen] + pstatCollector = PStatCollector('App:Show code:eventManager:' + name) + pstatCollector.start() if self.eventHandler: - if self._wantPstats: - cppPstatCollector.start() - self.eventHandler.dispatchEvent(event) - # ******************************************************** - # ******** Duplicate any changes in processEvent ********* - # ******************************************************** + cppPstatCollector = PStatCollector( + 'App:Show code:eventManager:' + name + ':C++') - if self._wantPstats: - if self.eventHandler: - cppPstatCollector.stop() - pstatCollector.stop() + messenger.send(eventName, paramList) + + # Also send the event down into C++ land + handler = self.eventHandler + if handler: + cppPstatCollector.start() + handler.dispatchEvent(event) + cppPstatCollector.stop() + + pstatCollector.stop() else: # An unnamed event from C++ is probably a bad thing EventManager.notify.warning('unnamed event in processEvent') - def restart(self): - if self.eventQueue == None: + if self.eventQueue is None: self.eventQueue = EventQueue.getGlobalEventQueue() - if self.eventHandler == None: + if self.eventHandler is None: if self.eventQueue == EventQueue.getGlobalEventQueue(): # If we are using the global event queue, then we also # want to use the global event handler. diff --git a/direct/src/showbase/FindCtaPaths.py b/direct/src/showbase/FindCtaPaths.py deleted file mode 100755 index 4832400b46..0000000000 --- a/direct/src/showbase/FindCtaPaths.py +++ /dev/null @@ -1,100 +0,0 @@ -"""This module is used only by the VR Studio programmers who are using -the ctattach tools. It is imported before any other package, and its -job is to figure out the correct paths to each of the packages. - -This module is not needed if you are not using ctattach; in this case -all of the Panda packages will be collected under a common directory, -which you will presumably have already on your PYTHONPATH. """ - -__all__ = ['deCygwinify', 'getPaths'] - -import os -import sys - -def deCygwinify(path): - if os.name in ['nt'] and path[0] == '/': - # On Windows, we may need to convert from a Cygwin-style path - # to a native Windows path. - - # Check for a case like /i/ or /p/: this converts - # to i:\ or p:\. - - dirs = path.split('/') - if len(dirs) > 2 and len(dirs[1]) == 1: - path = '%s:\%s' % (dirs[1], '\\'.join(dirs[2:])) - - else: - # Otherwise, prepend $PANDA_ROOT and flip the slashes. - pandaRoot = os.getenv('PANDA_ROOT') - if pandaRoot: - path = os.path.normpath(pandaRoot + path) - - return path - -def getPaths(): - """ - Add to sys.path the appropriate director(ies) to search for the - various Panda projects. Typically, these will all be in the same - directory (which is presumably already on sys.path), but if the VR - Studio ctattach tools are in use they could be scattered around in - several places. - """ - - ctprojs = os.getenv("CTPROJS") - if ctprojs: - # The CTPROJS environment variable is defined. We must be - # using the ctattach tools. In this case, we need to figure - # out the location of each of the separate trees, and put the - # parent directory of each one on sys.path. In many cases, - # these will all be siblings, so we filter out duplicate - # parent directories. - - print('Appending to sys.path based on $CTPROJS:') - - # First, get the list of packages, then reverse the list to - # put it in ctattach order. (The reversal may not matter too - # much these days, but let's be as correct as we can be.) - packages = [] - for proj in ctprojs.split(): - projName = proj.split(':')[0] - packages.append(projName) - packages.reverse() - - # Now walk through the packages and figure out the parent of - # each referenced directory. - - parents = [] - for package in packages: - tree = os.getenv(package) - if not tree: - print(" CTPROJS contains %s, but $%s is not defined." % (package, package)) - sys.exit(1) - - tree = deCygwinify(tree) - - parent, base = os.path.split(tree) - if base != package.lower(): - print(" Warning: $%s refers to a directory named %s (instead of %s)" % (package, base, package.lower())) - - if parent not in parents: - parents.append(parent) - - - # We also put tree/built/lib on sys.path by hand, because we - # will need to load up the generated C++ modules that got - # put there. Also, we will find the output of genPyCode - # in $DIRECT/built/lib/pandac. - libdir = os.path.join(tree, 'built', 'lib') - if os.path.isdir(libdir): - if libdir not in sys.path: - sys.path.append(libdir) - - - # Now the result goes onto sys.path. - for parent in parents: - print(" %s" % (parent)) - if parent not in sys.path: - sys.path.append(parent) - - -getPaths() diff --git a/direct/src/showbase/Loader.py b/direct/src/showbase/Loader.py index 924b396616..988982ab28 100644 --- a/direct/src/showbase/Loader.py +++ b/direct/src/showbase/Loader.py @@ -27,6 +27,42 @@ class Loader(DirectObject): # This indicates that this class behaves like a Future. _asyncio_future_blocking = False + class ResultAwaiter(object): + """Reinvents generators because of PEP 479, sigh. See #513.""" + + __slots__ = 'requestList', 'index' + + def __init__(self, requestList): + self.requestList = requestList + self.index = 0 + + def __await__(self): + return self + + def __anext__(self): + if self.index >= len(self.requestList): + raise StopAsyncIteration + return self + + def __iter__(self): + return self + + def __next__(self): + i = self.index + request = self.requestList[i] + if not request.done(): + return request + + self.index = i + 1 + + result = request.result() + if isinstance(result, PandaNode): + result = NodePath(result) + + exc = StopIteration(result) + exc.value = result + raise exc + def __init__(self, loader, numObjects, gotList, callback, extraArgs): self._loader = loader self.objects = [None] * numObjects @@ -81,16 +117,14 @@ class Loader(DirectObject): def __await__(self): """ Returns a generator that raises StopIteration when the loading is complete. This allows this class to be used with 'await'.""" + if self.requests: self._asyncio_future_blocking = True - yield self - # This should be a simple return, but older versions of Python - # don't allow return statements with arguments. - result = self.result() - exc = StopIteration(result) - exc.value = result - raise exc + if self.gotList: + return self.ResultAwaiter([self]) + else: + return self.ResultAwaiter(self.requestList) def __aiter__(self): """ This allows using `async for` to iterate asynchronously over @@ -100,19 +134,7 @@ class Loader(DirectObject): requestList = self.requestList assert requestList is not None, "Request was cancelled." - class AsyncIter: - index = 0 - def __anext__(self): - if self.index < len(requestList): - i = self.index - self.index = i + 1 - return requestList[i] - else: - raise StopAsyncIteration - - iter = AsyncIter() - iter.objects = self.objects - return iter + return self.ResultAwaiter(requestList) # special methods def __init__(self, base): @@ -125,12 +147,28 @@ class Loader(DirectObject): Loader.loaderIndex += 1 self.accept(self.hook, self.__gotAsyncObject) + if ConfigVariableBool('loader-support-entry-points', True): + self._loadPythonFileTypes() + def destroy(self): self.ignore(self.hook) self.loader.stopThreads() del self.base del self.loader + def _loadPythonFileTypes(self): + import importlib + try: + pkg_resources = importlib.import_module('pkg_resources') + except ImportError: + pkg_resources = None + + if pkg_resources: + registry = LoaderFileTypeRegistry.getGlobalPtr() + + for entry_point in pkg_resources.iter_entry_points('panda3d.loaders'): + registry.register_deferred_type(entry_point) + # model loading funcs def loadModel(self, modelPath, loaderOptions = None, noCache = None, allowInstance = False, okMissing = None, @@ -192,7 +230,7 @@ class Loader(DirectObject): """ - assert Loader.notify.debug("Loading model: %s" % (modelPath)) + assert Loader.notify.debug("Loading model: %s" % (modelPath,)) if loaderOptions is None: loaderOptions = LoaderOptions() else: diff --git a/direct/src/showbase/Messenger.py b/direct/src/showbase/Messenger.py index 4ac1b96f56..f1b7bcc1a7 100644 --- a/direct/src/showbase/Messenger.py +++ b/direct/src/showbase/Messenger.py @@ -624,8 +624,9 @@ class Messenger: for key in list(acceptorDict.keys()): function, extraArgs, persistent = acceptorDict[key] object = self._getObject(key) - if (type(object) == types.InstanceType): - className = object.__class__.__name__ + objectClass = getattr(object, '__class__', None) + if objectClass: + className = objectClass.__name__ else: className = "Not a class" functionName = function.__name__ diff --git a/direct/src/showbase/PythonUtil.py b/direct/src/showbase/PythonUtil.py index 2cd4e62d15..fbef9ecc70 100644 --- a/direct/src/showbase/PythonUtil.py +++ b/direct/src/showbase/PythonUtil.py @@ -1130,6 +1130,10 @@ def weightedChoice(choiceList, rng=random.random, sum=None): """given a list of (weight, item) pairs, chooses an item based on the weights. rng must return 0..1. if you happen to have the sum of the weights, pass it in 'sum'.""" + # Throw an IndexError if we got an empty list. + if not choiceList: + raise IndexError('Cannot choose from an empty sequence') + # TODO: add support for dicts if sum is None: sum = 0. @@ -1138,6 +1142,7 @@ def weightedChoice(choiceList, rng=random.random, sum=None): rand = rng() accum = rand * sum + item = None for weight, item in choiceList: accum -= weight if accum <= 0.: @@ -1652,17 +1657,15 @@ def itype(obj): # version of type that gives more complete information about instance types global dtoolSuperBase t = type(obj) - if t is types.InstanceType: - return '%s of >' % (repr(types.InstanceType)[:-1], - str(obj.__class__)) + if sys.version_info < (3, 0) and t is types.InstanceType: + return ">" % (obj.__class__) else: # C++ object instances appear to be types via type() # check if this is a C++ object if dtoolSuperBase is None: _getDtoolSuperBase() if isinstance(obj, dtoolSuperBase): - return '%s of %s>' % (repr(types.InstanceType)[:-1], - str(obj.__class__)) + return "" % (obj.__class__) return t def deeptype(obj, maxLen=100, _visitedIds=None): @@ -2513,7 +2516,7 @@ class AlphabetCounter: index = -1 while True: curChar = self._curCounter[index] - if curChar is 'Z': + if curChar == 'Z': nextChar = 'A' carry = True else: diff --git a/direct/src/showbase/SfxPlayer.py b/direct/src/showbase/SfxPlayer.py index 4f139a89c9..c50c498236 100644 --- a/direct/src/showbase/SfxPlayer.py +++ b/direct/src/showbase/SfxPlayer.py @@ -53,6 +53,8 @@ class SfxPlayer: d = node.getDistance(listenerNode) else: d = node.getDistance(base.cam) + if not cutoff: + cutoff = self.cutoffDistance if d == None or d > cutoff: volume = 0 else: @@ -70,9 +72,6 @@ class SfxPlayer: self, sfx, looping = 0, interrupt = 1, volume = None, time = 0.0, node=None, listenerNode = None, cutoff = None): if sfx: - if not cutoff: - cutoff = self.cutoffDistance - self.setFinalVolume(sfx, node, volume, listenerNode, cutoff) # don't start over if it's already playing, unless diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index bff3b4652c..f9506074a9 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -46,7 +46,6 @@ if __debug__: from direct.showbase import GarbageReport from direct.directutil import DeltaProfiler from . import OnScreenDebug -from . import AppRunnerGlobal @atexit.register def exitfunc(): @@ -81,7 +80,7 @@ class ShowBase(DirectObject.DirectObject): ## This contains the global appRunner instance, as imported from ## AppRunnerGlobal. This will be None if we are not running in the ## runtime environment (ie. from a .p3d file). - self.appRunner = AppRunnerGlobal.appRunner + self.appRunner = None self.app_runner = self.appRunner #debug running multiplier @@ -207,7 +206,10 @@ class ShowBase(DirectObject.DirectObject): # Has the clusterSyncFlag been set via a config variable self.clusterSyncFlag = self.config.GetBool('cluster-sync', 0) - self.hidden = NodePath('hidden') + # We've already created aspect2d in ShowBaseGlobal, for the + # benefit of creating DirectGui elements before ShowBase. + from . import ShowBaseGlobal + self.hidden = ShowBaseGlobal.hidden ## The global graphics engine, ie. GraphicsEngine.getGlobalPtr() self.graphicsEngine = GraphicsEngine.getGlobalPtr() @@ -829,9 +831,11 @@ class ShowBase(DirectObject.DirectObject): win.requestProperties(props) mainWindow = False - if self.win == None: + if self.win is None: mainWindow = True self.win = win + if hasattr(self, 'bufferViewer'): + self.bufferViewer.win = win self.winList.append(win) @@ -1677,13 +1681,19 @@ class ShowBase(DirectObject.DirectObject): return self.mouseWatcherNode.getModifierButtons().isDown( KeyboardButton.meta()) - def attachInputDevice(self, device, prefix=None): + def attachInputDevice(self, device, prefix=None, watch=False): """ This function attaches an input device to the data graph, which will cause the device to be polled and generate events. If a prefix is given and not None, it is used to prefix events generated by this device, separated by a hyphen. + The watch argument can be set to True (as of Panda3D 1.10.3) to set up + the default MouseWatcher to receive inputs from this device, allowing + it to be polled via mouseWatcherNode and control user interfaces. + Setting this to True will also make it generate unprefixed events, + regardless of the specified prefix. + If you call this, you should consider calling detachInputDevice when you are done with the device or when it is disconnected. """ @@ -1694,13 +1704,17 @@ class ShowBase(DirectObject.DirectObject): idn = self.dataRoot.attachNewNode(InputDeviceNode(device, device.name)) # Setup the button thrower to generate events for the device. - bt = idn.attachNewNode(ButtonThrower(device.name)) - if prefix is not None: - bt.node().setPrefix(prefix + '-') + if prefix is not None or not watch: + bt = idn.attachNewNode(ButtonThrower(device.name)) + if prefix is not None: + bt.node().setPrefix(prefix + '-') + self.deviceButtonThrowers.append(bt) assert self.notify.debug("Attached input device {0} with prefix {1}".format(device, prefix)) self.__inputDeviceNodes[device] = idn - self.deviceButtonThrowers.append(bt) + + if watch: + idn.node().addChild(self.mouseWatcherNode) def detachInputDevice(self, device): """ @@ -2926,7 +2940,7 @@ class ShowBase(DirectObject.DirectObject): if not self.wxApp: # Create a new base.wxApp. - self.wxApp = wx.PySimpleApp(redirect = False) + self.wxApp = wx.App(redirect = False) if ConfigVariableBool('wx-main-loop', True): # Put wxPython in charge of the main loop. It really diff --git a/direct/src/showbase/ShowBaseGlobal.py b/direct/src/showbase/ShowBaseGlobal.py index 487524dd6a..dc5f4596fc 100644 --- a/direct/src/showbase/ShowBaseGlobal.py +++ b/direct/src/showbase/ShowBaseGlobal.py @@ -25,6 +25,7 @@ pandaSystem = PandaSystem.getGlobalPtr() # This is defined here so GUI elements can be instantiated before ShowBase. aspect2d = NodePath(PGTop("aspect2d")) +hidden = NodePath("hidden") # Set direct notify categories now that we have config directNotify.setDconfigLevels() @@ -46,6 +47,7 @@ if sys.version_info >= (3, 0): else: import __builtin__ as builtins builtins.inspect = inspect +del sys # this also appears in AIBaseGlobal if (not __debug__) and __dev__: diff --git a/direct/src/showbase/TkGlobal.py b/direct/src/showbase/TkGlobal.py index 673d337e3c..e6cf9b7f84 100644 --- a/direct/src/showbase/TkGlobal.py +++ b/direct/src/showbase/TkGlobal.py @@ -12,6 +12,9 @@ else: if '_Pmw' in sys.modules: sys.modules['_Pmw'].__name__ = '_Pmw' +# Don't export this from the module. +del sys + # Hack to workaround broken Pmw.NoteBook in Python 3 def bordercolors(root, colorName): lightRGB = [] diff --git a/direct/src/showbase/Transitions.py b/direct/src/showbase/Transitions.py index 54d7bd2baa..e67ad68292 100644 --- a/direct/src/showbase/Transitions.py +++ b/direct/src/showbase/Transitions.py @@ -5,6 +5,7 @@ a particular color.""" __all__ = ['Transitions'] from panda3d.core import * +from direct.showbase import ShowBaseGlobal from direct.gui.DirectGui import DirectFrame from direct.gui import DirectGuiGlobals as DGG from direct.interval.LerpInterval import LerpColorScaleInterval, LerpColorInterval, LerpScaleInterval, LerpPosInterval @@ -73,7 +74,7 @@ class Transitions: # so that it will also obscure mouse events for objects # positioned behind it. self.fade = DirectFrame( - parent = hidden, + parent = ShowBaseGlobal.hidden, guiId = 'fade', relief = None, image = self.fadeModel, diff --git a/direct/src/showutil/TexMemWatcher.py b/direct/src/showutil/TexMemWatcher.py index 843395d58d..eb9a5b84cf 100644 --- a/direct/src/showutil/TexMemWatcher.py +++ b/direct/src/showutil/TexMemWatcher.py @@ -786,8 +786,9 @@ class TexMemWatcher(DirectObject): # Look for a single rectangular hole to hold this piece. tp = self.findHole(tr.area, tr.w, tr.h) if tp: - texCmp = cmp(tr.w, tr.h) - holeCmp = cmp(tp.p[1] - tp.p[0], tp.p[3] - tp.p[2]) + texCmp = (tr.w > tr.h) - (tr.w < tr.h) + holeCmp = ((tp.p[1] - tp.p[0]) > (tp.p[3] - tp.p[2])) \ + - ((tp.p[1] - tp.p[0]) < (tp.p[3] - tp.p[2])) if texCmp != 0 and holeCmp != 0 and texCmp != holeCmp: tp.rotated = True tr.placements = [tp] @@ -803,10 +804,11 @@ class TexMemWatcher(DirectObject): # in. tpList = self.findHolePieces(tr.area) if tpList: - texCmp = cmp(tr.w, tr.h) + texCmp = (tr.w > tr.h) - (tr.w < tr.h) tr.placements = tpList for tp in tpList: - holeCmp = cmp(tp.p[1] - tp.p[0], tp.p[3] - tp.p[2]) + holeCmp = ((tp.p[1] - tp.p[0]) > (tp.p[3] - tp.p[2])) \ + - ((tp.p[1] - tp.p[0]) < (tp.p[3] - tp.p[2])) if texCmp != 0 and holeCmp != 0 and texCmp != holeCmp: tp.rotated = True tp.setBitmasks(self.bitmasks) @@ -858,11 +860,11 @@ class TexMemWatcher(DirectObject): # we have to squish it? if tw < w: # We'd have to make it taller. - nh = min(area / tw, th) + nh = min(area // tw, th) th = nh elif th < h: # We'd have to make it narrower. - nw = min(area / th, tw) + nw = min(area // th, tw) tw = nw else: # Hey, we don't have to squish it after all! Just @@ -912,7 +914,7 @@ class TexMemWatcher(DirectObject): tpArea = (r - l) * (t - b) if tpArea >= area: # we're done. - shorten = (tpArea - area) / (r - l) + shorten = (tpArea - area) // (r - l) t -= shorten tp.p = (l, r, b, t) tp.area = (r - l) * (t - b) diff --git a/direct/src/stdpy/file.py b/direct/src/stdpy/file.py index 222739612a..394fde2436 100644 --- a/direct/src/stdpy/file.py +++ b/direct/src/stdpy/file.py @@ -83,6 +83,7 @@ def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, elif isinstance(file, strType): filename = core.Filename.fromOsSpecific(file) else: + # It's either a Filename object or an os.PathLike. # If a Filename is given, make a writable copy anyway. filename = core.Filename(file) diff --git a/direct/src/stdpy/threading.py b/direct/src/stdpy/threading.py index 45409f9e18..8903a2fbc0 100644 --- a/direct/src/stdpy/threading.py +++ b/direct/src/stdpy/threading.py @@ -201,17 +201,6 @@ class Lock(core.Mutex): def __init__(self, name = "PythonLock"): core.Mutex.__init__(self, name) - def acquire(self, blocking = True): - if blocking: - core.Mutex.acquire(self) - return True - else: - return core.Mutex.tryAcquire(self) - - __enter__ = acquire - - def __exit__(self, t, v, tb): - self.release() class RLock(core.ReMutex): """ This class provides a wrapper around Panda's ReMutex object. @@ -221,18 +210,6 @@ class RLock(core.ReMutex): def __init__(self, name = "PythonRLock"): core.ReMutex.__init__(self, name) - def acquire(self, blocking = True): - if blocking: - core.ReMutex.acquire(self) - return True - else: - return core.ReMutex.tryAcquire(self) - - __enter__ = acquire - - def __exit__(self, t, v, tb): - self.release() - class Condition(core.ConditionVarFull): """ This class provides a wrapper around Panda's ConditionVarFull diff --git a/direct/src/task/Task.py b/direct/src/task/Task.py index 8645e5bc43..031c403644 100644 --- a/direct/src/task/Task.py +++ b/direct/src/task/Task.py @@ -388,13 +388,10 @@ class TaskManager: def __setupTask(self, funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath): if isinstance(funcOrTask, AsyncTask): task = funcOrTask - elif hasattr(funcOrTask, '__call__'): - task = PythonTask(funcOrTask) - if name is None: - name = getattr(funcOrTask, '__qualname__', None) or \ - getattr(funcOrTask, '__name__', None) - elif hasattr(funcOrTask, 'cr_await') or type(funcOrTask) == types.GeneratorType: - # It's a coroutine, or something emulating one. + elif hasattr(funcOrTask, '__call__') or \ + hasattr(funcOrTask, 'cr_await') or \ + type(funcOrTask) == types.GeneratorType: + # It's a function, coroutine, or something emulating a coroutine. task = PythonTask(funcOrTask) if name is None: name = getattr(funcOrTask, '__qualname__', None) or \ diff --git a/direct/src/tkpanels/AnimPanel.py b/direct/src/tkpanels/AnimPanel.py index 8cee51e1a6..a8348c299d 100644 --- a/direct/src/tkpanels/AnimPanel.py +++ b/direct/src/tkpanels/AnimPanel.py @@ -9,13 +9,16 @@ __all__ = ['AnimPanel', 'ActorControl'] # Import Tkinter, Pmw, and the floater code from this directory tree. from direct.tkwidgets.AppShell import * from direct.showbase.TkGlobal import * -import Pmw, sys +import Pmw, sys, os from direct.task import Task +from panda3d.core import Filename, getModelPath if sys.version_info >= (3, 0): from tkinter.simpledialog import askfloat + from tkinter.filedialog import askopenfilename else: from tkSimpleDialog import askfloat + from tkFileDialog import askopenfilename FRAMES = 0 @@ -273,7 +276,7 @@ class AnimPanel(AppShell): title = 'Load Animation', parent = self.component('hull') ) - if (animFilename == ''): + if not animFilename: # no file selected, canceled return @@ -369,8 +372,9 @@ class AnimPanel(AppShell): def destroy(self): # First clean up taskMgr.remove(self.id + '_UpdateTask') - self.destroyCallBack() - self.destroyCallBack = None + if self.destroyCallBack is not None: + self.destroyCallBack() + self.destroyCallBack = None AppShell.destroy(self) class ActorControl(Pmw.MegaWidget): diff --git a/direct/src/tkpanels/MopathRecorder.py b/direct/src/tkpanels/MopathRecorder.py index e829ce233f..8ff2341865 100644 --- a/direct/src/tkpanels/MopathRecorder.py +++ b/direct/src/tkpanels/MopathRecorder.py @@ -4,6 +4,7 @@ __all__ = ['MopathRecorder'] # Import Tkinter, Pmw, and the dial code from this directory tree. from panda3d.core import * +from direct.showbase import ShowBaseGlobal from direct.showbase.DirectObject import DirectObject from direct.showbase.TkGlobal import * from direct.tkwidgets.AppShell import * @@ -852,7 +853,7 @@ class MopathRecorder(AppShell, DirectObject): if self.getVariable('Style', 'Marker').get(): self.playbackMarker.reparentTo(self.recorderNodePath) else: - self.playbackMarker.reparentTo(hidden) + self.playbackMarker.reparentTo(ShowBaseGlobal.hidden) def setNumSegs(self, value): self.numSegs = int(value) diff --git a/direct/src/tkpanels/ParticlePanel.py b/direct/src/tkpanels/ParticlePanel.py index 9c4ba56a79..b7a632f61a 100644 --- a/direct/src/tkpanels/ParticlePanel.py +++ b/direct/src/tkpanels/ParticlePanel.py @@ -96,22 +96,21 @@ class ParticlePanel(AppShell): ## MENUBAR ENTRIES ## # FILE MENU - # Get a handle on the file menu so commands can be inserted - # before quit item - fileMenu = self.menuBar.component('File-menu') - # MRM: Need to add load and save effects methods - fileMenu.insert_command( - fileMenu.index('Quit'), - label = 'Load Params', - command = self.loadParticleEffectFromFile) - fileMenu.insert_command( - fileMenu.index('Quit'), - label = 'Save Params', - command = self.saveParticleEffectToFile) - fileMenu.insert_command( - fileMenu.index('Quit'), - label = 'Print Params', - command = lambda s = self: s.particles.printParams()) + # Get a handle on the file menu, and delete the Quit item that AppShell + # created so we can add it back after adding the other items. + self.menuBar.deletemenuitems('File', 0, 0) + self.menuBar.addmenuitem('File', 'command', + label='Load Params', + command=self.loadParticleEffectFromFile) + self.menuBar.addmenuitem('File', 'command', + label='Save Params', + command=self.saveParticleEffectToFile) + self.menuBar.addmenuitem('File', 'command', + label='Print Params', + command=lambda s=self:s.particles.printParams()) + self.menuBar.addmenuitem('File', 'command', 'Quit this application', + label='Quit', + command=self.quit) # PARTICLE MANAGER MENU self.menuBar.addmenu('ParticleMgr', 'ParticleMgr Operations') @@ -215,23 +214,23 @@ class ParticlePanel(AppShell): ('System', 'Pool Size', 'Max number of simultaneous particles', self.setSystemPoolSize, - 1.0, 1.0), + 1.0, 2000000, 1.0), ('System', 'Birth Rate', 'Seconds between particle births', self.setSystemBirthRate, - 0.0, None), + 0.0, None, None), ('System', 'Litter Size', 'Number of particle created at each birth', self.setSystemLitterSize, - 1.0, 1.0), + 1.0, 0x7fffffff, 1.0), ('System', 'Litter Spread', 'Variation in litter size', self.setSystemLitterSpread, - 0.0, 1.0), + 0.0, 0x7fffffff, 1.0), ('System', 'Lifespan', 'Age in seconds at which the system (vs. particles) should die', self.setSystemLifespan, - 0.0, None) + 0.0, None, None) ) self.createFloaters(systemPage, systemFloaterDefs) @@ -270,27 +269,27 @@ class ParticlePanel(AppShell): ('Factory', 'Life Span', 'Average particle lifespan in seconds', self.setFactoryLifeSpan, - 0.0, None), + 0.0, None, None), ('Factory', 'Life Span Spread', 'Variation in lifespan', self.setFactoryLifeSpanSpread, - 0.0, None), + 0.0, None, None), ('Factory', 'Mass', 'Average particle mass', self.setFactoryParticleMass, - 0.001, None), + 0.001, None, None), ('Factory', 'Mass Spread', 'Variation in particle mass', self.setFactoryParticleMassSpread, - 0.0, None), + 0.0, None, None), ('Factory', 'Terminal Velocity', 'Cap on average particle velocity', self.setFactoryTerminalVelocity, - 0.0, None), + 0.0, None, None), ('Factory', 'Terminal Vel. Spread', 'Variation in terminal velocity', self.setFactoryTerminalVelocitySpread, - 0.0, None), + 0.0, None, None), ) self.createFloaters(factoryPage, factoryWidgets) @@ -967,19 +966,29 @@ class ParticlePanel(AppShell): def createFloaters(self, parent, widgetDefinitions): widgets = [] - for category, label, balloonHelp, command, min, resolution in widgetDefinitions: + for category, label, balloonHelp, command, min, max, resolution in widgetDefinitions: widgets.append( self.createFloater(parent, category, label, balloonHelp, - command, min, resolution) + command, min, max, resolution) ) return widgets def createFloater(self, parent, category, text, balloonHelp, - command = None, min = 0.0, resolution = None, - numDigits = 3, **kw): + command = None, min = 0.0, max = None, resolution = None, + numDigits = None, **kw): kw['text'] = text kw['min'] = min + if max is not None: + kw['max'] = max kw['resolution'] = resolution + if numDigits is None: + # If this is apparently an integer setting, show no decimals. + if resolution is not None and int(resolution) == resolution and \ + (min is None or int(min) == min) and \ + (max is None or int(max) == max): + numDigits = 0 + else: + numDigits = 3 kw['numDigits'] = numDigits widget = Floater.Floater(parent, **kw) # Do this after the widget so command isn't called on creation diff --git a/direct/src/tkwidgets/Tree.py b/direct/src/tkwidgets/Tree.py index ff8a273965..5674888c77 100644 --- a/direct/src/tkwidgets/Tree.py +++ b/direct/src/tkwidgets/Tree.py @@ -19,14 +19,14 @@ __all__ = ['TreeNode', 'TreeItem'] # - keep track of object ids to allow more careful cleaning # - optimize tree redraw after expand of subnode -import os +import os, sys from direct.showbase.TkGlobal import * from panda3d.core import * -# Initialize icon directory -ICONDIR = ConfigVariableSearchPath('model-path').findFile(Filename('icons')).toOsSpecific() -if not os.path.isdir(ICONDIR): - raise RuntimeError("can't find DIRECT icon directory (%s)" % repr(ICONDIR)) + +if sys.version_info < (3, 0): + FileNotFoundError = IOError + class TreeNode: @@ -70,14 +70,14 @@ class TreeNode: self.parent = None def geticonimage(self, name): - try: + if name in self.iconimages: return self.iconimages[name] - except KeyError: - pass - file, ext = os.path.splitext(name) - ext = ext or ".gif" - fullname = os.path.join(ICONDIR, file + ext) - image = PhotoImage(master=self.canvas, file=fullname) + + fn = Filename("icons", name) + if not fn.resolveFilename(getModelPath().value, "gif"): + raise FileNotFoundError("couldn't find \"%s\"" % (fn)) + + image = PhotoImage(master=self.canvas, file=fn.toOsSpecific()) self.iconimages[name] = image return image diff --git a/direct/src/tkwidgets/Valuator.py b/direct/src/tkwidgets/Valuator.py index 8b88d98680..4bc57c7896 100644 --- a/direct/src/tkwidgets/Valuator.py +++ b/direct/src/tkwidgets/Valuator.py @@ -7,6 +7,8 @@ from direct.showbase.TkGlobal import * from . import WidgetPropertiesDialog import Pmw from direct.directtools.DirectUtil import getTkColorString +from panda3d.core import Vec4 +import sys if sys.version_info >= (3, 0): from tkinter.colorchooser import askcolor @@ -655,25 +657,35 @@ def rgbPanel(nodePath, callback = None, style = 'mini'): pButton.pack(expand = 1, fill = BOTH) # Update menu - menu = vgp.component('menubar').component('Valuator Group-menu') + menubar = vgp.component('menubar') + menubar.deletemenuitems('Valuator Group', 1, 1) + # Some helper functions # Clear color - menu.insert_command(index = 1, label = 'Clear Color', - command = lambda: nodePath.clearColor()) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Clear Color', command=lambda: nodePath.clearColor()) # Set Clear Transparency - menu.insert_command(index = 2, label = 'Set Transparency', - command = lambda: nodePath.setTransparency(1)) - menu.insert_command( - index = 3, label = 'Clear Transparency', - command = lambda: nodePath.clearTransparency()) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Set Transparency', command=lambda: nodePath.setTransparency(1)) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Clear Transparency', command=lambda: nodePath.clearTransparency()) # System color picker - menu.insert_command(index = 4, label = 'Popup Color Picker', - command = popupColorPicker) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Popup Color Picker', command=popupColorPicker) - menu.insert_command(index = 5, label = 'Print to log', - command = printToLog) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Print to log', command=printToLog) + + menubar.addmenuitem( + 'Valuator Group', 'command', 'Dismiss Valuator Group panel', + label='Dismiss', command=vgp.destroy) def setNodePathColor(color): nodePath.setColor(color[0]/255.0, color[1]/255.0, @@ -723,18 +735,23 @@ def lightRGBPanel(light, style = 'mini'): # Update menu button vgp.component('menubar').component('Valuator Group-button')['text'] = ( 'Light Control Panel') + # Add a print button which will also serve as a color tile pButton = Button(vgp.interior(), text = 'Print to Log', bg = getTkColorString(initColor), command = printToLog) pButton.pack(expand = 1, fill = BOTH) + # Update menu - menu = vgp.component('menubar').component('Valuator Group-menu') + menubar = vgp.component('menubar') # System color picker - menu.insert_command(index = 4, label = 'Popup Color Picker', - command = popupColorPicker) - menu.insert_command(index = 5, label = 'Print to log', - command = printToLog) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Popup Color Picker', command=popupColorPicker) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Print to log', command=printToLog) + def setLightColor(color): light.setColor(Vec4(color[0]/255.0, color[1]/255.0, color[2]/255.0, color[3]/255.0)) diff --git a/direct/src/wxwidgets/WxPandaShell.py b/direct/src/wxwidgets/WxPandaShell.py index cae5fb9332..d6a1793684 100755 --- a/direct/src/wxwidgets/WxPandaShell.py +++ b/direct/src/wxwidgets/WxPandaShell.py @@ -202,15 +202,15 @@ class WxPandaShell(WxAppShell): """A step in the WX event loop. You can either call this yourself or use as task.""" while self.evtLoop.Pending(): self.evtLoop.Dispatch() - self.wxApp.ProcessIdle() + self.evtLoop.ProcessIdle() if task != None: return task.cont def appInit(self): """Overridden from WxAppShell.py.""" # Create a new event loop (to overide default wxEventLoop) - self.evtLoop = wx.EventLoop() - self.oldLoop = wx.EventLoop.GetActive() - wx.EventLoop.SetActive(self.evtLoop) + self.evtLoop = wx.GUIEventLoop() + self.oldLoop = wx.GUIEventLoop.GetActive() + wx.GUIEventLoop.SetActive(self.evtLoop) taskMgr.add(self.wxStep, "evtLoopTask") def onViewChange(self, evt, viewIdx): diff --git a/dmodels/src/level_editor/donaldsDockColors.txt b/dmodels/src/level_editor/donaldsDockColors.txt deleted file mode 100644 index d5c1c9c8f7..0000000000 --- a/dmodels/src/level_editor/donaldsDockColors.txt +++ /dev/null @@ -1,108 +0,0 @@ -corniceColor: Vec4(0.11, 0.80, 0.22, 1.0) -corniceColor: Vec4(0.21, 0.73, 0.31, 1.0) -corniceColor: Vec4(0.22, 0.96, 0.27, 1.0) -corniceColor: Vec4(0.57, 0.39, 0.34, 1.0) -corniceColor: Vec4(0.59, 1.00, 0.92, 1.0) -corniceColor: Vec4(0.66, 1.00, 0.86, 1.0) -corniceColor: Vec4(0.73, 0.51, 0.51, 1.0) -corniceColor: Vec4(0.75, 0.45, 0.45, 1.0) -corniceColor: Vec4(0.76, 0.57, 0.29, 1.0) -corniceColor: Vec4(0.76, 0.73, 0.64, 1.0) -corniceColor: Vec4(0.82, 0.91, 0.44, 1.0) -corniceColor: Vec4(0.90, 0.56, 0.34, 1.0) -corniceColor: Vec4(0.91, 0.54, 0.44, 1.0) -corniceColor: Vec4(0.91, 0.63, 0.44, 1.0) -corniceColor: Vec4(0.94, 0.35, 0.35, 1.0) -corniceColor: Vec4(0.94, 0.72, 0.66, 1.0) -corniceColor: Vec4(1.00, 0.42, 0.27, 1.0) -corniceColor: Vec4(1.00, 0.50, 0.38, 1.0) -corniceColor: Vec4(1.00, 0.69, 0.49, 1.0) -corniceColor: Vec4(1.00, 0.71, 0.27, 1.0) -corniceColor: Vec4(1.00, 0.84, 0.59, 1.0) -corniceColor: Vec4(1.00, 0.88, 0.80, 1.0) -corniceColor: Vec4(1.00, 0.92, 0.59, 1.0) -corniceColor: Vec4(1.00, 1.00, 0.59, 1.0) - -doorColor: Vec4(0.45, 0.53, 0.45, 1.0) -doorColor: Vec4(0.52, 0.61, 0.52, 1.0) -doorColor: Vec4(0.60, 0.67, 0.54, 1.0) -doorColor: Vec4(0.63, 0.48, 0.34, 1.0) -doorColor: Vec4(0.79, 0.47, 0.47, 1.0) -doorColor: Vec4(0.86, 0.48, 0.23, 1.0) -doorColor: Vec4(0.88, 0.43, 0.43, 1.0) -doorColor: Vec4(0.91, 0.34, 0.34, 1.0) -doorColor: Vec4(0.94, 0.70, 0.49, 1.0) -doorColor: Vec4(0.97, 0.55, 0.40, 1.0) -doorColor: Vec4(1.00, 0.59, 0.59, 1.0) - -wallColor: Vec4(0.17, 0.44, 0.28, 1.0) -wallColor: Vec4(0.17, 0.45, 0.23, 1.0) -wallColor: Vec4(0.36, 0.45, 0.22, 1.0) -wallColor: Vec4(0.38, 0.31, 0.19, 1.0) -wallColor: Vec4(0.38, 0.68, 0.46, 1.0) -wallColor: Vec4(0.42, 0.16, 0.16, 1.0) -wallColor: Vec4(0.42, 0.25, 0.25, 1.0) -wallColor: Vec4(0.44, 0.67, 0.45, 1.0) -wallColor: Vec4(0.45, 0.31, 0.17, 1.0) -wallColor: Vec4(0.48, 0.93, 0.74, 1.0) -wallColor: Vec4(0.52, 0.96, 0.79, 1.0) -wallColor: Vec4(0.57, 0.70, 0.35, 1.0) -wallColor: Vec4(0.63, 0.47, 0.24, 1.0) -wallColor: Vec4(0.63, 0.60, 0.40, 1.0) -wallColor: Vec4(0.64, 0.24, 0.32, 1.0) -wallColor: Vec4(0.71, 0.49, 0.35, 1.0) -wallColor: Vec4(0.75, 0.45, 0.45, 1.0) -wallColor: Vec4(0.81, 0.48, 0.48, 1.0) -wallColor: Vec4(0.87, 0.61, 0.61, 1.0) -wallColor: Vec4(0.87, 0.69, 0.42, 1.0) -wallColor: Vec4(0.91, 0.54, 0.54, 1.0) -wallColor: Vec4(0.92, 0.63, 0.42, 1.0) -wallColor: Vec4(0.93, 0.15, 0.15, 1.0) -wallColor: Vec4(1.00, 0.00, 0.00, 1.0) - -windowColor: Vec4(0.00, 0.53, 0.32, 1.0) -windowColor: Vec4(0.00, 0.61, 0.36, 1.0) -windowColor: Vec4(0.07, 0.47, 0.31, 1.0) -windowColor: Vec4(0.08, 0.47, 0.31, 1.0) -windowColor: Vec4(0.17, 0.64, 0.45, 1.0) -windowColor: Vec4(0.50, 0.38, 0.22, 1.0) -windowColor: Vec4(0.64, 0.93, 0.45, 1.0) -windowColor: Vec4(0.69, 0.44, 0.15, 1.0) -windowColor: Vec4(0.73, 0.46, 0.37, 1.0) -windowColor: Vec4(0.73, 0.63, 0.37, 1.0) -windowColor: Vec4(0.73, 0.63, 0.45, 1.0) -windowColor: Vec4(0.74, 0.55, 0.32, 1.0) -windowColor: Vec4(0.78, 0.49, 0.29, 1.0) -windowColor: Vec4(0.79, 0.47, 0.47, 1.0) -windowColor: Vec4(0.82, 0.82, 0.40, 1.0) -windowColor: Vec4(0.87, 0.55, 0.33, 1.0) -windowColor: Vec4(0.87, 0.65, 0.33, 1.0) -windowColor: Vec4(0.88, 0.43, 0.43, 1.0) -windowColor: Vec4(0.91, 0.34, 0.45, 1.0) -windowColor: Vec4(1.00, 0.50, 0.38, 1.0) -windowColor: Vec4(1.00, 0.63, 0.38, 1.0) - -propColor: Vec4(0.17, 0.44, 0.28, 1.0) -propColor: Vec4(0.17, 0.45, 0.23, 1.0) -propColor: Vec4(0.36, 0.45, 0.22, 1.0) -propColor: Vec4(0.38, 0.31, 0.19, 1.0) -propColor: Vec4(0.38, 0.68, 0.46, 1.0) -propColor: Vec4(0.42, 0.16, 0.16, 1.0) -propColor: Vec4(0.42, 0.25, 0.25, 1.0) -propColor: Vec4(0.44, 0.67, 0.45, 1.0) -propColor: Vec4(0.45, 0.31, 0.17, 1.0) -propColor: Vec4(0.48, 0.93, 0.74, 1.0) -propColor: Vec4(0.52, 0.96, 0.79, 1.0) -propColor: Vec4(0.57, 0.70, 0.35, 1.0) -propColor: Vec4(0.63, 0.47, 0.24, 1.0) -propColor: Vec4(0.63, 0.60, 0.40, 1.0) -propColor: Vec4(0.64, 0.24, 0.32, 1.0) -propColor: Vec4(0.71, 0.49, 0.35, 1.0) -propColor: Vec4(0.75, 0.45, 0.45, 1.0) -propColor: Vec4(0.81, 0.48, 0.48, 1.0) -propColor: Vec4(0.87, 0.61, 0.61, 1.0) -propColor: Vec4(0.87, 0.69, 0.42, 1.0) -propColor: Vec4(0.91, 0.54, 0.54, 1.0) -propColor: Vec4(0.92, 0.63, 0.42, 1.0) -propColor: Vec4(0.93, 0.15, 0.15, 1.0) -propColor: Vec4(1.00, 0.00, 0.00, 1.0) diff --git a/dmodels/src/level_editor/donaldsDockStyles.txt b/dmodels/src/level_editor/donaldsDockStyles.txt deleted file mode 100644 index 8e2d7a49b3..0000000000 --- a/dmodels/src/level_editor/donaldsDockStyles.txt +++ /dev/null @@ -1,64 +0,0 @@ -wallTexture: wall_md_blank_ur -wallColor: Vec4(0.417323, 0.15711, 0.15711, 1.0) -windowTexture: window_sm_square_ur -windowColor: Vec4(0.874016, 0.654655, 0.329041, 1.0) -corniceTexture: cornice_marble_ur -corniceColor: Vec4(0.76378, 0.572086, 0.287541, 1.0) - -wallTexture: wall_sm_wood_ur -wallColor: Vec4(0.874016, 0.610097, 0.610097, 1.0) -windowTexture: window_sm_shuttered_ur -windowColor: Vec4(0.874016, 0.548402, 0.329041, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_sm_wood_ur -wallColor: Vec4(0.913386, 0.540868, 0.540868, 1.0) -windowTexture: window_porthole_ur -windowColor: Vec4(0.0778138, 0.472441, 0.314961, 1.0) -corniceTexture: cornice_horizontal_ur -corniceColor: Vec4(1.0, 0.501961, 0.376471, 1.0) - -wallTexture: wall_sm_wood_ur -wallColor: Vec4(0.913386, 0.540868, 0.540868, 1.0) -windowTexture: window_porthole_ur -windowColor: Vec4(0.0778138, 0.472441, 0.314961, 1.0) -corniceTexture: cornice_shingles_ur -corniceColor: Vec4(0.732283, 0.511163, 0.511163, 1.0) - -wallTexture: wall_md_blank_ur -wallColor: Vec4(0.384314, 0.305635, 0.187618, 1.0) -windowTexture: window_sm_round_ur -windowColor: Vec4(0.779528, 0.489115, 0.293469, 1.0) -corniceTexture: cornice_dental_ur -corniceColor: Vec4(0.574803, 0.38771, 0.340374, 1.0) - -wallTexture: wall_bricks_dr -wallColor: Vec4(0.629921, 0.471823, 0.237147, 1.0) -windowTexture: window_sm_shuttered_ur -windowColor: Vec4(1.0, 0.627451, 0.376471, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_board_ur -wallColor: Vec4(0.929134, 0.153034, 0.153034, 1.0) -windowTexture: window_porthole_ur -windowColor: Vec4(0.0, 0.532747, 0.317894, 1.0) -corniceTexture: cornice_shingles_ur -corniceColor: Vec4(0.944882, 0.715146, 0.659565, 1.0) - -wallTexture: wall_lg_brick_ur -wallColor: Vec4(0.166003, 0.440945, 0.276671, 1.0) -windowTexture: window_md_curtains_ur -windowColor: Vec4(0.17258, 0.637795, 0.450208, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_board_ur -wallColor: Vec4(0.929134, 0.153034, 0.153034, 1.0) -windowTexture: window_porthole_ur -windowColor: Vec4(0.0, 0.532747, 0.317894, 1.0) -corniceTexture: None -corniceColor: None - - diff --git a/dmodels/src/level_editor/donalds_dock_layout.flt b/dmodels/src/level_editor/donalds_dock_layout.flt deleted file mode 100644 index 88ff832b95..0000000000 Binary files a/dmodels/src/level_editor/donalds_dock_layout.flt and /dev/null differ diff --git a/dmodels/src/level_editor/minniesMelodyLandColors.txt b/dmodels/src/level_editor/minniesMelodyLandColors.txt deleted file mode 100644 index a949502b4d..0000000000 --- a/dmodels/src/level_editor/minniesMelodyLandColors.txt +++ /dev/null @@ -1,61 +0,0 @@ -corniceColor: Vec4(0.43, 0.32, 0.85, 1.0) -corniceColor: Vec4(0.67, 0.40, 0.99, 1.0) -corniceColor: Vec4(0.68, 0.22, 0.80, 1.0) -corniceColor: Vec4(0.80, 0.22, 0.68, 1.0) -corniceColor: Vec4(0.81, 0.30, 0.51, 1.0) -corniceColor: Vec4(1.00, 1.00, 0.58, 1.0) - -doorColor: Vec4(0.42, 0.42, 0.85, 1.0) -doorColor: Vec4(0.45, 0.22, 0.60, 1.0) -doorColor: Vec4(0.49, 0.79, 1.00, 1.0) -doorColor: Vec4(0.56, 0.80, 0.65, 1.0) -doorColor: Vec4(0.59, 0.49, 1.00, 1.0) -doorColor: Vec4(0.64, 0.45, 0.92, 1.0) -doorColor: Vec4(0.65, 0.40, 0.82, 1.0) -doorColor: Vec4(0.69, 0.33, 0.69, 1.0) -doorColor: Vec4(0.80, 0.56, 0.65, 1.0) -doorColor: Vec4(0.87, 0.80, 0.47, 1.0) -doorColor: Vec4(0.97, 0.87, 0.47, 1.0) -doorColor: Vec4(1.00, 0.42, 0.85, 1.0) -doorColor: Vec4(1.00, 0.79, 0.49, 1.0) - -wallColor: Vec4(0.49, 0.69, 1.00, 1.0) -wallColor: Vec4(0.59, 0.75, 1.00, 1.0) -wallColor: Vec4(0.70, 0.76, 1.00, 1.0) -wallColor: Vec4(0.80, 0.47, 0.80, 1.0) -wallColor: Vec4(0.80, 0.80, 0.47, 1.0) -wallColor: Vec4(0.91, 0.91, 0.54, 1.0) -wallColor: Vec4(0.96, 0.96, 0.47, 1.0) -wallColor: Vec4(0.96, 0.96, 0.73, 1.0) -wallColor: Vec4(0.97, 0.36, 0.36, 1.0) -wallColor: Vec4(0.97, 0.47, 0.67, 1.0) -wallColor: Vec4(1.00, 0.70, 0.70, 1.0) -wallColor: Vec4(1.00, 1.00, 1.00, 1.0) - -windowColor: Vec4(0.42, 0.42, 0.85, 1.0) -windowColor: Vec4(0.45, 0.22, 0.60, 1.0) -windowColor: Vec4(0.49, 0.79, 1.00, 1.0) -windowColor: Vec4(0.56, 0.80, 0.65, 1.0) -windowColor: Vec4(0.59, 0.49, 1.00, 1.0) -windowColor: Vec4(0.64, 0.45, 0.92, 1.0) -windowColor: Vec4(0.65, 0.40, 0.82, 1.0) -windowColor: Vec4(0.69, 0.33, 0.69, 1.0) -windowColor: Vec4(0.80, 0.45, 0.71, 1.0) -windowColor: Vec4(0.80, 0.56, 0.65, 1.0) -windowColor: Vec4(0.97, 0.87, 0.47, 1.0) -windowColor: Vec4(1.00, 0.79, 0.49, 1.0) - -propColor: Vec4(0.49, 0.69, 1.00, 1.0) -propColor: Vec4(0.59, 0.75, 1.00, 1.0) -propColor: Vec4(0.70, 0.76, 1.00, 1.0) -propColor: Vec4(0.80, 0.47, 0.80, 1.0) -propColor: Vec4(0.80, 0.80, 0.47, 1.0) -propColor: Vec4(0.91, 0.91, 0.54, 1.0) -propColor: Vec4(0.96, 0.96, 0.47, 1.0) -propColor: Vec4(0.96, 0.96, 0.73, 1.0) -propColor: Vec4(0.97, 0.36, 0.36, 1.0) -propColor: Vec4(0.97, 0.47, 0.67, 1.0) -propColor: Vec4(1.00, 0.70, 0.70, 1.0) -propColor: Vec4(1.00, 1.00, 1.00, 1.0) -propColor: Vec4(0.92, 0.92, 0.92, 1.0) -wallColor: Vec4(0.97, 0.58, 0.55, 1.0) diff --git a/dmodels/src/level_editor/minniesMelodyLandStyles.txt b/dmodels/src/level_editor/minniesMelodyLandStyles.txt deleted file mode 100644 index 4786a8de56..0000000000 --- a/dmodels/src/level_editor/minniesMelodyLandStyles.txt +++ /dev/null @@ -1,79 +0,0 @@ -wallTexture: wall_md_bricks_ur -wallColor: Vec4(0.59, 0.75, 1.00, 1.0) -windowTexture: window_sm_round_ur -windowColor: Vec4(0.45, 0.22, 0.60, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_blank_ur -wallColor: Vec4(0.91, 0.91, 0.54, 1.0) -windowTexture: window_sm_square_ur -windowColor: Vec4(0.65, 0.40, 0.82, 1.0) -corniceTexture: cornice_curved_ur -corniceColor: Vec4(0.68, 0.22, 0.80, 1.0) - -wallTexture: wall_sm_brick_pink_ur -wallColor: Vec4(1.00, 1.00, 1.00, 1.0) -windowTexture: window_sm_curved_ur -windowColor: Vec4(0.49, 0.79, 1.00, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_lg_rock_ur -wallColor: Vec4(0.80, 0.47, 0.80, 1.0) -windowTexture: window_sm_square_ur -windowColor: Vec4(0.80, 0.56, 0.65, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_lg_rock_ur -wallColor: Vec4(0.80, 0.80, 0.47, 1.0) -windowTexture: window_sm_square_ur -windowColor: Vec4(0.56, 0.80, 0.65, 1.0) -corniceTexture: cornice_marble_ur -corniceColor: Vec4(0.80, 0.22, 0.68, 1.0) - -wallTexture: wall_md_blank_ur -wallColor: Vec4(0.49, 0.69, 1.00, 1.0) -windowTexture: window_sm_curved_ur -windowColor: Vec4(0.59, 0.49, 1.00, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_bricks_ur -wallColor: Vec4(1.00, 0.70, 0.70, 1.0) -windowTexture: window_md_curved_ur -windowColor: Vec4(1.00, 0.79, 0.49, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_blank_ur -wallColor: Vec4(0.96, 0.96, 0.47, 1.0) -windowTexture: window_sm_round_ur -windowColor: Vec4(0.69, 0.33, 0.69, 1.0) -corniceTexture: cornice_brick_ur -corniceColor: Vec4(0.81, 0.30, 0.51, 1.0) - -wallTexture: wall_md_bricks_ur -wallColor: Vec4(0.70, 0.76, 1.00, 1.0) -windowTexture: window_porthole_ur -windowColor: Vec4(0.42, 0.42, 0.85, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_lg_brick_ur -wallColor: Vec4(0.97, 0.47, 0.67, 1.0) -windowTexture: window_md_curtains_ur -windowColor: Vec4(0.64, 0.45, 0.92, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_blank_ur -wallColor: Vec4(0.97, 0.36, 0.36, 1.0) -windowTexture: window_sm_curved_ur -windowColor: Vec4(0.97, 0.87, 0.47, 1.0) -corniceTexture: cornice_curved_ur -corniceColor: Vec4(0.43, 0.32, 0.85, 1.0) - - - diff --git a/dmodels/src/level_editor/minnies_melody_land_layout.flt b/dmodels/src/level_editor/minnies_melody_land_layout.flt deleted file mode 100644 index 4859de3db6..0000000000 Binary files a/dmodels/src/level_editor/minnies_melody_land_layout.flt and /dev/null differ diff --git a/dmodels/src/level_editor/theBurrrghColors.txt b/dmodels/src/level_editor/theBurrrghColors.txt deleted file mode 100644 index bb3e20c21e..0000000000 --- a/dmodels/src/level_editor/theBurrrghColors.txt +++ /dev/null @@ -1,65 +0,0 @@ -corniceColor: Vec4(0.00, 0.34, 0.58, 1.0) -corniceColor: Vec4(0.34, 0.90, 0.87, 1.0) -corniceColor: Vec4(0.41, 0.58, 0.85, 1.0) -corniceColor: Vec4(0.42, 0.98, 0.74, 1.0) -corniceColor: Vec4(1.00, 1.00, 1.00, 1.0) - -doorColor: Vec4(0.00, 0.87, 0.69, 1.0) -doorColor: Vec4(0.22, 0.83, 0.83, 1.0) -doorColor: Vec4(0.23, 0.71, 0.84, 1.0) -doorColor: Vec4(0.23, 0.85, 0.60, 1.0) -doorColor: Vec4(0.24, 0.76, 0.89, 1.0) -doorColor: Vec4(0.27, 1.00, 0.85, 1.0) -doorColor: Vec4(0.38, 0.63, 1.00, 1.0) -doorColor: Vec4(0.49, 0.69, 1.00, 1.0) -doorColor: Vec4(0.49, 0.89, 1.00, 1.0) -doorColor: Vec4(0.50, 0.64, 0.85, 1.0) -doorColor: Vec4(0.59, 1.00, 0.92, 1.0) -doorColor: Vec4(1.00, 1.00, 1.00, 1.0) - -wallColor: Vec4(0.00, 0.87, 0.69, 1.0) -wallColor: Vec4(0.22, 0.83, 0.83, 1.0) -wallColor: Vec4(0.23, 0.85, 0.60, 1.0) -wallColor: Vec4(0.24, 0.76, 0.89, 1.0) -wallColor: Vec4(0.25, 0.64, 0.91, 1.0) -wallColor: Vec4(0.27, 1.00, 0.85, 1.0) -wallColor: Vec4(0.38, 0.63, 1.00, 1.0) -wallColor: Vec4(0.49, 0.69, 1.00, 1.0) -wallColor: Vec4(0.49, 0.89, 1.00, 1.0) -wallColor: Vec4(0.50, 0.64, 0.85, 1.0) -wallColor: Vec4(0.59, 1.00, 0.92, 1.0) -wallColor: Vec4(1.00, 1.00, 1.00, 1.0) - -windowColor: Vec4(0.00, 0.69, 0.69, 1.0) -windowColor: Vec4(0.00, 0.87, 0.69, 1.0) -windowColor: Vec4(0.14, 0.42, 0.85, 1.0) -windowColor: Vec4(0.16, 0.67, 1.00, 1.0) -windowColor: Vec4(0.20, 0.53, 0.46, 1.0) -windowColor: Vec4(0.22, 0.83, 0.83, 1.0) -windowColor: Vec4(0.23, 0.71, 0.84, 1.0) -windowColor: Vec4(0.23, 0.85, 0.60, 1.0) -windowColor: Vec4(0.24, 0.76, 0.89, 1.0) -windowColor: Vec4(0.27, 1.00, 0.85, 1.0) -windowColor: Vec4(0.38, 0.63, 1.00, 1.0) -windowColor: Vec4(0.38, 0.87, 1.00, 1.0) -windowColor: Vec4(0.49, 0.69, 1.00, 1.0) -windowColor: Vec4(0.49, 0.89, 1.00, 1.0) -windowColor: Vec4(0.49, 1.00, 1.00, 1.0) -windowColor: Vec4(0.50, 0.64, 0.85, 1.0) -windowColor: Vec4(0.59, 0.85, 0.69, 1.0) -windowColor: Vec4(0.59, 0.92, 0.61, 1.0) -windowColor: Vec4(0.59, 1.00, 0.92, 1.0) -windowColor: Vec4(1.00, 1.00, 1.00, 1.0) - -propColor: Vec4(0.00, 0.87, 0.69, 1.0) -propColor: Vec4(0.22, 0.83, 0.83, 1.0) -propColor: Vec4(0.23, 0.85, 0.60, 1.0) -propColor: Vec4(0.24, 0.76, 0.89, 1.0) -propColor: Vec4(0.25, 0.64, 0.91, 1.0) -propColor: Vec4(0.27, 1.00, 0.85, 1.0) -propColor: Vec4(0.38, 0.63, 1.00, 1.0) -propColor: Vec4(0.49, 0.69, 1.00, 1.0) -propColor: Vec4(0.49, 0.89, 1.00, 1.0) -propColor: Vec4(0.50, 0.64, 0.85, 1.0) -propColor: Vec4(0.59, 1.00, 0.92, 1.0) -propColor: Vec4(1.00, 1.00, 1.00, 1.0) diff --git a/dmodels/src/level_editor/theBurrrghStyles.txt b/dmodels/src/level_editor/theBurrrghStyles.txt deleted file mode 100644 index f4cff44b35..0000000000 --- a/dmodels/src/level_editor/theBurrrghStyles.txt +++ /dev/null @@ -1,90 +0,0 @@ -wallTexture: wall_lg_brick_ur -wallColor: Vec4(0.25, 0.64, 0.91, 1.0) -windowTexture: window_sm_round_ur -windowColor: Vec4(0.23, 0.71, 0.84, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_blank_ur -wallColor: Vec4(0.00, 0.87, 0.69, 1.0) -windowTexture: window_sm_round_ur -windowColor: Vec4(0.23, 0.71, 0.84, 1.0) -corniceTexture: cornice_curved_ur -corniceColor: Vec4(0.00, 0.34, 0.58, 1.0) - -wallTexture: wall_md_blank_ur -wallColor: Vec4(0.49, 0.69, 1.00, 1.0) -windowTexture: window_sm_curved_ur -windowColor: Vec4(0.20, 0.53, 0.46, 1.0) -corniceTexture: cornice_ice_ur -corniceColor: Vec4(1.00, 1.00, 1.00, 1.0) - -wallTexture: wall_sm_brick_blue_ur -wallColor: Vec4(1.00, 1.00, 1.00, 1.0) -windowTexture: window_sm_square_ur -windowColor: Vec4(0.49, 1.00, 1.00, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_blank_ur -wallColor: Vec4(0.27, 1.00, 0.85, 1.0) -windowTexture: window_sm_square_ur -windowColor: Vec4(0.49, 1.00, 1.00, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_blank_ur -wallColor: Vec4(0.49, 0.89, 1.00, 1.0) -windowTexture: window_sm_pointed_ur -windowColor: Vec4(0.00, 0.69, 0.69, 1.0) -corniceTexture: cornice_ice_ur -corniceColor: Vec4(1.00, 1.00, 1.00, 1.0) - -wallTexture: wall_md_bricks_ur -wallColor: Vec4(0.50, 0.64, 0.85, 1.0) -windowTexture: window_porthole_ur -windowColor: Vec4(0.14, 0.42, 0.85, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_blank_ur -wallColor: Vec4(0.38, 0.63, 1.00, 1.0) -windowTexture: window_porthole_ur -windowColor: Vec4(0.14, 0.42, 0.85, 1.0) -corniceTexture: cornice_dental_ur -corniceColor: Vec4(0.41, 0.58, 0.85, 1.0) - -wallTexture: wall_bricks_ur -wallColor: Vec4(0.23, 0.85, 0.60, 1.0) -windowTexture: window_sm_square_ur -windowColor: Vec4(0.59, 0.85, 0.69, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_bricks_ur -wallColor: Vec4(0.59, 1.00, 0.92, 1.0) -windowTexture: window_md_curved_ur -windowColor: Vec4(0.16, 0.67, 1.00, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_blank_ur -wallColor: Vec4(0.22, 0.83, 0.83, 1.0) -windowTexture: window_sm_curved_ur -windowColor: Vec4(0.38, 0.87, 1.00, 1.0) -corniceTexture: cornice_ice_ur -corniceColor: Vec4(1.00, 1.00, 1.00, 1.0) - -wallTexture: wall_sm_cement_blue_ur -wallColor: Vec4(1.00, 1.00, 1.00, 1.0) -windowTexture: window_sm_round_ur -windowColor: Vec4(0.59, 0.92, 0.61, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_bricks_ur -wallColor: Vec4(0.24, 0.76, 0.89, 1.0) -windowTexture: window_sm_round_ur -windowColor: Vec4(0.59, 0.92, 0.61, 1.0) -corniceTexture: None -corniceColor: None diff --git a/dmodels/src/level_editor/the_burrrgh_layout.flt b/dmodels/src/level_editor/the_burrrgh_layout.flt deleted file mode 100644 index f2aaabf601..0000000000 Binary files a/dmodels/src/level_editor/the_burrrgh_layout.flt and /dev/null differ diff --git a/dmodels/src/level_editor/toontownCentralColors.txt b/dmodels/src/level_editor/toontownCentralColors.txt deleted file mode 100644 index 68fd3cdc23..0000000000 --- a/dmodels/src/level_editor/toontownCentralColors.txt +++ /dev/null @@ -1,106 +0,0 @@ -corniceColor: Vec4(0.11, 0.80, 0.22, 1.0) -corniceColor: Vec4(0.21, 0.73, 0.31, 1.0) -corniceColor: Vec4(0.22, 0.96, 0.27, 1.0) -corniceColor: Vec4(0.27, 0.77, 0.42, 1.0) -corniceColor: Vec4(0.32, 0.54, 0.36, 1.0) -corniceColor: Vec4(0.44, 0.91, 0.63, 1.0) -corniceColor: Vec4(0.76, 0.73, 0.64, 1.0) -corniceColor: Vec4(0.79, 0.36, 0.19, 1.0) -corniceColor: Vec4(0.82, 0.91, 0.44, 1.0) -corniceColor: Vec4(0.90, 0.56, 0.34, 1.0) -corniceColor: Vec4(0.91, 0.54, 0.44, 1.0) -corniceColor: Vec4(0.91, 0.63, 0.44, 1.0) -corniceColor: Vec4(0.94, 0.35, 0.35, 1.0) -corniceColor: Vec4(0.94, 0.71, 0.66, 1.0) -corniceColor: Vec4(0.98, 0.58, 0.42, 1.0) -corniceColor: Vec4(1.00, 0.42, 0.27, 1.0) -corniceColor: Vec4(1.00, 0.56, 0.21, 1.0) -corniceColor: Vec4(1.00, 0.69, 0.49, 1.0) -corniceColor: Vec4(1.00, 0.71, 0.27, 1.0) -corniceColor: Vec4(1.00, 0.75, 0.38, 1.0) -corniceColor: Vec4(1.00, 0.84, 0.59, 1.0) -corniceColor: Vec4(1.00, 0.88, 0.80, 1.0) -corniceColor: Vec4(1.00, 0.92, 0.59, 1.0) -corniceColor: Vec4(1.00, 1.00, 0.59, 1.0) - -doorColor: Vec4(0.75, 0.79, 0.55, 1.0) -doorColor: Vec4(0.81, 0.61, 0.48, 1.0) -doorColor: Vec4(1.00, 0.59, 0.59, 1.0) -doorColor: Vec4(1.00, 0.63, 0.38, 1.0) -doorColor: Vec4(1.00, 0.87, 0.38, 1.0) -doorColor: Vec4(1.00, 0.88, 0.56, 1.0) -doorColor: Vec4(1.00, 0.88, 0.56, 1.0) - -wallColor: Vec4(0.11, 0.80, 0.22, 1.0) -wallColor: Vec4(0.21, 0.73, 0.31, 1.0) -wallColor: Vec4(0.22, 0.96, 0.27, 1.0) -wallColor: Vec4(0.24, 0.79, 0.32, 1.0) -wallColor: Vec4(0.25, 0.65, 0.29, 1.0) -wallColor: Vec4(0.49, 0.96, 0.42, 1.0) -wallColor: Vec4(0.76, 0.73, 0.64, 1.0) -wallColor: Vec4(0.82, 0.91, 0.44, 1.0) -wallColor: Vec4(0.90, 0.56, 0.34, 1.0) -wallColor: Vec4(0.91, 0.54, 0.44, 1.0) -wallColor: Vec4(0.91, 0.63, 0.44, 1.0) -wallColor: Vec4(0.94, 0.35, 0.35, 1.0) -wallColor: Vec4(0.95, 0.89, 0.17, 1.0) -wallColor: Vec4(0.99, 0.67, 0.25, 1.0) -wallColor: Vec4(0.99, 0.69, 0.49, 1.0) -wallColor: Vec4(0.99, 0.89, 0.49, 1.0) -wallColor: Vec4(1.00, 0.42, 0.27, 1.0) -wallColor: Vec4(1.00, 0.67, 0.59, 1.0) -wallColor: Vec4(1.00, 0.69, 0.49, 1.0) -wallColor: Vec4(1.00, 0.71, 0.27, 1.0) -wallColor: Vec4(1.00, 0.84, 0.59, 1.0) -wallColor: Vec4(1.00, 0.88, 0.80, 1.0) -wallColor: Vec4(1.00, 0.90, 0.33, 1.0) -wallColor: Vec4(1.00, 0.92, 0.59, 1.0) -wallColor: Vec4(1.00, 1.00, 0.59, 1.0) - -windowColor: Vec4(0.10, 0.59, 0.39, 1.0) -windowColor: Vec4(0.11, 0.67, 0.33, 1.0) -windowColor: Vec4(0.13, 0.78, 0.52, 1.0) -windowColor: Vec4(0.14, 0.53, 0.30, 1.0) -windowColor: Vec4(0.27, 1.00, 0.42, 1.0) -windowColor: Vec4(0.31, 0.63, 0.37, 1.0) -windowColor: Vec4(0.32, 0.54, 0.36, 1.0) -windowColor: Vec4(0.32, 0.67, 0.60, 1.0) -windowColor: Vec4(0.40, 0.61, 0.67, 1.0) -windowColor: Vec4(0.59, 1.00, 0.49, 1.0) -windowColor: Vec4(0.88, 0.44, 0.15, 1.0) -windowColor: Vec4(0.91, 0.63, 0.44, 1.0) -windowColor: Vec4(0.95, 0.89, 0.17, 1.0) -windowColor: Vec4(0.99, 0.99, 0.49, 1.0) -windowColor: Vec4(1.00, 0.27, 0.27, 1.0) -windowColor: Vec4(1.00, 0.50, 0.38, 1.0) -windowColor: Vec4(1.00, 0.59, 0.59, 1.0) -windowColor: Vec4(1.00, 0.63, 0.30, 1.0) -windowColor: Vec4(1.00, 0.63, 0.38, 1.0) -windowColor: Vec4(1.00, 0.87, 0.38, 1.0) -windowColor: Vec4(1.00, 0.92, 0.59, 1.0) - -propColor: Vec4(0.11, 0.80, 0.22, 1.0) -propColor: Vec4(0.21, 0.73, 0.31, 1.0) -propColor: Vec4(0.22, 0.96, 0.27, 1.0) -propColor: Vec4(0.24, 0.79, 0.32, 1.0) -propColor: Vec4(0.25, 0.65, 0.29, 1.0) -propColor: Vec4(0.49, 0.96, 0.42, 1.0) -propColor: Vec4(0.76, 0.73, 0.64, 1.0) -propColor: Vec4(0.82, 0.91, 0.44, 1.0) -propColor: Vec4(0.90, 0.56, 0.34, 1.0) -propColor: Vec4(0.91, 0.54, 0.44, 1.0) -propColor: Vec4(0.91, 0.63, 0.44, 1.0) -propColor: Vec4(0.94, 0.35, 0.35, 1.0) -propColor: Vec4(0.95, 0.89, 0.17, 1.0) -propColor: Vec4(0.99, 0.67, 0.25, 1.0) -propColor: Vec4(0.99, 0.69, 0.49, 1.0) -propColor: Vec4(0.99, 0.89, 0.49, 1.0) -propColor: Vec4(1.00, 0.42, 0.27, 1.0) -propColor: Vec4(1.00, 0.67, 0.59, 1.0) -propColor: Vec4(1.00, 0.69, 0.49, 1.0) -propColor: Vec4(1.00, 0.71, 0.27, 1.0) -propColor: Vec4(1.00, 0.84, 0.59, 1.0) -propColor: Vec4(1.00, 0.88, 0.80, 1.0) -propColor: Vec4(1.00, 0.90, 0.33, 1.0) -propColor: Vec4(1.00, 0.92, 0.59, 1.0) -propColor: Vec4(1.00, 1.00, 0.59, 1.0) diff --git a/dmodels/src/level_editor/toontownCentralStyles.txt b/dmodels/src/level_editor/toontownCentralStyles.txt deleted file mode 100644 index 6b39d3adee..0000000000 --- a/dmodels/src/level_editor/toontownCentralStyles.txt +++ /dev/null @@ -1,84 +0,0 @@ -wallTexture: wall_md_pillars_ur -wallColor: Vec4(1.0, 0.917, 0.592, 1.0) -windowTexture: window_sm_pointed_ur -windowColor: Vec4(0.396, 0.611, 0.666, 1.0) -corniceTexture: cornice_stone_ur -corniceColor: Vec4(1.0, 1.0, 0.592, 1.0) - -wallTexture: wall_md_pillars_ur -wallColor: Vec4(1.0, 1.0, 0.592157, 1.0) -windowTexture: window_sm_pointed_ur -windowColor: Vec4(0.142751, 0.527559, 0.295847, 1.0) -corniceTexture: cornice_stone_ur -corniceColor: Vec4(1.0, 1.0, 0.592157, 1.0) - -wallTexture: wall_lg_brick_ur -wallColor: Vec4(1.0, 0.415686, 0.270588, 1.0) -windowTexture: window_porthole_ur -windowColor: Vec4(0.306315, 0.627451, 0.370542, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_sm_cement_ur -wallColor: Vec4(1.0, 0.882353, 0.803922, 1.0) -windowTexture: window_porthole_ur -windowColor: Vec4(0.0972673, 0.590551, 0.393701, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_dental_ur -wallColor: Vec4(0.996078, 0.894118, 0.486275, 1.0) -windowTexture: window_md_curved_ur -windowColor: Vec4(1.0, 0.87451, 0.376471, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_pillars_ur -wallColor: Vec4(0.996078, 0.690196, 0.486275, 1.0) -windowTexture: window_porthole_ur -windowColor: Vec4(0.31706, 0.535433, 0.361155, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_pillars_ur -wallColor: Vec4(0.996078, 0.690196, 0.486275, 1.0) -windowTexture: window_porthole_ur -windowColor: Vec4(0.31706, 0.535433, 0.361155, 1.0) -corniceTexture: cornice_brick_ur -corniceColor: Vec4(0.31706, 0.535433, 0.361155, 1.0) - -wallTexture: wall_lg_brick_ur -wallColor: Vec4(0.996078, 0.690196, 0.486275, 1.0) -windowTexture: window_sm_curved_ur -windowColor: Vec4(0.996078, 0.996078, 0.486275, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_blank_ur -wallColor: Vec4(1.0, 0.415686, 0.270588, 1.0) -windowTexture: window_sm_curved_ur -windowColor: Vec4(1.0, 0.87451, 0.376471, 1.0) -corniceTexture: cornice_marble_ur -corniceColor: Vec4(1.0, 0.74902, 0.376471, 1.0) - -wallTexture: wall_sm_brick_ur -wallColor: Vec4(1.0, 0.67451, 0.592157, 1.0) -windowTexture: window_sm_pointed_ur -windowColor: Vec4(0.88189, 0.439216, 0.145252, 1.0) -corniceTexture: None -corniceColor: None - -wallTexture: wall_md_blank_ur -wallColor: Vec4(1.0, 0.705882, 0.270588, 1.0) -windowTexture: window_sm_pointed_ur -windowColor: Vec4(0.110236, 0.669291, 0.333333, 1.0) -corniceTexture: cornice_stone_ur -corniceColor: Vec4(0.944882, 0.711441, 0.559518, 1.0) - -wallTexture: wall_md_dental_ur -wallColor: Vec4(0.909804, 0.630415, 0.444156, 1.0) -windowTexture: window_sm_round_ur -windowColor: Vec4(1.0, 0.270588, 0.270588, 1.0) -corniceTexture: None -corniceColor: None - diff --git a/dmodels/src/level_editor/toontown_central_layout.flt b/dmodels/src/level_editor/toontown_central_layout.flt deleted file mode 100644 index 51096d15f8..0000000000 Binary files a/dmodels/src/level_editor/toontown_central_layout.flt and /dev/null differ diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index 062f129ebd..56b4b0d49d 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -1,3 +1,141 @@ +------------------------ RELEASE 1.10.4.1 --------------------- + +This release fixes only one critical regression: calling destroy() +on a DirectGUI item would cause an exception. + +------------------------ RELEASE 1.10.4 ----------------------- + +This release fixes a regression with DirectScrolledList in 1.10.3, +fixes various other bugs, and introduces a few minor features. + +* Fix exception trying to create DirectScrolledList +* Fix flickering in DirectScrolledFrame and other scissor issues (#681) +* Experimental support for Python 3.8 +* Support adding icons to deployed applications +* Support non-affine (eg. projective) transforms in calc_tight_bounds +* Allow setting notify-output after initial import +* Fix macOS issue locating Panda3D using Python 2.7.13+ from python.org +* Support for Maya 2019 +* On Windows, pip is now installed by the installer (#690) +* Fix Actor.makeSubpart on models with pre-bound animations (#647) +* Properly interrupt task manager if first task chain raises error (#692) +* Fix return value of encrypt_string in Python 3 (#684) +* Support writing loader plug-ins in Python +* Fix reading multiple p3d_TextureMatrix[] values from GLSL shaders +* Fix shader error flag not being set if GLSL compilation failed (#622) +* Add NodePath.replace_texture() convenience method +* Fix deadlock when building with SIMPLE_THREADS=1 (#704) +* Fix DirectOptionMenu cancelFrame not working inside scrolled frame (#658) +* Fix assertion when calling analyze() on geometry with strip cut index +* Implement fallback in GL renderer when F_sluminance is not supported (#693) +* Set reasonable limits for sliders in ParticlePanel +* Fix for DirectEntry autoCapitalize feature on Python 3 (#628) +* Fix various DirectGUI items not working before ShowBase is instantiated +* Work around an MSVC compiler bug in the release build +* PythonUtil.weightedChoice now raises IndexError on empty list +* Support changing DirectScrollBar width after initialiation (#699) +* Workaround for Bullet deadlock when adding shape to a scaled body (#689) +* Support setting DirectEntryScroll entry after initialization (#702) +* Fix some missing imports in directtools (#698) +* Fix undefined behavior issue when using musl-libc +* Update Eigen in Windows thirdparty packages to 3.3.7 +* Update metadata of pip wheels + +------------------------ RELEASE 1.10.3 ----------------------- + +This is another bugfix release that addresses a variety of issues +in 1.10.2 and further improves the stability. + +* Fix crash when unplugging certain devices on macOS +* Fix crash on macOS when using RIME input +* Fix logging issues/crashes in apps deployed with Python 2.7 +* Fix issues when starting in fullscreen on Linux/X11 +* Fix mapping of several gamepads including Trust GXT 24 +* Fix Linux crash when no input devices are present +* Unbreak support for matrix arrays in vertex data in OpenGL +* Allow creating multisample FBO in OpenGL with non-MS host window +* Support playing and looping compressed Ogg and WAV audio files +* Fix generation of CollisionBox for transformed geometry in .egg +* Fix Bullet rigid body transform not updating after reparenting +* Fix sporadic color scales with lighting and custom GLSL shader +* Prevent faulty shaders from shutting down GSG on some drivers +* Allow None as either argument to OdeJoint.attach() +* Fix BufferViewer when main window is not opened right away +* Properly detect extension of pz/gz compressed video/audio files +* Fix for invalid behavior of SparseArray methods to clear bits +* FilterManager now allows overriding framebuffer properties +* Fix detection of core-only OpenGL profile on some drivers +* Add gl-forward-compatible config var for OpenGL context creation +* Add paste-emit-keystrokes variable to disable Ctrl+V on Windows +* Fix in-place |= operator on Panda types (such as SparseArray) +* Fix rare FFmpeg "bad src image pointers" errors after seek +* Fix uses of types.InstanceType in some obscure direct functions +* Fix capsule-into-sphere collision test in degenerate case +* KeyboardButton.ascii_key now also accepts a str character +* Fix errors in various Tkinter DIRECT widgets +* Expose save_egg_file/save_egg_data functions in Python API +* Fix assertion error in BoundingBox.set_min_max +* Fix typo in CollisionTraverser.respect_prev_transform property +* Properly install Python bindings when building FreeBSD installer + +------------------------ RELEASE 1.10.2 ----------------------- + +This release fixes several more bugs, including a few regressions +in 1.10.1. Upgrading is highly recommended. + +* Fix regression on Windows causing freezes and instability +* Fix a memory leak issue in Python applications +* Fix crash reading unaligned float4 column in GeomVertexReader +* Fixes for switching to fullscreen at runtime on Windows and Linux +* Fix incorrect display mode listing in some Linux distributions +* Fix threading crash on Linux when using get_keyboard_map() +* Support "from __future__ import division" for Panda types +* Support building with Visual Studio 2019 in makepanda +* Work around Assimp crash when loading multiple .ply models +* On Windows, a Python 3-compatible version of Pmw is included +* Fix ParticlePanel spam when hovering over File menu items +* TexMemWatcher has been fixed for Python 3 +* Prevent macOS window getting stuck after base.destroy() +* Fix assertion setting mass before shape with Bullet debug build +* Don't error if DirectScrolledFrame is destroyed twice +* Fix reference count corruption accessing task.__dict__ +* Fix writing to SequenceNode frame_rate property +* Fix collider sort not copied when copying CollisionNode +* Add OpenCollective backer file + +------------------------ RELEASE 1.10.1 ----------------------- + +This is a bugfix release intended to fix several issues in 1.10.0. + +* Fix crashes when gamepad is plugged in on 32-bit Windows +* Fix deploy-ng error regarding 'exist_ok' on Python 2 +* Fix Linux install from pip not working with some mesa drivers +* Fix compatibility issues with upcoming Python 3.8 +* Fix regression with Audio3DManager.setSoundVelocityAuto() +* Fix issues when awaiting loader.loadModel in Python 3.7 +* Audio3DManager accepts tuple in setSoundVelocity/setListenerVelocity +* Fix lighting being disabled when only an AmbientLight is active +* Fix an error saving from Particle Panel in Python 3 +* Depth buffer now defaults to 24-bit on macOS (fixes flickering) +* Fix no devices being detected on Windows with threading-model +* Implement collision tests from Capsule and Box into InvSphere +* Fix odd behavior and occasional crash in QuatInterval +* Fix SpriteAnim error in particle system +* Fix ShaderGenerator error when using too many shadowing lights +* Fix interrogate crash in Python 3 with optional wstring args +* Fix compilation errors for x86 Android platform +* Fix permissions of directories created by installpanda +* Improvements to API reference documentation +* Fix incorrect features printed out when printing an InputDevice +* Support cross-compiling for Android platforms in makepanda +* Work around various bugs when compiling with OS X 10.7's libc++ +* Fix wrong error sometimes being reported when loading plug-in +* Allow getting NodePath from CullTraverserData object +* Add config options to Assimp loader for generating normals +* Fix multisampling in floating-point framebuffers on OpenGL +* Parse egg files with 4-component tangents (must be 1 or -1) +* StencilAttrib.make() write_mask argument is now optional + ------------------------ RELEASE 1.10.0 ----------------------- This is a major release with significant changes. Please review the diff --git a/doc/man/fltcopy.1 b/doc/man/fltcopy.1 deleted file mode 100644 index 59632efe50..0000000000 --- a/doc/man/fltcopy.1 +++ /dev/null @@ -1,44 +0,0 @@ -.\" Automatically generated by fltcopy -write-bam -.TH FLTCOPY 1 "27 December 2014" "1.9.0" Panda3D -.SH NAME -fltcopy \- copy MultiGen .flt files into a CVS source hierarchy -.SH SYNOPSIS -\fBfltcopy\fR [opts] file.flt [file.flt ... ] -.SH DESCRIPTION -fltcopy copies one or more MultiGen .flt files into a CVS source hierarchy. Rather than copying the named files immediately into the current directory, it first scans the entire source hierarchy, identifying all the already\-existing files. If the named file to copy matches the name of an already\-existing file in the current directory or elsewhere in the hierarchy, that file is overwritten. Other .flt files, as well as texture files, that are externally referenced by the named .flt file(s) are similarly copied. -.SH OPTIONS -.TP -.BI "\-pr " "path_replace" -Sometimes references to other files (textures, external references) are stored with a full path that is appropriate for some other system, but does not exist here. This option may be used to specify how those invalid paths map to correct paths. Generally, this is of the form 'orig_prefix=replacement_prefix', which indicates a particular initial sequence of characters that should be replaced with a new sequence; e.g. '/c/home/models=/beta/fish'. If the replacement prefix does not begin with a slash, the file will then be searched for along the search path specified by -pp. You may use standard filename matching characters ('*', '?', etc.) in the original prefix, and '**' as a component by itself stands for any number of components. - -This option may be repeated as necessary; each file will be tried against each specified method, in the order in which they appear in the command line, until the file is found. If the file is not found, the last matching prefix is used anyway. -.TP -.BI "\-pp " "dirname" -Adds the indicated directory name to the list of directories to search for filenames referenced by the source file. This is used only for relative paths, or for paths that are made relative by a -pr replacement string that doesn't begin with a leading slash. The model-path is always implicitly searched anyway. -.TP -.B \-f -Force the copy to happen without any input from the user. If a file with the same name exists anywhere in the source hierarchy, it will be overwritten without prompting; if a file does not yet exist, it will be created in the directory named by -d or by -m, as appropriate. -.TP -.B \-i -The opposite of -f, this will prompt the user before each action. The default is only to prompt the user when an action is ambiguous or unusual. -.TP -.BI "\-d " "dirname" -Copy model files that are not already present somewhere in the tree to the indicated directory. The default is the current directory. -.TP -.BI "\-m " "dirname" -Copy texture map files to the indicated directory. The default is src/maps from the root directory. -.TP -.BI "\-root " "dirname" -Specify the root of the CVS source hierarchy. The default is to use the ppremake convention of locating the directory above the -d directory that contains a file called Package.pp. -.TP -.BI "\-key " "filename" -Specify the name of the file that must exist in each directory for it to be considered part of the CVS source hierarchy. The default is the ppremake convention, "Sources.pp". Other likely candidates are "CVS" to search a CVS hierarchy, or "." to include all subdirectories indiscriminately. -.TP -.B \-nc -Do not attempt to add newly-created files to CVS. The default is to add them. -.TP -.BI "\-cvs " "cvs_binary" -Specify how to run the cvs program for adding newly-created files. The default is simply "cvs". -.TP -.B \-h -Display this help page. diff --git a/dtool/PandaVersion.pp b/dtool/PandaVersion.pp index ba362a70ac..e31c9da99a 100644 --- a/dtool/PandaVersion.pp +++ b/dtool/PandaVersion.pp @@ -7,7 +7,7 @@ // place to put this. // Use spaces to separate the major, minor, and sequence numbers here. -#define PANDA_VERSION 1 10 0 +#define PANDA_VERSION 1 11 0 // This variable will be defined to false in the CVS repository, but // scripts that generate source tarballs and/or binary releases for @@ -21,31 +21,3 @@ // It should be set by whoever provides a particular distribution of // Panda. If you build your own Panda, leave this unchanged. #define PANDA_DISTRIBUTOR homebuilt - -// This string is used to describe the Panda3D "package" associated -// with this current build of Panda. It should increment with major -// and minor version changes, but not sequence (or "bugfix") changes. -// It should be unique for each unique distributor. The default is -// the empty string, which means this build does not precisely match -// any distributable Panda3D packages. If you are making a Panda3D -// build which you will be using to produce a distributable Panda3D -// package, you should set this string appropriately. -#define PANDA_PACKAGE_VERSION - -// We also define a version for the Panda3D plugin/runtime, -// i.e. nppanda3d.dll, p3dactivex.ocx, and panda3d.exe. This is an -// independent version number from PANDA_VERSION or -// PANDA_PACKAGE_VERSION, because it is anticipated that this plugin -// code, once settled, will need to be updated much less frequently -// than Panda itself. -#define P3D_PLUGIN_VERSION 1 0 4 - -// Finally, there's a separate version number for the Core API. At -// first, we didn't believe we needed a Core API version number, but -// in this belief we were naive. This version number is a little less -// strict in its format requirements than P3D_PLUGIN_VERSION, above, -// and it doesn't necessarily consist of a specific number of -// integers, but by convention it will consist of four integers, with -// the first three matching the plugin version, and the fourth integer -// being incremented with each new Core API revision. -#define P3D_COREAPI_VERSION $[P3D_PLUGIN_VERSION] 2 diff --git a/dtool/src/cppparser/cppIdentifier.cxx b/dtool/src/cppparser/cppIdentifier.cxx index 43c937177b..e339baf24e 100644 --- a/dtool/src/cppparser/cppIdentifier.cxx +++ b/dtool/src/cppparser/cppIdentifier.cxx @@ -254,19 +254,23 @@ get_scope(CPPScope *current_scope, CPPScope *global_scope, } while (i + 1 < (int)_names.size() && scope != nullptr) { - CPPScope *next_scope = scope->find_scope(_names[i].get_name(), global_scope); + // Check for an explicitly specialized scope first. + CPPScope *next_scope = scope->find_scope(_names[i].get_name_with_templ(), global_scope); if (next_scope == nullptr) { - if (error_sink != nullptr) { - error_sink->error("Symbol " + _names[i].get_name() + - " is not a known scope in " + - scope->get_fully_scoped_name(), - _loc); + next_scope = scope->find_scope(_names[i].get_name(), global_scope); + if (next_scope == nullptr) { + if (error_sink != nullptr) { + error_sink->error("Symbol " + _names[i].get_name() + + " is not a known scope in " + + scope->get_fully_scoped_name(), + _loc); + } + return nullptr; + } + if (_names[i].has_templ()) { + next_scope = next_scope->instantiate(_names[i].get_templ(), + current_scope, global_scope); } - return nullptr; - } - if (_names[i].has_templ()) { - next_scope = next_scope->instantiate(_names[i].get_templ(), - current_scope, global_scope); } scope = next_scope; i++; diff --git a/dtool/src/dtoolbase/atomicAdjustI386Impl.h b/dtool/src/dtoolbase/atomicAdjustI386Impl.h index bcd1a62c61..4daa335b2a 100644 --- a/dtool/src/dtoolbase/atomicAdjustI386Impl.h +++ b/dtool/src/dtoolbase/atomicAdjustI386Impl.h @@ -17,7 +17,7 @@ #include "dtoolbase.h" #include "selectThreadImpl.h" -#if (defined(__i386__) || defined(_M_IX86)) && !defined(__APPLE__) +#if (defined(__i386__) || defined(_M_IX86)) && !defined(__APPLE__) && !defined(__ANDROID__) #include "numeric_types.h" diff --git a/dtool/src/dtoolbase/dllbase.txt b/dtool/src/dtoolbase/dllbase.txt deleted file mode 100644 index 75beee7229..0000000000 --- a/dtool/src/dtoolbase/dllbase.txt +++ /dev/null @@ -1,47 +0,0 @@ -; This file is used by the MSVC linker to specify DLL base addresses to -; prevent runtime relocation of DLLs by the windows loader. This speeds -; loading. This file was generated using $PANDATOOL/src/scripts/basedlls.pl -; which uses rebase.exe from the MS platform SDK. It may additionally be -; hand-hacked to simplify addresses for popular dlls to aid debugging. - -; release dlls - -libpanda 0x0000000060000000 0x004c0000 -libwindisplay 0x00000000604c0000 0x00010000 -libpandadx8 0x0000000061000000 0x00090000 -libpandadx7 0x0000000061000000 0x00040000 -libpandagl 0x0000000061000000 0x00030000 -libpandaexpress 0x00000000605d0000 0x00150000 -libpandaphysics 0x0000000060720000 0x00060000 -libpandafx 0x0000000060780000 0x00020000 -libdtool 0x00000000607a0000 0x00020000 -libdtoolconfig 0x00000000607c0000 0x00040000 -libpystub 0x0000000060800000 0x00010000 -libdirect 0x0000000060810000 0x00050000 -libtoontown 0x0000000060860000 0x000b0000 -; TODO put in a base for Pirates Opt 4 DLL -libpirates 0x0000000060860000 0x000b0000 -libmiles_audio 0x0000000060910000 0x00020000 -libpandaegg 0x0000000060930000 0x00130000 -libframework 0x0000000060a60000 0x00020000 - -; debug dlls - -libpanda_d 0x0000000060000000 0x007e0000 -libwindisplay_d 0x00000000607e0000 0x00020000 -libpandadx8_d 0x0000000060800000 0x000d0000 -libpandadx7_d 0x00000000608d0000 0x00060000 -libpandagl_d 0x0000000060930000 0x00040000 -libpandaexpress_d 0x0000000060970000 0x00220000 -libpandaphysics_d 0x0000000060b90000 0x000a0000 -libpandafx_d 0x0000000060c30000 0x00030000 -libdtool_d 0x0000000060c60000 0x00030000 -libdtoolconfig_d 0x0000000060c90000 0x00070000 -libpystub_d 0x0000000060d00000 0x00010000 -libdirect_d 0x0000000060d10000 0x00080000 -libtoontown_d 0x0000000060d90000 0x00130000 -; TODO put in a base for Pirates -libpirates_d 0x0000000060d90000 0x00130000 -libmiles_audio_d 0x0000000060ec0000 0x00030000 -libpandaegg_d 0x0000000060ef0000 0x00210000 -libframework_d 0x0000000061100000 0x00030000 diff --git a/dtool/src/dtoolbase/dtoolsymbols.h b/dtool/src/dtoolbase/dtoolsymbols.h index 5f972e776f..ebabe47515 100644 --- a/dtool/src/dtoolbase/dtoolsymbols.h +++ b/dtool/src/dtoolbase/dtoolsymbols.h @@ -125,16 +125,4 @@ #define EXPTP_MISC IMPORT_TEMPL #endif /* BUILDING_MISC */ - -/* These two are always defined empty, because pystub is statically - built. But we leave the symbol around in case we change our minds - to make pystub once again be a dynamic library. */ -#if __GNUC__ >= 4 -/* In GCC, though, we still need to mark the symbols as visible. */ -#define EXPCL_PYSTUB __attribute__((visibility("default"))) -#else -#define EXPCL_PYSTUB -#endif -#define EXPTP_PYSTUB - #endif diff --git a/dtool/src/dtoolbase/epvector.h b/dtool/src/dtoolbase/epvector.h index e743ea4060..f97c80cda3 100644 --- a/dtool/src/dtoolbase/epvector.h +++ b/dtool/src/dtoolbase/epvector.h @@ -20,6 +20,8 @@ #include +#include + /** * Unfortunately, on Windows, std::vector can't be used for classes with * explicitly alignment requirements, due to a minor mistake in the template @@ -46,6 +48,7 @@ public: epvector(size_type n, TypeHandle type_handle = pvector_type_handle) : base_class(n, Type(), allocator()) { } epvector(size_type n, const Type &value, TypeHandle type_handle = pvector_type_handle) : base_class(n, value, allocator()) { } epvector(const Type *begin, const Type *end, TypeHandle type_handle = pvector_type_handle) : base_class(begin, end, allocator()) { } + epvector(std::initializer_list init, TypeHandle type_handle = pvector_type_handle) : base_class(std::move(init), allocator()) { } }; #else // HAVE_EIGEN diff --git a/dtool/src/dtoolbase/memoryBase.h b/dtool/src/dtoolbase/memoryBase.h index b6545ff66e..d1d9f00ca5 100644 --- a/dtool/src/dtoolbase/memoryBase.h +++ b/dtool/src/dtoolbase/memoryBase.h @@ -23,8 +23,6 @@ // MemoryBase; this macro is provided to resolve problems with multiple // inheritance or some such. -#ifndef USE_MEMORY_NOWRAPPERS - #define ALLOC_MEMORY_BASE \ inline void *operator new(size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT) { \ return PANDA_MALLOC_SINGLE(size); \ @@ -51,12 +49,6 @@ inline void operator delete[](void *, void *) { \ } -#else // USE_MEMORY_NOWRAPPERS - -#define ALLOC_MEMORY_BASE - -#endif // USE_MEMORY_NOWRAPPERS - /** * This class is intended to be the base class of all objects in Panda that * might be allocated and deleted via the new and delete operators. It diff --git a/dtool/src/dtoolbase/mutexImpl.h b/dtool/src/dtoolbase/mutexImpl.h index f309634ffa..add705bc39 100644 --- a/dtool/src/dtoolbase/mutexImpl.h +++ b/dtool/src/dtoolbase/mutexImpl.h @@ -34,7 +34,7 @@ typedef MutexSpinlockImpl MutexImpl; #include "mutexWin32Impl.h" typedef MutexWin32Impl MutexImpl; -typedef MutexWin32Impl ReMutexImpl; // Win32 Mutexes are always reentrant. +typedef ReMutexWin32Impl ReMutexImpl; #define HAVE_REMUTEXIMPL 1 #elif defined(THREAD_POSIX_IMPL) diff --git a/dtool/src/dtoolbase/mutexWin32Impl.I b/dtool/src/dtoolbase/mutexWin32Impl.I index acbbd68b56..b61d314d61 100644 --- a/dtool/src/dtoolbase/mutexWin32Impl.I +++ b/dtool/src/dtoolbase/mutexWin32Impl.I @@ -16,7 +16,11 @@ */ INLINE MutexWin32Impl:: ~MutexWin32Impl() { - DeleteCriticalSection(&_lock); + // If the lock has been contended, and we use the Windows XP implementation, + // we have a handle to close. Otherwise, this field will be null. + if (_lock[1] != nullptr) { + CloseHandle(_lock[1]); + } } /** @@ -24,7 +28,7 @@ INLINE MutexWin32Impl:: */ INLINE void MutexWin32Impl:: lock() { - EnterCriticalSection(&_lock); + _funcs._lock(_lock); } /** @@ -32,13 +36,45 @@ lock() { */ INLINE bool MutexWin32Impl:: try_lock() { - return (TryEnterCriticalSection(&_lock) != 0); + return (_funcs._try_lock(_lock) != 0); } /** * */ INLINE void MutexWin32Impl:: +unlock() { + _funcs._unlock(_lock); +} + +/** + * + */ +INLINE ReMutexWin32Impl:: +~ReMutexWin32Impl() { + DeleteCriticalSection(&_lock); +} + +/** + * + */ +INLINE void ReMutexWin32Impl:: +lock() { + EnterCriticalSection(&_lock); +} + +/** + * + */ +INLINE bool ReMutexWin32Impl:: +try_lock() { + return (TryEnterCriticalSection(&_lock) != 0); +} + +/** + * + */ +INLINE void ReMutexWin32Impl:: unlock() { LeaveCriticalSection(&_lock); } diff --git a/dtool/src/dtoolbase/mutexWin32Impl.cxx b/dtool/src/dtoolbase/mutexWin32Impl.cxx index 594f6c0962..4b0e63a357 100644 --- a/dtool/src/dtoolbase/mutexWin32Impl.cxx +++ b/dtool/src/dtoolbase/mutexWin32Impl.cxx @@ -13,16 +13,412 @@ #include "selectThreadImpl.h" -#ifdef WIN32_VC +#if defined(WIN32_VC) && !defined(CPPPARSER) #include "mutexWin32Impl.h" +// The number of spins to do before suspending the thread. +static const unsigned int spin_count = 4000; + +// Only compile the below nonsense if we're not compiling for a Vista minimum. +#if _WIN32_WINNT < 0x0600 + +// If this is true, we will use SRWLock on Windows Vista and above instead of +// our own implementation. +static const bool prefer_srwlock = true; + +// These configure our own Windows XP implementation. +static const uintptr_t lock_bit = 0x40000000; + +// This gets set to spin_count if we are on a multi-core system. +static unsigned int effective_spin_count = 0; + +/** + * Windows XP implementation of lock(), which uses a combination of a spinlock + * and an event. + */ +static void __stdcall lock_xp(volatile PVOID *lock) { + // In the Windows XP case, lock consists of two words: the first one is a + // number of waiters plus a bit to indicate that it is locked, the second + // one is the handle of an event that is created in case of contention. + // The first word can be in the following states: + // + // lock bit | waiters | meaning + // ---------|---------|--------- + // unset | 0 | unlocked + // set | 0 | locked, nobody waiting on event + // set | >0 | locked, at least one thread waiting on event + // unset | >0 | handing off lock to one of waiters + // + // The last state is a little subtle: at this point, the thread that was + // holding the lock has stopped holding it, but is about to fire off a + // signal to a waiting thread, which will attempt to grab the lock. In this + // case, the waiting thread has first dibs on the lock, and any new threads + // will still treat it as locked and wait until there are no more waiters. + + // First try to acquire the lock without suspending the thread. This only + // works if the waiter count is 0; this way, we give priority to threads + // that are already waiting for the event. + if (InterlockedCompareExchangePointer(lock, (void *)lock_bit, nullptr) == nullptr) { + // Got the lock on the first try. + return; + } + + // On multi-core systems, we keep trying for the configured spin_count. + const unsigned int max_spins = effective_spin_count; + for (unsigned int spins = 0; spins < max_spins; ++spins) { + if (InterlockedCompareExchangePointer(lock, (void *)lock_bit, nullptr) == nullptr) { + // We managed to acquire the lock. + return; + } + + // Emit the pause instruction. This is NOT a thread yield. + YieldProcessor(); + } + + // Looks like we might have to go to sleep for a while using an event. + HANDLE event = lock[1]; + if (event == nullptr) { + // We don't have an event yet. Create an auto-reset event. + HANDLE new_event = CreateEvent(nullptr, false, false, nullptr); + while (new_event == nullptr) { + // Hmm, out of memory? Just yield to another thread for now until the + // lock is either freed or until we can create an event. + Sleep(1); + if (InterlockedCompareExchangePointer(lock, (void *)lock_bit, nullptr) == 0) { + return; + } + new_event = CreateEvent(nullptr, false, false, nullptr); + } + + // Push the new event. + event = InterlockedCompareExchangePointer(lock + 1, new_event, nullptr); + if (event == nullptr) { + // Set successfully. + event = new_event; + } else { + // Another thread created an event; delete ours and use that one instead. + CloseHandle(new_event); + } + } + + // OK, now we have an event. We need to let the unlock() function know that + // we are waiting. + while (true) { + uintptr_t waiters = (uintptr_t)lock[0]; + if (waiters == 0) { + // It became unlocked while we were creating an event. Quick, grab it. + if (InterlockedCompareExchangePointer(lock, (void *)lock_bit, nullptr) == nullptr) { + return; + } + } + + // If the lock bit gets unset while we try this, just keep trying. It + // would be dangerous to increment this while the lock bit is unset. + waiters |= lock_bit; + uintptr_t new_waiters = (uintptr_t)InterlockedCompareExchangePointer(lock, (void *)(waiters + 1), (void *)waiters); + if (new_waiters == waiters) { + // Made the change successfully. + break; + } else if (new_waiters == 0) { + // It just became unlocked. Quick, grab it. + if (InterlockedCompareExchangePointer(lock, (void *)lock_bit, nullptr) == nullptr) { + return; + } + } + YieldProcessor(); + } + + // Sleep well, thread. + while (true) { + WaitForSingleObjectEx(event, INFINITE, FALSE); + + // We were woken up. Does that mean the lock can be ours? + while (true) { + uintptr_t waiters = (uintptr_t)lock[0]; + if (waiters & lock_bit) { + // False alarm. Go back to sleep. + break; + } + assert(waiters > 0); + + // Grab the lock immediately, and simultaneously tell it that we are no + // longer waiting. + uintptr_t new_waiters = (uintptr_t)InterlockedCompareExchangePointer(lock, (void *)((waiters - 1) | lock_bit), (void *)waiters); + if (new_waiters == waiters) { + // The lock is ours. + return; + } else if (new_waiters & lock_bit) { + // Another thread beat us to it. Go back to sleep. + break; + } + YieldProcessor(); + } + } + + // Never supposed to get here. + assert(false); +} + +/** + * Windows XP implementation of try_lock(). + */ +static BOOL __stdcall try_lock_xp(volatile PVOID *lock) { + return (InterlockedCompareExchangePointer(lock, (void *)lock_bit, nullptr) == nullptr); +} + +/** + * Windows XP implementation of unlock(). + */ +static void __stdcall unlock_xp(volatile PVOID *lock) { + // Clear the lock flag. +#ifdef _WIN64 + uintptr_t waiters = _InterlockedAnd64((volatile __int64 *)lock, ~lock_bit); +#else + uintptr_t waiters = _InterlockedAnd((volatile long *)lock, ~lock_bit); +#endif + + // If this triggers, the lock wasn't held to begin with. + assert((waiters & lock_bit) != 0); + + // Have any threads begun to sleep (or are about to) waiting for this lock? + if ((waiters & ~lock_bit) == 0) { + // No contention, nothing to do. + return; + } else { + // By signalling the auto-resetting event, we wake up one waiting thread. + HANDLE event = lock[1]; + assert(event != nullptr); + SetEvent(event); + } +} + +/** + * Windows XP implementation to wait for a condition variable. + */ +static BOOL __stdcall +cvar_wait_xp(volatile PVOID *cvar, volatile PVOID *lock, DWORD timeout, ULONG) { + // Increment the number of waiters. +#ifdef _WIN64 + _InterlockedIncrement64((volatile __int64 *)cvar); +#else + _InterlockedIncrement((volatile long *)cvar); +#endif + + // Make sure we have two events created: one auto-reset event and one + // manual-reset, to handle signal and broadcast, respectively. + if (cvar[1] == nullptr) { + cvar[1] = CreateEvent(nullptr, false, false, nullptr); + } + if (cvar[2] == nullptr) { + cvar[2] = CreateEvent(nullptr, true, false, nullptr); + } + + // It's ok to release the external_mutex here since Win32 manual-reset + // events maintain state when used with SetEvent(). This avoids the "lost + // wakeup" bug... + unlock_xp(lock); + + // Wait for either event to become signaled due to notify() being called or + // notify_all() being called. + int result = WaitForMultipleObjects(2, (const HANDLE *)(cvar + 1), FALSE, timeout); + + // Decrement the counter. If it reached zero, we were the last waiter. +#ifdef _WIN64 + bool nonzero = (_InterlockedDecrement64((volatile __int64 *)cvar) != 0); +#else + bool nonzero = (_InterlockedDecrement((volatile long *)cvar) != 0); +#endif + bool last_waiter = (result == WAIT_OBJECT_0 + 1 && !nonzero); + + // Some thread called notify_all(). + if (last_waiter) { + // We're the last waiter to be notified or to stop waiting, so reset the + // manual event. + ResetEvent(cvar[2]); + } + + // Reacquire the . + lock_xp(lock); + return TRUE; +} + +/** + * Wakes one thread waiting for a condition variable. + */ +static void __stdcall +cvar_notify_one_xp(volatile PVOID *cvar) { + // If there are any waiters, signal one of them to wake up by signalling the + // auto-reset event. + if ((uintptr_t)cvar[0] > 0) { + SetEvent(cvar[1]); + } +} + +/** + * Wakes all threads waiting for a condition variable. + */ +static void __stdcall +cvar_notify_all_xp(volatile PVOID *cvar) { + // If there are any waiters, signal the manual-reset event, which will be + // reset by the last thread to wake up. + if ((uintptr_t)cvar[0] > 0) { + SetEvent(cvar[2]); + } +} + +#endif // _WIN32_WINNT < 0x0600 + +/** + * This is put initially in the _lock slot; it makes sure that the lock + * functions get initialized the first time someone tries to grab a lock. + */ +void __stdcall MutexWin32Impl:: +lock_initially(volatile PVOID *lock) { + MutexWin32Impl::init_lock_funcs(); + MutexWin32Impl::_funcs._lock(lock); +} + +/** + * This is put initially in the _try_lock slot; it makes sure that the lock + * functions get initialized the first time someone tries to grab a lock. + */ +BOOL __stdcall MutexWin32Impl:: +try_lock_initially(volatile PVOID *lock) { + MutexWin32Impl::init_lock_funcs(); + return MutexWin32Impl::_funcs._try_lock(lock); +} + +/** + * This gets put initially in the _unlock slot and should never be called, + * since the initial lock/try_lock implementation will replace the pointers. + */ +void __stdcall MutexWin32Impl:: +unlock_initially(volatile PVOID *) { +#if !defined(NDEBUG) || defined(DEBUG_THREADS) + std::cerr << "Attempt to release a mutex at static init time before acquiring it!\n"; + assert(false); +#endif +} + +/** + * Same as above for condition variables. + */ +static BOOL __stdcall +cvar_wait_initially(volatile PVOID *cvar, volatile PVOID *lock, DWORD timeout, ULONG) { +#if !defined(NDEBUG) || defined(DEBUG_THREADS) + std::cerr << "Attempt to wait for condition variable at static init time before acquiring mutex!\n"; + assert(false); +#endif + return FALSE; +} + +/** + * Does nothing. + */ +static void __stdcall +noop(volatile PVOID *) { +} + +// We initially set the function pointers to functions that do initialization +// first. +MutexWin32Impl::LockFunctions MutexWin32Impl::_funcs = { + &MutexWin32Impl::lock_initially, + &MutexWin32Impl::try_lock_initially, +#ifndef NDEBUG + &MutexWin32Impl::unlock_initially, +#else + &noop, +#endif + + &cvar_wait_initially, +#ifndef NDEBUG + &MutexWin32Impl::unlock_initially, + &MutexWin32Impl::unlock_initially, +#else + &noop, + &noop, +#endif +}; + +/** + * Called the first time a lock is grabbed. + */ +void MutexWin32Impl:: +init_lock_funcs() { + if (MutexWin32Impl::_funcs._lock != &MutexWin32Impl::lock_initially) { + // Already initialized. + return; + } + +#if _WIN32_WINNT >= 0x0600 + _funcs._lock = (LockFunc)AcquireSRWLockExclusive; + _funcs._try_lock = (TryLockFunc)TryAcquireSRWLockExclusive; + _funcs._unlock = (LockFunc)ReleaseSRWLockExclusive; + _funcs._cvar_wait = (CondWaitFunc)SleepConditionVariableSRW; + _funcs._cvar_notify_one = (LockFunc)WakeConditionVariable; + _funcs._cvar_notify_all = (LockFunc)WakeAllConditionVariable; +#else + // We don't need to be very thread safe here. This can only ever be called + // at static init time, when there is still only one thread. + if (prefer_srwlock) { + HMODULE module = GetModuleHandleA("kernel32"); + if (module != nullptr) { + _funcs._lock = (LockFunc)GetProcAddress(module, "AcquireSRWLockExclusive"); + _funcs._try_lock = (TryLockFunc)GetProcAddress(module, "TryAcquireSRWLockExclusive"); + _funcs._unlock = (LockFunc)GetProcAddress(module, "ReleaseSRWLockExclusive"); + _funcs._cvar_wait = (CondWaitFunc)GetProcAddress(module, "SleepConditionVariableSRW"); + _funcs._cvar_notify_one = (LockFunc)GetProcAddress(module, "WakeConditionVariable"); + _funcs._cvar_notify_all = (LockFunc)GetProcAddress(module, "WakeAllConditionVariable"); + if (_funcs._lock != nullptr && + _funcs._try_lock != nullptr && + _funcs._unlock != nullptr && + _funcs._cvar_wait != nullptr && + _funcs._cvar_notify_one != nullptr && + _funcs._cvar_notify_all != nullptr) { + return; + } + } + } + + // Fall back to our custom Event-based implementation on Windows XP. + _funcs._lock = &lock_xp; + _funcs._try_lock = &try_lock_xp; + _funcs._unlock = &unlock_xp; + _funcs._cvar_wait = &cvar_wait_xp; + _funcs._cvar_notify_one = &cvar_notify_one_xp; + _funcs._cvar_notify_all = &cvar_notify_all_xp; + + // Are we on a multi-core system? If so, enable the spinlock. + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + if (sysinfo.dwNumberOfProcessors > 1) { + effective_spin_count = spin_count; + } else { + effective_spin_count = 0; + } +#endif // _WIN32_WINNT < 0x0600 +} + +/** + * Ensures that the lock functions are initialized at static init time. This + * prevents us from having to implement synchronization in our initialization. + */ +class LockFuncsInitializer { +public: + LockFuncsInitializer() { + MutexWin32Impl::init_lock_funcs(); + } +}; + +static LockFuncsInitializer _lock_funcs_init; + /** * */ -MutexWin32Impl:: -MutexWin32Impl() { - InitializeCriticalSectionAndSpinCount(&_lock, 4000); +ReMutexWin32Impl:: +ReMutexWin32Impl() { + InitializeCriticalSectionAndSpinCount(&_lock, spin_count); } #endif // WIN32_VC diff --git a/dtool/src/dtoolbase/mutexWin32Impl.h b/dtool/src/dtoolbase/mutexWin32Impl.h index 550d4944ab..47f31a2e8f 100644 --- a/dtool/src/dtoolbase/mutexWin32Impl.h +++ b/dtool/src/dtoolbase/mutexWin32Impl.h @@ -24,16 +24,69 @@ #include /** - * Uses Windows native calls to implement a mutex. + * Uses SRWLock on Vista and above to implement a mutex. Older versions of + * Windows fall back to a combination of a spinlock and an Event object. */ class EXPCL_DTOOL_DTOOLBASE MutexWin32Impl { public: - MutexWin32Impl(); + constexpr MutexWin32Impl() = default; MutexWin32Impl(const MutexWin32Impl ©) = delete; INLINE ~MutexWin32Impl(); MutexWin32Impl &operator = (const MutexWin32Impl ©) = delete; +public: + INLINE void lock(); + INLINE bool try_lock(); + INLINE void unlock(); + + static void init_lock_funcs(); + +private: +#ifndef CPPPARSER + // Store function pointers; these point directly to the SRWLock Win32 API + // functions on Vista and above, or to our own implementation on Windows XP. + typedef void (__stdcall *LockFunc)(volatile PVOID *lock); + typedef BOOL (__stdcall *TryLockFunc)(volatile PVOID *lock); + typedef BOOL (__stdcall *CondWaitFunc)(volatile PVOID *cvar, volatile PVOID *lock, DWORD, ULONG); + + struct LockFunctions { + LockFunc _lock; + TryLockFunc _try_lock; + LockFunc _unlock; + + CondWaitFunc _cvar_wait; + LockFunc _cvar_notify_one; + LockFunc _cvar_notify_all; + }; + + static LockFunctions _funcs; + + static void __stdcall lock_initially(volatile PVOID *lock); + static BOOL __stdcall try_lock_initially(volatile PVOID *lock); + static void __stdcall unlock_initially(volatile PVOID *lock); +#endif + +private: + // In the SRWLock implementation, only the first field is used. On Windows + // XP, the first field contains a waiter count and lock bit, and the second + // field contains an Event handle if contention has occurred. + volatile PVOID _lock[2] = {nullptr, nullptr}; + + friend class ConditionVarWin32Impl; +}; + +/** + * Uses CRITICAL_SECTION to implement a recursive mutex. + */ +class EXPCL_DTOOL_DTOOLBASE ReMutexWin32Impl { +public: + ReMutexWin32Impl(); + ReMutexWin32Impl(const ReMutexWin32Impl ©) = delete; + INLINE ~ReMutexWin32Impl(); + + ReMutexWin32Impl &operator = (const ReMutexWin32Impl ©) = delete; + public: INLINE void lock(); INLINE bool try_lock(); @@ -41,8 +94,6 @@ public: private: CRITICAL_SECTION _lock; - friend class ConditionVarWin32Impl; - friend class ConditionVarFullWin32Impl; }; #include "mutexWin32Impl.I" diff --git a/dtool/src/dtoolbase/pdeque.h b/dtool/src/dtoolbase/pdeque.h index e7e9f96c28..2b5e5793aa 100644 --- a/dtool/src/dtoolbase/pdeque.h +++ b/dtool/src/dtoolbase/pdeque.h @@ -18,7 +18,7 @@ #include "pallocator.h" #include "register_type.h" #include - +#include #if !defined(USE_STL_ALLOCATOR) || defined(CPPPARSER) // If we're not using custom allocators, just use the standard class @@ -40,6 +40,7 @@ public: pdeque(TypeHandle type_handle = pdeque_type_handle) : std::deque >(allocator(type_handle)) { } pdeque(size_type n, TypeHandle type_handle = pdeque_type_handle) : std::deque >(n, Type(), allocator(type_handle)) { } pdeque(size_type n, const Type &value, TypeHandle type_handle = pdeque_type_handle) : std::deque >(n, value, allocator(type_handle)) { } + pdeque(std::initializer_list init, TypeHandle type_handle = pdeque_type_handle) : std::deque >(std::move(init), allocator(type_handle)) { } }; #endif // USE_STL_ALLOCATOR diff --git a/dtool/src/dtoolbase/plist.h b/dtool/src/dtoolbase/plist.h index 12d29880f3..b9810342b5 100644 --- a/dtool/src/dtoolbase/plist.h +++ b/dtool/src/dtoolbase/plist.h @@ -18,6 +18,7 @@ #include "pallocator.h" #include "register_type.h" #include +#include #if !defined(USE_STL_ALLOCATOR) || defined(CPPPARSER) // If we're not using custom allocators, just use the standard class @@ -40,6 +41,7 @@ public: plist(TypeHandle type_handle = plist_type_handle) : base_class(allocator(type_handle)) { } plist(size_type n, TypeHandle type_handle = plist_type_handle) : base_class(n, Type(), allocator(type_handle)) { } plist(size_type n, const Type &value, TypeHandle type_handle = plist_type_handle) : base_class(n, value, allocator(type_handle)) { } + plist(std::initializer_list init, TypeHandle type_handle = plist_type_handle) : base_class(std::move(init), allocator(type_handle)) { } typedef typename base_class::iterator iterator; typedef typename base_class::const_iterator const_iterator; diff --git a/dtool/src/dtoolbase/pset.h b/dtool/src/dtoolbase/pset.h index ff54486dfe..feece41b5b 100644 --- a/dtool/src/dtoolbase/pset.h +++ b/dtool/src/dtoolbase/pset.h @@ -24,6 +24,8 @@ #include #endif +#include + #if !defined(USE_STL_ALLOCATOR) || defined(CPPPARSER) // If we're not using custom allocators, just use the standard class // definition. @@ -52,6 +54,7 @@ public: typedef std::set base_class; pset(TypeHandle type_handle = pset_type_handle) : base_class(Compare(), allocator(type_handle)) { } pset(const Compare &comp, TypeHandle type_handle = pset_type_handle) : base_class(comp, type_handle) { } + pset(std::initializer_list init, TypeHandle type_handle = pset_type_handle) : base_class(std::move(init), allocator(type_handle)) { } #ifdef USE_TAU std::pair @@ -110,6 +113,7 @@ public: typedef pallocator_single allocator; pmultiset(TypeHandle type_handle = pset_type_handle) : std::multiset(Compare(), allocator(type_handle)) { } pmultiset(const Compare &comp, TypeHandle type_handle = pset_type_handle) : std::multiset(comp, type_handle) { } + pmultiset(std::initializer_list init, TypeHandle type_handle = pset_type_handle) : std::multiset(std::move(init), allocator(type_handle)) { } }; #ifdef HAVE_STL_HASH diff --git a/dtool/src/dtoolbase/pvector.h b/dtool/src/dtoolbase/pvector.h index 4199506d13..be60897d21 100644 --- a/dtool/src/dtoolbase/pvector.h +++ b/dtool/src/dtoolbase/pvector.h @@ -15,6 +15,7 @@ #define PVECTOR_H #include +#include #include "dtoolbase.h" #include "pallocator.h" @@ -51,6 +52,7 @@ public: explicit pvector(size_type n, TypeHandle type_handle = pvector_type_handle) : base_class(n, Type(), allocator(type_handle)) { } explicit pvector(size_type n, const Type &value, TypeHandle type_handle = pvector_type_handle) : base_class(n, value, allocator(type_handle)) { } pvector(const Type *begin, const Type *end, TypeHandle type_handle = pvector_type_handle) : base_class(begin, end, allocator(type_handle)) { } + pvector(std::initializer_list init, TypeHandle type_handle = pvector_type_handle) : base_class(std::move(init), allocator(type_handle)) { } pvector &operator =(const pvector ©) { base_class::operator =(copy); diff --git a/dtool/src/dtoolbase/register_type.I b/dtool/src/dtoolbase/register_type.I index 0bd43e1501..f1108fc0ca 100644 --- a/dtool/src/dtoolbase/register_type.I +++ b/dtool/src/dtoolbase/register_type.I @@ -25,37 +25,41 @@ register_type(TypeHandle &type_handle, const std::string &name) { INLINE void register_type(TypeHandle &type_handle, const std::string &name, TypeHandle parent1) { - if (TypeRegistry::ptr()->register_type(type_handle, name)) { - TypeRegistry::ptr()->record_derivation(type_handle, parent1); + TypeRegistry *registry = TypeRegistry::ptr(); + if (registry->register_type(type_handle, name)) { + registry->record_derivation(type_handle, parent1); } } INLINE void register_type(TypeHandle &type_handle, const std::string &name, TypeHandle parent1, TypeHandle parent2) { - if (TypeRegistry::ptr()->register_type(type_handle, name)) { - TypeRegistry::ptr()->record_derivation(type_handle, parent1); - TypeRegistry::ptr()->record_derivation(type_handle, parent2); + TypeRegistry *registry = TypeRegistry::ptr(); + if (registry->register_type(type_handle, name)) { + registry->record_derivation(type_handle, parent1); + registry->record_derivation(type_handle, parent2); } } INLINE void register_type(TypeHandle &type_handle, const std::string &name, TypeHandle parent1, TypeHandle parent2, TypeHandle parent3) { - if (TypeRegistry::ptr()->register_type(type_handle, name)) { - TypeRegistry::ptr()->record_derivation(type_handle, parent1); - TypeRegistry::ptr()->record_derivation(type_handle, parent2); - TypeRegistry::ptr()->record_derivation(type_handle, parent3); + TypeRegistry *registry = TypeRegistry::ptr(); + if (registry->register_type(type_handle, name)) { + registry->record_derivation(type_handle, parent1); + registry->record_derivation(type_handle, parent2); + registry->record_derivation(type_handle, parent3); } } INLINE void register_type(TypeHandle &type_handle, const std::string &name, TypeHandle parent1, TypeHandle parent2, TypeHandle parent3, TypeHandle parent4) { - if (TypeRegistry::ptr()->register_type(type_handle, name)) { - TypeRegistry::ptr()->record_derivation(type_handle, parent1); - TypeRegistry::ptr()->record_derivation(type_handle, parent2); - TypeRegistry::ptr()->record_derivation(type_handle, parent3); - TypeRegistry::ptr()->record_derivation(type_handle, parent4); + TypeRegistry *registry = TypeRegistry::ptr(); + if (registry->register_type(type_handle, name)) { + registry->record_derivation(type_handle, parent1); + registry->record_derivation(type_handle, parent2); + registry->record_derivation(type_handle, parent3); + registry->record_derivation(type_handle, parent4); } } @@ -71,40 +75,40 @@ register_dynamic_type(const std::string &name) { } INLINE TypeHandle register_dynamic_type(const std::string &name, TypeHandle parent1) { - TypeHandle type_handle = - TypeRegistry::ptr()->register_dynamic_type(name); - TypeRegistry::ptr()->record_derivation(type_handle, parent1); + TypeRegistry *registry = TypeRegistry::ptr(); + TypeHandle type_handle = registry->register_dynamic_type(name); + registry->record_derivation(type_handle, parent1); return type_handle; } INLINE TypeHandle register_dynamic_type(const std::string &name, TypeHandle parent1, TypeHandle parent2) { - TypeHandle type_handle = - TypeRegistry::ptr()->register_dynamic_type(name); - TypeRegistry::ptr()->record_derivation(type_handle, parent1); - TypeRegistry::ptr()->record_derivation(type_handle, parent2); + TypeRegistry *registry = TypeRegistry::ptr(); + TypeHandle type_handle = registry->register_dynamic_type(name); + registry->record_derivation(type_handle, parent1); + registry->record_derivation(type_handle, parent2); return type_handle; } INLINE TypeHandle register_dynamic_type(const std::string &name, TypeHandle parent1, TypeHandle parent2, TypeHandle parent3) { - TypeHandle type_handle = - TypeRegistry::ptr()->register_dynamic_type(name); - TypeRegistry::ptr()->record_derivation(type_handle, parent1); - TypeRegistry::ptr()->record_derivation(type_handle, parent2); - TypeRegistry::ptr()->record_derivation(type_handle, parent3); + TypeRegistry *registry = TypeRegistry::ptr(); + TypeHandle type_handle = registry->register_dynamic_type(name); + registry->record_derivation(type_handle, parent1); + registry->record_derivation(type_handle, parent2); + registry->record_derivation(type_handle, parent3); return type_handle; } INLINE TypeHandle register_dynamic_type(const std::string &name, TypeHandle parent1, TypeHandle parent2, TypeHandle parent3, TypeHandle parent4) { - TypeHandle type_handle = - TypeRegistry::ptr()->register_dynamic_type(name); - TypeRegistry::ptr()->record_derivation(type_handle, parent1); - TypeRegistry::ptr()->record_derivation(type_handle, parent2); - TypeRegistry::ptr()->record_derivation(type_handle, parent3); - TypeRegistry::ptr()->record_derivation(type_handle, parent4); + TypeRegistry *registry = TypeRegistry::ptr(); + TypeHandle type_handle = registry->register_dynamic_type(name); + registry->record_derivation(type_handle, parent1); + registry->record_derivation(type_handle, parent2); + registry->record_derivation(type_handle, parent3); + registry->record_derivation(type_handle, parent4); return type_handle; } diff --git a/dtool/src/dtoolbase/typeRegistry.I b/dtool/src/dtoolbase/typeRegistry.I index 7d47c7f7f7..77c83de82c 100644 --- a/dtool/src/dtoolbase/typeRegistry.I +++ b/dtool/src/dtoolbase/typeRegistry.I @@ -36,16 +36,6 @@ ptr() { return _global_pointer; } -/** - * Ensures the lock pointer has been allocated. - */ -INLINE void TypeRegistry:: -init_lock() { - if (_lock == nullptr) { - _lock = new MutexImpl; - } -} - /** * Returns the TypeRegistryNode associated with the indicated TypeHandle. If * there is no associated TypeRegistryNode, reports an error condition and diff --git a/dtool/src/dtoolbase/typeRegistry.cxx b/dtool/src/dtoolbase/typeRegistry.cxx index 360dfcd9e4..f2646eddb9 100644 --- a/dtool/src/dtoolbase/typeRegistry.cxx +++ b/dtool/src/dtoolbase/typeRegistry.cxx @@ -25,7 +25,7 @@ using std::ostream; using std::ostringstream; using std::string; -MutexImpl *TypeRegistry::_lock = nullptr; +MutexImpl TypeRegistry::_lock; TypeRegistry *TypeRegistry::_global_pointer = nullptr; /** @@ -37,7 +37,7 @@ TypeRegistry *TypeRegistry::_global_pointer = nullptr; */ bool TypeRegistry:: register_type(TypeHandle &type_handle, const string &name) { - _lock->lock(); + _lock.lock(); if (type_handle != TypeHandle::none()) { // Here's a type that was already registered. Just make sure everything's @@ -45,7 +45,7 @@ register_type(TypeHandle &type_handle, const string &name) { TypeRegistryNode *rnode = look_up(type_handle, nullptr); if (&type_handle == &rnode->_ref) { // No problem. - _lock->unlock(); + _lock.unlock(); assert(rnode->_name == name); return false; } @@ -67,7 +67,7 @@ register_type(TypeHandle &type_handle, const string &name) { _derivations_fresh = false; type_handle = new_handle; - _lock->unlock(); + _lock.unlock(); return true; } TypeRegistryNode *rnode = (*ri).second; @@ -83,7 +83,7 @@ register_type(TypeHandle &type_handle, const string &name) { if (type_handle == rnode->_handle) { // No problem. - _lock->unlock(); + _lock.unlock(); return false; } // But wait--the type_handle has changed! We kept a reference to the @@ -92,7 +92,7 @@ register_type(TypeHandle &type_handle, const string &name) { // time, but now it's different! Bad juju. cerr << "Reregistering " << name << "\n"; type_handle = rnode->_handle; - _lock->unlock(); + _lock.unlock(); return false; } @@ -108,7 +108,7 @@ register_type(TypeHandle &type_handle, const string &name) { type_handle = rnode->_handle; } - _lock->unlock(); + _lock.unlock(); return false; } @@ -119,7 +119,7 @@ register_type(TypeHandle &type_handle, const string &name) { */ TypeHandle TypeRegistry:: register_dynamic_type(const string &name) { - _lock->lock(); + _lock.lock(); NameRegistry::iterator ri; ri = _name_registry.find(name); @@ -139,14 +139,14 @@ register_dynamic_type(const string &name) { _name_registry[name] = rnode; _derivations_fresh = false; - _lock->unlock(); + _lock.unlock(); return *new_handle; } // Return the TypeHandle previously obtained. TypeRegistryNode *rnode = (*ri).second; TypeHandle handle = rnode->_handle; - _lock->unlock(); + _lock.unlock(); return handle; } @@ -157,7 +157,7 @@ register_dynamic_type(const string &name) { */ void TypeRegistry:: record_derivation(TypeHandle child, TypeHandle parent) { - _lock->lock(); + _lock.lock(); TypeRegistryNode *cnode = look_up(child, nullptr); assert(cnode != nullptr); @@ -176,7 +176,7 @@ record_derivation(TypeHandle child, TypeHandle parent) { _derivations_fresh = false; } - _lock->unlock(); + _lock.unlock(); } /** @@ -187,7 +187,7 @@ record_derivation(TypeHandle child, TypeHandle parent) { */ void TypeRegistry:: record_alternate_name(TypeHandle type, const string &name) { - _lock->lock(); + _lock.lock(); TypeRegistryNode *rnode = look_up(type, nullptr); if (rnode != nullptr) { @@ -195,7 +195,7 @@ record_alternate_name(TypeHandle type, const string &name) { _name_registry.insert(NameRegistry::value_type(name, rnode)).first; if ((*ri).second != rnode) { - _lock->unlock(); + _lock.unlock(); cerr << "Name " << name << " already assigned to TypeHandle " << rnode->_name << "; cannot reassign to " << type << "\n"; @@ -204,24 +204,24 @@ record_alternate_name(TypeHandle type, const string &name) { } - _lock->unlock(); + _lock.unlock(); } #ifdef HAVE_PYTHON /** * Records the given Python type pointer in the type registry for the benefit - * of interrogate. + * of interrogate, which expects this to contain a Dtool_PyTypedObject. */ void TypeRegistry:: record_python_type(TypeHandle type, PyObject *python_type) { - _lock->lock(); + _lock.lock(); TypeRegistryNode *rnode = look_up(type, nullptr); if (rnode != nullptr) { rnode->_python_type = python_type; } - _lock->unlock(); + _lock.unlock(); } #endif @@ -231,7 +231,7 @@ record_python_type(TypeHandle type, PyObject *python_type) { */ TypeHandle TypeRegistry:: find_type(const string &name) const { - _lock->lock(); + _lock.lock(); TypeHandle handle = TypeHandle::none(); NameRegistry::const_iterator ri; @@ -239,7 +239,7 @@ find_type(const string &name) const { if (ri != _name_registry.end()) { handle = (*ri).second->_handle; } - _lock->unlock(); + _lock.unlock(); return handle; } @@ -271,11 +271,11 @@ find_type_by_id(int id) const { */ string TypeRegistry:: get_name(TypeHandle type, TypedObject *object) const { - _lock->lock(); + _lock.lock(); TypeRegistryNode *rnode = look_up(type, object); assert(rnode != nullptr); string name = rnode->_name; - _lock->unlock(); + _lock.unlock(); return name; } @@ -296,7 +296,7 @@ get_name(TypeHandle type, TypedObject *object) const { bool TypeRegistry:: is_derived_from(TypeHandle child, TypeHandle base, TypedObject *child_object) { - _lock->lock(); + _lock.lock(); const TypeRegistryNode *child_node = look_up(child, child_object); const TypeRegistryNode *base_node = look_up(base, nullptr); @@ -307,7 +307,7 @@ is_derived_from(TypeHandle child, TypeHandle base, freshen_derivations(); bool result = TypeRegistryNode::is_derived_from(child_node, base_node); - _lock->unlock(); + _lock.unlock(); return result; } @@ -316,9 +316,9 @@ is_derived_from(TypeHandle child, TypeHandle base, */ int TypeRegistry:: get_num_typehandles() { - _lock->lock(); + _lock.lock(); int num_types = (int)_handle_registry.size(); - _lock->unlock(); + _lock.unlock(); return num_types; } @@ -327,12 +327,12 @@ get_num_typehandles() { */ TypeHandle TypeRegistry:: get_typehandle(int n) { - _lock->lock(); + _lock.lock(); TypeRegistryNode *rnode = nullptr; if (n >= 0 && n < (int)_handle_registry.size()) { rnode = _handle_registry[n]; } - _lock->unlock(); + _lock.unlock(); if (rnode != nullptr) { return rnode->_handle; @@ -347,10 +347,10 @@ get_typehandle(int n) { */ int TypeRegistry:: get_num_root_classes() { - _lock->lock(); + _lock.lock(); freshen_derivations(); int num_roots = (int)_root_classes.size(); - _lock->unlock(); + _lock.unlock(); return num_roots; } @@ -359,7 +359,7 @@ get_num_root_classes() { */ TypeHandle TypeRegistry:: get_root_class(int n) { - _lock->lock(); + _lock.lock(); freshen_derivations(); TypeHandle handle; if (n >= 0 && n < (int)_root_classes.size()) { @@ -367,7 +367,7 @@ get_root_class(int n) { } else { handle = TypeHandle::none(); } - _lock->unlock(); + _lock.unlock(); return handle; } @@ -385,11 +385,11 @@ get_root_class(int n) { */ int TypeRegistry:: get_num_parent_classes(TypeHandle child, TypedObject *child_object) const { - _lock->lock(); + _lock.lock(); TypeRegistryNode *rnode = look_up(child, child_object); assert(rnode != nullptr); int num_parents = (int)rnode->_parent_classes.size(); - _lock->unlock(); + _lock.unlock(); return num_parents; } @@ -399,7 +399,7 @@ get_num_parent_classes(TypeHandle child, TypedObject *child_object) const { */ TypeHandle TypeRegistry:: get_parent_class(TypeHandle child, int index) const { - _lock->lock(); + _lock.lock(); TypeHandle handle; TypeRegistryNode *rnode = look_up(child, nullptr); assert(rnode != nullptr); @@ -408,7 +408,7 @@ get_parent_class(TypeHandle child, int index) const { } else { handle = TypeHandle::none(); } - _lock->unlock(); + _lock.unlock(); return handle; } @@ -422,11 +422,11 @@ get_parent_class(TypeHandle child, int index) const { */ int TypeRegistry:: get_num_child_classes(TypeHandle child, TypedObject *child_object) const { - _lock->lock(); + _lock.lock(); TypeRegistryNode *rnode = look_up(child, child_object); assert(rnode != nullptr); int num_children = (int)rnode->_child_classes.size(); - _lock->unlock(); + _lock.unlock(); return num_children; } @@ -436,7 +436,7 @@ get_num_child_classes(TypeHandle child, TypedObject *child_object) const { */ TypeHandle TypeRegistry:: get_child_class(TypeHandle child, int index) const { - _lock->lock(); + _lock.lock(); TypeHandle handle; TypeRegistryNode *rnode = look_up(child, nullptr); assert(rnode != nullptr); @@ -445,7 +445,7 @@ get_child_class(TypeHandle child, int index) const { } else { handle = TypeHandle::none(); } - _lock->unlock(); + _lock.unlock(); return handle; } @@ -462,7 +462,7 @@ get_child_class(TypeHandle child, int index) const { TypeHandle TypeRegistry:: get_parent_towards(TypeHandle child, TypeHandle base, TypedObject *child_object) { - _lock->lock(); + _lock.lock(); TypeHandle handle; const TypeRegistryNode *child_node = look_up(child, child_object); const TypeRegistryNode *base_node = look_up(base, nullptr); @@ -470,7 +470,7 @@ get_parent_towards(TypeHandle child, TypeHandle base, base_node != nullptr); freshen_derivations(); handle = TypeRegistryNode::get_parent_towards(child_node, base_node); - _lock->unlock(); + _lock.unlock(); return handle; } @@ -484,8 +484,7 @@ get_parent_towards(TypeHandle child, TypeHandle base, */ void TypeRegistry:: reregister_types() { - init_lock(); - _lock->lock(); + _lock.lock(); HandleRegistry::iterator ri; TypeRegistry *reg = ptr(); for (ri = reg->_handle_registry.begin(); @@ -496,7 +495,7 @@ reregister_types() { cerr << "Reregistering " << rnode->_name << "\n"; } } - _lock->unlock(); + _lock.unlock(); } @@ -506,9 +505,9 @@ reregister_types() { */ void TypeRegistry:: write(ostream &out) const { - _lock->lock(); + _lock.lock(); do_write(out); - _lock->unlock(); + _lock.unlock(); } /** @@ -540,7 +539,6 @@ TypeRegistry() { */ void TypeRegistry:: init_global_pointer() { - init_lock(); init_memory_hook(); _global_pointer = new TypeRegistry; } @@ -636,9 +634,9 @@ look_up_invalid(TypeHandle handle, TypedObject *object) const { // But we're lucky enough to have a TypedObject pointer handy! Maybe we // can use it to resolve the error. We have to drop the lock while we // do this, so we don't get a recursive lock. - _lock->unlock(); + _lock.unlock(); handle = object->force_init_type(); - _lock->lock(); + _lock.lock(); if (handle._index == 0) { // Strange. diff --git a/dtool/src/dtoolbase/typeRegistry.h b/dtool/src/dtoolbase/typeRegistry.h index 43b1b8e41f..0051ba68a4 100644 --- a/dtool/src/dtoolbase/typeRegistry.h +++ b/dtool/src/dtoolbase/typeRegistry.h @@ -101,8 +101,6 @@ private: void write_node(std::ostream &out, int indent_level, const TypeRegistryNode *node) const; - static INLINE void init_lock(); - typedef std::vector HandleRegistry; HandleRegistry _handle_registry; @@ -114,7 +112,7 @@ private: bool _derivations_fresh; - static MutexImpl *_lock; + static MutexImpl _lock; static TypeRegistry *_global_pointer; friend class TypeHandle; diff --git a/dtool/src/dtoolutil/executionEnvironment.cxx b/dtool/src/dtoolutil/executionEnvironment.cxx index 4152a8c356..79d4a07e04 100644 --- a/dtool/src/dtoolutil/executionEnvironment.cxx +++ b/dtool/src/dtoolutil/executionEnvironment.cxx @@ -85,6 +85,44 @@ extern char **GLOBAL_ARGV; extern int GLOBAL_ARGC; #endif +// One of the responsibilities of ExecutionEnvironment is to determine the path +// to the binary file that contains itself (this is useful for making other +// components able to read files relative to Panda's installation directory). +// When built statically, this is easy - just use the main executable filename. +// When built shared, ExecutionEnvironment will introspect the memory map of +// the running process to look for dynamic library paths matching this list of +// predetermined filenames (ordered most likely to least likely). + +#ifndef LINK_ALL_STATIC +static const char *const libp3dtool_filenames[] = { +#if defined(LIBP3DTOOL_FILENAMES) + + // The build system is communicating the expected filename(s) for the + // libp3dtool dynamic library - no guesswork needed. + LIBP3DTOOL_FILENAMES + +#elif defined(WIN32_VC) + +#ifdef _DEBUG + "libp3dtool_d.dll", +#else + "libp3dtool.dll", +#endif + +#elif defined(__APPLE__) + + "libp3dtool." PANDA_ABI_VERSION_STR ".dylib", + "libp3dtool.dylib", + +#else + + "libp3dtool.so." PANDA_ABI_VERSION_STR, + "libp3dtool.so", + +#endif +}; +#endif /* !LINK_ALL_STATIC */ + // Linux with GNU libc does have global argvargc variables, but we can't // safely access them at stat init time--at least, not in libc5. (It does seem // to work with glibc2, however.) @@ -546,13 +584,14 @@ read_args() { // First, we need to fill in _dtool_name. This contains the full path to // the p3dtool library. -#ifdef WIN32_VC -#ifdef _DEBUG - HMODULE dllhandle = GetModuleHandle("libp3dtool_d.dll"); -#else - HMODULE dllhandle = GetModuleHandle("libp3dtool.dll"); -#endif - if (dllhandle != 0) { +#ifndef LINK_ALL_STATIC +#if defined(WIN32_VC) + for (const char *filename : libp3dtool_filenames) { + if (!_dtool_name.empty()) break; + + HMODULE dllhandle = GetModuleHandle(filename); + if (!dllhandle) continue; + static const DWORD buffer_size = 1024; wchar_t buffer[buffer_size]; DWORD size = GetModuleFileNameW(dllhandle, buffer, buffer_size); @@ -562,46 +601,44 @@ read_args() { _dtool_name = tmp; } } -#endif -#if defined(__APPLE__) +#elif defined(__APPLE__) // And on OSX we don't have procselfmaps, but some _dyld_* functions. - if (_dtool_name.empty()) { - uint32_t ic = _dyld_image_count(); - for (uint32_t i = 0; i < ic; ++i) { - const char *buffer = _dyld_get_image_name(i); - const char *tail = strrchr(buffer, '/'); - if (tail && (strcmp(tail, "/libp3dtool." PANDA_ABI_VERSION_STR ".dylib") == 0 - || strcmp(tail, "/libp3dtool.dylib") == 0)) { + uint32_t ic = _dyld_image_count(); + for (uint32_t i = 0; i < ic; ++i) { + if (!_dtool_name.empty()) break; + + const char *buffer = _dyld_get_image_name(i); + if (!buffer) continue; + const char *tail = strrchr(buffer, '/'); + if (!tail) continue; + + for (const char *filename : libp3dtool_filenames) { + if (strcmp(&tail[1], filename) == 0) { _dtool_name = buffer; + break; } } } -#endif -#if defined(RTLD_DI_ORIGIN) +#elif defined(RTLD_DI_ORIGIN) // When building with glibc/uClibc, we typically have access to RTLD_DI_ORIGIN in Unix-like operating systems. char origin[PATH_MAX + 1]; - if (_dtool_name.empty()) { - void *dtool_handle = dlopen("libp3dtool.so." PANDA_ABI_VERSION_STR, RTLD_NOW | RTLD_NOLOAD); + for (const char *filename : libp3dtool_filenames) { + if (!_dtool_name.empty()) break; + + void *dtool_handle = dlopen(filename, RTLD_NOW | RTLD_NOLOAD); if (dtool_handle != nullptr && dlinfo(dtool_handle, RTLD_DI_ORIGIN, origin) != -1) { _dtool_name = origin; - _dtool_name += "/libp3dtool.so." PANDA_ABI_VERSION_STR; - } else { - // Try the version of libp3dtool.so without ABI suffix. - dtool_handle = dlopen("libp3dtool.so", RTLD_NOW | RTLD_NOLOAD); - if (dtool_handle != nullptr && dlinfo(dtool_handle, RTLD_DI_ORIGIN, origin) != -1) { - _dtool_name = origin; - _dtool_name += "/libp3dtool.so"; - } + _dtool_name += '/'; + _dtool_name += filename; } } -#endif -#if !defined(RTLD_DI_ORIGIN) && defined(RTLD_DI_LINKMAP) +#elif defined(RTLD_DI_LINKMAP) // On platforms without RTLD_DI_ORIGIN, we can use dlinfo with RTLD_DI_LINKMAP to get the origin of a loaded library. if (_dtool_name.empty()) { struct link_map *map; @@ -610,16 +647,24 @@ read_args() { #else void *self = dlopen(NULL, RTLD_NOW | RTLD_NOLOAD); #endif - dlinfo(self, RTLD_DI_LINKMAP, &map); + if (dlinfo(self, RTLD_DI_LINKMAP, &map)) { + while (map != nullptr) { + if (!_dtool_name.empty()) break; - while (map != nullptr) { - const char *tail = strrchr(map->l_name, '/'); - const char *head = strchr(map->l_name, '/'); - if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0 - || strcmp(tail, "/libp3dtool.so") == 0)) { - _dtool_name = head; + const char *head = strchr(map->l_name, '/'); + if (!head) continue; + const char *tail = strrchr(head, '/'); + if (!tail) continue; + + for (const char *filename : libp3dtool_filenames) { + if (strcmp(&tail[1], filename) == 0) { + _dtool_name = head; + break; + } + } + + map = map->l_next; } - map = map->l_next; } } #endif @@ -634,19 +679,27 @@ read_args() { pifstream maps("/proc/self/maps"); #endif while (!maps.fail() && !maps.eof()) { + if (!_dtool_name.empty()) break; + char buffer[PATH_MAX]; buffer[0] = 0; maps.getline(buffer, PATH_MAX); - const char *tail = strrchr(buffer, '/'); const char *head = strchr(buffer, '/'); - if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0 - || strcmp(tail, "/libp3dtool.so") == 0)) { - _dtool_name = head; + if (!head) continue; + const char *tail = strrchr(head, '/'); + if (!tail) continue; + + for (const char *filename : libp3dtool_filenames) { + if (strcmp(&tail[1], filename) == 0) { + _dtool_name = head; + break; + } } } maps.close(); } #endif +#endif /* !LINK_ALL_STATIC */ // Now, we need to fill in _binary_name. This contains the full path to the // currently running executable. diff --git a/dtool/src/dtoolutil/iostream_ext.cxx b/dtool/src/dtoolutil/iostream_ext.cxx new file mode 100644 index 0000000000..09cfaff6d7 --- /dev/null +++ b/dtool/src/dtoolutil/iostream_ext.cxx @@ -0,0 +1,317 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file iostream_ext.cxx + * @author rdb + * @date 2017-07-24 + */ + +#include "iostream_ext.h" + +#ifdef HAVE_PYTHON + +#ifndef CPPPARSER +extern struct Dtool_PyTypedObject Dtool_std_istream; +#endif + +/** + * Reads the given number of bytes from the stream, returned as bytes object. + * If the given size is -1, all bytes are read from the stream. + */ +PyObject *Extension:: +read(Py_ssize_t size) { + if (size < 0) { + return readall(); + } + + char *buffer = nullptr; + std::streamsize read_bytes = 0; + + if (size > 0) { + std::streambuf *buf = _this->rdbuf(); + nassertr(buf != nullptr, nullptr); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS +#endif + + buffer = (char *)alloca((size_t)size); + read_bytes = buf->sgetn(buffer, (size_t)size); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + Py_BLOCK_THREADS +#endif + } + +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize(buffer, read_bytes); +#else + return PyString_FromStringAndSize(buffer, read_bytes); +#endif +} + +/** + * Reads from the underlying stream, but using at most one call. The number + * of returned bytes may therefore be less than what was requested, but it + * will always be greater than 0 until EOF is reached. + */ +PyObject *Extension:: +read1(Py_ssize_t size) { + std::streambuf *buf = _this->rdbuf(); + nassertr(buf != nullptr, nullptr); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS +#endif + + std::streamsize avail = buf->in_avail(); + if (avail == 0) { + avail = 4096; + } + + if (size >= 0 && (std::streamsize)size < avail) { + avail = (std::streamsize)size; + } + + // Don't read more than 4K at a time + if (avail > 4096) { + avail = 4096; + } + + char *buffer = (char *)alloca(avail); + std::streamsize read_bytes = buf->sgetn(buffer, avail); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + Py_BLOCK_THREADS +#endif + +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize(buffer, read_bytes); +#else + return PyString_FromStringAndSize(buffer, read_bytes); +#endif +} + +/** + * Reads all of the bytes in the stream. + */ +PyObject *Extension:: +readall() { + std::streambuf *buf = _this->rdbuf(); + nassertr(buf != nullptr, nullptr); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS +#endif + + std::vector result; + + static const size_t buffer_size = 4096; + char buffer[buffer_size]; + + std::streamsize count = buf->sgetn(buffer, buffer_size); + while (count != 0) { + thread_consider_yield(); + result.insert(result.end(), buffer, buffer + count); + count = buf->sgetn(buffer, buffer_size); + } + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + Py_BLOCK_THREADS +#endif + +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize((char *)result.data(), result.size()); +#else + return PyString_FromStringAndSize((char *)result.data(), result.size()); +#endif +} + +/** + * Reads bytes into a preallocated, writable, bytes-like object, returning the + * number of bytes read. + */ +std::streamsize Extension:: +readinto(PyObject *b) { + std::streambuf *buf = _this->rdbuf(); + nassertr(buf != nullptr, 0); + + Py_buffer view; + if (PyObject_GetBuffer(b, &view, PyBUF_CONTIG) == -1) { + PyErr_SetString(PyExc_TypeError, + "write() requires a contiguous, read-write bytes-like object"); + return 0; + } + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS +#endif + + std::streamsize count = buf->sgetn((char *)view.buf, view.len); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + Py_BLOCK_THREADS +#endif + + PyBuffer_Release(&view); + return count; +} + +/** + * Extracts one line up to and including the trailing newline character. + * Returns empty string when the end of file is reached. + */ +PyObject *Extension:: +readline(Py_ssize_t size) { + std::streambuf *buf = _this->rdbuf(); + nassertr(buf != nullptr, nullptr); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS +#endif + + std::string line; + int ch = buf->sbumpc(); + while (ch != EOF && (--size) != 0) { + line.push_back(ch); + if (ch == '\n') { + // Here's the newline character. + break; + } + ch = buf->sbumpc(); + } + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + Py_BLOCK_THREADS +#endif + +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize(line.data(), line.size()); +#else + return PyString_FromStringAndSize(line.data(), line.size()); +#endif +} + +/** + * Reads all the lines at once and returns a list. Also see the documentation + * for readline(). + */ +PyObject *Extension:: +readlines(Py_ssize_t hint) { + PyObject *lst = PyList_New(0); + if (lst == nullptr) { + return nullptr; + } + + PyObject *py_line = readline(-1); + + if (hint < 0) { + while (Py_SIZE(py_line) > 0) { + PyList_Append(lst, py_line); + Py_DECREF(py_line); + + py_line = readline(-1); + } + } else { + Py_ssize_t totchars = 0; + while (Py_SIZE(py_line) > 0) { + totchars += Py_SIZE(py_line); + PyList_Append(lst, py_line); + Py_DECREF(py_line); + + if (totchars > hint) { + break; + } + + py_line = readline(-1); + } + } + + return lst; +} + +/** + * Yields continuously to read all the lines from the istream. + */ +static PyObject *gen_next(PyObject *self) { + istream *stream = nullptr; + if (!Dtool_Call_ExtractThisPointer(self, Dtool_std_istream, (void **)&stream)) { + return nullptr; + } + + PyObject *line = invoke_extension(stream).readline(); + if (Py_SIZE(line) > 0) { + return line; + } else { + PyErr_SetObject(PyExc_StopIteration, nullptr); + return nullptr; + } +} + +/** + * Iterates over the lines of the file. + */ +PyObject *Extension:: +__iter__(PyObject *self) { + return Dtool_NewGenerator(self, &gen_next); +} + +/** + * Writes the bytes object to the stream. + */ +void Extension:: +write(PyObject *b) { + std::streambuf *buf = _this->rdbuf(); + nassertv(buf != nullptr); + + Py_buffer view; + if (PyObject_GetBuffer(b, &view, PyBUF_CONTIG_RO) == -1) { + PyErr_SetString(PyExc_TypeError, "write() requires a contiguous buffer"); + return; + } + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS + buf->sputn((const char *)view.buf, view.len); + Py_BLOCK_THREADS +#else + buf->sputn((const char *)view.buf, view.len); +#endif + + PyBuffer_Release(&view); +} + +/** + * Write a list of lines to the stream. Line separators are not added, so it + * is usual for each of the lines provided to have a line separator at the + * end. + */ +void Extension:: +writelines(PyObject *lines) { + PyObject *seq = PySequence_Fast(lines, "writelines() expects a sequence"); + if (seq == nullptr) { + return; + } + + PyObject **items = PySequence_Fast_ITEMS(seq); + Py_ssize_t len = PySequence_Fast_GET_SIZE(seq); + + for (Py_ssize_t i = 0; i < len; ++i) { + write(items[i]); + } + + Py_DECREF(seq); +} + +#endif // HAVE_PYTHON diff --git a/dtool/src/dtoolutil/iostream_ext.h b/dtool/src/dtoolutil/iostream_ext.h new file mode 100644 index 0000000000..54d3ab1a43 --- /dev/null +++ b/dtool/src/dtoolutil/iostream_ext.h @@ -0,0 +1,53 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file iostream_ext.h + * @author rdb + * @date 2017-07-24 + */ + +#ifndef IOSTREAM_EXT_H +#define IOSTREAM_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include +#include "py_panda.h" + +/** + * These classes define the extension methods for istream and ostream, which + * are called instead of any C++ methods with the same prototype. + * + * These are designed to allow streams to be treated as file-like objects. + */ +template<> +class Extension : public ExtensionBase { +public: + PyObject *read(Py_ssize_t size=-1); + PyObject *read1(Py_ssize_t size=-1); + PyObject *readall(); + std::streamsize readinto(PyObject *b); + + PyObject *readline(Py_ssize_t size=-1); + PyObject *readlines(Py_ssize_t hint=-1); + PyObject *__iter__(PyObject *self); +}; + +template<> +class Extension : public ExtensionBase { +public: + void write(PyObject *b); + void writelines(PyObject *lines); +}; + +#endif // HAVE_PYTHON + +#endif // IOSTREAM_EXT_H diff --git a/dtool/src/dtoolutil/load_dso.cxx b/dtool/src/dtoolutil/load_dso.cxx index 79d821b5fa..fcd0c9fffe 100644 --- a/dtool/src/dtoolutil/load_dso.cxx +++ b/dtool/src/dtoolutil/load_dso.cxx @@ -50,19 +50,7 @@ load_dso(const DSearchPath &path, const Filename &filename) { return nullptr; } std::wstring os_specific_w = abspath.to_os_specific_w(); - - // Try using LoadLibraryEx, if possible. - typedef HMODULE (WINAPI *tLoadLibraryEx)(LPCWSTR, HANDLE, DWORD); - tLoadLibraryEx pLoadLibraryEx; - HINSTANCE hLib = LoadLibrary("kernel32.dll"); - if (hLib) { - pLoadLibraryEx = (tLoadLibraryEx)GetProcAddress(hLib, "LoadLibraryExW"); - if (pLoadLibraryEx) { - return pLoadLibraryEx(os_specific_w.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH); - } - } - - return LoadLibraryW(os_specific_w.c_str()); + return LoadLibraryExW(os_specific_w.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH); } bool @@ -132,6 +120,9 @@ void * load_dso(const DSearchPath &path, const Filename &filename) { Filename abspath = resolve_dso(path, filename); if (!abspath.is_regular_file()) { + // Make sure the error flag is cleared, to prevent a subsequent call to + // load_dso_error() from returning a previously stored error. + dlerror(); return nullptr; } string os_specific = abspath.to_os_specific(); diff --git a/dtool/src/dtoolutil/p3dtoolutil_ext_composite.cxx b/dtool/src/dtoolutil/p3dtoolutil_ext_composite.cxx index 2cd825ff58..ceb2f01bf1 100644 --- a/dtool/src/dtoolutil/p3dtoolutil_ext_composite.cxx +++ b/dtool/src/dtoolutil/p3dtoolutil_ext_composite.cxx @@ -1,3 +1,4 @@ #include "filename_ext.cxx" #include "globPattern_ext.cxx" +#include "iostream_ext.cxx" #include "textEncoder_ext.cxx" diff --git a/dtool/src/dtoolutil/pandaSystem.cxx b/dtool/src/dtoolutil/pandaSystem.cxx index 3d80d0f6b5..c3a6fda015 100644 --- a/dtool/src/dtoolutil/pandaSystem.cxx +++ b/dtool/src/dtoolutil/pandaSystem.cxx @@ -33,8 +33,8 @@ PandaSystem() : // These are settable via Config.prc, but only in development (!NDEBUG) // mode, and only if they are not already defined. - _package_version_string = PANDA_PACKAGE_VERSION_STR; - _package_host_url = PANDA_PACKAGE_HOST_URL; + _package_version_string = ""; + _package_host_url = ""; #ifdef STDFLOAT_DOUBLE add_system("stdfloat-double"); @@ -94,14 +94,13 @@ get_version_string() { * If this string is empty, then the currently-executing Panda was built * independently, and is not part of a distributable package. * - * This string is set explicitly at compilation time. Normally, it should be - * set to a nonempty string only when building a Panda3D package for - * distribution. + * @deprecated Runtime/plugin environment has been removed, this now always + * returns an empty string. */ string PandaSystem:: get_package_version_string() { #ifdef NDEBUG - return PANDA_PACKAGE_VERSION_STR; + return ""; #else return get_global_ptr()->_package_version_string; #endif @@ -118,11 +117,14 @@ get_package_version_string() { * This string is set explicitly at compilation time. Normally, it should be * set to a nonempty string only when building a Panda3D package for * distribution. + * + * @deprecated Runtime/plugin environment has been removed, this now always + * returns an empty string. */ string PandaSystem:: get_package_host_url() { #ifdef NDEBUG - return PANDA_PACKAGE_HOST_URL; + return ""; #else return get_global_ptr()->_package_host_url; #endif @@ -138,14 +140,13 @@ get_package_host_url() { * provide a particular Core API, which will be the normal case in a * development SDK. However, you should not use this method to determine * whether you are running in a runtime environment or not. + * + * @deprecated Runtime/plugin environment has been removed, this now always + * returns an empty string. */ string PandaSystem:: get_p3d_coreapi_version_string() { -#ifndef P3D_COREAPI_VERSION_STR return ""; -#else - return P3D_COREAPI_VERSION_STR; -#endif } /** @@ -468,7 +469,7 @@ reset_system_names() { */ void PandaSystem:: set_package_version_string(const string &package_version_string) { - _package_version_string = PANDA_PACKAGE_VERSION_STR; + _package_version_string = ""; if (_package_version_string.empty()) { _package_version_string = package_version_string; } @@ -483,7 +484,7 @@ set_package_version_string(const string &package_version_string) { */ void PandaSystem:: set_package_host_url(const string &package_host_url) { - _package_host_url = PANDA_PACKAGE_HOST_URL; + _package_host_url = ""; if (_package_host_url.empty()) { _package_host_url = package_host_url; } diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index fabbb8a311..d5d6ab643c 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -397,17 +397,25 @@ get_call_str(const string &container, const vector_string &pexprs) const { } // It's not possible to assign arrays in C++, we have to copy them. - CPPArrayType *array_type = _parameters[_first_true_parameter]._remap->get_orig_type()->as_array_type(); + bool paren_close = false; + CPPType *param_type = _parameters[_first_true_parameter]._remap->get_orig_type(); + CPPArrayType *array_type = param_type->as_array_type(); if (array_type != nullptr) { call << "std::copy(" << expr << ", " << expr << " + " << *array_type->_bounds << ", "; - } else { + paren_close = true; + } + else if (TypeManager::is_pointer_to_PyObject(param_type)) { + call << "Dtool_Assign_PyObject(" << expr << ", "; + paren_close = true; + } + else { call << expr << " = "; } _parameters[_first_true_parameter]._remap->pass_parameter(call, get_parameter_expr(_first_true_parameter, pexprs)); - if (array_type != nullptr) { + if (paren_close) { call << ')'; } @@ -772,6 +780,11 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak _return_value_destructor = builder.get_destructor_for(return_meat_type); } + if (_type == T_getter && TypeManager::is_pointer_to_PyObject(return_type)) { + _manage_reference_count = true; + _return_value_needs_management = true; + } + // Check for a special meaning by name and signature. size_t first_param = 0; if (_has_this) { diff --git a/dtool/src/interrogate/interfaceMakerC.cxx b/dtool/src/interrogate/interfaceMakerC.cxx index 64b36fc366..0deaa685be 100644 --- a/dtool/src/interrogate/interfaceMakerC.cxx +++ b/dtool/src/interrogate/interfaceMakerC.cxx @@ -191,7 +191,7 @@ write_function_instance(ostream &out, InterfaceMaker::Function *func, out << "/*\n" << " * C wrapper for\n" << " * "; - remap->write_orig_prototype(out, 0); + remap->write_orig_prototype(out, 0, false, remap->_num_default_parameters); out << "\n" << " */\n"; diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index ca2ed7eb8d..5e8f76e9a5 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -117,6 +117,8 @@ const char *pythonKeywords[] = { "and", "as", "assert", + "async", + "await", "break", "class", "continue", @@ -431,6 +433,12 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap, return true; } + if (method_name == "operator |=") { + def._answer_location = "nb_inplace_or"; + def._wrapper_type = WT_inplace_binary_operator; + return true; + } + if (method_name == "__ipow__") { def._answer_location = "nb_inplace_power"; def._wrapper_type = WT_inplace_ternary_operator; @@ -1690,7 +1698,6 @@ write_module_class(ostream &out, Object *obj) { SlottedFunctionDef def; def._answer_location = true_key; def._wrapper_type = slotted_def._wrapper_type; - def._min_version = 0x03000000; def._wrapper_name = func->_name + "_" + true_key; slots[true_key] = def; } @@ -1860,6 +1867,12 @@ write_module_class(ostream &out, Object *obj) { } else { return_flags |= RF_pyobject; } + bool all_nonconst = true; + for (FunctionRemap *remap : def._remaps) { + if (remap->_const_method) { + all_nonconst = false; + } + } out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n"; @@ -1872,9 +1885,18 @@ write_module_class(ostream &out, Object *obj) { // This is for things like __sub__, which Python likes to call on // the wrong-type objects. out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; - out << " if (local_this == nullptr) {\n"; + if (all_nonconst) { + out << " if (local_this == nullptr || DtoolInstance_IS_CONST(self)) {\n"; + } else { + out << " if (local_this == nullptr) {\n"; + } out << " Py_INCREF(Py_NotImplemented);\n"; out << " return Py_NotImplemented;\n"; + } else if (all_nonconst) { + out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" + << ClassName << ", (void **)&local_this, \"" << ClassName + << "." << methodNameFromCppName(fname, "", false) << "\")) {\n"; + out << " return nullptr;\n"; } else { out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; out << " return nullptr;\n"; @@ -1883,7 +1905,7 @@ write_module_class(ostream &out, Object *obj) { string expected_params; write_function_forset(out, def._remaps, 1, 1, expected_params, 2, true, true, - AT_single_arg, return_flags, false); + AT_single_arg, return_flags, false, !all_nonconst); if (rfi->second._wrapper_type != WT_one_param) { out << " Py_INCREF(Py_NotImplemented);\n"; @@ -2858,8 +2880,13 @@ write_module_class(ostream &out, Object *obj) { // destructor tp_dealloc; out << " &Dtool_FreeInstance_" << ClassName << ",\n"; - // printfunc tp_print; + + out << "#if PY_VERSION_HEX >= 0x03080000\n"; + out << " 0, // tp_vectorcall_offset\n"; + out << "#else\n"; write_function_slot(out, 4, slots, "tp_print"); + out << "#endif\n"; + // getattrfunc tp_getattr; write_function_slot(out, 4, slots, "tp_getattr"); // setattrfunc tp_setattr; @@ -3050,6 +3077,10 @@ write_module_class(ostream &out, Object *obj) { out << "#if PY_VERSION_HEX >= 0x03040000\n"; out << " nullptr, // tp_finalize\n"; out << "#endif\n"; + // vectorcallfunc tp_vectorcall + out << "#if PY_VERSION_HEX >= 0x03080000\n"; + out << " nullptr, // tp_vectorcall\n"; + out << "#endif\n"; out << " },\n"; // It's tempting to initialize the type handle here, but this causes static @@ -4701,7 +4732,7 @@ write_function_instance(ostream &out, FunctionRemap *remap, if (is_optional) { extra_convert - << "wchar_t *" << param_name << "_str;\n" + << "wchar_t *" << param_name << "_str = nullptr;\n" << "if (" << param_name << " != nullptr) {\n" << "#if PY_VERSION_HEX >= 0x03030000\n" << " " << param_name << "_str = PyUnicode_AsWideCharString(" << param_name << ", nullptr);\n" @@ -4748,7 +4779,7 @@ write_function_instance(ostream &out, FunctionRemap *remap, if (is_optional) { extra_convert << "Py_ssize_t " << param_name << "_len;\n" - << "wchar_t *" << param_name << "_str;\n" + << "wchar_t *" << param_name << "_str = nullptr;\n" << "std::wstring " << param_name << "_wstr;\n" << "if (" << param_name << " != nullptr) {\n" << "#if PY_VERSION_HEX >= 0x03030000\n" @@ -4932,13 +4963,14 @@ write_function_instance(ostream &out, FunctionRemap *remap, expected_params += "NoneType"; } else if (TypeManager::is_char(type)) { - indent(out, indent_level) << "char " << param_name << default_expr << ";\n"; + indent(out, indent_level) << "char *" << param_name << "_str;\n"; + indent(out, indent_level) << "Py_ssize_t " << param_name << "_len;\n"; - format_specifiers += "c"; - parameter_list += ", &" + param_name; + format_specifiers += "s#"; + parameter_list += ", &" + param_name + "_str, &" + param_name + "_len"; + extra_param_check << " && " << param_name << "_len == 1"; - // extra_param_check << " && isascii(" << param_name << ")"; - pexpr_string = "(char) " + param_name; + pexpr_string = param_name + "_str[0]"; expected_params += "char"; only_pyobjects = false; @@ -5980,7 +6012,11 @@ write_function_instance(ostream &out, FunctionRemap *remap, indent(out, indent_level) << "}\n"; } - return_expr = manage_return_value(out, indent_level, remap, "return_value"); + if (TypeManager::is_pointer_to_PyObject(remap->_return_type->get_orig_type())) { + indent(out, indent_level) << "Py_XINCREF(return_value);\n"; + } else { + return_expr = manage_return_value(out, indent_level, remap, "return_value"); + } return_expr = remap->_return_type->temporary_to_return(return_expr); } @@ -7526,6 +7562,12 @@ is_remap_legal(FunctionRemap *remap) { } } + // Don't export global operators. + if (!remap->_has_this && + remap->_cppfunc->get_simple_name().compare(0, 9, "operator ") == 0) { + return false; + } + // ok all looks ok. return true; } diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.h b/dtool/src/interrogate/interfaceMakerPythonNative.h index 8b18ef1957..c667ab22fa 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.h +++ b/dtool/src/interrogate/interfaceMakerPythonNative.h @@ -121,7 +121,7 @@ private: public: std::string _answer_location; WrapperType _wrapper_type; - int _min_version; + int _min_version = 0; std::string _wrapper_name; std::set _remaps; bool _keep_method; diff --git a/dtool/src/interrogate/interrogate.cxx b/dtool/src/interrogate/interrogate.cxx index e3cab85a6b..8a69843bb3 100644 --- a/dtool/src/interrogate/interrogate.cxx +++ b/dtool/src/interrogate/interrogate.cxx @@ -574,6 +574,8 @@ main(int argc, char **argv) { the_output_include = &output_include; } + int status = 0; + // Now output all of the wrapper functions. if (!output_code_filename.empty()) { @@ -595,6 +597,7 @@ main(int argc, char **argv) { if (output_code.fail()) { nout << "Unable to write to " << output_code_filename << "\n"; + status = -1; } else { builder.write_code(output_code,the_output_include, def); } @@ -609,13 +612,13 @@ main(int argc, char **argv) { pofstream output_data; output_data_filename.open_write(output_data); - if (output_data.fail()) - { + if (output_data.fail()) { nout << "Unable to write to " << output_data_filename << "\n"; + status = -1; } else { InterrogateDatabase::get_ptr()->write(output_data, def); } } - return (0); + return status; } diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index 7473d085d3..77528e6474 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -388,9 +388,6 @@ write_code(ostream &out_code,ostream * out_include, InterrogateModuleDef *def) { declaration_bodies << "#include \n"; if (build_python_native) { - if (library_name.size() > 1) { - declaration_bodies << "#define PANDA_LIBRARY_NAME_" << library_name << "\n"; - } declaration_bodies << "#include \"py_panda.h\"\n"; declaration_bodies << "#include \"extension.h\"\n"; declaration_bodies << "#include \"dcast.h\"\n"; @@ -2038,33 +2035,11 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP iproperty._length_function = length_function; } - if (make_property->_type == CPPMakeProperty::T_normal) { - if (getter != nullptr) { - iproperty._flags |= InterrogateElement::F_has_getter; - iproperty._getter = get_function(getter, "", struct_type, - struct_type->get_scope(), 0); - nassertr(iproperty._getter, 0); - } - } else { - // We could have a mixed sequence/mapping property, so synthesize a - // getitem function. We don't really care what's in here; we just use - // this to store the remaps. - if (!iproperty.has_getter()) { - iproperty._flags |= InterrogateElement::F_has_getter; - iproperty._getter = InterrogateDatabase::get_ptr()->get_next_index(); - InterrogateFunction *ifunction = new InterrogateFunction; - ifunction->_instances = new InterrogateFunction::Instances; - InterrogateDatabase::get_ptr()->add_function(iproperty._getter, ifunction); - } - - // Add our getter to the generated getitem function. - string signature = TypeManager::get_function_signature(getter); - InterrogateFunction &ifunction = - InterrogateDatabase::get_ptr()->update_function(iproperty._getter); - if (ifunction._instances == nullptr) { - ifunction._instances = new InterrogateFunction::Instances; - } - ifunction._instances->insert(InterrogateFunction::Instances::value_type(signature, getter)); + if (getter != nullptr) { + iproperty._flags |= InterrogateElement::F_has_getter; + iproperty._getter = get_function(getter, "", struct_type, + struct_type->get_scope(), 0); + nassertr(iproperty._getter, 0); } if (hasser != nullptr) { diff --git a/dtool/src/interrogate/parameterRemapBasicStringToString.cxx b/dtool/src/interrogate/parameterRemapBasicStringToString.cxx index 21faabfaac..46ce08c9c7 100644 --- a/dtool/src/interrogate/parameterRemapBasicStringToString.cxx +++ b/dtool/src/interrogate/parameterRemapBasicStringToString.cxx @@ -51,7 +51,7 @@ pass_parameter(ostream &out, const string &variable_name) { string ParameterRemapBasicStringToString:: prepare_return_expr(ostream &out, int indent_level, const string &expression) { InterfaceMaker::indent(out, indent_level) - << "static string string_holder = " << expression << ";\n"; + << "static std::string string_holder = " << expression << ";\n"; return "string_holder"; } @@ -97,7 +97,7 @@ pass_parameter(ostream &out, const string &variable_name) { string ParameterRemapBasicWStringToWString:: prepare_return_expr(ostream &out, int indent_level, const string &expression) { InterfaceMaker::indent(out, indent_level) - << "static wstring string_holder = " << expression << ";\n"; + << "static std::wstring string_holder = " << expression << ";\n"; return "string_holder"; } diff --git a/dtool/src/interrogatedb/dtool_super_base.cxx b/dtool/src/interrogatedb/dtool_super_base.cxx index 9422084299..918ab36cc8 100644 --- a/dtool/src/interrogatedb/dtool_super_base.cxx +++ b/dtool/src/interrogatedb/dtool_super_base.cxx @@ -80,7 +80,7 @@ Dtool_PyTypedObject *Dtool_GetSuperBase() { sizeof(Dtool_PyInstDef), 0, // tp_itemsize &Dtool_FreeInstance_DTOOL_SUPER_BASE, - nullptr, // tp_print + 0, // tp_vectorcall_offset nullptr, // tp_getattr nullptr, // tp_setattr #if PY_MAJOR_VERSION >= 3 @@ -129,6 +129,13 @@ Dtool_PyTypedObject *Dtool_GetSuperBase() { nullptr, // tp_subclasses nullptr, // tp_weaklist nullptr, // tp_del + 0, // tp_version_tag, +#if PY_VERSION_HEX >= 0x03040000 + nullptr, // tp_finalize +#endif +#if PY_VERSION_HEX >= 0x03080000 + nullptr, // tp_vectorcall +#endif }, TypeHandle::none(), Dtool_PyModuleClassInit_DTOOL_SUPER_BASE, diff --git a/dtool/src/interrogatedb/py_compat.h b/dtool/src/interrogatedb/py_compat.h index 11771d1948..a0d52c7fdc 100644 --- a/dtool/src/interrogatedb/py_compat.h +++ b/dtool/src/interrogatedb/py_compat.h @@ -139,7 +139,7 @@ typedef long Py_hash_t; /* Python 3.6 */ -#ifndef _PyObject_CallNoArg +#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 diff --git a/dtool/src/interrogatedb/py_panda.I b/dtool/src/interrogatedb/py_panda.I index 12f20f1f1e..d6b95efe35 100644 --- a/dtool/src/interrogatedb/py_panda.I +++ b/dtool/src/interrogatedb/py_panda.I @@ -90,6 +90,19 @@ INLINE PyObject *DtoolInstance_RichComparePointers(PyObject *v1, PyObject *v2, i Py_RETURN_RICHCOMPARE(cmpval, 0, op); } +/** + * Utility function for assigning a PyObject pointer while managing refcounts. + */ +ALWAYS_INLINE void +Dtool_Assign_PyObject(PyObject *&ptr, PyObject *value) { + PyObject *prev_value = ptr; + if (prev_value != value) { + Py_XINCREF(value); + ptr = value; + Py_XDECREF(prev_value); + } +} + /** * Converts the enum value to a C long. */ diff --git a/dtool/src/interrogatedb/py_panda.h b/dtool/src/interrogatedb/py_panda.h index 353af53608..026d1c6858 100644 --- a/dtool/src/interrogatedb/py_panda.h +++ b/dtool/src/interrogatedb/py_panda.h @@ -238,6 +238,8 @@ EXPCL_PYPANDA PyObject *_Dtool_Return(PyObject *value); #define Dtool_Return(value) _Dtool_Return(value) #endif +ALWAYS_INLINE void Dtool_Assign_PyObject(PyObject *&ptr, PyObject *value); + /** * Wrapper around Python 3.4's enum library, which does not have a C API. */ diff --git a/dtool/src/interrogatedb/py_wrappers.cxx b/dtool/src/interrogatedb/py_wrappers.cxx index 0b8a751cad..6aed3d2bbb 100644 --- a/dtool/src/interrogatedb/py_wrappers.cxx +++ b/dtool/src/interrogatedb/py_wrappers.cxx @@ -47,6 +47,7 @@ static void Dtool_WrapperBase_dealloc(PyObject *self) { Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self; nassertv(wrap); Py_XDECREF(wrap->_self); + Py_TYPE(self)->tp_free(self); } static PyObject *Dtool_WrapperBase_repr(PyObject *self) { @@ -540,7 +541,7 @@ static PyObject *Dtool_MappingWrapper_keys(PyObject *self, PyObject *) { sizeof(Dtool_SequenceWrapper), 0, // tp_itemsize Dtool_WrapperBase_dealloc, - nullptr, // tp_print + 0, // tp_vectorcall_offset nullptr, // tp_getattr nullptr, // tp_setattr nullptr, // tp_compare @@ -581,6 +582,13 @@ static PyObject *Dtool_MappingWrapper_keys(PyObject *self, PyObject *) { nullptr, // tp_subclasses nullptr, // tp_weaklist nullptr, // tp_del + 0, // tp_version_tag, +#if PY_VERSION_HEX >= 0x03040000 + nullptr, // tp_finalize +#endif +#if PY_VERSION_HEX >= 0x03080000 + nullptr, // tp_vectorcall +#endif }; static bool registered = false; @@ -674,7 +682,7 @@ static PyObject *Dtool_MappingWrapper_values(PyObject *self, PyObject *) { sizeof(Dtool_MappingWrapper), 0, // tp_itemsize Dtool_WrapperBase_dealloc, - nullptr, // tp_print + 0, // tp_vectorcall_offset nullptr, // tp_getattr nullptr, // tp_setattr nullptr, // tp_compare @@ -715,6 +723,13 @@ static PyObject *Dtool_MappingWrapper_values(PyObject *self, PyObject *) { nullptr, // tp_subclasses nullptr, // tp_weaklist nullptr, // tp_del + 0, // tp_version_tag, +#if PY_VERSION_HEX >= 0x03040000 + nullptr, // tp_finalize +#endif +#if PY_VERSION_HEX >= 0x03080000 + nullptr, // tp_vectorcall +#endif }; static bool registered = false; @@ -816,7 +831,7 @@ static PyObject *Dtool_MappingWrapper_items(PyObject *self, PyObject *) { sizeof(Dtool_MappingWrapper), 0, // tp_itemsize Dtool_WrapperBase_dealloc, - nullptr, // tp_print + 0, // tp_vectorcall_offset nullptr, // tp_getattr nullptr, // tp_setattr nullptr, // tp_compare @@ -857,6 +872,13 @@ static PyObject *Dtool_MappingWrapper_items(PyObject *self, PyObject *) { nullptr, // tp_subclasses nullptr, // tp_weaklist nullptr, // tp_del + 0, // tp_version_tag, +#if PY_VERSION_HEX >= 0x03040000 + nullptr, // tp_finalize +#endif +#if PY_VERSION_HEX >= 0x03080000 + nullptr, // tp_vectorcall +#endif }; static bool registered = false; @@ -1091,7 +1113,11 @@ static PyObject *Dtool_GeneratorWrapper_iternext(PyObject *self) { */ static void Dtool_StaticProperty_dealloc(PyDescrObject *descr) { +#if PY_VERSION_HEX >= 0x03080000 + PyObject_GC_UnTrack(descr); +#else _PyObject_GC_UNTRACK(descr); +#endif Py_XDECREF(descr->d_type); Py_XDECREF(descr->d_name); //#if PY_MAJOR_VERSION >= 3 @@ -1187,7 +1213,7 @@ Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name sizeof(Dtool_SequenceWrapper), 0, // tp_itemsize Dtool_WrapperBase_dealloc, - nullptr, // tp_print + 0, // tp_vectorcall_offset nullptr, // tp_getattr nullptr, // tp_setattr nullptr, // tp_compare @@ -1228,6 +1254,13 @@ Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name nullptr, // tp_subclasses nullptr, // tp_weaklist nullptr, // tp_del + 0, // tp_version_tag, +#if PY_VERSION_HEX >= 0x03040000 + nullptr, // tp_finalize +#endif +#if PY_VERSION_HEX >= 0x03080000 + nullptr, // tp_vectorcall +#endif }; static bool registered = false; @@ -1291,7 +1324,7 @@ Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, co sizeof(Dtool_MutableSequenceWrapper), 0, // tp_itemsize Dtool_WrapperBase_dealloc, - nullptr, // tp_print + 0, // tp_vectorcall_offset nullptr, // tp_getattr nullptr, // tp_setattr nullptr, // tp_compare @@ -1332,6 +1365,13 @@ Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, co nullptr, // tp_subclasses nullptr, // tp_weaklist nullptr, // tp_del + 0, // tp_version_tag, +#if PY_VERSION_HEX >= 0x03040000 + nullptr, // tp_finalize +#endif +#if PY_VERSION_HEX >= 0x03080000 + nullptr, // tp_vectorcall +#endif }; static bool registered = false; @@ -1399,7 +1439,7 @@ Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name) sizeof(Dtool_MappingWrapper), 0, // tp_itemsize Dtool_WrapperBase_dealloc, - nullptr, // tp_print + 0, // tp_vectorcall_offset nullptr, // tp_getattr nullptr, // tp_setattr nullptr, // tp_compare @@ -1440,6 +1480,13 @@ Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name) nullptr, // tp_subclasses nullptr, // tp_weaklist nullptr, // tp_del + 0, // tp_version_tag, +#if PY_VERSION_HEX >= 0x03040000 + nullptr, // tp_finalize +#endif +#if PY_VERSION_HEX >= 0x03080000 + nullptr, // tp_vectorcall +#endif }; static bool registered = false; @@ -1512,7 +1559,7 @@ Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char sizeof(Dtool_MappingWrapper), 0, // tp_itemsize Dtool_WrapperBase_dealloc, - nullptr, // tp_print + 0, // tp_vectorcall_offset nullptr, // tp_getattr nullptr, // tp_setattr nullptr, // tp_compare @@ -1553,6 +1600,13 @@ Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char nullptr, // tp_subclasses nullptr, // tp_weaklist nullptr, // tp_del + 0, // tp_version_tag, +#if PY_VERSION_HEX >= 0x03040000 + nullptr, // tp_finalize +#endif +#if PY_VERSION_HEX >= 0x03080000 + nullptr, // tp_vectorcall +#endif }; static bool registered = false; @@ -1589,7 +1643,7 @@ Dtool_NewGenerator(PyObject *self, iternextfunc gen_next) { sizeof(Dtool_GeneratorWrapper), 0, // tp_itemsize Dtool_WrapperBase_dealloc, - nullptr, // tp_print + 0, // tp_vectorcall_offset nullptr, // tp_getattr nullptr, // tp_setattr nullptr, // tp_compare @@ -1630,6 +1684,13 @@ Dtool_NewGenerator(PyObject *self, iternextfunc gen_next) { nullptr, // tp_subclasses nullptr, // tp_weaklist nullptr, // tp_del + 0, // tp_version_tag, +#if PY_VERSION_HEX >= 0x03040000 + nullptr, // tp_finalize +#endif +#if PY_VERSION_HEX >= 0x03080000 + nullptr, // tp_vectorcall +#endif }; if (PyType_Ready(&wrapper_type) < 0) { @@ -1658,7 +1719,7 @@ Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) { sizeof(PyGetSetDescrObject), 0, // tp_itemsize (destructor)Dtool_StaticProperty_dealloc, - nullptr, // tp_print + 0, // tp_vectorcall_offset nullptr, // tp_getattr nullptr, // tp_setattr nullptr, // tp_reserved @@ -1691,7 +1752,7 @@ Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) { nullptr, // tp_init nullptr, // tp_alloc nullptr, // tp_new - nullptr, // tp_del + nullptr, // tp_free nullptr, // tp_is_gc nullptr, // tp_bases nullptr, // tp_mro @@ -1699,6 +1760,13 @@ Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) { nullptr, // tp_subclasses nullptr, // tp_weaklist nullptr, // tp_del + 0, // tp_version_tag, +#if PY_VERSION_HEX >= 0x03040000 + nullptr, // tp_finalize +#endif +#if PY_VERSION_HEX >= 0x03080000 + nullptr, // tp_vectorcall +#endif }; if (PyType_Ready(&wrapper_type) < 0) { diff --git a/dtool/src/parser-inc/atomic b/dtool/src/parser-inc/atomic index a7e174937b..8167308f33 100644 --- a/dtool/src/parser-inc/atomic +++ b/dtool/src/parser-inc/atomic @@ -16,4 +16,50 @@ namespace std { template struct atomic; struct atomic_flag; + + using atomic_bool = atomic; + using atomic_char = atomic; + using atomic_schar = atomic; + using atomic_uchar = atomic; + using atomic_short = atomic; + using atomic_ushort = atomic; + using atomic_int = atomic; + using atomic_uint = atomic; + using atomic_long = atomic; + using atomic_ulong = atomic; + using atomic_llong = atomic; + using atomic_ullong = atomic; + using atomic_char16_t = atomic; + using atomic_char32_t = atomic; + using atomic_wchar_t = atomic; + using atomic_int8_t = atomic; + using atomic_uint8_t = atomic; + using atomic_int16_t = atomic; + using atomic_uint16_t = atomic; + using atomic_int32_t = atomic; + using atomic_uint32_t = atomic; + using atomic_int64_t = atomic; + using atomic_uint64_t = atomic; + using atomic_int_least8_t = atomic; + using atomic_uint_least8_t = atomic; + using atomic_int_least16_t = atomic; + using atomic_uint_least16_t = atomic; + using atomic_int_least32_t = atomic; + using atomic_uint_least32_t = atomic; + using atomic_int_least64_t = atomic; + using atomic_uint_least64_t = atomic; + using atomic_int_fast8_t = atomic; + using atomic_uint_fast8_t = atomic; + using atomic_int_fast16_t = atomic; + using atomic_uint_fast16_t = atomic; + using atomic_int_fast32_t = atomic; + using atomic_uint_fast32_t = atomic; + using atomic_int_fast64_t = atomic; + using atomic_uint_fast64_t = atomic; + using atomic_intptr_t = atomic; + using atomic_uintptr_t = atomic; + using atomic_size_t = atomic; + using atomic_ptrdiff_t = atomic; + using atomic_intmax_t = atomic; + using atomic_uintmax_t = atomic; } diff --git a/dtool/src/parser-inc/iostream b/dtool/src/parser-inc/iostream index 8bfe91d0aa..4524e7486d 100644 --- a/dtool/src/parser-inc/iostream +++ b/dtool/src/parser-inc/iostream @@ -1,16 +1,15 @@ -// Filename: iostream -// Created by: drose (12May00) -// -//////////////////////////////////////////////////////////////////// -// -// PANDA 3D SOFTWARE -// Copyright (c) Carnegie Mellon University. All rights reserved. -// -// All use of this software is subject to the terms of the revised BSD -// license. You should have received a copy of this license along -// with this source code in a file named "LICENSE." -// -//////////////////////////////////////////////////////////////////// +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file iostream + * @author drose + * @date 2000-05-12 + */ // This file, and all the other files in this directory, aren't // intended to be compiled--they're just parsed by CPPParser (and @@ -24,6 +23,7 @@ #include #include #include +#include // We don't care (much) about the actual definition of the various // iostream classes, but we do need to know the classnames that are @@ -34,6 +34,9 @@ namespace std { __published: ostream(const ostream&) = delete; + __extension void write(PyObject *b); + __extension void writelines(PyObject *lines); + void put(char c); void flush(); streampos tellp(); @@ -43,10 +46,20 @@ namespace std { protected: ostream(ostream &&); }; + class istream : virtual public ios { __published: istream(const istream&) = delete; + __extension PyObject *read(Py_ssize_t size=-1); + __extension PyObject *read1(Py_ssize_t size=-1); + __extension PyObject *readall(); + __extension std::streamsize readinto(PyObject *b); + + __extension PyObject *readline(Py_ssize_t size=-1); + __extension PyObject *readlines(Py_ssize_t hint=-1); + __extension PyObject *__iter__(PyObject *self); + int get(); streampos tellg(); void seekg(streampos pos); @@ -55,6 +68,7 @@ namespace std { protected: istream(istream &&); }; + class iostream : public istream, public ostream { __published: iostream(const iostream&) = delete; diff --git a/dtool/src/parser-inc/libtar.h b/dtool/src/parser-inc/libtar.h deleted file mode 100644 index af0be1f33a..0000000000 --- a/dtool/src/parser-inc/libtar.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file libtar.h - * @author drose - * @date 2007-06-28 - */ - diff --git a/dtool/src/parser-inc/synchapi.h b/dtool/src/parser-inc/synchapi.h new file mode 100755 index 0000000000..3332ebd6e7 --- /dev/null +++ b/dtool/src/parser-inc/synchapi.h @@ -0,0 +1,10 @@ +#ifndef SYNCHAPI_H +#define SYNCHAPI_H + +#include "winnt.h" + +#define SRWLOCK_INIT RTL_SRWLOCK_INIT + +typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK; + +#endif diff --git a/dtool/src/parser-inc/winnt.h b/dtool/src/parser-inc/winnt.h new file mode 100755 index 0000000000..0984c7312f --- /dev/null +++ b/dtool/src/parser-inc/winnt.h @@ -0,0 +1,16 @@ +#ifndef WINNT_H +#define WINNT_H + +typedef void *PVOID; + +typedef struct _RTL_SRWLOCK { + PVOID Ptr; +} RTL_SRWLOCK, *PRTL_SRWLOCK; + +#define RTL_SRWLOCK_INIT {0} + +typedef struct _RTL_CONDITION_VARIABLE { + PVOID Ptr; +} RTL_CONDITION_VARIABLE, *PRTL_CONDITION_VARIABLE; + +#endif diff --git a/dtool/src/prc/configDeclaration.cxx b/dtool/src/prc/configDeclaration.cxx index b401df73d4..3f8e7582cd 100644 --- a/dtool/src/prc/configDeclaration.cxx +++ b/dtool/src/prc/configDeclaration.cxx @@ -21,6 +21,8 @@ using std::string; +static MutexImpl this_prc_dir_lock; + /** * Use the ConfigPage::make_declaration() interface to create a new * declaration. @@ -139,13 +141,6 @@ set_double_word(size_t n, double value) { */ Filename ConfigDeclaration:: get_filename_value() const { - // Since we are about to set THIS_PRC_DIR globally, we need to ensure that - // no two threads call this method at the same time. - // NB. MSVC doesn't guarantee that this mutex is initialized in a - // thread-safe manner. But chances are that the first time this is called - // is at static init time, when there is no risk of data races. - static MutexImpl lock; - string str = _string_value; // Are there any variables to be expanded? @@ -153,11 +148,13 @@ get_filename_value() const { Filename page_filename(_page->get_name()); Filename page_dirname = page_filename.get_dirname(); - lock.lock(); + // Since we are about to set THIS_PRC_DIR globally, we need to ensure that + // no two threads call this method at the same time. + this_prc_dir_lock.lock(); ExecutionEnvironment::shadow_environment_variable("THIS_PRC_DIR", page_dirname.to_os_specific()); str = ExecutionEnvironment::expand_string(str); ExecutionEnvironment::clear_shadow("THIS_PRC_DIR"); - lock.unlock(); + this_prc_dir_lock.unlock(); } Filename fn; diff --git a/dtool/src/prc/configPageManager.cxx b/dtool/src/prc/configPageManager.cxx index 72e00f0189..b26eb17c69 100644 --- a/dtool/src/prc/configPageManager.cxx +++ b/dtool/src/prc/configPageManager.cxx @@ -129,7 +129,12 @@ reload_implicit_pages() { #else const BlobInfo *blobinfo = (const BlobInfo *)dlsym(dlopen(NULL, RTLD_NOW), "blobinfo"); #endif - if (blobinfo != nullptr && (blobinfo->version == 0 || blobinfo->num_pointers < 10)) { + if (blobinfo == nullptr) { +#ifndef _MSC_VER + // Clear the error flag. + dlerror(); +#endif + } else if (blobinfo->version == 0 || blobinfo->num_pointers < 10) { blobinfo = nullptr; } diff --git a/dtool/src/prc/configVariableBool.cxx b/dtool/src/prc/configVariableBool.cxx index 701f40c65d..27556f3f2b 100644 --- a/dtool/src/prc/configVariableBool.cxx +++ b/dtool/src/prc/configVariableBool.cxx @@ -12,6 +12,7 @@ */ #include "configVariableBool.h" +#include "mutexImpl.h" /** * Refreshes the cached value. @@ -21,7 +22,10 @@ reload_value() const { // NB. MSVC doesn't guarantee that this mutex is initialized in a // thread-safe manner. But chances are that the first time this is called // is at static init time, when there is no risk of data races. - static MutexImpl lock; + + // This needs to be a recursive mutex, because get_bool_word() may end up + // indirectly querying another bool config variable. + static ReMutexImpl lock; lock.lock(); // We check again for cache validity since another thread may have beaten diff --git a/dtool/src/prc/configVariableFilename.cxx b/dtool/src/prc/configVariableFilename.cxx index 1015a32e66..026ad80d68 100644 --- a/dtool/src/prc/configVariableFilename.cxx +++ b/dtool/src/prc/configVariableFilename.cxx @@ -13,17 +13,16 @@ #include "configVariableFilename.h" #include "executionEnvironment.h" +#include "mutexImpl.h" + +static MutexImpl filename_lock; /** * Recopies the config variable into the Filename for returning its value. */ void ConfigVariableFilename:: reload_cache() { - // NB. MSVC doesn't guarantee that this mutex is initialized in a - // thread-safe manner. But chances are that the first time this is called - // is at static init time, when there is no risk of data races. - static MutexImpl lock; - lock.lock(); + filename_lock.lock(); // We check again for cache validity since another thread may have beaten // us to the punch while we were waiting for the lock. @@ -34,5 +33,5 @@ reload_cache() { _cache = decl->get_filename_value(); mark_cache_valid(_local_modified); } - lock.unlock(); + filename_lock.unlock(); } diff --git a/dtool/src/prc/configVariableString.cxx b/dtool/src/prc/configVariableString.cxx index ebb4a751d2..8ecf2b1830 100644 --- a/dtool/src/prc/configVariableString.cxx +++ b/dtool/src/prc/configVariableString.cxx @@ -12,17 +12,16 @@ */ #include "configVariableString.h" +#include "mutexImpl.h" + +static MutexImpl string_lock; /** * Refreshes the variable's cached value. */ void ConfigVariableString:: reload_cache() { - // NB. MSVC doesn't guarantee that this mutex is initialized in a - // thread-safe manner. But chances are that the first time this is called - // is at static init time, when there is no risk of data races. - static MutexImpl lock; - lock.lock(); + string_lock.lock(); // We check again for cache validity since another thread may have beaten // us to the punch while we were waiting for the lock. @@ -31,5 +30,5 @@ reload_cache() { mark_cache_valid(_local_modified); } - lock.unlock(); + string_lock.unlock(); } diff --git a/dtool/src/prc/encryptStream.cxx b/dtool/src/prc/encryptStream.cxx index 24656b7416..e444f3e8d3 100644 --- a/dtool/src/prc/encryptStream.cxx +++ b/dtool/src/prc/encryptStream.cxx @@ -12,3 +12,27 @@ */ #include "encryptStream.h" + +#ifdef HAVE_OPENSSL + +/** + * Must be called immediately after open_read(). Decrypts the given number of + * bytes and checks that they match. The amount of header bytes are added to + * an offset so that skipping to 0 will skip past the header. + * + * Returns true if the read magic matches the given magic, false on error. + */ +bool IDecryptStream:: +read_magic(const char *magic, size_t size) { + char *this_magic = (char *)alloca(size); + read(this_magic, size); + + if (!fail() && (size_t)gcount() == size && memcmp(this_magic, magic, size) == 0) { + _buf.set_magic_length(size); + return true; + } else { + return false; + } +} + +#endif diff --git a/dtool/src/prc/encryptStream.h b/dtool/src/prc/encryptStream.h index 94deaeb62e..6605ea5998 100644 --- a/dtool/src/prc/encryptStream.h +++ b/dtool/src/prc/encryptStream.h @@ -53,6 +53,9 @@ PUBLISHED: MAKE_PROPERTY(key_length, get_key_length); MAKE_PROPERTY(iteration_count, get_iteration_count); +public: + bool read_magic(const char *magic, size_t size); + private: EncryptStreamBuf _buf; }; diff --git a/dtool/src/prc/encryptStreamBuf.I b/dtool/src/prc/encryptStreamBuf.I index cbf9f59470..b840625fc1 100644 --- a/dtool/src/prc/encryptStreamBuf.I +++ b/dtool/src/prc/encryptStreamBuf.I @@ -81,3 +81,21 @@ INLINE int EncryptStreamBuf:: get_iteration_count() const { return _iteration_count; } + +/** + * Sets the amount of the encrypted data at the beginning that are skipped + * when seeking back to zero. + */ +INLINE void EncryptStreamBuf:: +set_magic_length(size_t length) { + _magic_length = length; +} + +/** + * Sets the amount of the encrypted data at the beginning that are skipped + * when seeking back to zero. + */ +INLINE size_t EncryptStreamBuf:: +get_magic_length() const { + return _magic_length; +} diff --git a/dtool/src/prc/encryptStreamBuf.cxx b/dtool/src/prc/encryptStreamBuf.cxx index fe562e59e6..b087c21fae 100644 --- a/dtool/src/prc/encryptStreamBuf.cxx +++ b/dtool/src/prc/encryptStreamBuf.cxx @@ -177,6 +177,7 @@ open_read(std::istream *source, bool owns_source, const std::string &password) { _read_overflow_buffer = new unsigned char[_read_block_size]; _in_read_overflow_buffer = 0; + _finished = false; thread_consider_yield(); } @@ -322,6 +323,57 @@ close_write() { } } +/** + * Implements seeking within the stream. EncryptStreamBuf only allows seeking + * back to the beginning of the stream. + */ +std::streampos EncryptStreamBuf:: +seekoff(std::streamoff off, ios_seekdir dir, ios_openmode which) { + if (which != std::ios::in) { + // We can only do this with the input stream. + return -1; + } + + if (off != 0 || dir != std::ios::beg) { + // We only know how to reposition to the beginning. + return -1; + } + + size_t n = egptr() - gptr(); + gbump(n); + + if (_source->rdbuf()->pubseekpos(0, std::ios::in) == (std::streampos)0) { + int result = EVP_DecryptInit(_read_ctx, nullptr, nullptr, nullptr); + nassertr_always(result > 0, -1); + + _source->clear(); + _in_read_overflow_buffer = 0; + _finished = false; + + // Skip past the header. + int iv_length = EVP_CIPHER_CTX_iv_length(_read_ctx); + _source->ignore(6 + iv_length); + + // Ignore the magic bytes. + size_t magic_length = get_magic_length(); + char *buffer = (char *)alloca(magic_length); + if (read_chars(buffer, magic_length) == magic_length) { + return 0; + } + } + + return -1; +} + +/** + * Implements seeking within the stream. EncryptStreamBuf only allows seeking + * back to the beginning of the stream. + */ +std::streampos EncryptStreamBuf:: +seekpos(std::streampos pos, ios_openmode which) { + return seekoff(pos, std::ios::beg, which); +} + /** * Called by the system ostream implementation when its internal buffer is * filled, plus one character. @@ -423,7 +475,7 @@ read_chars(char *start, size_t length) { do { // Get more bytes from the stream. - if (_read_ctx == nullptr) { + if (_read_ctx == nullptr || _finished) { return 0; } @@ -439,8 +491,7 @@ read_chars(char *start, size_t length) { } else { result = EVP_DecryptFinal(_read_ctx, read_buffer, &bytes_read); - EVP_CIPHER_CTX_free(_read_ctx); - _read_ctx = nullptr; + _finished = true; } if (result <= 0) { diff --git a/dtool/src/prc/encryptStreamBuf.h b/dtool/src/prc/encryptStreamBuf.h index 7bc4db5199..2922aec939 100644 --- a/dtool/src/prc/encryptStreamBuf.h +++ b/dtool/src/prc/encryptStreamBuf.h @@ -44,6 +44,12 @@ public: INLINE void set_iteration_count(int iteration_count); INLINE int get_iteration_count() const; + INLINE void set_magic_length(size_t length); + INLINE size_t get_magic_length() const; + + virtual std::streampos seekoff(std::streamoff off, ios_seekdir dir, ios_openmode which); + virtual std::streampos seekpos(std::streampos pos, ios_openmode which); + protected: virtual int overflow(int c); virtual int sync(); @@ -71,6 +77,9 @@ private: EVP_CIPHER_CTX *_write_ctx; size_t _write_block_size; + + size_t _magic_length = 0; + bool _finished = false; }; #include "encryptStreamBuf.I" diff --git a/dtool/src/prc/notify.cxx b/dtool/src/prc/notify.cxx index f9c39135e8..9c2ea8b79a 100644 --- a/dtool/src/prc/notify.cxx +++ b/dtool/src/prc/notify.cxx @@ -21,6 +21,10 @@ #include +#ifdef PHAVE_ATOMIC +#include +#endif + #ifdef BUILD_IPHONE #include #endif @@ -422,28 +426,29 @@ string_severity(const string &str) { */ void Notify:: config_initialized() { - static bool already_initialized = false; - if (already_initialized) { - nout << "Notify::config_initialized() called more than once.\n"; - return; - } - already_initialized = true; + // We allow this to be called more than once to allow the user to specify a + // notify-output even after the initial import of Panda3D modules. However, + // it cannot be changed after the first time it is set. if (_ostream_ptr == &cerr) { - ConfigVariableFilename notify_output + static ConfigVariableFilename notify_output ("notify-output", "", "The filename to which to write all the output of notify"); - if (!notify_output.empty()) { - if (notify_output == "stdout") { + // We use this to ensure that only one thread can initialize the output. + static std::atomic_flag initialized = ATOMIC_FLAG_INIT; + + std::string value = notify_output.get_value(); + if (!value.empty() && !initialized.test_and_set()) { + if (value == "stdout") { cout.setf(std::ios::unitbuf); set_ostream_ptr(&cout, false); - } else if (notify_output == "stderr") { + } else if (value == "stderr") { set_ostream_ptr(&cerr, false); } else { - Filename filename = notify_output; + Filename filename = value; filename.set_text(); #ifdef BUILD_IPHONE // On the iPhone, route everything through cerr, and then send cerr to diff --git a/dtool/src/prc/notifyCategory.I b/dtool/src/prc/notifyCategory.I index 20678fbace..74c1965778 100644 --- a/dtool/src/prc/notifyCategory.I +++ b/dtool/src/prc/notifyCategory.I @@ -64,14 +64,17 @@ is_on(NotifySeverity severity) const { return (int)severity >= (int)get_severity(); } -#if defined(NOTIFY_DEBUG) || defined(CPPPARSER) /** * A shorthand way to write is_on(NS_spam). */ INLINE bool NotifyCategory:: is_spam() const { +#if defined(NOTIFY_DEBUG) || defined(CPPPARSER) // Instruct the compiler to optimize for the usual case. return UNLIKELY(is_on(NS_spam)); +#else + return false; +#endif } /** @@ -79,10 +82,13 @@ is_spam() const { */ INLINE bool NotifyCategory:: is_debug() const { +#if defined(NOTIFY_DEBUG) || defined(CPPPARSER) // Instruct the compiler to optimize for the usual case. return UNLIKELY(is_on(NS_debug)); -} +#else + return false; #endif +} /** * A shorthand way to write is_on(NS_info). diff --git a/dtool/src/prc/notifyCategory.cxx b/dtool/src/prc/notifyCategory.cxx index 3d856ceb08..86bf63f736 100644 --- a/dtool/src/prc/notifyCategory.cxx +++ b/dtool/src/prc/notifyCategory.cxx @@ -181,10 +181,16 @@ update_severity_cache() { } else { // Unless, of course, we're the root. _severity_cache = NS_info; + + // Take this opportunity to have Notify check whether the notify-output + // variable changed. + Notify::ptr()->config_initialized(); } } else { _severity_cache = _severity; + Notify::ptr()->config_initialized(); } + mark_cache_valid(_local_modified); } diff --git a/dtool/src/prc/notifyCategory.h b/dtool/src/prc/notifyCategory.h index 9a2a9fa4e2..96d2897921 100644 --- a/dtool/src/prc/notifyCategory.h +++ b/dtool/src/prc/notifyCategory.h @@ -51,13 +51,8 @@ PUBLISHED: // to present a consistent interface to our scripting language, so during // the interrogate pass (that is, when CPPPARSER is defined), we still // pretend they're nonstatic. -#if defined(NOTIFY_DEBUG) || defined(CPPPARSER) INLINE bool is_spam() const; INLINE bool is_debug() const; -#else - constexpr static bool is_spam() { return false; } - constexpr static bool is_debug() { return false; } -#endif INLINE bool is_info() const; INLINE bool is_warning() const; INLINE bool is_error() const; diff --git a/dtool/src/prc/notifyCategoryProxy.I b/dtool/src/prc/notifyCategoryProxy.I index fbdac1f0ca..ddd10b772d 100644 --- a/dtool/src/prc/notifyCategoryProxy.I +++ b/dtool/src/prc/notifyCategoryProxy.I @@ -65,26 +65,30 @@ is_on(NotifySeverity severity) { /** * */ -#ifdef NOTIFY_DEBUG template INLINE bool NotifyCategoryProxy:: is_spam() { +#ifdef NOTIFY_DEBUG // Instruct the compiler to optimize for the usual case. return UNLIKELY(get_unsafe_ptr()->is_spam()); -} +#else + return false; #endif +} /** * */ -#ifdef NOTIFY_DEBUG template INLINE bool NotifyCategoryProxy:: is_debug() { +#ifdef NOTIFY_DEBUG // Instruct the compiler to optimize for the usual case. return UNLIKELY(get_unsafe_ptr()->is_debug()); -} +#else + return false; #endif +} /** * diff --git a/dtool/src/prc/notifyCategoryProxy.h b/dtool/src/prc/notifyCategoryProxy.h index 5465529583..5ca79a6594 100644 --- a/dtool/src/prc/notifyCategoryProxy.h +++ b/dtool/src/prc/notifyCategoryProxy.h @@ -71,13 +71,8 @@ public: INLINE bool is_on(NotifySeverity severity); -#if defined(NOTIFY_DEBUG) || defined(CPPPARSER) INLINE bool is_spam(); INLINE bool is_debug(); -#else - constexpr static bool is_spam() { return false; } - constexpr static bool is_debug() { return false; } -#endif INLINE bool is_info(); INLINE bool is_warning(); INLINE bool is_error(); diff --git a/dtool/src/prc/streamReader.I b/dtool/src/prc/streamReader.I index e15953aeba..36f8b93e92 100644 --- a/dtool/src/prc/streamReader.I +++ b/dtool/src/prc/streamReader.I @@ -43,7 +43,18 @@ StreamReader(const StreamReader ©) : } /** - * The copy constructor does not copy ownership of the stream. + * The move constructor steals ownership of the stream. + */ +INLINE StreamReader:: +StreamReader(StreamReader &&from) noexcept : + _in(from._in), + _owns_stream(from._owns_stream) +{ + from._owns_stream = false; +} + +/** + * The copy assignment operator does not copy ownership of the stream. */ INLINE void StreamReader:: operator = (const StreamReader ©) { @@ -54,6 +65,19 @@ operator = (const StreamReader ©) { _owns_stream = false; } +/** + * The move assignment operator steals ownership of the stream. + */ +INLINE void StreamReader:: +operator = (StreamReader &&from) noexcept { + if (_owns_stream) { + delete _in; + } + _in = from._in; + _owns_stream = from._owns_stream; + from._owns_stream = false; +} + /** * */ diff --git a/dtool/src/prc/streamReader.cxx b/dtool/src/prc/streamReader.cxx index 74f278e220..3286eca67a 100644 --- a/dtool/src/prc/streamReader.cxx +++ b/dtool/src/prc/streamReader.cxx @@ -26,6 +26,9 @@ get_string() { // First, get the length of the string size_t size = get_uint16(); + if (size == 0) { + return string(); + } char *buffer = (char *)alloca(size); _in->read(buffer, size); @@ -42,6 +45,9 @@ get_string32() { // First, get the length of the string size_t size = get_uint32(); + if (size == 0) { + return string(); + } char *buffer = (char *)PANDA_MALLOC_ARRAY(size); _in->read(buffer, size); @@ -60,7 +66,7 @@ get_z_string() { string result; int ch = _in->get(); - while (!_in->eof() && !_in->fail() && ch != '\0') { + while (!_in->fail() && ch != EOF && ch != '\0') { result += (char)ch; ch = _in->get(); } @@ -76,6 +82,10 @@ string StreamReader:: get_fixed_string(size_t size) { nassertr(!_in->eof() && !_in->fail(), string()); + if (size == 0) { + return string(); + } + char *buffer = (char *)alloca(size); _in->read(buffer, size); size_t read_bytes = _in->gcount(); @@ -90,8 +100,9 @@ get_fixed_string(size_t size) { */ void StreamReader:: skip_bytes(size_t size) { - nassertv(!_in->eof() && !_in->fail()); + nassertv(!_in->fail()); nassertv((int)size >= 0); + nassertv(size == 0 || !_in->eof()); while (size > 0) { _in->get(); @@ -145,9 +156,9 @@ string StreamReader:: readline() { string line; int ch = _in->get(); - while (!_in->eof() && !_in->fail()) { + while (ch != EOF && !_in->fail()) { line += (char)ch; - if (ch == '\n') { + if (ch == '\n' || _in->eof()) { // Here's the newline character. return line; } diff --git a/dtool/src/prc/streamReader.h b/dtool/src/prc/streamReader.h index a317f0a783..4d32e0616e 100644 --- a/dtool/src/prc/streamReader.h +++ b/dtool/src/prc/streamReader.h @@ -31,7 +31,9 @@ public: PUBLISHED: INLINE explicit StreamReader(std::istream *in, bool owns_stream); INLINE StreamReader(const StreamReader ©); + INLINE StreamReader(StreamReader &&from) noexcept; INLINE void operator = (const StreamReader ©); + INLINE void operator = (StreamReader &&from) noexcept; INLINE ~StreamReader(); INLINE std::istream *get_istream() const; diff --git a/dtool/src/prc/streamReader_ext.cxx b/dtool/src/prc/streamReader_ext.cxx index 94c346e370..8e1b2b42ec 100644 --- a/dtool/src/prc/streamReader_ext.cxx +++ b/dtool/src/prc/streamReader_ext.cxx @@ -45,9 +45,9 @@ readline() { std::string line; int ch = in->get(); - while (!in->eof() && !in->fail()) { + while (ch != EOF && !in->fail()) { line += ch; - if (ch == '\n') { + if (ch == '\n' || in->eof()) { // Here's the newline character. break; } diff --git a/dtool/src/prc/streamWriter.I b/dtool/src/prc/streamWriter.I index b2485d3fc7..1f12c1517e 100644 --- a/dtool/src/prc/streamWriter.I +++ b/dtool/src/prc/streamWriter.I @@ -51,7 +51,21 @@ StreamWriter(const StreamWriter ©) : } /** - * The copy constructor does not copy ownership of the stream. + * The move constructor steals ownership of the stream. + */ +INLINE StreamWriter:: +StreamWriter(StreamWriter &&from) noexcept : +#ifdef HAVE_PYTHON + softspace(0), +#endif + _out(from._out), + _owns_stream(from._owns_stream) +{ + from._owns_stream = false; +} + +/** + * The copy assignment operator does not copy ownership of the stream. */ INLINE void StreamWriter:: operator = (const StreamWriter ©) { @@ -62,6 +76,19 @@ operator = (const StreamWriter ©) { _owns_stream = false; } +/** + * The move assignment operator steals ownership of the stream. + */ +INLINE void StreamWriter:: +operator = (StreamWriter &&from) noexcept { + if (_owns_stream) { + delete _out; + } + _out = from._out; + _owns_stream = from._owns_stream; + from._owns_stream = false; +} + /** * */ diff --git a/dtool/src/prc/streamWriter.h b/dtool/src/prc/streamWriter.h index cc00ef9ce3..8072e8aa40 100644 --- a/dtool/src/prc/streamWriter.h +++ b/dtool/src/prc/streamWriter.h @@ -32,7 +32,9 @@ public: PUBLISHED: INLINE explicit StreamWriter(std::ostream *out, bool owns_stream); INLINE StreamWriter(const StreamWriter ©); + INLINE StreamWriter(StreamWriter &&from) noexcept; INLINE void operator = (const StreamWriter ©); + INLINE void operator = (StreamWriter &&from) noexcept; INLINE ~StreamWriter(); INLINE std::ostream *get_ostream() const; diff --git a/dtool/src/pystub/pystub.cxx b/dtool/src/pystub/pystub.cxx deleted file mode 100644 index 299da8fa0f..0000000000 --- a/dtool/src/pystub/pystub.cxx +++ /dev/null @@ -1,492 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file pystub.cxx - * @author drose - * @date 2000-08-09 - */ - -#include "pystub.h" - -extern "C" { - EXPCL_PYSTUB int PyArg_Parse(...); - EXPCL_PYSTUB int PyArg_ParseTuple(...); - EXPCL_PYSTUB int PyArg_ParseTupleAndKeywords(...); - EXPCL_PYSTUB int PyArg_UnpackTuple(...); - EXPCL_PYSTUB int PyBool_FromLong(...); - EXPCL_PYSTUB int PyBuffer_Release(...); - EXPCL_PYSTUB int PyBytes_AsString(...); - EXPCL_PYSTUB int PyBytes_AsStringAndSize(...); - EXPCL_PYSTUB int PyBytes_FromStringAndSize(...); - EXPCL_PYSTUB int PyBytes_Size(...); - EXPCL_PYSTUB int PyCFunction_New(...); - EXPCL_PYSTUB int PyCFunction_NewEx(...); - EXPCL_PYSTUB int PyCallable_Check(...); - EXPCL_PYSTUB int PyCapsule_GetPointer(...); - EXPCL_PYSTUB int PyCapsule_New(...); - EXPCL_PYSTUB int PyDict_DelItem(...); - EXPCL_PYSTUB int PyDict_DelItemString(...); - EXPCL_PYSTUB int PyDict_GetItem(...); - EXPCL_PYSTUB int PyDict_GetItemString(...); - EXPCL_PYSTUB int PyDict_New(...); - EXPCL_PYSTUB int PyDict_Next(...); - EXPCL_PYSTUB int PyDict_SetItem(...); - EXPCL_PYSTUB int PyDict_SetItemString(...); - EXPCL_PYSTUB int PyDict_Size(...); - EXPCL_PYSTUB int PyErr_Clear(...); - EXPCL_PYSTUB int PyErr_ExceptionMatches(...); - EXPCL_PYSTUB int PyErr_Fetch(...); - EXPCL_PYSTUB int PyErr_Format(...); - EXPCL_PYSTUB int PyErr_NoMemory(...); - EXPCL_PYSTUB int PyErr_Occurred(...); - EXPCL_PYSTUB int PyErr_Print(...); - EXPCL_PYSTUB int PyErr_Restore(...); - EXPCL_PYSTUB int PyErr_SetString(...); - EXPCL_PYSTUB int PyErr_Warn(...); - EXPCL_PYSTUB int PyErr_WarnEx(...); - EXPCL_PYSTUB int PyEval_CallFunction(...); - EXPCL_PYSTUB int PyEval_CallObjectWithKeywords(...); - EXPCL_PYSTUB int PyEval_RestoreThread(...); - EXPCL_PYSTUB int PyEval_SaveThread(...); - EXPCL_PYSTUB int PyFloat_AsDouble(...); - EXPCL_PYSTUB int PyFloat_FromDouble(...); - EXPCL_PYSTUB int PyGen_Check(...); - EXPCL_PYSTUB int PyGILState_Ensure(...); - EXPCL_PYSTUB int PyGILState_Release(...); - EXPCL_PYSTUB int PyImport_GetModuleDict(...); - EXPCL_PYSTUB int PyImport_ImportModule(...); - EXPCL_PYSTUB int PyInt_AsLong(...); - EXPCL_PYSTUB int PyInt_AsSsize_t(...); - EXPCL_PYSTUB int PyInt_FromLong(...); - EXPCL_PYSTUB int PyInt_FromSize_t(...); - EXPCL_PYSTUB int PyIter_Next(...); - EXPCL_PYSTUB int PyList_Append(...); - EXPCL_PYSTUB int PyList_AsTuple(...); - EXPCL_PYSTUB int PyList_GetItem(...); - EXPCL_PYSTUB int PyList_New(...); - EXPCL_PYSTUB int PyList_SetItem(...); - EXPCL_PYSTUB int PyLong_AsLong(...); - EXPCL_PYSTUB int PyLong_AsLongLong(...); - EXPCL_PYSTUB int PyLong_AsSsize_t(...); - EXPCL_PYSTUB int PyLong_AsUnsignedLong(...); - EXPCL_PYSTUB int PyLong_AsUnsignedLongLong(...); - EXPCL_PYSTUB int PyLong_FromLong(...); - EXPCL_PYSTUB int PyLong_FromLongLong(...); - EXPCL_PYSTUB int PyLong_FromSize_t(...); - EXPCL_PYSTUB int PyLong_FromUnsignedLong(...); - EXPCL_PYSTUB int PyLong_FromUnsignedLongLong(...); - EXPCL_PYSTUB int PyMapping_GetItemString(...); - EXPCL_PYSTUB int PyMem_Free(...); - EXPCL_PYSTUB int PyMemoryView_FromObject(...); - EXPCL_PYSTUB int PyModule_AddIntConstant(...); - EXPCL_PYSTUB int PyModule_AddObject(...); - EXPCL_PYSTUB int PyModule_AddStringConstant(...); - EXPCL_PYSTUB int PyModule_Create2(...); - EXPCL_PYSTUB int PyModule_Create2TraceRefs(...); - EXPCL_PYSTUB int PyModule_GetDict(...); - EXPCL_PYSTUB int PyNumber_AsSsize_t(...); - EXPCL_PYSTUB int PyNumber_Check(...); - EXPCL_PYSTUB int PyNumber_Float(...); - EXPCL_PYSTUB int PyNumber_Int(...); - EXPCL_PYSTUB int PyNumber_Long(...); - EXPCL_PYSTUB int PyObject_ASCII(...); - EXPCL_PYSTUB int PyObject_Call(...); - EXPCL_PYSTUB int PyObject_CallFunction(...); - EXPCL_PYSTUB int PyObject_CallFunctionObjArgs(...); - EXPCL_PYSTUB int PyObject_CallMethod(...); - EXPCL_PYSTUB int PyObject_CallMethodObjArgs(...); - EXPCL_PYSTUB int PyObject_CallObject(...); - EXPCL_PYSTUB int PyObject_Cmp(...); - EXPCL_PYSTUB int PyObject_Compare(...); - EXPCL_PYSTUB int PyObject_Free(...); - EXPCL_PYSTUB int PyObject_GC_Del(...); - EXPCL_PYSTUB int PyObject_GenericGetAttr(...); - EXPCL_PYSTUB int PyObject_GenericSetAttr(...); - EXPCL_PYSTUB int PyObject_GetAttrString(...); - EXPCL_PYSTUB int PyObject_GetBuffer(...); - EXPCL_PYSTUB int PyObject_GetIter(...); - EXPCL_PYSTUB int PyObject_HasAttrString(...); - EXPCL_PYSTUB int PyObject_IsInstance(...); - EXPCL_PYSTUB int PyObject_IsTrue(...); - EXPCL_PYSTUB int PyObject_Malloc(...); - EXPCL_PYSTUB int PyObject_Repr(...); - EXPCL_PYSTUB int PyObject_RichCompareBool(...); - EXPCL_PYSTUB int PyObject_SelfIter(...); - EXPCL_PYSTUB int PyObject_SetAttr(...); - EXPCL_PYSTUB int PyObject_SetAttrString(...); - EXPCL_PYSTUB int PyObject_Str(...); - EXPCL_PYSTUB int PySeqIter_New(...); - EXPCL_PYSTUB int PySequence_Check(...); - EXPCL_PYSTUB int PySequence_Fast(...); - EXPCL_PYSTUB int PySequence_GetItem(...); - EXPCL_PYSTUB int PySequence_Size(...); - EXPCL_PYSTUB int PySequence_Tuple(...); - EXPCL_PYSTUB int PyString_AsString(...); - EXPCL_PYSTUB int PyString_AsStringAndSize(...); - EXPCL_PYSTUB int PyString_FromFormat(...); - EXPCL_PYSTUB int PyString_FromString(...); - EXPCL_PYSTUB int PyString_FromStringAndSize(...); - EXPCL_PYSTUB int PyString_InternFromString(...); - EXPCL_PYSTUB int PyString_InternInPlace(...); - EXPCL_PYSTUB int PyString_Size(...); - EXPCL_PYSTUB int PySys_GetObject(...); - EXPCL_PYSTUB int PySys_SetObject(...); - EXPCL_PYSTUB int PyThreadState_Clear(...); - EXPCL_PYSTUB int PyThreadState_Delete(...); - EXPCL_PYSTUB int PyThreadState_Get(...); - EXPCL_PYSTUB int PyThreadState_New(...); - EXPCL_PYSTUB int PyThreadState_Swap(...); - EXPCL_PYSTUB int PyTuple_GetItem(...); - EXPCL_PYSTUB int PyTuple_New(...); - EXPCL_PYSTUB int PyTuple_Pack(...); - EXPCL_PYSTUB int PyTuple_Size(...); - EXPCL_PYSTUB int PyType_GenericAlloc(...); - EXPCL_PYSTUB int PyType_IsSubtype(...); - EXPCL_PYSTUB int PyType_Ready(...); - EXPCL_PYSTUB int PyUnicodeUCS2_AsWideChar(...); - EXPCL_PYSTUB int PyUnicodeUCS2_AsWideCharString(...); - EXPCL_PYSTUB int PyUnicodeUCS2_CompareWithASCIIString(...); - EXPCL_PYSTUB int PyUnicodeUCS2_FromFormat(...); - EXPCL_PYSTUB int PyUnicodeUCS2_FromString(...); - EXPCL_PYSTUB int PyUnicodeUCS2_FromStringAndSize(...); - EXPCL_PYSTUB int PyUnicodeUCS2_FromWideChar(...); - EXPCL_PYSTUB int PyUnicodeUCS2_GetSize(...); - EXPCL_PYSTUB int PyUnicodeUCS4_AsWideChar(...); - EXPCL_PYSTUB int PyUnicodeUCS4_AsWideCharString(...); - EXPCL_PYSTUB int PyUnicodeUCS4_CompareWithASCIIString(...); - EXPCL_PYSTUB int PyUnicodeUCS4_FromFormat(...); - EXPCL_PYSTUB int PyUnicodeUCS4_FromString(...); - EXPCL_PYSTUB int PyUnicodeUCS4_FromStringAndSize(...); - EXPCL_PYSTUB int PyUnicodeUCS4_FromWideChar(...); - EXPCL_PYSTUB int PyUnicodeUCS4_GetSize(...); - EXPCL_PYSTUB int PyUnicode_AsUTF8(...); - EXPCL_PYSTUB int PyUnicode_AsUTF8AndSize(...); - EXPCL_PYSTUB int PyUnicode_AsWideChar(...); - EXPCL_PYSTUB int PyUnicode_AsWideCharString(...); - EXPCL_PYSTUB int PyUnicode_CompareWithASCIIString(...); - EXPCL_PYSTUB int PyUnicode_FromFormat(...); - EXPCL_PYSTUB int PyUnicode_FromString(...); - EXPCL_PYSTUB int PyUnicode_FromStringAndSize(...); - EXPCL_PYSTUB int PyUnicode_FromWideChar(...); - EXPCL_PYSTUB int PyUnicode_GetSize(...); - EXPCL_PYSTUB int PyUnicode_InternFromString(...); - EXPCL_PYSTUB int PyUnicode_InternInPlace(...); - EXPCL_PYSTUB int Py_BuildValue(...); - EXPCL_PYSTUB int Py_GetVersion(...); - EXPCL_PYSTUB int Py_InitModule4(...); - EXPCL_PYSTUB int Py_InitModule4_64(...); - EXPCL_PYSTUB int Py_InitModule4TraceRefs(...); - EXPCL_PYSTUB int Py_InitModule4TraceRefs_64(...); - EXPCL_PYSTUB int _PyArg_ParseTuple_SizeT(...); - EXPCL_PYSTUB int _PyArg_ParseTupleAndKeywords_SizeT(...); - EXPCL_PYSTUB int _PyArg_Parse_SizeT(...); - EXPCL_PYSTUB int _PyErr_BadInternalCall(...); - EXPCL_PYSTUB int _PyLong_AsByteArray(...); - EXPCL_PYSTUB int _PyLong_Sign(...); - EXPCL_PYSTUB int _PyObject_CallFunction_SizeT(...); - EXPCL_PYSTUB int _PyObject_CallMethod_SizeT(...); - EXPCL_PYSTUB int _PyObject_DebugFree(...); - EXPCL_PYSTUB int _PyObject_Del(...); - EXPCL_PYSTUB int _PyObject_FastCallDict(...); - EXPCL_PYSTUB int _PyUnicode_AsString(...); - EXPCL_PYSTUB int _PyUnicode_AsStringAndSize(...); - EXPCL_PYSTUB int _PyUnicode_EqualToASCIIString(...); - EXPCL_PYSTUB int _Py_AddToAllObjects(...); - EXPCL_PYSTUB int _Py_BuildValue_SizeT(...); - EXPCL_PYSTUB int _Py_Dealloc(...); - EXPCL_PYSTUB int _Py_ForgetReference(...); - EXPCL_PYSTUB int _Py_NegativeRefcount(...); - EXPCL_PYSTUB int _Py_NewReference(...); - EXPCL_PYSTUB int _Py_PrintReferenceAddresses(...); - EXPCL_PYSTUB int _Py_PrintReferences(...); - EXPCL_PYSTUB int _Py_RefTotal(...); - - EXPCL_PYSTUB void Py_Initialize(); - EXPCL_PYSTUB int Py_IsInitialized(); - EXPCL_PYSTUB void PyEval_InitThreads(); - - EXPCL_PYSTUB extern void *PyExc_AssertionError; - EXPCL_PYSTUB extern void *PyExc_AttributeError; - EXPCL_PYSTUB extern void *PyExc_BufferError; - EXPCL_PYSTUB extern void *PyExc_ConnectionError; - EXPCL_PYSTUB extern void *PyExc_Exception; - EXPCL_PYSTUB extern void *PyExc_FutureWarning; - EXPCL_PYSTUB extern void *PyExc_ImportError; - EXPCL_PYSTUB extern void *PyExc_IndexError; - EXPCL_PYSTUB extern void *PyExc_KeyError; - EXPCL_PYSTUB extern void *PyExc_NameError; - EXPCL_PYSTUB extern void *PyExc_OSError; - EXPCL_PYSTUB extern void *PyExc_OverflowError; - EXPCL_PYSTUB extern void *PyExc_RuntimeError; - EXPCL_PYSTUB extern void *PyExc_StandardError; - EXPCL_PYSTUB extern void *PyExc_StopIteration; - EXPCL_PYSTUB extern void *PyExc_SystemExit; - EXPCL_PYSTUB extern void *PyExc_TypeError; - EXPCL_PYSTUB extern void *PyExc_ValueError; - EXPCL_PYSTUB extern void *PyDict_Type; - EXPCL_PYSTUB extern void *PyFloat_Type; - EXPCL_PYSTUB extern void *PyGen_Type; - EXPCL_PYSTUB extern void *PyInt_Type; - EXPCL_PYSTUB extern void *PyList_Type; - EXPCL_PYSTUB extern void *PyLong_Type; - EXPCL_PYSTUB extern void *PyObject_Type; - EXPCL_PYSTUB extern void *PyString_Type; - EXPCL_PYSTUB extern void *PyTuple_Type; - EXPCL_PYSTUB extern void *PyType_Type; - EXPCL_PYSTUB extern void *PyUnicode_Type; - EXPCL_PYSTUB extern void *_PyThreadState_Current; - EXPCL_PYSTUB extern void *_Py_FalseStruct; - EXPCL_PYSTUB extern void *_Py_NoneStruct; - EXPCL_PYSTUB extern void *_Py_NotImplementedStruct; - EXPCL_PYSTUB extern void *_Py_TrueStruct; - EXPCL_PYSTUB extern void *_Py_ZeroStruct; -}; - - -int PyArg_Parse(...) { return 0; }; -int PyArg_ParseTuple(...) { return 0; } -int PyArg_ParseTupleAndKeywords(...) { return 0; } -int PyArg_UnpackTuple(...) { return 0; }; -int PyBool_FromLong(...) { return 0; } -int PyBuffer_Release(...) { return 0; } -int PyBytes_AsString(...) { return 0; } -int PyBytes_AsStringAndSize(...) { return 0; } -int PyBytes_FromStringAndSize(...) { return 0; } -int PyBytes_Size(...) { return 0; } -int PyCFunction_New(...) { return 0; }; -int PyCFunction_NewEx(...) { return 0; }; -int PyCallable_Check(...) { return 0; } -int PyCapsule_GetPointer(...) { return 0; } -int PyCapsule_New(...) { return 0; } -int PyDict_DelItem(...) { return 0; } -int PyDict_DelItemString(...) { return 0; } -int PyDict_GetItem(...) { return 0; } -int PyDict_GetItemString(...) { return 0; } -int PyDict_New(...) { return 0; }; -int PyDict_Next(...) { return 0; }; -int PyDict_SetItem(...) { return 0; }; -int PyDict_SetItemString(...) { return 0; }; -int PyDict_Size(...){ return 0; } -int PyErr_Clear(...) { return 0; }; -int PyErr_ExceptionMatches(...) { return 0; }; -int PyErr_Fetch(...) { return 0; } -int PyErr_Format(...) { return 0; }; -int PyErr_NoMemory(...) { return 0; } -int PyErr_Occurred(...) { return 0; } -int PyErr_Print(...) { return 0; } -int PyErr_Restore(...) { return 0; } -int PyErr_SetString(...) { return 0; } -int PyErr_Warn(...) { return 0; } -int PyErr_WarnEx(...) { return 0; } -int PyEval_CallFunction(...) { return 0; } -int PyEval_CallObjectWithKeywords(...) { return 0; } -int PyEval_InitThreads(...) { return 0; } -int PyEval_RestoreThread(...) { return 0; } -int PyEval_SaveThread(...) { return 0; } -int PyFloat_AsDouble(...) { return 0; } -int PyFloat_FromDouble(...) { return 0; } -int PyGen_Check(...) { return 0; } -int PyGILState_Ensure(...) { return 0; } -int PyGILState_Release(...) { return 0; } -int PyImport_GetModuleDict(...) { return 0; } -int PyImport_ImportModule(...) { return 0; } -int PyInt_AsLong(...) { return 0; } -int PyInt_AsSsize_t(...) { return 0; } -int PyInt_FromLong(...) { return 0; } -int PyInt_FromSize_t(...) { return 0; } -int PyIter_Next(...) { return 0; } -int PyList_Append(...) { return 0; } -int PyList_AsTuple(...) { return 0; } -int PyList_GetItem(...) { return 0; } -int PyList_New(...) { return 0; } -int PyList_SetItem(...) { return 0; } -int PyLong_AsLong(...) { return 0; } -int PyLong_AsLongLong(...) { return 0; } -int PyLong_AsSsize_t(...) { return 0; } -int PyLong_AsUnsignedLong(...) { return 0; } -int PyLong_AsUnsignedLongLong(...) { return 0; } -int PyLong_FromLong(...) { return 0; } -int PyLong_FromLongLong(...) { return 0; } -int PyLong_FromSize_t(...) { return 0; } -int PyLong_FromUnsignedLong(...) { return 0; } -int PyLong_FromUnsignedLongLong(...) { return 0; } -int PyMapping_GetItemString(...) { return 0; } -int PyMem_Free(...) { return 0; } -int PyMemoryView_FromObject(...) { return 0; } -int PyModule_AddIntConstant(...) { return 0; }; -int PyModule_AddObject(...) { return 0; }; -int PyModule_AddStringConstant(...) { return 0; }; -int PyModule_Create2(...) { return 0; }; -int PyModule_Create2TraceRefs(...) { return 0; }; -int PyModule_GetDict(...) { return 0; }; -int PyNumber_AsSsize_t(...) { return 0; } -int PyNumber_Check(...) { return 0; } -int PyNumber_Float(...) { return 0; } -int PyNumber_Int(...) { return 0; } -int PyNumber_Long(...) { return 0; } -int PyObject_ASCII(...) { return 0; } -int PyObject_Call(...) { return 0; } -int PyObject_CallFunction(...) { return 0; } -int PyObject_CallFunctionObjArgs(...) { return 0; } -int PyObject_CallMethod(...) { return 0; } -int PyObject_CallMethodObjArgs(...) { return 0; } -int PyObject_CallObject(...) { return 0; } -int PyObject_Cmp(...) { return 0; } -int PyObject_Compare(...) { return 0; } -int PyObject_Free(...) { return 0; } -int PyObject_GC_Del(...) { return 0; } -int PyObject_GenericGetAttr(...) { return 0; }; -int PyObject_GenericSetAttr(...) { return 0; }; -int PyObject_GetAttrString(...) { return 0; } -int PyObject_GetBuffer(...) { return 0; } -int PyObject_GetIter(...) { return 0; } -int PyObject_HasAttrString(...) { return 0; } -int PyObject_IsInstance(...) { return 0; } -int PyObject_IsTrue(...) { return 0; } -int PyObject_Malloc(...) { return 0; } -int PyObject_Repr(...) { return 0; } -int PyObject_RichCompareBool(...) { return 0; } -int PyObject_SelfIter(...) { return 0; } -int PyObject_SetAttr(...) { return 0; } -int PyObject_SetAttrString(...) { return 0; } -int PyObject_Str(...) { return 0; } -int PySeqIter_New(...) { return 0; } -int PySequence_Check(...) { return 0; } -int PySequence_Fast(...) { return 0; } -int PySequence_GetItem(...) { return 0; } -int PySequence_Size(...) { return 0; } -int PySequence_Tuple(...) { return 0; } -int PyString_AsString(...) { return 0; } -int PyString_AsStringAndSize(...) { return 0; } -int PyString_FromFormat(...) { return 0; } -int PyString_FromString(...) { return 0; } -int PyString_FromStringAndSize(...) { return 0; } -int PyString_InternFromString(...) { return 0; } -int PyString_InternInPlace(...) { return 0; } -int PySys_GetObject(...) { return 0; } -int PySys_SetObject(...) { return 0; } -int PyThreadState_Clear(...) { return 0; } -int PyThreadState_Delete(...) { return 0; } -int PyThreadState_Get(...) { return 0; } -int PyThreadState_New(...) { return 0; } -int PyThreadState_Swap(...) { return 0; } -int PyTuple_GetItem(...) { return 0; } -int PyTuple_New(...) { return 0; } -int PyTuple_Pack(...) { return 0; } -int PyTuple_Size(...) { return 0; }; -int PyType_GenericAlloc(...) { return 0; }; -int PyType_IsSubtype(...) { return 0; } -int PyType_Ready(...) { return 0; }; -int PyUnicodeUCS2_AsWideChar(...) { return 0; } -int PyUnicodeUCS2_AsWideCharString(...) { return 0; } -int PyUnicodeUCS2_CompareWithASCIIString(...) { return 0; } -int PyUnicodeUCS2_FromFormat(...) { return 0; } -int PyUnicodeUCS2_FromString(...) { return 0; } -int PyUnicodeUCS2_FromStringAndSize(...) { return 0; } -int PyUnicodeUCS2_FromWideChar(...) { return 0; } -int PyUnicodeUCS2_GetSize(...) { return 0; } -int PyUnicodeUCS4_AsWideChar(...) { return 0; } -int PyUnicodeUCS4_AsWideCharString(...) { return 0; } -int PyUnicodeUCS4_CompareWithASCIIString(...) { return 0; } -int PyUnicodeUCS4_FromFormat(...) { return 0; } -int PyUnicodeUCS4_FromString(...) { return 0; } -int PyUnicodeUCS4_FromStringAndSize(...) { return 0; } -int PyUnicodeUCS4_FromWideChar(...) { return 0; } -int PyUnicodeUCS4_GetSize(...) { return 0; } -int PyUnicode_AsUTF8(...) { return 0; } -int PyUnicode_AsUTF8AndSize(...) { return 0; } -int PyUnicode_AsWideChar(...) { return 0; } -int PyUnicode_AsWideCharString(...) { return 0; } -int PyUnicode_CompareWithASCIIString(...) { return 0; } -int PyUnicode_FromFormat(...) { return 0; } -int PyUnicode_FromString(...) { return 0; } -int PyUnicode_FromStringAndSize(...) { return 0; } -int PyUnicode_FromWideChar(...) { return 0; } -int PyUnicode_GetSize(...) { return 0; } -int PyUnicode_InternFromString(...) { return 0; } -int PyUnicode_InternInPlace(...) { return 0; } -int Py_GetVersion(...) { return 0; } -int Py_BuildValue(...) { return 0; } -int Py_InitModule4(...) { return 0; } -int Py_InitModule4_64(...) { return 0; } -int Py_InitModule4TraceRefs(...) { return 0; }; -int Py_InitModule4TraceRefs_64(...) { return 0; }; -int _PyArg_ParseTuple_SizeT(...) { return 0; }; -int _PyArg_ParseTupleAndKeywords_SizeT(...) { return 0; }; -int _PyArg_Parse_SizeT(...) { return 0; }; -int _PyErr_BadInternalCall(...) { return 0; }; -int _PyLong_AsByteArray(...) { return 0; }; -int _PyLong_Sign(...) { return 0; }; -int _PyObject_CallFunction_SizeT(...) { return 0; }; -int _PyObject_CallMethod_SizeT(...) { return 0; }; -int _PyObject_DebugFree(...) { return 0; }; -int _PyObject_Del(...) { return 0; }; -int _PyObject_FastCallDict(...) { return 0; }; -int _PyUnicode_AsString(...) { return 0; }; -int _PyUnicode_AsStringAndSize(...) { return 0; }; -int _PyUnicode_EqualToASCIIString(...) { return 0; }; -int _Py_AddToAllObjects(...) { return 0; }; -int _Py_BuildValue_SizeT(...) { return 0; }; -int _Py_Dealloc(...) { return 0; }; -int _Py_ForgetReference(...) { return 0; }; -int _Py_NegativeRefcount(...) { return 0; }; -int _Py_NewReference(...) { return 0; }; -int _Py_PrintReferenceAddresses(...) { return 0; }; -int _Py_PrintReferences(...) { return 0; }; -int _Py_RefTotal(...) { return 0; }; - -// We actually might call this one. -void Py_Initialize() { -} -int Py_IsInitialized() { - return 0; -} -void PyEval_InitThreads() { -} - - -void *PyExc_AssertionError = nullptr; -void *PyExc_AttributeError = nullptr; -void *PyExc_BufferError = nullptr; -void *PyExc_ConnectionError = nullptr; -void *PyExc_Exception = nullptr; -void *PyExc_FutureWarning = nullptr; -void *PyExc_ImportError = nullptr; -void *PyExc_IndexError = nullptr; -void *PyExc_KeyError = nullptr; -void *PyExc_NameError = nullptr; -void *PyExc_OSError = nullptr; -void *PyExc_OverflowError = nullptr; -void *PyExc_RuntimeError = nullptr; -void *PyExc_StandardError = nullptr; -void *PyExc_StopIteration = nullptr; -void *PyExc_SystemExit = nullptr; -void *PyExc_TypeError = nullptr; -void *PyExc_ValueError = nullptr; -void *PyDict_Type = nullptr; -void *PyFloat_Type = nullptr; -void *PyGen_Type = nullptr; -void *PyInt_Type = nullptr; -void *PyList_Type = nullptr; -void *PyLong_Type = nullptr; -void *PyObject_Type = nullptr; -void *PyString_Type = nullptr; -void *PyTuple_Type = nullptr; -void *PyType_Type = nullptr; -void *PyUnicode_Type = nullptr; -void *_PyThreadState_Current = nullptr; -void *_Py_FalseStruct = nullptr; -void *_Py_NoneStruct = nullptr; -void *_Py_NotImplementedStruct = nullptr; -void *_Py_TrueStruct = nullptr; -void *_Py_ZeroStruct = nullptr; - - -void -pystub() { -} diff --git a/dtool/src/pystub/pystub.h b/dtool/src/pystub/pystub.h deleted file mode 100644 index d812a98148..0000000000 --- a/dtool/src/pystub/pystub.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file pystub.h - * @author drose - * @date 2000-08-08 - */ - -#ifndef PYSTUB_H -#define PYSTUB_H - -#include "dtoolbase.h" - -// The sole purpose of this header file is to allow a program other than -// Python to load in a module that includes Python wrappers. - -// We need this if we build the Panda libraries with Python wrappers, but want -// to run a standalone program with those libraries. - -// This header file just stubs out the Python functions that these wrappers -// will call. You should include this header file in exactly one .C file in -// your project, preferably in the .C file that defines main(), and then link -// with -lpystub. Do not include this header file in a .C or .h file that -// will become part of an .so that might eventually link with Python. - - -// You might need to call this function in main() or somewhere to force the -// .so to be linked in--some OS'es try to be smart about not pulling in shared -// libraries whose symbols aren't referenced anywhere. -EXPCL_PYSTUB void pystub(); - - -#endif diff --git a/makepanda/config.in b/makepanda/config.in index 1317517339..e262da5bf0 100644 --- a/makepanda/config.in +++ b/makepanda/config.in @@ -31,7 +31,7 @@ fullscreen #f # The framebuffer-hardware flag forces it to use an accelerated driver. # The framebuffer-software flag forces it to use a software renderer. -# If you don't set either, it will use whatever's available. +# If you set both to false, it will use whatever's available. framebuffer-hardware #t framebuffer-software #f diff --git a/makepanda/getversion.py b/makepanda/getversion.py index 4595c0d589..9f30f8b9dd 100755 --- a/makepanda/getversion.py +++ b/makepanda/getversion.py @@ -4,13 +4,10 @@ # and returns it on the command-line. This is useful for the # automated scripts that build the Panda3D releases. -from makepandacore import ParsePandaVersion, ParsePluginVersion +from makepandacore import ParsePandaVersion, GetMetadataValue import sys -if '--runtime' in sys.argv: - version = ParsePluginVersion("dtool/PandaVersion.pp") -else: - version = ParsePandaVersion("dtool/PandaVersion.pp") +version = GetMetadataValue('version') version = version.strip() sys.stdout.write(version) diff --git a/makepanda/installer.nsi b/makepanda/installer.nsi index 60de4bcc9d..f800fa222c 100644 --- a/makepanda/installer.nsi +++ b/makepanda/installer.nsi @@ -84,6 +84,7 @@ LangString DESC_SecTools ${LANG_ENGLISH} "Useful tools and model converters to h LangString DESC_SecGroupPython ${LANG_ENGLISH} "Contains modules that provide Python support for Panda3D." LangString DESC_SecPyShared ${LANG_ENGLISH} "Contains the common Python code used by the Panda3D Python bindings." LangString DESC_SecPython ${LANG_ENGLISH} "Contains a ${REGVIEW}-bit copy of Python ${INCLUDE_PYVER} preconfigured to make use of Panda3D." +LangString DESC_SecEnsurePip ${LANG_ENGLISH} "Installs the pip package manager into the included Python installation." LangString DESC_SecHeadersLibs ${LANG_ENGLISH} "Headers and libraries needed for C++ development with Panda3D." LangString DESC_SecSamples ${LANG_ENGLISH} "The sample programs demonstrate how to make Python applications with Panda3D." LangString DESC_SecMaxPlugins ${LANG_ENGLISH} "Plug-ins for Autodesk 3ds Max (${REGVIEW}-bit) that can be used to export models to Panda3D." @@ -477,6 +478,19 @@ Section "Python ${INCLUDE_PYVER}" SecPython SkipRegPath: SectionEnd + +Section "Install pip" SecEnsurePip + SectionIn 1 2 3 + + SetDetailsPrint both + DetailPrint "Installing the pip package manager..." + SetDetailsPrint listonly + + SetOutPath $INSTDIR + nsExec::ExecToLog '"$INSTDIR\python\python.exe" -m ensurepip --default-pip' + Pop $0 + DetailPrint "Command returned exit status $0" +SectionEnd !endif !macro MaybeEnablePyBindingSection PYVER @@ -537,6 +551,18 @@ Function .onSelChange IntOp $R0 $R0 | ${SF_SELECTED} SectionSetFlags ${SecPyShared} $R0 ${EndIf} + + !ifdef INCLUDE_PYVER + ${If} ${SectionIsSelected} ${SecPython} + !insertmacro SectionFlagIsSet ${SecEnsurePip} ${SF_RO} 0 SkipSelectEnsurePip + !insertmacro SelectSection ${SecEnsurePip} + SkipSelectEnsurePip: + !insertmacro ClearSectionFlag ${SecEnsurePip} ${SF_RO} + ${Else} + !insertmacro UnselectSection ${SecEnsurePip} + !insertmacro SetSectionFlag ${SecEnsurePip} ${SF_RO} + ${EndIf} + !endif FunctionEnd !ifdef INCLUDE_PYVER @@ -917,6 +943,7 @@ SectionEnd !endif !ifdef INCLUDE_PYVER !insertmacro MUI_DESCRIPTION_TEXT ${SecPython} $(DESC_SecPython) + !insertmacro MUI_DESCRIPTION_TEXT ${SecEnsurePip} $(DESC_SecEnsurePip) !endif !insertmacro MUI_DESCRIPTION_TEXT ${SecHeadersLibs} $(DESC_SecHeadersLibs) !ifdef HAVE_SAMPLES diff --git a/makepanda/installpanda.py b/makepanda/installpanda.py index 7f9b79cd60..83e23bac2c 100644 --- a/makepanda/installpanda.py +++ b/makepanda/installpanda.py @@ -21,18 +21,10 @@ MIME_INFO = ( ("bam.pz", "model/x-compressed-bam", "Compressed Panda3D binary model file", "pview"), ) -MIME_INFO_PLUGIN = ( - ("p3d", "application/x-panda3d", "Panda3D game/applet", "panda3d"), -) - APP_INFO = ( ("pview", "Panda3D Model Viewer", ("egg", "bam", "egg.pz", "bam.pz")), ) -APP_INFO_PLUGIN = ( - ("panda3d", "Panda3D", ("p3d")), -) - def WriteApplicationsFile(fname, appinfo, mimeinfo): fhandle = open(fname, "w") for app, desc, exts in appinfo: @@ -62,7 +54,7 @@ def WriteMimeXMLFile(fname, info): fhandle.write("\t\n" % (mime)) fhandle.write("\t\t%s\n" % (desc)) fhandle.write("\t\t\n" % (ext)) - fhandle.write("\t\s") + fhandle.write("\t\n") fhandle.write("\n") fhandle.close() @@ -155,24 +147,29 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir( libdir = prefix + "/" + libdir # Create the directory structure that we will be putting our files in. - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/bin") - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/include") - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/panda3d") - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/mime-info") - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/mime/packages") - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/application-registry") - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/applications") - oscmd("mkdir -m 0755 -p "+destdir+libdir+"/panda3d") + # Don't use os.makedirs or mkdir -p; neither properly set permissions for + # created intermediate directories. + MakeDirectory(destdir+prefix+"/bin", mode=0o755, recursive=True) + MakeDirectory(destdir+prefix+"/include", mode=0o755) + MakeDirectory(destdir+prefix+"/include/panda3d", mode=0o755) + MakeDirectory(destdir+prefix+"/share", mode=0o755) + MakeDirectory(destdir+prefix+"/share/panda3d", mode=0o755) + MakeDirectory(destdir+prefix+"/share/mime-info", mode=0o755) + MakeDirectory(destdir+prefix+"/share/mime", mode=0o755) + MakeDirectory(destdir+prefix+"/share/mime/packages", mode=0o755) + MakeDirectory(destdir+prefix+"/share/application-registry", mode=0o755) + MakeDirectory(destdir+prefix+"/share/applications", mode=0o755) + MakeDirectory(destdir+libdir+"/panda3d", mode=0o755, recursive=True) for python_version in python_versions: - oscmd("mkdir -m 0755 -p "+destdir+python_version["purelib"]) - oscmd("mkdir -m 0755 -p "+destdir+python_version["platlib"]+"/panda3d") + MakeDirectory(destdir+python_version["purelib"], mode=0o755, recursive=True) + MakeDirectory(destdir+python_version["platlib"]+"/panda3d", mode=0o755, recursive=True) if (sys.platform.startswith("freebsd")): - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/etc") - oscmd("mkdir -m 0755 -p "+destdir+"/usr/local/libdata/ldconfig") + MakeDirectory(destdir+prefix+"/etc", mode=0o755) + MakeDirectory(destdir+"/usr/local/libdata/ldconfig", mode=0o755, recursive=True) else: - oscmd("mkdir -m 0755 -p "+destdir+"/etc/ld.so.conf.d") + MakeDirectory(destdir+"/etc/ld.so.conf.d", mode=0o755, recursive=True) # Write the Config.prc file. Configrc = ReadFile(outputdir+"/etc/Config.prc") @@ -184,7 +181,7 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir( WriteFile(destdir+"/etc/Config.prc", Configrc) oscmd("cp "+outputdir+"/etc/Confauto.prc "+destdir+"/etc/Confauto.prc") - oscmd("cp -R "+outputdir+"/include "+destdir+prefix+"/include/panda3d") + oscmd("cp -R "+outputdir+"/include/* "+destdir+prefix+"/include/panda3d/") oscmd("cp -R "+outputdir+"/pandac "+destdir+prefix+"/share/panda3d/") oscmd("cp -R "+outputdir+"/models "+destdir+prefix+"/share/panda3d/") if os.path.isdir("samples"): oscmd("cp -R samples "+destdir+prefix+"/share/panda3d/") @@ -221,7 +218,7 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir( oscmd("echo '"+libdir+"/panda3d'> "+destdir+"/etc/ld.so.conf.d/panda3d.conf") for base in os.listdir(outputdir+"/lib"): - if (not base.endswith(".a")) or base == "libp3pystub.a": + if not base.endswith(".a"): # We really need to specify -R in order not to follow symlinks on non-GNU oscmd("cp -R -P "+outputdir+"/lib/"+base+" "+destdir+libdir+"/panda3d/"+base) @@ -237,8 +234,8 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir( DeleteEmptyDirs(destdir+prefix+"/include/panda3d") # Change permissions on include directory. - os.chmod(destdir + prefix + "/include", 0o755) - for root, dirs, files in os.walk(destdir + prefix + "/include"): + os.chmod(destdir + prefix + "/include/panda3d", 0o755) + for root, dirs, files in os.walk(destdir + prefix + "/include/panda3d"): for basename in dirs: os.chmod(os.path.join(root, basename), 0o755) for basename in files: @@ -248,38 +245,6 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir( if (os.path.isfile(destdir+prefix+"/share/panda3d/direct/leveleditor/copyfiles.pl")): os.remove(destdir+prefix+"/share/panda3d/direct/leveleditor/copyfiles.pl") -def InstallRuntime(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir()): - if (not prefix.startswith("/")): - prefix = "/" + prefix - libdir = prefix + "/" + libdir - - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/bin") - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/mime-info") - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/mime/packages") - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/application-registry") - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/applications") - if (os.path.exists(outputdir+"/plugins/nppanda3d.so")): - oscmd("mkdir -m 0755 -p "+destdir+libdir) - oscmd("cp "+outputdir+"/plugins/nppanda3d.so "+destdir+libdir+"/nppanda3d.so") - if sys.platform.startswith("freebsd"): - oscmd("mkdir -m 0755 -p "+destdir+libdir+"/browser_plugins/symlinks/gecko19") - oscmd("mkdir -m 0755 -p "+destdir+libdir+"/libxul/plugins") - oscmd("ln -f -s "+libdir+"/nppanda3d.so "+destdir+libdir+"/browser_plugins/symlinks/gecko19/nppanda3d.so") - oscmd("ln -f -s "+libdir+"/nppanda3d.so "+destdir+libdir+"/libxul/plugins/nppanda3d.so") - else: - oscmd("mkdir -m 0755 -p "+destdir+libdir+"/mozilla/plugins") - oscmd("mkdir -m 0755 -p "+destdir+libdir+"/mozilla-firefox/plugins") - oscmd("mkdir -m 0755 -p "+destdir+libdir+"/xulrunner-addons/plugins") - oscmd("ln -f -s "+libdir+"/nppanda3d.so "+destdir+libdir+"/mozilla/plugins/nppanda3d.so") - oscmd("ln -f -s "+libdir+"/nppanda3d.so "+destdir+libdir+"/mozilla-firefox/plugins/nppanda3d.so") - oscmd("ln -f -s "+libdir+"/nppanda3d.so "+destdir+libdir+"/xulrunner-addons/plugins/nppanda3d.so") - WriteMimeFile(destdir+prefix+"/share/mime-info/panda3d-runtime.mime", MIME_INFO_PLUGIN) - WriteKeysFile(destdir+prefix+"/share/mime-info/panda3d-runtime.keys", MIME_INFO_PLUGIN) - WriteMimeXMLFile(destdir+prefix+"/share/mime/packages/panda3d-runtime.xml", MIME_INFO_PLUGIN) - WriteApplicationsFile(destdir+prefix+"/share/application-registry/panda3d-runtime.applications", APP_INFO_PLUGIN, MIME_INFO_PLUGIN) - oscmd("cp makepanda/panda3d.desktop "+destdir+prefix+"/share/applications/panda3d.desktop") - oscmd("cp "+outputdir+"/bin/panda3d "+destdir+prefix+"/bin/") - if (__name__ == "__main__"): if (sys.platform.startswith("win") or sys.platform == "darwin"): exit("This script is not supported on Windows or Mac OS X at the moment!") @@ -290,7 +255,6 @@ if (__name__ == "__main__"): parser.add_option('', '--outputdir', dest = 'outputdir', help = 'Makepanda\'s output directory (default: built)', default = 'built') parser.add_option('', '--destdir', dest = 'destdir', help = 'Destination directory [default=%s]' % destdir, default = destdir) parser.add_option('', '--prefix', dest = 'prefix', help = 'Prefix [default=/usr/local]', default = '/usr/local') - parser.add_option('', '--runtime', dest = 'runtime', help = 'Specify if runtime build [default=no]', action = 'store_true', default = False) parser.add_option('', '--verbose', dest = 'verbose', help = 'Print commands that are executed [default=no]', action = 'store_true', default = False) (options, args) = parser.parse_args() @@ -307,13 +271,9 @@ if (__name__ == "__main__"): if options.verbose: SetVerbose(True) - if (options.runtime): - print("Installing Panda3D Runtime into " + destdir + options.prefix) - InstallRuntime(destdir = destdir, prefix = options.prefix, outputdir = options.outputdir) - else: - print("Installing Panda3D SDK into " + destdir + options.prefix) - InstallPanda(destdir=destdir, - prefix=options.prefix, - outputdir=options.outputdir, - python_versions=ReadPythonVersionInfoFile()) + print("Installing Panda3D SDK into " + destdir + options.prefix) + InstallPanda(destdir=destdir, + prefix=options.prefix, + outputdir=options.outputdir, + python_versions=ReadPythonVersionInfoFile()) print("Installation finished!") diff --git a/makepanda/makechm.py b/makepanda/makechm.py index 1127eb41f5..5a14813380 100755 --- a/makepanda/makechm.py +++ b/makepanda/makechm.py @@ -213,11 +213,11 @@ def makeCHM(outputfile, dirname, title, special = None): # Now, execute the command to compile the files. if "PROGRAMFILES" in os.environ and os.path.isdir("%s\\HTML Help Workshop" % os.environ["PROGRAMFILES"]): cmd = "\"%s\\HTML Help Workshop\\hhc.exe\" %s.hhp" % (os.environ["PROGRAMFILES"], base) - elif os.path.isdir("C:\Program Files\HTML Help Workshop"): + elif os.path.isdir("C:\\Program Files\\HTML Help Workshop"): cmd = "\"C:\\Program Files\\HTML Help Workshop\\hhc.exe\" %s.hhp" % base else: cmd = "hhc \"%s.hhp\"" % base - print cmd + print(cmd) os.system(cmd) if not KEEPTEMP: if os.path.isfile("%s.hhp" % base): os.remove("%s.hhp" % base) @@ -225,7 +225,7 @@ def makeCHM(outputfile, dirname, title, special = None): if os.path.isfile("%s.hhk" % base): os.remove("%s.hhk" % base) if os.path.isfile("%s.chw" % base): os.remove("%s.chw" % base) if not os.path.isfile(base + ".chm"): - print "An error has occurred!" + print("An error has occurred!") if __name__ == "__main__": exit(1) else: @@ -246,7 +246,7 @@ if __name__ == "__main__": VERSION = None try: f = file("built/include/pandaVersion.h","r") - pattern = re.compile('^\s*[#]\s*define\s+PANDA_VERSION_STR\s+["]([0-9.]+)["]') + pattern = re.compile('^\\s*[#]\\s*define\\s+PANDA_VERSION_STR\\s+["]([0-9.]+)["]') for line in f: match = pattern.match(line,0) if (match): @@ -260,22 +260,22 @@ if __name__ == "__main__": # Now, make CHM's for both the manual and reference, if we have them. for lang in ["python", "cxx"]: if not os.path.isdir("manual-" + lang): - print "No directory named 'manual-%s' found" % lang + print("No directory named 'manual-%s' found" % lang) else: - print "Making CHM file for manual-%s..." % lang + print("Making CHM file for manual-%s..." % lang) if VERSION == None: makeManualCHM("manual-%s.chm" % lang, "manual-" + lang, "Panda3D Manual") else: makeManualCHM("manual-%s-%s.chm" % (VERSION, lang), "manual-" + lang, "Panda3D %s Manual" % VERSION) if not os.path.isdir("reference-" + lang): - print "No directory named 'reference-%s' found" % lang + print("No directory named 'reference-%s' found" % lang) else: - print "Making CHM file for reference-%s..." % lang + print("Making CHM file for reference-%s..." % lang) if VERSION == None: makeReferenceCHM("reference-%s.chm" % lang, "reference-" + lang, "Panda3D Reference") else: makeReferenceCHM("reference-%s-%s.chm" % (VERSION, lang), "reference-" + lang, "Panda3D %s Reference" % VERSION) - print "Done!" + print("Done!") diff --git a/makepanda/makepackage.py b/makepanda/makepackage.py index daf0877544..560d1a2c55 100755 --- a/makepanda/makepackage.py +++ b/makepanda/makepackage.py @@ -5,6 +5,9 @@ from installpanda import * import sys import os import shutil +import glob +import re +import subprocess INSTALLER_DEB_FILE = """ @@ -29,23 +32,6 @@ Description: Panda3D free 3D engine SDK """ -RUNTIME_INSTALLER_DEB_FILE = """ -Package: panda3d-runtime -Version: VERSION -Section: web -Priority: optional -Architecture: ARCH -Essential: no -Depends: DEPENDS -Provides: panda3d-runtime -Maintainer: rdb -Installed-Size: INSTSIZE -Description: Runtime binary and browser plugin for the Panda3D Game Engine - This package contains the runtime distribution and browser plugin of the Panda3D engine. It allows you view webpages that contain Panda3D content and to run games created with Panda3D that are packaged as .p3d file. - -""" - - # We're not putting "python" in the "Requires" field, # since the rpm-based distros don't have a common # naming for the Python package. @@ -61,7 +47,7 @@ BuildRoot: PANDASOURCE/targetroot Panda3D is a game engine which includes graphics, audio, I/O, collision detection, and other abilities relevant to the creation of 3D games. Panda3D is open source and free software under the revised BSD license, and can be used for both free and commercial game development at no financial cost. Panda3D's intended game-development language is Python. The engine itself is written in C++, and utilizes an automatic wrapper-generator to expose the complete functionality of the engine in a Python interface. -This package contains the SDK for development with Panda3D, install panda3d-runtime for the runtime files. +This package contains the SDK for development with Panda3D. %post /sbin/ldconfig %postun @@ -83,30 +69,6 @@ INSTALLER_SPEC_FILE_PVIEW = \ /usr/share/application-registry/panda3d.applications """ -RUNTIME_INSTALLER_SPEC_FILE = """ -Summary: Runtime binary and browser plugin for the Panda3D Game Engine -Name: panda3d-runtime -Version: VERSION -Release: RPMRELEASE -License: BSD License -Group: Productivity/Graphics/Other -BuildRoot: PANDASOURCE/targetroot -%description -This package contains the runtime distribution and browser plugin of the Panda3D engine. It allows you view webpages that contain Panda3D content and to run games created with Panda3D that are packaged as .p3d file. -%files -%defattr(-,root,root) -/usr/bin/panda3d -/usr/%_lib/nppanda3d.so -/usr/%_lib/mozilla/plugins/nppanda3d.so -/usr/%_lib/mozilla-firefox/plugins/nppanda3d.so -/usr/%_lib/xulrunner-addons/plugins/nppanda3d.so -/usr/share/mime-info/panda3d-runtime.mime -/usr/share/mime-info/panda3d-runtime.keys -/usr/share/mime/packages/panda3d-runtime.xml -/usr/share/application-registry/panda3d-runtime.applications -/usr/share/applications/*.desktop -""" - # plist file for Mac OSX Info_plist = """ @@ -131,16 +93,7 @@ INSTALLER_PKG_DESCR_FILE = """ Panda3D is a game engine which includes graphics, audio, I/O, collision detection, and other abilities relevant to the creation of 3D games. Panda3D is open source and free software under the revised BSD license, and can be used for both free and commercial game development at no financial cost. Panda3D's intended game-development language is Python. The engine itself is written in C++, and utilizes an automatic wrapper-generator to expose the complete functionality of the engine in a Python interface. -This package contains the SDK for development with Panda3D, install panda3d-runtime for the runtime files. - -WWW: https://www.panda3d.org/ -""" - -# FreeBSD pkg-descr -RUNTIME_INSTALLER_PKG_DESCR_FILE = """ -Runtime binary and browser plugin for the Panda3D Game Engine - -This package contains the runtime distribution and browser plugin of the Panda3D engine. It allows you view webpages that contain Panda3D content and to run games created with Panda3D that are packaged as .p3d file. +This package contains the SDK for development with Panda3D. WWW: https://www.panda3d.org/ """ @@ -160,7 +113,7 @@ deps: {DEPENDS} """ -def MakeInstallerNSIS(version, file, title, installdir, runtime=False, compressor="lzma", **kwargs): +def MakeInstallerNSIS(version, file, title, installdir, compressor="lzma", **kwargs): outputdir = GetOutputDir() if os.path.isfile(file): @@ -173,23 +126,6 @@ def MakeInstallerNSIS(version, file, title, installdir, runtime=False, compresso else: regview = '32' - if runtime: - # Invoke the make_installer script. - AddToPathEnv("PATH", outputdir + "\\bin") - AddToPathEnv("PATH", outputdir + "\\plugins") - - cmd = sys.executable + " -B -u " + os.path.join("direct", "src", "plugin_installer", "make_installer.py") - cmd += " --version %s --regview %s" % (version, regview) - - if GetTargetArch() == 'x64': - cmd += " --install \"$PROGRAMFILES64\\Panda3D\" " - else: - cmd += " --install \"$PROGRAMFILES32\\Panda3D\" " - - oscmd(cmd) - shutil.move(os.path.join("direct", "src", "plugin_installer", "p3d-setup.exe"), file) - return - print("Building "+title+" installer at %s" % (file)) if compressor != "lzma": print("Note: you are using zlib, which is faster, but lzma gives better compression.") @@ -257,7 +193,7 @@ def MakeDebugSymbolArchive(zipname, dirname): zip.close() -def MakeInstallerLinux(version, debversion=None, rpmrelease=1, runtime=False, +def MakeInstallerLinux(version, debversion=None, rpmrelease=1, python_versions=[], **kwargs): outputdir = GetOutputDir() @@ -266,19 +202,18 @@ def MakeInstallerLinux(version, debversion=None, rpmrelease=1, runtime=False, python3_ver = None install_python_versions = [] - if not runtime: - # What's the system version of Python 3? - oscmd('python3 -V > "%s/tmp/python3_version.txt"' % (outputdir)) - sys_python3_ver = '.'.join(ReadFile(outputdir + "/tmp/python3_version.txt").strip().split(' ')[1].split('.')[:2]) + # What's the system version of Python 3? + oscmd('python3 -V > "%s/tmp/python3_version.txt"' % (outputdir)) + sys_python3_ver = '.'.join(ReadFile(outputdir + "/tmp/python3_version.txt").strip().split(' ')[1].split('.')[:2]) - # Check that we built with support for these. - for version_info in python_versions: - if version_info["version"] == "2.7": - python2_ver = "2.7" - install_python_versions.append(version_info) - elif version_info["version"] == sys_python3_ver: - python3_ver = sys_python3_ver - install_python_versions.append(version_info) + # Check that we built with support for these. + for version_info in python_versions: + if version_info["version"] == "2.7": + python2_ver = "2.7" + install_python_versions.append(version_info) + elif version_info["version"] == sys_python3_ver: + python3_ver = sys_python3_ver + install_python_versions.append(version_info) major_version = '.'.join(version.split('.')[:2]) if not debversion: @@ -301,43 +236,31 @@ def MakeInstallerLinux(version, debversion=None, rpmrelease=1, runtime=False, if dpkg_present: # Invoke installpanda.py to install it into a temporary dir lib_dir = GetDebLibDir() - if runtime: - InstallRuntime(destdir="targetroot", prefix="/usr", - outputdir=outputdir, libdir=lib_dir) - else: - InstallPanda(destdir="targetroot", prefix="/usr", - outputdir=outputdir, libdir=lib_dir, - python_versions=install_python_versions) - oscmd("chmod -R 755 targetroot/usr/share/panda3d") - oscmd("mkdir -m 0755 -p targetroot/usr/share/man/man1") - oscmd("install -m 0644 doc/man/*.1 targetroot/usr/share/man/man1/") + InstallPanda(destdir="targetroot", prefix="/usr", + outputdir=outputdir, libdir=lib_dir, + python_versions=install_python_versions) + oscmd("chmod -R 755 targetroot/usr/share/panda3d") + oscmd("mkdir -m 0755 -p targetroot/usr/share/man/man1") + oscmd("install -m 0644 doc/man/*.1 targetroot/usr/share/man/man1/") oscmd("dpkg --print-architecture > "+outputdir+"/tmp/architecture.txt") pkg_arch = ReadFile(outputdir+"/tmp/architecture.txt").strip() - if runtime: - txt = RUNTIME_INSTALLER_DEB_FILE[1:] - else: - txt = INSTALLER_DEB_FILE[1:] + txt = INSTALLER_DEB_FILE[1:] txt = txt.replace("VERSION", debversion).replace("ARCH", pkg_arch).replace("MAJOR", major_version) txt = txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") // 1024)) oscmd("mkdir -m 0755 -p targetroot/DEBIAN") oscmd("cd targetroot && (find usr -type f -exec md5sum {} ;) > DEBIAN/md5sums") - if not runtime: - oscmd("cd targetroot && (find etc -type f -exec md5sum {} ;) >> DEBIAN/md5sums") - WriteFile("targetroot/DEBIAN/conffiles","/etc/Config.prc\n") + oscmd("cd targetroot && (find etc -type f -exec md5sum {} ;) >> DEBIAN/md5sums") + WriteFile("targetroot/DEBIAN/conffiles","/etc/Config.prc\n") WriteFile("targetroot/DEBIAN/postinst","#!/bin/sh\necho running ldconfig\nldconfig\n") oscmd("cp targetroot/DEBIAN/postinst targetroot/DEBIAN/postrm") # Determine the package name and the locations that # dpkg-shlibdeps should look in for executables. pkg_version = debversion - if runtime: - pkg_name = "panda3d-runtime" - lib_pattern = "debian/%s/usr/%s/*.so" % (pkg_name, lib_dir) - else: - pkg_name = "panda3d" + major_version - lib_pattern = "debian/%s/usr/%s/panda3d/*.so*" % (pkg_name, lib_dir) + pkg_name = "panda3d" + major_version + lib_pattern = "debian/%s/usr/%s/panda3d/*.so*" % (pkg_name, lib_dir) bin_pattern = "debian/%s/usr/bin/*" % (pkg_name) # dpkg-shlibdeps looks in the debian/{pkg_name}/DEBIAN/shlibs directory @@ -350,45 +273,38 @@ def MakeInstallerLinux(version, debversion=None, rpmrelease=1, runtime=False, if GetVerbose(): dpkg_shlibdeps += " -v" - if runtime: - # The runtime doesn't export any useful symbols, so just query the dependencies. - oscmd("cd targetroot && %(dpkg_shlibdeps)s -x%(pkg_name)s %(lib_pattern)s %(bin_pattern)s*" % locals()) - depends = ReadFile("targetroot/debian/substvars").replace("shlibs:Depends=", "").strip() - recommends = "" - provides = "panda3d-runtime" - else: - pkg_name = "panda3d" + major_version - pkg_dir = "debian/panda3d" + major_version + pkg_name = "panda3d" + major_version + pkg_dir = "debian/panda3d" + major_version - # Generate a symbols file so that other packages can know which symbols we export. - oscmd("cd targetroot && dpkg-gensymbols -q -ODEBIAN/symbols -v%(pkg_version)s -p%(pkg_name)s -e%(lib_pattern)s" % locals()) + # Generate a symbols file so that other packages can know which symbols we export. + oscmd("cd targetroot && dpkg-gensymbols -q -ODEBIAN/symbols -v%(pkg_version)s -p%(pkg_name)s -e%(lib_pattern)s" % locals()) - # Library dependencies are required, binary dependencies are recommended. - # We explicitly exclude libphysx-extras since we don't want to depend on PhysX. - oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_dep --ignore-missing-info -x%(pkg_name)s -xlibphysx-extras %(lib_pattern)s" % locals()) - oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_rec --ignore-missing-info -x%(pkg_name)s %(bin_pattern)s" % locals()) + # Library dependencies are required, binary dependencies are recommended. + # We explicitly exclude libphysx-extras since we don't want to depend on PhysX. + oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_dep --ignore-missing-info -x%(pkg_name)s -xlibphysx-extras %(lib_pattern)s" % locals()) + oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_rec --ignore-missing-info -x%(pkg_name)s %(bin_pattern)s" % locals()) - # Parse the substvars files generated by dpkg-shlibdeps. - depends = ReadFile("targetroot/debian/substvars_dep").replace("shlibs:Depends=", "").strip() - recommends = ReadFile("targetroot/debian/substvars_rec").replace("shlibs:Depends=", "").strip() - provides = "panda3d" + # Parse the substvars files generated by dpkg-shlibdeps. + depends = ReadFile("targetroot/debian/substvars_dep").replace("shlibs:Depends=", "").strip() + recommends = ReadFile("targetroot/debian/substvars_rec").replace("shlibs:Depends=", "").strip() + provides = "panda3d" - if python2_ver or python3_ver: - recommends += ", python-pmw" + if python2_ver or python3_ver: + recommends += ", python-pmw" - if python2_ver: - depends += ", python%s" % (python2_ver) - recommends += ", python-wxversion" - recommends += ", python-tk (>= %s)" % (python2_ver) - provides += ", python2-panda3d" + if python2_ver: + depends += ", python%s" % (python2_ver) + recommends += ", python-wxversion" + recommends += ", python-tk (>= %s)" % (python2_ver) + provides += ", python2-panda3d" - if python3_ver: - depends += ", python%s" % (python3_ver) - recommends += ", python3-tk (>= %s)" % (python3_ver) - provides += ", python3-panda3d" + if python3_ver: + depends += ", python%s" % (python3_ver) + recommends += ", python3-tk (>= %s)" % (python3_ver) + provides += ", python3-panda3d" - if not PkgSkip("NVIDIACG"): - depends += ", nvidia-cg-toolkit" + if not PkgSkip("NVIDIACG"): + depends += ", nvidia-cg-toolkit" # Write back the dependencies, and delete the dummy set-up. txt = txt.replace("DEPENDS", depends.strip(', ')) @@ -400,44 +316,38 @@ def MakeInstallerLinux(version, debversion=None, rpmrelease=1, runtime=False, # Package it all up into a .deb file. oscmd("chmod -R 755 targetroot/DEBIAN") oscmd("chmod 644 targetroot/DEBIAN/control targetroot/DEBIAN/md5sums") - if not runtime: - oscmd("chmod 644 targetroot/DEBIAN/conffiles targetroot/DEBIAN/symbols") + oscmd("chmod 644 targetroot/DEBIAN/conffiles targetroot/DEBIAN/symbols") oscmd("fakeroot dpkg-deb -b targetroot %s_%s_%s.deb" % (pkg_name, pkg_version, pkg_arch)) elif rpmbuild_present: # Invoke installpanda.py to install it into a temporary dir - if runtime: - InstallRuntime(destdir="targetroot", prefix="/usr", outputdir=outputdir, libdir=GetRPMLibDir()) - else: - InstallPanda(destdir="targetroot", prefix="/usr", - outputdir=outputdir, libdir=GetRPMLibDir(), - python_versions=install_python_versions) - oscmd("chmod -R 755 targetroot/usr/share/panda3d") + InstallPanda(destdir="targetroot", prefix="/usr", + outputdir=outputdir, libdir=GetRPMLibDir(), + python_versions=install_python_versions) + oscmd("chmod -R 755 targetroot/usr/share/panda3d") oscmd("rpm -E '%_target_cpu' > "+outputdir+"/tmp/architecture.txt") arch = ReadFile(outputdir+"/tmp/architecture.txt").strip() pandasource = os.path.abspath(os.getcwd()) - if runtime: - txt = RUNTIME_INSTALLER_SPEC_FILE[1:] - else: - txt = INSTALLER_SPEC_FILE[1:] + txt = INSTALLER_SPEC_FILE[1:] - # Add the MIME associations if we have pview - if not PkgSkip("PVIEW"): - txt += INSTALLER_SPEC_FILE_PVIEW + # Add the MIME associations if we have pview + if not PkgSkip("PVIEW"): + txt += INSTALLER_SPEC_FILE_PVIEW - # Add the platform-specific Python directories. - dirs = set() - for version_info in install_python_versions: - dirs.add(version_info["platlib"]) - dirs.add(version_info["purelib"]) + # Add the platform-specific Python directories. + dirs = set() + for version_info in install_python_versions: + dirs.add(version_info["platlib"]) + dirs.add(version_info["purelib"]) - for dir in dirs: - txt += dir + "\n" + for dir in dirs: + txt += dir + "\n" - # Add the binaries in /usr/bin explicitly to the spec file - for base in os.listdir(outputdir + "/bin"): + # Add the binaries in /usr/bin explicitly to the spec file + for base in os.listdir(outputdir + "/bin"): + if not base.startswith("deploy-stub"): txt += "/usr/bin/%s\n" % (base) # Write out the spec file. @@ -447,33 +357,19 @@ def MakeInstallerLinux(version, debversion=None, rpmrelease=1, runtime=False, WriteFile("panda3d.spec", txt) oscmd("fakeroot rpmbuild --define '_rpmdir "+pandasource+"' --buildroot '"+os.path.abspath("targetroot")+"' -bb panda3d.spec") - if runtime: - oscmd("mv "+arch+"/panda3d-runtime-"+version+"-"+rpmrelease+"."+arch+".rpm .") - else: - oscmd("mv "+arch+"/panda3d-"+version+"-"+rpmrelease+"."+arch+".rpm .") + oscmd("mv "+arch+"/panda3d-"+version+"-"+rpmrelease+"."+arch+".rpm .") oscmd("rm -rf "+arch, True) else: exit("To build an installer, either rpmbuild or dpkg-deb must be present on your system!") -def MakeInstallerOSX(version, runtime=False, python_versions=[], **kwargs): +def MakeInstallerOSX(version, python_versions=[], **kwargs): outputdir = GetOutputDir() - if runtime: - # Invoke the make_installer script. - AddToPathEnv("DYLD_LIBRARY_PATH", outputdir + "/plugins") - cmdstr = sys.executable + " " - if sys.version_info >= (2, 6): - cmdstr += "-B " - - cmdstr += "direct/src/plugin_installer/make_installer.py --version %s" % version - oscmd(cmdstr) - return - dmg_name = "Panda3D-" + version if len(python_versions) == 1 and not python_versions[0]["version"].startswith("2."): - dmg_name += "-py" + pyver + dmg_name += "-py" + python_versions[0]["version"] dmg_name += ".dmg" if (os.path.isfile(dmg_name)): oscmd("rm -f %s" % dmg_name) @@ -558,9 +454,9 @@ def MakeInstallerOSX(version, runtime=False, python_versions=[], **kwargs): oscmd("mkdir -p dstroot/pybindings%s/Library/Python/%s/site-packages" % (pyver, pyver)) WriteFile("dstroot/pybindings%s/Library/Python/%s/site-packages/Panda3D.pth" % (pyver, pyver), "/Developer/Panda3D") - # Evidently not all Python 3.7 installations read the above path, so do this for now - # See GitHub #502 - if pyver == "3.7": + # Somewhere in Python 2.7.13 and 3.7, the above path was removed from + # sys.path of the python.org distribution. See bpo-28440 and GH #502. + if pyver not in ("3.0", "3.1", "3.2", "3.3", "3.4", "3.5", "3.6"): dir = "dstroot/pybindings%s/Library/Frameworks/Python.framework/Versions/%s/lib/python%s/site-packages" % (pyver, pyver, pyver) oscmd("mkdir -p %s" % (dir)) WriteFile("%s/Panda3D.pth" % (dir), "/Developer/Panda3D") @@ -580,8 +476,6 @@ def MakeInstallerOSX(version, runtime=False, python_versions=[], **kwargs): oscmd("mkdir -p dstroot/headers/Developer/Panda3D/lib") oscmd("cp -R %s/include dstroot/headers/Developer/Panda3D/include" % outputdir) - if os.path.isfile(outputdir + "/lib/libp3pystub.a"): - oscmd("cp -R -P %s/lib/libp3pystub.a dstroot/headers/Developer/Panda3D/lib/" % outputdir) if os.path.isdir("samples"): oscmd("mkdir -p dstroot/samples/Developer/Examples/Panda3D") @@ -744,17 +638,14 @@ def MakeInstallerOSX(version, runtime=False, python_versions=[], **kwargs): oscmd('rm -f Panda3D-rw.dmg') -def MakeInstallerFreeBSD(version, runtime=False, **kwargs): +def MakeInstallerFreeBSD(version, python_versions=[], **kwargs): outputdir = GetOutputDir() oscmd("rm -rf targetroot +DESC pkg-plist +MANIFEST") oscmd("mkdir targetroot") # Invoke installpanda.py to install it into a temporary dir - if runtime: - InstallRuntime(destdir="targetroot", prefix="/usr/local", outputdir=outputdir) - else: - InstallPanda(destdir="targetroot", prefix="/usr/local", outputdir=outputdir) + InstallPanda(destdir="targetroot", prefix="/usr/local", outputdir=outputdir, python_versions=python_versions) if not os.path.exists("/usr/sbin/pkg"): exit("Cannot create an installer without pkg") @@ -764,15 +655,14 @@ def MakeInstallerFreeBSD(version, runtime=False, **kwargs): for f in files: plist_txt += os.path.join(root, f)[21:] + "\n" - if not runtime: - plist_txt += "@postexec /sbin/ldconfig -m /usr/local/lib/panda3d\n" - plist_txt += "@postunexec /sbin/ldconfig -R\n" + plist_txt += "@postexec /sbin/ldconfig -m /usr/local/lib/panda3d\n" + plist_txt += "@postunexec /sbin/ldconfig -R\n" - for remdir in ("lib/panda3d", "share/panda3d", "include/panda3d"): - for root, dirs, files in os.walk("targetroot/usr/local/" + remdir, False): - for d in dirs: - plist_txt += "@dir %s\n" % os.path.join(root, d)[21:] - plist_txt += "@dir %s\n" % remdir + for remdir in ("lib/panda3d", "share/panda3d", "include/panda3d"): + for root, dirs, files in os.walk("targetroot/usr/local/" + remdir, False): + for d in dirs: + plist_txt += "@dir %s\n" % os.path.join(root, d)[21:] + plist_txt += "@dir %s\n" % remdir oscmd("echo \"`pkg config abi | tr '[:upper:]' '[:lower:]' | cut -d: -f1,2`:*\" > " + outputdir + "/tmp/architecture.txt") pkg_arch = ReadFile(outputdir+"/tmp/architecture.txt").strip() @@ -793,15 +683,15 @@ def MakeInstallerFreeBSD(version, runtime=False, **kwargs): if python_pkg: dependencies += python_pkg - manifest_txt = INSTALLER_PKG_MANIFEST_FILE[1:].replace("NAME", 'panda3d' if not runtime else 'panda3d-runtime') + manifest_txt = INSTALLER_PKG_MANIFEST_FILE[1:].replace("NAME", 'panda3d') manifest_txt = manifest_txt.replace("VERSION", version) manifest_txt = manifest_txt.replace("ARCH", pkg_arch) - manifest_txt = manifest_txt.replace("ORIGIN", 'devel/panda3d' if not runtime else 'graphics/panda3d-runtime') + manifest_txt = manifest_txt.replace("ORIGIN", 'devel/panda3d') manifest_txt = manifest_txt.replace("DEPENDS", dependencies) manifest_txt = manifest_txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") // 1024 // 1024)) WriteFile("pkg-plist", plist_txt) - WriteFile("+DESC", INSTALLER_PKG_DESCR_FILE[1:] if not runtime else RUNTIME_INSTALLER_PKG_DESCR_FILE[1:]) + WriteFile("+DESC", INSTALLER_PKG_DESCR_FILE[1:]) WriteFile("+MANIFEST", manifest_txt) oscmd("pkg create -p pkg-plist -r %s -m . -o . %s" % (os.path.abspath("targetroot"), "--verbose" if GetVerbose() else "--quiet")) @@ -834,7 +724,8 @@ def MakeInstallerAndroid(version, **kwargs): # off any suffix (eg. libfile.so.1.0), as Android does not support them. source_dir = os.path.join(outputdir, "lib") target_dir = os.path.join("apkroot", "lib", SDK["ANDROID_ABI"]) - oscmd("mkdir -p %s" % (target_dir)) + if not os.path.exists(target_dir): + os.makedirs(target_dir, mode=0o755) # Determine the library directories we should look in. libpath = [source_dir] @@ -856,19 +747,26 @@ def MakeInstallerAndroid(version, **kwargs): # Already processed. return - oscmd("cp %s %s" % (source, target)) + shutil.copy(source, target) # Walk through the library dependencies. - oscmd("ldd %s | grep .so > %s/tmp/otool-libs.txt" % (target, outputdir), True) - for line in open(outputdir + "/tmp/otool-libs.txt", "r"): - line = line.strip() - if not line: + handle = subprocess.Popen(['readelf', '--dynamic', target], stdout=subprocess.PIPE) + for line in handle.communicate()[0].splitlines(): + # The line will look something like: + # 0x0000000000000001 (NEEDED) Shared library: [libpanda.so] + line = line.decode('utf-8', 'replace').strip() + if not line or '(NEEDED)' not in line or '[' not in line or ']' not in line: continue - if '.so.' in line: - dep = line.rpartition('.so.')[0] + '.so' - oscmd("patchelf --replace-needed %s %s %s" % (line, dep, target), True) - else: - dep = line + + # Extract the part between square brackets. + idx = line.index('[') + dep = line[idx + 1 : line.index(']', idx)] + + # Change .so.1.2 suffix to .so, as needed for loading in .apk + if '.so.' in dep: + orig_dep = dep + dep = dep.rpartition('.so.')[0] + '.so' + oscmd("patchelf --replace-needed %s %s %s" % (orig_dep, dep, target), True) # Find it on the LD_LIBRARY_PATH. for dir in libpath: @@ -957,21 +855,28 @@ def MakeInstallerAndroid(version, **kwargs): aapt_cmd += " -F %s" % (apk_unaligned) aapt_cmd += " -M apkroot/AndroidManifest.xml" aapt_cmd += " -A apkroot/assets -S apkroot/res" - aapt_cmd += " -I $PREFIX/share/aapt/android.jar" + aapt_cmd += " -I %s" % (SDK["ANDROID_JAR"]) oscmd(aapt_cmd) # And add all the libraries to it. - oscmd("cd apkroot && aapt add ../%s classes.dex" % (apk_unaligned)) + oscmd("aapt add %s classes.dex" % (os.path.join('..', apk_unaligned)), cwd="apkroot") for path, dirs, files in os.walk('apkroot/lib'): if files: rel = os.path.relpath(path, 'apkroot') - oscmd("cd apkroot && aapt add ../%s %s/*" % (apk_unaligned, rel)) + rel_files = [os.path.join(rel, file).replace('\\', '/') for file in files] + oscmd("aapt add %s %s" % (os.path.join('..', apk_unaligned), ' '.join(rel_files)), cwd="apkroot") # Now align the .apk, which is necessary for Android to load it. oscmd("zipalign -v -p 4 %s %s" % (apk_unaligned, apk_unsigned)) # Finally, sign it using a debug key. This is generated if it doesn't exist. - oscmd("apksigner debug.ks %s panda3d.apk" % (apk_unsigned)) + if GetHost() == 'android': + # Termux version of apksigner automatically generates a debug key. + oscmd("apksigner debug.ks %s panda3d.apk" % (apk_unsigned)) + else: + if not os.path.isfile('debug.ks'): + oscmd("keytool -genkey -noprompt -dname CN=Panda3D,O=Panda3D,C=US -keystore debug.ks -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 1000") + oscmd("apksigner sign --ks debug.ks --ks-pass pass:android --min-sdk-version %s --out panda3d.apk %s" % (SDK["ANDROID_API"], apk_unsigned)) # Clean up. oscmd("rm -rf apkroot") @@ -985,17 +890,12 @@ def MakeInstaller(version, **kwargs): fn = "Panda3D-" dir = "Panda3D-" + version - runtime = kwargs.get('runtime', False) - if runtime: - fn += "Runtime-" - title = "Panda3D " + version - else: - title = "Panda3D SDK " + version + title = "Panda3D SDK " + version fn += version python_versions = kwargs.get('python_versions', []) - if not runtime and len(python_versions) == 1 and python_versions[0]["version"] != "2.7": + if len(python_versions) == 1 and python_versions[0]["version"] != "2.7": fn += '-py' + python_versions[0]["version"] if GetOptimize() <= 2: @@ -1005,8 +905,7 @@ def MakeInstaller(version, **kwargs): dir += '-x64' MakeInstallerNSIS(version, fn + '.exe', title, 'C:\\' + dir, **kwargs) - if not runtime: - MakeDebugSymbolArchive(fn + '-pdb.zip', dir) + MakeDebugSymbolArchive(fn + '-pdb.zip', dir) elif target == 'linux': MakeInstallerLinux(version, **kwargs) elif target == 'darwin': @@ -1020,7 +919,7 @@ def MakeInstaller(version, **kwargs): if __name__ == "__main__": - version = ParsePandaVersion("dtool/PandaVersion.pp") + version = GetMetadataValue('version') parser = OptionParser() parser.add_option('', '--version', dest='version', help='Panda3D version number (default: %s)' % (version), default=version) @@ -1028,7 +927,6 @@ if __name__ == "__main__": parser.add_option('', '--rpmrelease', dest='rpmrelease', help='Release number for .rpm file', default='1') parser.add_option('', '--outputdir', dest='outputdir', help='Makepanda\'s output directory (default: built)', default='built') parser.add_option('', '--verbose', dest='verbose', help='Enable verbose output', action='store_true', default=False) - parser.add_option('', '--runtime', dest='runtime', help='Runtime instead of SDK', action='store_true', default=False) parser.add_option('', '--lzma', dest='compressor', help='Use LZMA compression', action='store_const', const='lzma', default='zlib') (options, args) = parser.parse_args() @@ -1052,7 +950,7 @@ if __name__ == "__main__": PkgDisable(pkg) # Parse the version. - match = re.match(r'^\d+\.\d+\.\d+', options.version) + match = re.match(r'^\d+\.\d+(\.\d+)+', options.version) if not match: exit("version requires three digits") @@ -1062,5 +960,4 @@ if __name__ == "__main__": compressor=options.compressor, debversion=options.debversion, rpmrelease=options.rpmrelease, - runtime=options.runtime, python_versions=ReadPythonVersionInfoFile()) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 40febe3cd0..2dc92ac7c0 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -49,22 +49,14 @@ THREADCOUNT=0 CFLAGS="" CXXFLAGS="" LDFLAGS="" -RTDIST=0 -RTDIST_VERSION=None -RUNTIME=0 DISTRIBUTOR="" VERSION=None DEBVERSION=None WHLVERSION=None RPMRELEASE="1" GIT_COMMIT=None -P3DSUFFIX=None MAJOR_VERSION=None -COREAPI_VERSION=None -PLUGIN_VERSION=None OSXTARGET=None -OSX_ARCHS=[] -HOST_URL=None global STRDXSDKVERSION, BOOUSEINTELCOMPILER STRDXSDKVERSION = 'default' WINDOWS_SDK = None @@ -94,7 +86,7 @@ PkgListSet(["PYTHON", "DIRECT", # Python support "GTK2", # GTK2 is used for PStats on Unix "MFC", "WX", "FLTK", # Used for web plug-in only "ROCKET", # GUI libraries - "CARBON", "COCOA", # Mac OS X toolkits + "COCOA", # Mac OS X toolkits "X11", # Unix platform support "PANDATOOL", "PVIEW", "DEPLOYTOOLS", # Toolchain "SKEL", # Example SKEL project @@ -130,7 +122,6 @@ def usage(problem): print("") print(" --help (print the help message you're reading now)") print(" --verbose (print out more information)") - print(" --runtime (build a runtime build instead of an SDK build)") print(" --tests (run the test suite)") print(" --installer (build an installer)") print(" --wheel (build a pip-installable .whl)") @@ -139,10 +130,8 @@ def usage(problem): print(" --lzma (use lzma compression when building Windows installer)") print(" --distributor X (short string identifying the distributor of the build)") print(" --outputdir X (use the specified directory instead of 'built')") - print(" --host URL (set the host url (runtime build only))") print(" --threads N (use the multithreaded build system. see manual)") print(" --osxtarget N (the OS X version number to build for (OS X only))") - print(" --universal (build universal binaries (OS X only))") print(" --override \"O=V\" (override dtool_config/prc option value)") print(" --static (builds libraries for static linking)") print(" --target X (experimental cross-compilation (android only))") @@ -159,7 +148,7 @@ def usage(problem): print(" --everything (enable every third-party lib)") print(" --directx-sdk=X (specify version of DirectX SDK to use: jun2010, aug2009, mar2009, aug2006)") print(" --windows-sdk=X (specify Windows SDK version, eg. 7.0, 7.1 or 10. Default is 7.1)") - print(" --msvc-version=X (specify Visual C++ version, eg. 10, 11, 12, 14. Default is 14)") + print(" --msvc-version=X (specify Visual C++ version, eg. 10, 11, 12, 14, 14.1, 14.2. Default is 14)") print(" --use-icl (experimental setting to use an intel compiler instead of MSVC on Windows)") print("") print("The simplest way to compile panda is to just type:") @@ -169,32 +158,32 @@ def usage(problem): os._exit(1) def parseopts(args): - global INSTALLER,WHEEL,RUNTESTS,RTDIST,RUNTIME,GENMAN,DISTRIBUTOR,VERSION - global COMPRESSOR,THREADCOUNT,OSXTARGET,OSX_ARCHS,HOST_URL - global DEBVERSION,WHLVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION + global INSTALLER,WHEEL,RUNTESTS,GENMAN,DISTRIBUTOR,VERSION + global COMPRESSOR,THREADCOUNT,OSXTARGET + global DEBVERSION,WHLVERSION,RPMRELEASE,GIT_COMMIT global STRDXSDKVERSION, WINDOWS_SDK, MSVC_VERSION, BOOUSEINTELCOMPILER global COPY_PYTHON # Options for which to display a deprecation warning. removedopts = [ "use-touchinput", "no-touchinput", "no-awesomium", "no-directscripts", + "no-carbon", "universal", ] # All recognized options. longopts = [ - "help","distributor=","verbose","runtime","osxtarget=","tests", + "help","distributor=","verbose","osxtarget=","tests", "optimize=","everything","nothing","installer","wheel","rtdist","nocolor", "version=","lzma","no-python","threads=","outputdir=","override=", - "static","host=","debversion=","rpmrelease=","p3dsuffix=","rtdist-version=", + "static","debversion=","rpmrelease=","p3dsuffix=","rtdist-version=", "directx-sdk=", "windows-sdk=", "msvc-version=", "clean", "use-icl", - "universal", "target=", "arch=", "git-commit=", "no-copy-python", + "target=", "arch=", "git-commit=", "no-copy-python", ] + removedopts anything = 0 optimize = "" target = None target_arch = None - universal = False clean_build = False for pkg in PkgListGet(): longopts.append("use-" + pkg.lower()) @@ -212,20 +201,17 @@ def parseopts(args): elif (option=="--wheel"): WHEEL=1 elif (option=="--verbose"): SetVerbose(True) elif (option=="--distributor"): DISTRIBUTOR=value - elif (option=="--rtdist"): RTDIST=1 - elif (option=="--runtime"): RUNTIME=1 elif (option=="--genman"): GENMAN=1 elif (option=="--everything"): PkgEnableAll() elif (option=="--nothing"): PkgDisableAll() elif (option=="--threads"): THREADCOUNT=int(value) elif (option=="--outputdir"): SetOutputDir(value.strip()) elif (option=="--osxtarget"): OSXTARGET=value.strip() - elif (option=="--universal"): universal = True elif (option=="--target"): target = value.strip() elif (option=="--arch"): target_arch = value.strip() elif (option=="--nocolor"): DisableColors() elif (option=="--version"): - match = re.match(r'^\d+\.\d+\.\d+', value) + match = re.match(r'^\d+\.\d+(\.\d+)+', value) if not match: usage("version requires three digits") WHLVERSION = value @@ -233,12 +219,9 @@ def parseopts(args): elif (option=="--lzma"): COMPRESSOR="lzma" elif (option=="--override"): AddOverride(value.strip()) elif (option=="--static"): SetLinkAllStatic(True) - elif (option=="--host"): HOST_URL=value elif (option=="--debversion"): DEBVERSION=value elif (option=="--rpmrelease"): RPMRELEASE=value elif (option=="--git-commit"): GIT_COMMIT=value - elif (option=="--p3dsuffix"): P3DSUFFIX=value - elif (option=="--rtdist-version"): RTDIST_VERSION=value # Backward compatibility, OPENGL was renamed to GL elif (option=="--use-opengl"): PkgEnable("GL") elif (option=="--no-opengl"): PkgDisable("GL") @@ -279,15 +262,9 @@ def parseopts(args): usage(sys.exc_info()[1]) if not anything: - if RUNTIME: - PkgEnableAll() - else: - usage("You should specify a list of packages to use or --everything to enable all packages.") + usage("You should specify a list of packages to use or --everything to enable all packages.") - if (RTDIST and RUNTIME): - usage("Options --runtime and --rtdist cannot be specified at the same time!") - if (optimize=="" and (RTDIST or RUNTIME)): optimize = "4" - elif (optimize==""): optimize = "3" + if (optimize==""): optimize = "3" if OSXTARGET: try: @@ -302,25 +279,6 @@ def parseopts(args): if target is not None or target_arch is not None: SetTarget(target, target_arch) - if universal: - if target_arch: - exit("--universal is incompatible with --arch") - - OSX_ARCHS.append("i386") - if OSXTARGET: - osxver = OSXTARGET - else: - maj, min = platform.mac_ver()[0].split('.')[:2] - osxver = int(maj), int(min) - - if osxver[1] < 6: - OSX_ARCHS.append("ppc") - else: - OSX_ARCHS.append("x86_64") - - elif HasTargetArch(): - OSX_ARCHS.append(GetTargetArch()) - try: SetOptimize(int(optimize)) assert GetOptimize() in [1, 2, 3, 4] @@ -392,31 +350,20 @@ if GetHost() == "darwin" and OSXTARGET is not None: ## ######################################################################## -PLUGIN_VERSION = ParsePluginVersion("dtool/PandaVersion.pp") -COREAPI_VERSION = PLUGIN_VERSION + "." + ParseCoreapiVersion("dtool/PandaVersion.pp") - if VERSION is None: - if RUNTIME: - VERSION = PLUGIN_VERSION - else: - # Take the value from the setup.cfg file. - VERSION = GetMetadataValue('version') + # Take the value from the setup.cfg file. + VERSION = GetMetadataValue('version') if WHLVERSION is None: WHLVERSION = VERSION print("Version: %s" % VERSION) -if RUNTIME or RTDIST: - print("Core API Version: %s" % COREAPI_VERSION) if DEBVERSION is None: DEBVERSION = VERSION MAJOR_VERSION = '.'.join(VERSION.split('.')[:2]) -if P3DSUFFIX is None: - P3DSUFFIX = MAJOR_VERSION - # Now determine the distutils-style platform tag for the target system. target = GetTarget() if target == 'windows': @@ -432,22 +379,7 @@ elif target == 'darwin': maj, min = platform.mac_ver()[0].split('.')[:2] osxver = int(maj), int(min) - arch_tag = None - if not OSX_ARCHS: - arch_tag = GetTargetArch() - elif len(OSX_ARCHS) == 1: - arch_tag = OSX_ARCHS[0] - elif frozenset(OSX_ARCHS) == frozenset(('i386', 'ppc')): - arch_tag = 'fat' - elif frozenset(OSX_ARCHS) == frozenset(('x86_64', 'i386')): - arch_tag = 'intel' - elif frozenset(OSX_ARCHS) == frozenset(('x86_64', 'ppc64')): - arch_tag = 'fat64' - elif frozenset(OSX_ARCHS) == frozenset(('x86_64', 'i386', 'ppc')): - arch_tag = 'fat32' - else: - raise RuntimeError('No arch tag for arch combination %s' % OSX_ARCHS) - + arch_tag = GetTargetArch() PLATFORM = 'macosx-{0}.{1}-{2}'.format(osxver[0], osxver[1], arch_tag) elif target == 'linux' and (os.path.isfile("/lib/libc-2.5.so") or os.path.isfile("/lib64/libc-2.5.so")) and os.path.isdir("/opt/python"): @@ -473,32 +405,15 @@ else: target_arch = GetTargetArch() if target_arch == 'amd64': target_arch = 'x86_64' - PLATFORM = '{0}-{1}' % (target, target_arch) + PLATFORM = '{0}-{1}'.format(target, target_arch) print("Platform: %s" % PLATFORM) outputdir_suffix = "" -if (RUNTIME or RTDIST): - # Compiling Maya/Max is pointless in rtdist build - for ver in MAYAVERSIONS + MAXVERSIONS: - PkgDisable(ver) - - if (DISTRIBUTOR.strip() == ""): - exit("You must provide a valid distributor name when making a runtime or rtdist build!") - - outputdir_suffix += "_" + DISTRIBUTOR.strip() - if (RUNTIME): - outputdir_suffix += "_rt" - if DISTRIBUTOR == "": DISTRIBUTOR = "makepanda" -elif not RTDIST_VERSION: - RTDIST_VERSION = DISTRIBUTOR.strip() + "_" + MAJOR_VERSION - -if not RTDIST_VERSION: - RTDIST_VERSION = "dev" if not IsCustomOutputDir(): if GetTarget() == "windows" and GetTargetArch() == 'x64': @@ -506,40 +421,12 @@ if not IsCustomOutputDir(): SetOutputDir("built" + outputdir_suffix) -if (RUNTIME): - for pkg in PkgListGet(): - if pkg in ["GTK2", "MFC"]: - # Optional package(s) for runtime. - pass - elif pkg in ["OPENSSL", "ZLIB"]: - # Required packages for runtime. - if (PkgSkip(pkg)==1): - exit("Runtime must be compiled with OpenSSL and ZLib support!") - else: - # Unused packages for runtime. - PkgDisable(pkg) - -if (INSTALLER and RTDIST): - exit("Cannot build an installer for the rtdist build!") - -if (WHEEL and RUNTIME): - exit("Cannot build a wheel for the runtime build!") - -if (WHEEL and RTDIST): - exit("Cannot build a wheel for the rtdist build!") - -if (INSTALLER) and (PkgSkip("PYTHON")) and (not RUNTIME) and GetTarget() == 'windows': +if (INSTALLER) and (PkgSkip("PYTHON")) and GetTarget() == 'windows': exit("Cannot build installer on Windows without python") if WHEEL and PkgSkip("PYTHON"): exit("Cannot build wheel without Python") -if (RTDIST) and (PkgSkip("WX") and PkgSkip("FLTK")): - exit("Cannot build rtdist without wx or fltk") - -if (RUNTIME): - SetLinkAllStatic(True) - if not os.path.isdir("contrib"): PkgDisable("CONTRIB") @@ -563,7 +450,7 @@ SdkLocateDirectX(STRDXSDKVERSION) SdkLocateMaya() SdkLocateMax() SdkLocateMacOSX(OSXTARGET) -SdkLocatePython(RTDIST) +SdkLocatePython(False) SdkLocateWindows(WINDOWS_SDK) SdkLocatePhysX() SdkLocateSpeedTree() @@ -575,21 +462,16 @@ SdkAutoDisableMax() SdkAutoDisablePhysX() SdkAutoDisableSpeedTree() -if RTDIST and DISTRIBUTOR == "cmu": - # Some validation checks for the CMU builds. - if (RTDIST_VERSION == "cmu_1.7" and SDK["PYTHONVERSION"] != "python2.6"): - exit("The CMU 1.7 runtime distribution must be built against Python 2.6!") - elif (RTDIST_VERSION == "cmu_1.8" and SDK["PYTHONVERSION"] != "python2.7"): - exit("The CMU 1.8 runtime distribution must be built against Python 2.7!") - elif (RTDIST_VERSION == "cmu_1.9" and SDK["PYTHONVERSION"] != "python2.7"): - exit("The CMU 1.9 runtime distribution must be built against Python 2.7!") - -if RTDIST and not HOST_URL: - exit("You must specify a host URL when building the rtdist!") - -if RUNTIME and not HOST_URL: - # Set this to a nice default. - HOST_URL = "https://runtime.panda3d.org/" +if not PkgSkip("PYTHON") and SDK["PYTHONVERSION"] == "python2.7": + warn_prefix = "%sWARNING:%s " % (GetColor("red"), GetColor()) + print("=========================================================================") + print(warn_prefix + "Python 2.7 will reach EOL after December 31, 2019, and will not") + print(warn_prefix + "be supported after that date. Please ensure you are prepared") + print(warn_prefix + "by planning your upgrade to Python 3 now.") + print("=========================================================================") + sys.stdout.flush() + # Give the user some time to contemplate their sins + time.sleep(6.0) ######################################################################## ## @@ -622,7 +504,6 @@ if (COMPILER == "MSVC"): PkgDisable("GLES") PkgDisable("GLES2") PkgDisable("EGL") - PkgDisable("CARBON") PkgDisable("COCOA") DefSymbol("FLEX", "YY_NO_UNISTD_H") if (PkgSkip("PYTHON")==0): @@ -790,17 +671,6 @@ if (COMPILER == "MSVC"): LibName("FMODEX", GetThirdpartyDir() + "fmodex/lib/fmodex64_vc.lib") else: LibName("FMODEX", GetThirdpartyDir() + "fmodex/lib/fmodex_vc.lib") - if (PkgSkip("FLTK")==0 and RTDIST): - LibName("FLTK", GetThirdpartyDir() + "fltk/lib/fltk.lib") - if not PkgSkip("FLTK"): - # If we have fltk, we don't need wx - PkgDisable("WX") - if (PkgSkip("WX")==0 and RTDIST): - LibName("WX", GetThirdpartyDir() + "wx/lib/wxbase28u.lib") - LibName("WX", GetThirdpartyDir() + "wx/lib/wxmsw28u_core.lib") - DefSymbol("WX", "__WXMSW__", "") - DefSymbol("WX", "_UNICODE", "") - DefSymbol("WX", "UNICODE", "") if (PkgSkip("VORBIS")==0): for lib in ('ogg', 'vorbis', 'vorbisfile'): path = GetThirdpartyDir() + "vorbis/lib/lib{0}_static.lib".format(lib) @@ -875,14 +745,7 @@ if (COMPILER == "MSVC"): if (COMPILER=="GCC"): if GetTarget() != "darwin": - PkgDisable("CARBON") PkgDisable("COCOA") - elif RUNTIME: - # We don't support Cocoa in the runtime yet. - PkgDisable("COCOA") - if 'x86_64' in OSX_ARCHS: - # 64-bits OS X doesn't have Carbon. - PkgDisable("CARBON") #if (PkgSkip("PYTHON")==0): # IncDirectory("PYTHON", SDK["PYTHON"]) @@ -905,91 +768,89 @@ if (COMPILER=="GCC"): fcollada_libs = ("FColladaD", "FColladaSD", "FColladaS") # WARNING! The order of the ffmpeg libraries matters! ffmpeg_libs = ("libavformat", "libavcodec", "libavutil") + assimp_libs = ("libassimp", "libassimpd") # Name pkg-config libs, include(dir)s - if (not RUNTIME): - SmartPkgEnable("EIGEN", "eigen3", (), ("Eigen/Dense",), target_pkg = 'ALWAYS') - SmartPkgEnable("ARTOOLKIT", "", ("AR"), "AR/ar.h") - SmartPkgEnable("FCOLLADA", "", ChooseLib(fcollada_libs, "FCOLLADA"), ("FCollada", "FCollada/FCollada.h")) - SmartPkgEnable("ASSIMP", "assimp", ("assimp"), "assimp/Importer.hpp") - SmartPkgEnable("FFMPEG", ffmpeg_libs, ffmpeg_libs, ("libavformat/avformat.h", "libavcodec/avcodec.h", "libavutil/avutil.h")) - SmartPkgEnable("SWSCALE", "libswscale", "libswscale", ("libswscale/swscale.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg") - SmartPkgEnable("SWRESAMPLE","libswresample", "libswresample", ("libswresample/swresample.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg") - SmartPkgEnable("FFTW", "", ("fftw3"), ("fftw.h")) - SmartPkgEnable("FMODEX", "", ("fmodex"), ("fmodex", "fmodex/fmod.h")) - SmartPkgEnable("FREETYPE", "freetype2", ("freetype"), ("freetype2", "freetype2/freetype/freetype.h")) - SmartPkgEnable("HARFBUZZ", "harfbuzz", ("harfbuzz"), ("harfbuzz", "harfbuzz/hb-ft.h")) - SmartPkgEnable("GL", "gl", ("GL"), ("GL/gl.h"), framework = "OpenGL") - SmartPkgEnable("GLES", "glesv1_cm", ("GLESv1_CM"), ("GLES/gl.h"), framework = "OpenGLES") - SmartPkgEnable("GLES2", "glesv2", ("GLESv2"), ("GLES2/gl2.h")) #framework = "OpenGLES"? - SmartPkgEnable("EGL", "egl", ("EGL"), ("EGL/egl.h")) - SmartPkgEnable("NVIDIACG", "", ("Cg"), "Cg/cg.h", framework = "Cg") - SmartPkgEnable("ODE", "", ("ode"), "ode/ode.h", tool = "ode-config") - SmartPkgEnable("OPENAL", "openal", ("openal"), "AL/al.h", framework = "OpenAL") - SmartPkgEnable("SQUISH", "", ("squish"), "squish.h") - SmartPkgEnable("TIFF", "libtiff-4", ("tiff"), "tiff.h") - SmartPkgEnable("OPENEXR", "OpenEXR", ("IlmImf", "Imath", "Half", "Iex", "IexMath", "IlmThread"), ("OpenEXR", "OpenEXR/ImfOutputFile.h")) - SmartPkgEnable("VRPN", "", ("vrpn", "quat"), ("vrpn", "quat.h", "vrpn/vrpn_Types.h")) - SmartPkgEnable("BULLET", "bullet", ("BulletSoftBody", "BulletDynamics", "BulletCollision", "LinearMath"), ("bullet", "bullet/btBulletDynamicsCommon.h")) - SmartPkgEnable("VORBIS", "vorbisfile",("vorbisfile", "vorbis", "ogg"), ("ogg/ogg.h", "vorbis/vorbisfile.h")) - SmartPkgEnable("OPUS", "opusfile", ("opusfile", "opus", "ogg"), ("ogg/ogg.h", "opus/opusfile.h", "opus")) - SmartPkgEnable("JPEG", "", ("jpeg"), "jpeglib.h") - SmartPkgEnable("PNG", "libpng", ("png"), "png.h", tool = "libpng-config") + SmartPkgEnable("EIGEN", "eigen3", (), ("Eigen/Dense",), target_pkg = 'ALWAYS') + SmartPkgEnable("ARTOOLKIT", "", ("AR"), "AR/ar.h") + SmartPkgEnable("FCOLLADA", "", ChooseLib(fcollada_libs, "FCOLLADA"), ("FCollada", "FCollada/FCollada.h")) + SmartPkgEnable("ASSIMP", "assimp", ChooseLib(assimp_libs, "ASSIMP"), "assimp/Importer.hpp") + SmartPkgEnable("FFMPEG", ffmpeg_libs, ffmpeg_libs, ("libavformat/avformat.h", "libavcodec/avcodec.h", "libavutil/avutil.h")) + SmartPkgEnable("SWSCALE", "libswscale", "libswscale", ("libswscale/swscale.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg") + SmartPkgEnable("SWRESAMPLE","libswresample", "libswresample", ("libswresample/swresample.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg") + SmartPkgEnable("FFTW", "", ("fftw3"), ("fftw.h")) + SmartPkgEnable("FMODEX", "", ("fmodex"), ("fmodex", "fmodex/fmod.h")) + SmartPkgEnable("FREETYPE", "freetype2", ("freetype"), ("freetype2", "freetype2/freetype/freetype.h")) + SmartPkgEnable("HARFBUZZ", "harfbuzz", ("harfbuzz"), ("harfbuzz", "harfbuzz/hb-ft.h")) + SmartPkgEnable("GL", "gl", ("GL"), ("GL/gl.h"), framework = "OpenGL") + SmartPkgEnable("GLES", "glesv1_cm", ("GLESv1_CM"), ("GLES/gl.h"), framework = "OpenGLES") + SmartPkgEnable("GLES2", "glesv2", ("GLESv2"), ("GLES2/gl2.h")) #framework = "OpenGLES"? + SmartPkgEnable("EGL", "egl", ("EGL"), ("EGL/egl.h")) + SmartPkgEnable("NVIDIACG", "", ("Cg"), "Cg/cg.h", framework = "Cg") + SmartPkgEnable("ODE", "", ("ode"), "ode/ode.h", tool = "ode-config") + SmartPkgEnable("OPENAL", "openal", ("openal"), "AL/al.h", framework = "OpenAL") + SmartPkgEnable("SQUISH", "", ("squish"), "squish.h") + SmartPkgEnable("TIFF", "libtiff-4", ("tiff"), "tiff.h") + SmartPkgEnable("OPENEXR", "OpenEXR", ("IlmImf", "Imath", "Half", "Iex", "IexMath", "IlmThread"), ("OpenEXR", "OpenEXR/ImfOutputFile.h")) + SmartPkgEnable("VRPN", "", ("vrpn", "quat"), ("vrpn", "quat.h", "vrpn/vrpn_Types.h")) + SmartPkgEnable("BULLET", "bullet", ("BulletSoftBody", "BulletDynamics", "BulletCollision", "LinearMath"), ("bullet", "bullet/btBulletDynamicsCommon.h")) + SmartPkgEnable("VORBIS", "vorbisfile",("vorbisfile", "vorbis", "ogg"), ("ogg/ogg.h", "vorbis/vorbisfile.h")) + SmartPkgEnable("OPUS", "opusfile", ("opusfile", "opus", "ogg"), ("ogg/ogg.h", "opus/opusfile.h", "opus")) + SmartPkgEnable("JPEG", "", ("jpeg"), "jpeglib.h") + SmartPkgEnable("PNG", "libpng", ("png"), "png.h", tool = "libpng-config") - if not PkgSkip("FFMPEG"): - if GetTarget() == "darwin": - LibName("FFMPEG", "-Wl,-read_only_relocs,suppress") - LibName("FFMPEG", "-framework VideoDecodeAcceleration") - elif os.path.isfile(GetThirdpartyDir() + "ffmpeg/lib/libavcodec.a"): - # Needed when linking ffmpeg statically on Linux. - LibName("FFMPEG", "-Wl,-Bsymbolic") + if not PkgSkip("FFMPEG"): + if GetTarget() == "darwin": + LibName("FFMPEG", "-framework VideoDecodeAcceleration") + elif os.path.isfile(GetThirdpartyDir() + "ffmpeg/lib/libavcodec.a"): + # Needed when linking ffmpeg statically on Linux. + LibName("FFMPEG", "-Wl,-Bsymbolic") - if PkgSkip("FFMPEG") or GetTarget() == "darwin": - cv_lib = ChooseLib(("opencv_core", "cv"), "OPENCV") - if cv_lib == "opencv_core": - OPENCV_VER_23 = True - SmartPkgEnable("OPENCV", "opencv", ("opencv_core", "opencv_highgui"), ("opencv2/core/core.hpp")) - else: - SmartPkgEnable("OPENCV", "opencv", ("cv", "highgui", "cvaux", "ml", "cxcore"), - ("opencv", "opencv/cv.h", "opencv/cxcore.h", "opencv/highgui.h")) + if PkgSkip("FFMPEG") or GetTarget() == "darwin": + cv_lib = ChooseLib(("opencv_core", "cv"), "OPENCV") + if cv_lib == "opencv_core": + OPENCV_VER_23 = True + SmartPkgEnable("OPENCV", "opencv", ("opencv_core", "opencv_highgui"), ("opencv2/core/core.hpp")) else: - PkgDisable("OPENCV") + SmartPkgEnable("OPENCV", "opencv", ("cv", "highgui", "cvaux", "ml", "cxcore"), + ("opencv", "opencv/cv.h", "opencv/cxcore.h", "opencv/highgui.h")) + else: + PkgDisable("OPENCV") - if not PkgSkip("ASSIMP") and \ - os.path.isfile(GetThirdpartyDir() + "assimp/lib/libassimp.a"): - # Also pick up IrrXML, which is needed when linking statically. - irrxml = GetThirdpartyDir() + "assimp/lib/libIrrXML.a" - if os.path.isfile(irrxml): - LibName("ASSIMP", irrxml) + if GetTarget() == "darwin" and not PkgSkip("OPENAL"): + LibName("OPENAL", "-framework AudioToolbox") + LibName("OPENAL", "-framework CoreAudio") - rocket_libs = ("RocketCore", "RocketControls") - if (GetOptimize() <= 3): - rocket_libs += ("RocketDebugger",) - rocket_libs += ("boost_python",) - SmartPkgEnable("ROCKET", "", rocket_libs, "Rocket/Core.h") + if not PkgSkip("ASSIMP") and \ + os.path.isfile(GetThirdpartyDir() + "assimp/lib/libassimp.a"): + # Also pick up IrrXML, which is needed when linking statically. + irrxml = GetThirdpartyDir() + "assimp/lib/libIrrXML.a" + if os.path.isfile(irrxml): + LibName("ASSIMP", irrxml) - if not PkgSkip("PYTHON"): - python_lib = SDK["PYTHONVERSION"] - SmartPkgEnable("PYTHON", "", python_lib, (SDK["PYTHONVERSION"], SDK["PYTHONVERSION"] + "/Python.h")) + rocket_libs = ("RocketCore", "RocketControls") + if (GetOptimize() <= 3): + rocket_libs += ("RocketDebugger",) + rocket_libs += ("boost_python",) + SmartPkgEnable("ROCKET", "", rocket_libs, "Rocket/Core.h") - if GetTarget() == "linux": - LibName("PYTHON", "-lutil") - LibName("PYTHON", "-lrt") + if not PkgSkip("PYTHON"): + python_lib = SDK["PYTHONVERSION"] + SmartPkgEnable("PYTHON", "", python_lib, (SDK["PYTHONVERSION"], SDK["PYTHONVERSION"] + "/Python.h")) + + if GetTarget() == "linux": + LibName("PYTHON", "-lutil") + LibName("PYTHON", "-lrt") SmartPkgEnable("OPENSSL", "openssl", ("ssl", "crypto"), ("openssl/ssl.h", "openssl/crypto.h")) SmartPkgEnable("ZLIB", "zlib", ("z"), "zlib.h") SmartPkgEnable("GTK2", "gtk+-2.0") - if (RTDIST): - SmartPkgEnable("WX", tool = "wx-config") - SmartPkgEnable("FLTK", "", ("fltk"), ("FL/Fl.H"), tool = "fltk-config") - if GetTarget() != 'darwin': # CgGL is covered by the Cg framework, and we don't need X11 components on OSX - if not PkgSkip("NVIDIACG") and not RUNTIME: + if not PkgSkip("NVIDIACG"): SmartPkgEnable("CGGL", "", ("CgGL"), "Cg/cgGL.h", thirdparty_dir = "nvidiacg") - if not RUNTIME: - SmartPkgEnable("X11", "x11", "X11", ("X11", "X11/Xlib.h", "X11/XKBlib.h")) + SmartPkgEnable("X11", "x11", "X11", ("X11", "X11/Xlib.h", "X11/XKBlib.h")) if GetHost() != "darwin": # Workaround for an issue where pkg-config does not include this path @@ -1007,15 +868,6 @@ if (COMPILER=="GCC"): elif not PkgSkip("X11"): LibDirectory("ALWAYS", "/usr/X11R6/lib") - if RUNTIME: - # For the runtime, these packages are required - for pkg in ["OPENSSL", "ZLIB"]: - skips = [] - if (pkg in PkgListGet() and PkgSkip(pkg)==1): - skips.append(pkg) - if skips: - exit("Runtime must be compiled with OpenSSL and ZLib support (missing %s)" % (', '.join(skips))) - for pkg in MAYAVERSIONS: if (PkgSkip(pkg)==0 and (pkg in SDK)): if (GetHost() == "darwin"): @@ -1040,12 +892,26 @@ if (COMPILER=="GCC"): if GetTarget() == 'darwin': LibName("ALWAYS", "-framework AppKit") + LibName("IOKIT", "-framework IOKit") + LibName("QUARTZ", "-framework Quartz") LibName("AGL", "-framework AGL") LibName("CARBON", "-framework Carbon") LibName("COCOA", "-framework Cocoa") # Fix for a bug in OSX Leopard: LibName("GL", "-dylib_file /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib:/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib") + # Temporary exceptions to removal of this flag + if not PkgSkip("ROCKET"): + LibName("ROCKET", "-undefined dynamic_lookup") + if not PkgSkip("FFMPEG"): + LibName("FFMPEG", "-undefined dynamic_lookup") + if not PkgSkip("ASSIMP"): + LibName("ASSIMP", "-undefined dynamic_lookup") + if not PkgSkip("OPENEXR"): + LibName("OPENEXR", "-undefined dynamic_lookup") + if not PkgSkip("VRPN"): + LibName("VRPN", "-undefined dynamic_lookup") + if GetTarget() == 'android': LibName("ALWAYS", '-llog') LibName("ANDROID", '-landroid') @@ -1131,13 +997,6 @@ def printStatus(header,warnings): else: tkeep = tkeep + x + " " - if RTDIST: - print("Makepanda: Runtime distribution build") - elif RUNTIME: - print("Makepanda: Runtime build") - else: - print("Makepanda: Regular build") - print("Makepanda: Compiler: %s" % (COMPILER)) print("Makepanda: Optimize: %d" % (GetOptimize())) print("Makepanda: Keep Pkg: %s" % (tkeep)) @@ -1148,7 +1007,7 @@ def printStatus(header,warnings): else: print("Makepanda: Don't generate API reference manual") - if GetHost() == "windows" and not RTDIST: + if GetHost() == "windows": if INSTALLER: print("Makepanda: Build installer, using %s" % (COMPRESSOR)) else: @@ -1171,6 +1030,7 @@ def BracketNameWithQuotes(name): # Workaround for OSX bug - compiler doesn't like those flags quoted. if (name.startswith("-framework")): return name if (name.startswith("-dylib_file")): return name + if (name.startswith("-undefined ")): return name # Don't add quotes when it's not necessary. if " " not in name: return name @@ -1351,9 +1211,11 @@ def CompileCxx(obj,src,opts): cmd += " -isysroot " + SDK["MACOSX"] cmd += " -mmacosx-version-min=%d.%d" % (OSXTARGET) - for arch in OSX_ARCHS: - if 'NOARCH:' + arch.upper() not in opts: - cmd += " -arch %s" % arch + # Use libc++ to enable C++11 features. + cmd += " -stdlib=libc++" + + arch = GetTargetArch() + cmd += " -arch %s" % arch if "SYSROOT" in SDK: if GetTarget() != "android": @@ -1570,7 +1432,7 @@ def CompileIgate(woutd,wsrc,opts): # NOTE: this 1600 value is the version number for VC2010. cmd += ' -D_MSC_VER=1600 -D"__declspec(param)=" -D__cdecl -D_near -D_far -D__near -D__far -D__stdcall' if (COMPILER=="GCC"): - cmd += ' -D__attribute__\(x\)=' + cmd += ' -D__attribute__\\(x\\)=' target_arch = GetTargetArch() if target_arch in ("x86_64", "amd64"): cmd += ' -D_LP64' @@ -1675,9 +1537,9 @@ def CompileLib(lib, obj, opts): cmd += " /MACHINE:" + GetTargetArch().upper() cmd += ' /OUT:' + BracketNameWithQuotes(lib) for x in obj: cmd += ' ' + BracketNameWithQuotes(x) - cmd += ' /LIBPATH:"C:\Program Files (x86)\Intel\Composer XE 2011 SP1\ipp\lib\ia32"' - cmd += ' /LIBPATH:"C:\Program Files (x86)\Intel\Composer XE 2011 SP1\TBB\Lib\ia32\vc10"' - cmd += ' /LIBPATH:"C:\Program Files (x86)\Intel\Composer XE 2011 SP1\compiler\lib\ia32"' + cmd += ' /LIBPATH:"C:\\Program Files (x86)\\Intel\\Composer XE 2011 SP1\\ipp\\lib\\ia32"' + cmd += ' /LIBPATH:"C:\\Program Files (x86)\\Intel\\Composer XE 2011 SP1\\TBB\\Lib\\ia32\\vc10"' + cmd += ' /LIBPATH:"C:\\Program Files (x86)\\Intel\\Composer XE 2011 SP1\\compiler\\lib\\ia32"' oscmd(cmd) if (COMPILER=="GCC"): @@ -1730,12 +1592,6 @@ def CompileLink(dll, obj, opts): else: cmd += ' /NOD:{}.lib'.format(pythonv) - # Yes, we know we are importing "locally defined symbols". - for x in obj: - if x.endswith('libp3pystub.lib'): - cmd += ' /ignore:4049,4217' - break - # Set the subsystem. Specify that we want to target Windows XP. subsystem = GetValueOption(opts, "SUBSYSTEM:") or "CONSOLE" cmd += " /SUBSYSTEM:" + subsystem @@ -1783,9 +1639,9 @@ def CompileLink(dll, obj, opts): cmd += " /NOD:MFC90.LIB /NOD:MFC80.LIB /NOD:LIBCMT" cmd += " /NOD:LIBCI.LIB /DEBUG" cmd += " /nod:libc /nod:libcmtd /nod:atlthunk /nod:atls" - cmd += ' /LIBPATH:"C:\Program Files (x86)\Intel\Composer XE 2011 SP1\ipp\lib\ia32"' - cmd += ' /LIBPATH:"C:\Program Files (x86)\Intel\Composer XE 2011 SP1\TBB\Lib\ia32\vc10"' - cmd += ' /LIBPATH:"C:\Program Files (x86)\Intel\Composer XE 2011 SP1\compiler\lib\ia32"' + cmd += ' /LIBPATH:"C:\\Program Files (x86)\\Intel\\Composer XE 2011 SP1\\ipp\\lib\\ia32"' + cmd += ' /LIBPATH:"C:\\Program Files (x86)\\Intel\\Composer XE 2011 SP1\\TBB\\Lib\\ia32\\vc10"' + cmd += ' /LIBPATH:"C:\\Program Files (x86)\\Intel\\Composer XE 2011 SP1\\compiler\\lib\\ia32"' if (GetOrigExt(dll) != ".exe"): cmd += " /DLL" optlevel = GetOptimizeOption(opts) if (optlevel==1): cmd += " /MAP /MAPINFO:EXPORTS /NOD:MSVCRT.LIB /NOD:MSVCPRT.LIB /NOD:MSVCIRT.LIB" @@ -1842,9 +1698,11 @@ def CompileLink(dll, obj, opts): cmd += ' -Wl,--allow-shlib-undefined' else: if (GetTarget() == "darwin"): - cmd = cxx + ' -undefined dynamic_lookup' - if ("BUNDLE" in opts or GetOrigExt(dll) == ".pyd"): - cmd += ' -bundle ' + cmd = cxx + if GetOrigExt(dll) == ".pyd": + cmd += ' -bundle -undefined dynamic_lookup' + elif "BUNDLE" in opts: + cmd += ' -bundle' else: install_name = '@loader_path/../lib/' + os.path.basename(dll) cmd += ' -dynamiclib -install_name ' + install_name @@ -1871,9 +1729,11 @@ def CompileLink(dll, obj, opts): cmd += " -isysroot " + SDK["MACOSX"] + " -Wl,-syslibroot," + SDK["MACOSX"] cmd += " -mmacosx-version-min=%d.%d" % (OSXTARGET) - for arch in OSX_ARCHS: - if 'NOARCH:' + arch.upper() not in opts: - cmd += " -arch %s" % arch + # Use libc++ to enable C++11 features. + cmd += " -stdlib=libc++" + + arch = GetTargetArch() + cmd += " -arch %s" % arch elif GetTarget() == 'android': arch = GetTargetArch() @@ -1907,7 +1767,7 @@ def CompileLink(dll, obj, opts): cmd += " " + LDFLAGS # Don't link libraries with Python, except on Android. - if "PYTHON" in opts and GetOrigExt(dll) != ".exe" and not RTDIST and GetTarget() != 'android': + if "PYTHON" in opts and GetOrigExt(dll) != ".exe" and GetTarget() != 'android': opts = opts[:] opts.remove("PYTHON") @@ -2043,7 +1903,10 @@ def CompileRsrc(target, src, opts): def CompileJava(target, src, opts): """Compiles a .java file into a .class file.""" - cmd = "ecj " + if GetHost() == 'android': + cmd = "ecj " + else: + cmd = "javac -bootclasspath " + BracketNameWithQuotes(SDK["ANDROID_JAR"]) + " " optlevel = GetOptimizeOption(opts) if optlevel >= 4: @@ -2097,67 +1960,6 @@ def FreezePy(target, inputs, opts): if (not os.path.exists(target)): exit("FREEZER_ERROR") -########################################################################################## -# -# Package -# -########################################################################################## - -def Package(target, inputs, opts): - assert len(inputs) == 1 - # Invoke the ppackage script. - command = BracketNameWithQuotes(SDK["PYTHONEXEC"]) + " " - if GetOptimizeOption(opts) >= 4: - command += "-OO " - - if sys.version_info >= (2, 6): - command += "-B " - - command += "direct/src/p3d/ppackage.py" - - if not RTDIST: - # Don't compile Python sources, because we might not running in the same - # Python version as the selected host. - command += " -N" - - if GetTarget() == "darwin": - if SDK.get("MACOSX"): - command += " -R \"%s\"" % SDK["MACOSX"] - - for arch in OSX_ARCHS: - if arch == "x86_64": - arch = "amd64" - command += " -P osx_%s" % arch - - command += " -i \"" + GetOutputDir() + "/stage\"" - if (P3DSUFFIX): - command += ' -a "' + P3DSUFFIX + '"' - - command += " " + inputs[0] - - if GetOrigExt(target) == '.p3d': - # Build a specific .p3d file. - basename = os.path.basename(os.path.splitext(target)[0]) - command += " " + basename - oscmd(command) - - if GetTarget() == 'windows': - # Make an .exe that calls this .p3d. - objfile = FindLocation('p3dWrapper_' + basename + '.obj', []) - CompileCxx(objfile, 'direct/src/p3d/p3dWrapper.c', []) - - exefile = FindLocation(basename + '.exe', []) - CompileLink(exefile, [objfile], ['ADVAPI']) - - # Move it to the bin directory. - os.rename(GetOutputDir() + '/stage/' + basename + P3DSUFFIX + '.p3d', target) - - if sys.platform != 'win32': - oscmd('chmod +x ' + BracketNameWithQuotes(target)) - else: - # This is presumably a package or set of packages. - oscmd(command) - ########################################################################################## # # CompileBundle @@ -2256,12 +2058,6 @@ def CompileAnything(target, inputs, opts, progress = None): elif (infile.endswith(".idl")): ProgressOutput(progress, "Compiling MIDL file", infile) return CompileMIDL(target, infile, opts) - elif (infile.endswith(".pdef")): - if origsuffix == '.p3d': - ProgressOutput(progress, "Building package", target) - else: - ProgressOutput(progress, "Building package from pdef file", infile) - return Package(target, inputs, opts) elif origsuffix in SUFFIX_LIB: ProgressOutput(progress, "Linking static library", target) return CompileLib(target, inputs, opts) @@ -2272,7 +2068,7 @@ def CompileAnything(target, inputs, opts, progress = None): ProgressOutput(progress, "Linking dynamic library", target) # Add version number to the dynamic library, on unix - if origsuffix == ".dll" and "MODULE" not in opts and not RTDIST: + if origsuffix == ".dll" and "MODULE" not in opts: tplatform = GetTarget() if tplatform == "darwin": # On Mac, libraries are named like libpanda.1.2.dylib @@ -2349,7 +2145,6 @@ DTOOL_CONFIG=[ ("COMPILE_IN_DEFAULT_FONT", '1', '1'), ("STDFLOAT_DOUBLE", 'UNDEF', 'UNDEF'), ("HAVE_MAYA", '1', '1'), - ("HAVE_SOFTIMAGE", 'UNDEF', 'UNDEF'), ("REPORT_OPENSSL_ERRORS", '1', '1'), ("USE_PANDAFILESTREAM", '1', '1'), ("USE_DELETED_CHAIN", '1', '1'), @@ -2450,7 +2245,6 @@ DTOOL_CONFIG=[ ("HAVE_ARTOOLKIT", 'UNDEF', 'UNDEF'), ("HAVE_DIRECTCAM", 'UNDEF', 'UNDEF'), ("HAVE_SQUISH", 'UNDEF', 'UNDEF'), - ("HAVE_CARBON", 'UNDEF', 'UNDEF'), ("HAVE_COCOA", 'UNDEF', 'UNDEF'), ("HAVE_OPENAL_FRAMEWORK", 'UNDEF', 'UNDEF'), ("HAVE_ROCKET_PYTHON", '1', '1'), @@ -2459,7 +2253,6 @@ DTOOL_CONFIG=[ ("PRC_SAVE_DESCRIPTIONS", '1', '1'), # ("_SECURE_SCL", '0', 'UNDEF'), # ("_SECURE_SCL_THROWS", '0', 'UNDEF'), - ("HAVE_P3D_PLUGIN", 'UNDEF', 'UNDEF'), ] PRC_PARAMETERS=[ @@ -2482,7 +2275,6 @@ def WriteConfigSettings(): dtool_config={} prc_parameters={} speedtree_parameters={} - plugin_config={} if (GetTarget() == 'windows'): for key,win,unix in DTOOL_CONFIG: @@ -2588,28 +2380,6 @@ def WriteConfigSettings(): ## if GetTarget() == 'windows' and GetTargetArch() == 'x64': ## dtool_config["SIMPLE_THREADS"] = 'UNDEF' - if (RTDIST or RUNTIME): - prc_parameters["DEFAULT_PRC_DIR"] = '""' - if HOST_URL: - plugin_config["PANDA_PACKAGE_HOST_URL"] = HOST_URL - #plugin_config["P3D_PLUGIN_LOG_DIRECTORY"] = "" - plugin_config["P3D_PLUGIN_LOG_BASENAME1"] = "" - plugin_config["P3D_PLUGIN_LOG_BASENAME2"] = "" - plugin_config["P3D_PLUGIN_LOG_BASENAME3"] = "" - plugin_config["P3D_PLUGIN_P3D_PLUGIN"] = "" - plugin_config["P3D_PLUGIN_P3DPYTHON"] = "" - plugin_config["P3D_COREAPI_VERSION_STR"] = COREAPI_VERSION - plugin_config["P3D_PLUGIN_VERSION_STR"] = PLUGIN_VERSION - if PkgSkip("GTK2") == 0: - plugin_config["HAVE_GTK"] = '1' - - if (RUNTIME): - dtool_config["HAVE_P3D_PLUGIN"] = '1' - - # Whether it's present on the system doesn't matter here, - # as the runtime itself doesn't include or link with X11. - if (RUNTIME and GetTarget() == 'linux'): - dtool_config["HAVE_X11"] = '1' if ("GENERIC_DXERR_LIBRARY" in SDK): dtool_config["USE_GENERIC_DXERR_LIBRARY"] = "1" @@ -2641,14 +2411,6 @@ def WriteConfigSettings(): else: conf = conf + "#define " + key + " " + val + "\n" ConditionalWriteFile(GetOutputDir() + '/include/dtool_config.h', conf) - if (RTDIST or RUNTIME): - conf = "/* p3d_plugin_config.h. Generated automatically by makepanda.py */\n" - for key in sorted(plugin_config.keys()): - val = plugin_config[key] - if (val == 'UNDEF'): conf = conf + "#undef " + key + "\n" - else: conf = conf + "#define " + key + " \"" + val.replace("\\", "\\\\") + "\"\n" - ConditionalWriteFile(GetOutputDir() + '/include/p3d_plugin_config.h', conf) - if (PkgSkip("SPEEDTREE")==0): conf = "/* speedtree_parameters.h. Generated automatically by makepanda.py */\n" for key in sorted(speedtree_parameters.keys()): @@ -2689,27 +2451,9 @@ PANDAVERSION_H=""" #define PANDA_SEQUENCE_VERSION $VERSION3 #define PANDA_VERSION $NVERSION #define PANDA_NUMERIC_VERSION $NVERSION -#define PANDA_VERSION_STR "$VERSION1.$VERSION2.$VERSION3" +#define PANDA_VERSION_STR "$VERSION" #define PANDA_ABI_VERSION_STR "$VERSION1.$VERSION2" #define PANDA_DISTRIBUTOR "$DISTRIBUTOR" -#define PANDA_PACKAGE_VERSION_STR "$RTDIST_VERSION" -#define PANDA_PACKAGE_HOST_URL "$HOST_URL" -""" - -PANDAVERSION_H_RUNTIME=""" -#define PANDA_MAJOR_VERSION 0 -#define PANDA_MINOR_VERSION 0 -#define PANDA_SEQUENCE_VERSION 0 -#define PANDA_VERSION_STR "0.0.0" -#define PANDA_ABI_VERSION_STR "0.0" -#define P3D_PLUGIN_MAJOR_VERSION $VERSION1 -#define P3D_PLUGIN_MINOR_VERSION $VERSION2 -#define P3D_PLUGIN_SEQUENCE_VERSION $VERSION3 -#define P3D_PLUGIN_VERSION_STR "$VERSION1.$VERSION2.$VERSION3" -#define P3D_COREAPI_VERSION_STR "$COREAPI_VERSION" -#define PANDA_DISTRIBUTOR "$DISTRIBUTOR" -#define PANDA_PACKAGE_VERSION_STR "" -#define PANDA_PACKAGE_HOST_URL "$HOST_URL" """ CHECKPANDAVERSION_CXX=""" @@ -2732,65 +2476,6 @@ template class CheckPandaVersion; # endif """ -P3DACTIVEX_RC="""#include "resource.h" -#define APSTUDIO_READONLY_SYMBOLS -#include "afxres.h" -#undef APSTUDIO_READONLY_SYMBOLS -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif -#ifdef APSTUDIO_INVOKED -1 TEXTINCLUDE -BEGIN - "resource.h\\0" -END -2 TEXTINCLUDE -BEGIN - "#include ""afxres.h""\\r\\n" - "\\0" -END -3 TEXTINCLUDE -BEGIN - "1 TYPELIB ""P3DActiveX.tlb""\\r\\n" - "\\0" -END -#endif -%s -IDB_P3DACTIVEX BITMAP "P3DActiveXCtrl.bmp" -IDD_PROPPAGE_P3DACTIVEX DIALOG 0, 0, 250, 62 -STYLE DS_SETFONT | WS_CHILD -FONT 8, "MS Sans Serif" -BEGIN - LTEXT "TODO: Place controls to manipulate properties of P3DActiveX Control on this dialog.", - IDC_STATIC,7,25,229,16 -END -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_PROPPAGE_P3DACTIVEX, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 243 - TOPMARGIN, 7 - BOTTOMMARGIN, 55 - END -END -#endif -STRINGTABLE -BEGIN - IDS_P3DACTIVEX "P3DActiveX Control" - IDS_P3DACTIVEX_PPG "P3DActiveX Property Page" -END -STRINGTABLE -BEGIN - IDS_P3DACTIVEX_PPG_CAPTION "General" -END -#endif -#ifndef APSTUDIO_INVOKED -1 TYPELIB "P3DActiveX.tlb" -#endif""" def CreatePandaVersionFiles(): version1=int(VERSION.split(".")[0]) @@ -2801,18 +2486,13 @@ def CreatePandaVersionFiles(): # Subtract 1 if we are not an official version. nversion -= 1 - if (RUNTIME): - pandaversion_h = PANDAVERSION_H_RUNTIME - else: - pandaversion_h = PANDAVERSION_H + pandaversion_h = PANDAVERSION_H pandaversion_h = pandaversion_h.replace("$VERSION1",str(version1)) pandaversion_h = pandaversion_h.replace("$VERSION2",str(version2)) pandaversion_h = pandaversion_h.replace("$VERSION3",str(version3)) + pandaversion_h = pandaversion_h.replace("$VERSION",VERSION) pandaversion_h = pandaversion_h.replace("$NVERSION",str(nversion)) pandaversion_h = pandaversion_h.replace("$DISTRIBUTOR",DISTRIBUTOR) - pandaversion_h = pandaversion_h.replace("$RTDIST_VERSION",RTDIST_VERSION) - pandaversion_h = pandaversion_h.replace("$COREAPI_VERSION",COREAPI_VERSION) - pandaversion_h = pandaversion_h.replace("$HOST_URL",(HOST_URL or "")) if (DISTRIBUTOR == "cmu"): pandaversion_h += "\n#define PANDA_OFFICIAL_VERSION\n" else: @@ -2821,36 +2501,21 @@ def CreatePandaVersionFiles(): if GIT_COMMIT: pandaversion_h += "\n#define PANDA_GIT_COMMIT_STR \"%s\"\n" % (GIT_COMMIT) - if not RUNTIME: - checkpandaversion_cxx = CHECKPANDAVERSION_CXX.replace("$VERSION1",str(version1)) - checkpandaversion_cxx = checkpandaversion_cxx.replace("$VERSION2",str(version2)) - checkpandaversion_cxx = checkpandaversion_cxx.replace("$VERSION3",str(version3)) - checkpandaversion_cxx = checkpandaversion_cxx.replace("$NVERSION",str(nversion)) + checkpandaversion_cxx = CHECKPANDAVERSION_CXX.replace("$VERSION1",str(version1)) + checkpandaversion_cxx = checkpandaversion_cxx.replace("$VERSION2",str(version2)) + checkpandaversion_cxx = checkpandaversion_cxx.replace("$VERSION3",str(version3)) + checkpandaversion_cxx = checkpandaversion_cxx.replace("$NVERSION",str(nversion)) - checkpandaversion_h = CHECKPANDAVERSION_H.replace("$VERSION1",str(version1)) - checkpandaversion_h = checkpandaversion_h.replace("$VERSION2",str(version2)) - checkpandaversion_h = checkpandaversion_h.replace("$VERSION3",str(version3)) - checkpandaversion_h = checkpandaversion_h.replace("$NVERSION",str(nversion)) + checkpandaversion_h = CHECKPANDAVERSION_H.replace("$VERSION1",str(version1)) + checkpandaversion_h = checkpandaversion_h.replace("$VERSION2",str(version2)) + checkpandaversion_h = checkpandaversion_h.replace("$VERSION3",str(version3)) + checkpandaversion_h = checkpandaversion_h.replace("$NVERSION",str(nversion)) ConditionalWriteFile(GetOutputDir()+'/include/pandaVersion.h', pandaversion_h) - if RUNTIME: - ConditionalWriteFile(GetOutputDir()+'/include/checkPandaVersion.cxx', '') - ConditionalWriteFile(GetOutputDir()+'/include/checkPandaVersion.h', '') - else: - ConditionalWriteFile(GetOutputDir()+'/include/checkPandaVersion.cxx', checkpandaversion_cxx) - ConditionalWriteFile(GetOutputDir()+'/include/checkPandaVersion.h', checkpandaversion_h) + ConditionalWriteFile(GetOutputDir()+'/include/checkPandaVersion.cxx', checkpandaversion_cxx) + ConditionalWriteFile(GetOutputDir()+'/include/checkPandaVersion.h', checkpandaversion_h) ConditionalWriteFile(GetOutputDir()+"/tmp/null.cxx","") - if RUNTIME: - p3dactivex_rc = {"name" : "Panda3D Game Engine Plug-in", - "version" : VERSION, - "description" : "Runs 3-D games and interactive applets", - "filename" : "p3dactivex.ocx", - "mimetype" : "application/x-panda3d", - "extension" : "p3d", - "filedesc" : "Panda3D applet"} - ConditionalWriteFile(GetOutputDir()+"/include/P3DActiveX.rc", P3DACTIVEX_RC % GenerateResourceFile(**p3dactivex_rc)) - CreatePandaVersionFiles() ########################################################################################## @@ -2891,6 +2556,14 @@ for basename in del_files: p3d_init = """"Python bindings for the Panda3D libraries" __version__ = '%s' + +if __debug__: + import sys + if sys.version_info < (3, 0): + sys.stderr.write("WARNING: Python 2.7 will reach EOL after December 31, 2019.\\n") + sys.stderr.write("To suppress this warning, upgrade to Python 3.\\n") + sys.stderr.flush() + del sys """ % (WHLVERSION) if GetTarget() == 'windows': @@ -2900,7 +2573,9 @@ if '__file__' in locals(): bindir = os.path.join(os.path.dirname(__file__), '..', 'bin') if os.path.isdir(bindir): - if not os.environ.get('PATH'): + if hasattr(os, 'add_dll_directory'): + os.add_dll_directory(bindir) + elif not os.environ.get('PATH'): os.environ['PATH'] = bindir else: os.environ['PATH'] = bindir + os.pathsep + os.environ['PATH'] @@ -3039,9 +2714,6 @@ else: if (GetTarget() == 'darwin'): configprc = configprc.replace("$XDG_CACHE_HOME/panda3d", "$HOME/Library/Caches/Panda3D-%s" % MAJOR_VERSION) - # OpenAL is not yet working well on OSX for us, so let's do this for now. - configprc = configprc.replace("p3openal_audio", "p3fmod_audio") - if GetTarget() == 'windows': # Convert to Windows newlines. ConditionalWriteFile(GetOutputDir()+"/etc/Config.prc", configprc, newline='\r\n') @@ -3148,7 +2820,7 @@ if tp_dir is not None: if os.path.isdir(os.path.join(tp_dir, "extras", "bin")): CopyAllFiles(GetOutputDir() + "/bin/", tp_dir + "extras/bin/") - if not PkgSkip("PYTHON") and not RTDIST: + if not PkgSkip("PYTHON"): # We need to copy the Python DLL to the bin directory for now. pydll = "/" + SDK["PYTHONVERSION"].replace(".", "") if GetOptimize() <= 2: @@ -3210,9 +2882,8 @@ if tp_dir is not None: # Copy over the MSVC runtime. if GetTarget() == 'windows' and "VISUALSTUDIO" in SDK: - vsver = "%s%s" % SDK["VISUALSTUDIO_VERSION"] vcver = "%s%s" % (SDK["MSVC_VERSION"][0], 0) # ignore minor version. - crtname = "Microsoft.VC%s.CRT" % (vsver) + crtname = "Microsoft.VC%s.CRT" % (vcver) if ("VCTOOLSVERSION" in SDK): dir = os.path.join(SDK["VISUALSTUDIO"], "VC", "Redist", "MSVC", SDK["VCTOOLSVERSION"], "onecore", GetTargetArch(), crtname) else: @@ -3276,7 +2947,6 @@ CopyAllHeaders('dtool/src/prc', skip=["prc_parameters.h"]) CopyAllHeaders('dtool/src/dconfig') CopyAllHeaders('dtool/src/interrogatedb') CopyAllHeaders('dtool/metalibs/dtoolconfig') -CopyAllHeaders('dtool/src/pystub') CopyAllHeaders('dtool/src/interrogate') CopyAllHeaders('dtool/src/test_interrogate') CopyAllHeaders('panda/src/putil') @@ -3354,7 +3024,6 @@ if not PkgSkip("EGG"): if GetTarget() == 'windows': CopyAllHeaders('panda/src/wgldisplay') elif GetTarget() == 'darwin': - CopyAllHeaders('panda/src/osxdisplay') CopyAllHeaders('panda/src/cocoadisplay') elif GetTarget() == 'android': CopyAllHeaders('panda/src/android') @@ -3390,19 +3059,12 @@ if (PkgSkip("DIRECT")==0): CopyAllHeaders('direct/src/showbase') CopyAllHeaders('direct/src/dcparse') -if (RUNTIME or RTDIST): - CopyAllHeaders('direct/src/plugin', skip=["p3d_plugin_config.h"]) -if (RUNTIME): - CopyAllHeaders('direct/src/plugin_npapi') - CopyAllHeaders('direct/src/plugin_standalone') - if (PkgSkip("PANDATOOL")==0): CopyAllHeaders('pandatool/src/pandatoolbase') CopyAllHeaders('pandatool/src/converter') CopyAllHeaders('pandatool/src/progbase') CopyAllHeaders('pandatool/src/eggbase') CopyAllHeaders('pandatool/src/bam') - CopyAllHeaders('pandatool/src/cvscopy') CopyAllHeaders('pandatool/src/daeegg') CopyAllHeaders('pandatool/src/daeprogs') CopyAllHeaders('pandatool/src/dxf') @@ -3531,16 +3193,15 @@ TargetAdd('libp3dtool.dll', opts=['ADVAPI','WINSHELL','WINKERNEL']) # DIRECTORY: dtool/src/cppparser/ # -if (not RUNTIME): - OPTS=['DIR:dtool/src/cppparser', 'BISONPREFIX_cppyy'] - CreateFile(GetOutputDir()+"/include/cppBison.h") - TargetAdd('p3cppParser_cppBison.obj', opts=OPTS, input='cppBison.yxx') - TargetAdd('cppBison.h', input='p3cppParser_cppBison.obj', opts=['DEPENDENCYONLY']) - TargetAdd('p3cppParser_composite1.obj', opts=OPTS, input='p3cppParser_composite1.cxx') - TargetAdd('p3cppParser_composite2.obj', opts=OPTS, input='p3cppParser_composite2.cxx') - TargetAdd('libp3cppParser.ilb', input='p3cppParser_composite1.obj') - TargetAdd('libp3cppParser.ilb', input='p3cppParser_composite2.obj') - TargetAdd('libp3cppParser.ilb', input='p3cppParser_cppBison.obj') +OPTS=['DIR:dtool/src/cppparser', 'BISONPREFIX_cppyy'] +CreateFile(GetOutputDir()+"/include/cppBison.h") +TargetAdd('p3cppParser_cppBison.obj', opts=OPTS, input='cppBison.yxx') +TargetAdd('cppBison.h', input='p3cppParser_cppBison.obj', opts=['DEPENDENCYONLY']) +TargetAdd('p3cppParser_composite1.obj', opts=OPTS, input='p3cppParser_composite1.cxx') +TargetAdd('p3cppParser_composite2.obj', opts=OPTS, input='p3cppParser_composite2.cxx') +TargetAdd('libp3cppParser.ilb', input='p3cppParser_composite1.obj') +TargetAdd('libp3cppParser.ilb', input='p3cppParser_composite2.obj') +TargetAdd('libp3cppParser.ilb', input='p3cppParser_cppBison.obj') # # DIRECTORY: dtool/src/prc/ @@ -3583,60 +3244,47 @@ PyTargetAdd('interrogatedb.pyd', input='libp3dtool.dll') PyTargetAdd('interrogatedb.pyd', input='libp3dtoolconfig.dll') PyTargetAdd('interrogatedb.pyd', input='libp3interrogatedb.dll') -# -# DIRECTORY: dtool/src/pystub/ -# - -if not RUNTIME and not RTDIST: - OPTS=['DIR:dtool/src/pystub'] - TargetAdd('p3pystub_pystub.obj', opts=OPTS, input='pystub.cxx') - TargetAdd('libp3pystub.lib', input='p3pystub_pystub.obj') - #TargetAdd('libp3pystub.lib', input='libp3dtool.dll') - TargetAdd('libp3pystub.lib', opts=['ADVAPI']) - # # DIRECTORY: dtool/src/interrogate/ # -if (not RUNTIME): - OPTS=['DIR:dtool/src/interrogate', 'DIR:dtool/src/cppparser', 'DIR:dtool/src/interrogatedb'] - TargetAdd('interrogate_composite1.obj', opts=OPTS, input='interrogate_composite1.cxx') - TargetAdd('interrogate_composite2.obj', opts=OPTS, input='interrogate_composite2.cxx') - TargetAdd('interrogate.exe', input='interrogate_composite1.obj') - TargetAdd('interrogate.exe', input='interrogate_composite2.obj') - TargetAdd('interrogate.exe', input='libp3cppParser.ilb') - TargetAdd('interrogate.exe', input=COMMON_DTOOL_LIBS) - TargetAdd('interrogate.exe', input='libp3interrogatedb.dll') - TargetAdd('interrogate.exe', opts=['ADVAPI', 'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER']) +OPTS=['DIR:dtool/src/interrogate', 'DIR:dtool/src/cppparser', 'DIR:dtool/src/interrogatedb'] +TargetAdd('interrogate_composite1.obj', opts=OPTS, input='interrogate_composite1.cxx') +TargetAdd('interrogate_composite2.obj', opts=OPTS, input='interrogate_composite2.cxx') +TargetAdd('interrogate.exe', input='interrogate_composite1.obj') +TargetAdd('interrogate.exe', input='interrogate_composite2.obj') +TargetAdd('interrogate.exe', input='libp3cppParser.ilb') +TargetAdd('interrogate.exe', input=COMMON_DTOOL_LIBS) +TargetAdd('interrogate.exe', input='libp3interrogatedb.dll') +TargetAdd('interrogate.exe', opts=['ADVAPI', 'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER']) - preamble = WriteEmbeddedStringFile('interrogate_preamble_python_native', inputs=[ - 'dtool/src/interrogatedb/py_panda.cxx', - 'dtool/src/interrogatedb/py_compat.cxx', - 'dtool/src/interrogatedb/py_wrappers.cxx', - 'dtool/src/interrogatedb/dtool_super_base.cxx', - ]) - TargetAdd('interrogate_module_preamble_python_native.obj', opts=OPTS, input=preamble) - TargetAdd('interrogate_module_interrogate_module.obj', opts=OPTS, input='interrogate_module.cxx') - TargetAdd('interrogate_module.exe', input='interrogate_module_interrogate_module.obj') - TargetAdd('interrogate_module.exe', input='interrogate_module_preamble_python_native.obj') - TargetAdd('interrogate_module.exe', input='libp3cppParser.ilb') - TargetAdd('interrogate_module.exe', input=COMMON_DTOOL_LIBS) - TargetAdd('interrogate_module.exe', input='libp3interrogatedb.dll') - TargetAdd('interrogate_module.exe', opts=['ADVAPI', 'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER']) +preamble = WriteEmbeddedStringFile('interrogate_preamble_python_native', inputs=[ +'dtool/src/interrogatedb/py_panda.cxx', +'dtool/src/interrogatedb/py_compat.cxx', +'dtool/src/interrogatedb/py_wrappers.cxx', +'dtool/src/interrogatedb/dtool_super_base.cxx', +]) +TargetAdd('interrogate_module_preamble_python_native.obj', opts=OPTS, input=preamble) +TargetAdd('interrogate_module_interrogate_module.obj', opts=OPTS, input='interrogate_module.cxx') +TargetAdd('interrogate_module.exe', input='interrogate_module_interrogate_module.obj') +TargetAdd('interrogate_module.exe', input='interrogate_module_preamble_python_native.obj') +TargetAdd('interrogate_module.exe', input='libp3cppParser.ilb') +TargetAdd('interrogate_module.exe', input=COMMON_DTOOL_LIBS) +TargetAdd('interrogate_module.exe', input='libp3interrogatedb.dll') +TargetAdd('interrogate_module.exe', opts=['ADVAPI', 'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER']) - if (not RTDIST): - TargetAdd('parse_file_parse_file.obj', opts=OPTS, input='parse_file.cxx') - TargetAdd('parse_file.exe', input='parse_file_parse_file.obj') - TargetAdd('parse_file.exe', input='libp3cppParser.ilb') - TargetAdd('parse_file.exe', input=COMMON_DTOOL_LIBS) - TargetAdd('parse_file.exe', input='libp3interrogatedb.dll') - TargetAdd('parse_file.exe', opts=['ADVAPI', 'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER']) +TargetAdd('parse_file_parse_file.obj', opts=OPTS, input='parse_file.cxx') +TargetAdd('parse_file.exe', input='parse_file_parse_file.obj') +TargetAdd('parse_file.exe', input='libp3cppParser.ilb') +TargetAdd('parse_file.exe', input=COMMON_DTOOL_LIBS) +TargetAdd('parse_file.exe', input='libp3interrogatedb.dll') +TargetAdd('parse_file.exe', opts=['ADVAPI', 'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER']) # # DIRECTORY: dtool/src/prckeys/ # -if (PkgSkip("OPENSSL")==0 and not RUNTIME and not RTDIST): +if (PkgSkip("OPENSSL")==0): OPTS=['DIR:dtool/src/prckeys', 'OPENSSL'] TargetAdd('make-prc-key_makePrcKey.obj', opts=OPTS, input='makePrcKey.cxx') TargetAdd('make-prc-key.exe', input='make-prc-key_makePrcKey.obj') @@ -3647,13 +3295,12 @@ if (PkgSkip("OPENSSL")==0 and not RUNTIME and not RTDIST): # DIRECTORY: dtool/src/test_interrogate/ # -if (not RTDIST and not RUNTIME): - OPTS=['DIR:dtool/src/test_interrogate'] - TargetAdd('test_interrogate_test_interrogate.obj', opts=OPTS, input='test_interrogate.cxx') - TargetAdd('test_interrogate.exe', input='test_interrogate_test_interrogate.obj') - TargetAdd('test_interrogate.exe', input='libp3interrogatedb.dll') - TargetAdd('test_interrogate.exe', input=COMMON_DTOOL_LIBS) - TargetAdd('test_interrogate.exe', opts=['ADVAPI', 'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER']) +OPTS=['DIR:dtool/src/test_interrogate'] +TargetAdd('test_interrogate_test_interrogate.obj', opts=OPTS, input='test_interrogate.cxx') +TargetAdd('test_interrogate.exe', input='test_interrogate_test_interrogate.obj') +TargetAdd('test_interrogate.exe', input='libp3interrogatedb.dll') +TargetAdd('test_interrogate.exe', input=COMMON_DTOOL_LIBS) +TargetAdd('test_interrogate.exe', opts=['ADVAPI', 'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER']) # # DIRECTORY: dtool/src/dtoolbase/ @@ -3691,6 +3338,7 @@ IGATEFILES += [ "globPattern_ext.h", "pandaFileStream.h", "lineStream.h", + "iostream_ext.h", ] TargetAdd('libp3dtoolutil.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3dtoolutil.in', opts=['IMOD:panda3d.core', 'ILIB:libp3dtoolutil', 'SRCDIR:dtool/src/dtoolutil']) @@ -3739,7 +3387,6 @@ OPTS=['DIR:panda/src/downloader', 'OPENSSL', 'ZLIB'] IGATEFILES=GetDirectoryContents('panda/src/downloader', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3downloader.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3downloader.in', opts=['IMOD:panda3d.core', 'ILIB:libp3downloader', 'SRCDIR:panda/src/downloader']) -PyTargetAdd('p3downloader_stringStream_ext.obj', opts=OPTS, input='stringStream_ext.cxx') # # DIRECTORY: panda/metalibs/pandaexpress/ @@ -3760,320 +3407,295 @@ TargetAdd('libpandaexpress.dll', opts=['ADVAPI', 'WINSOCK2', 'OPENSSL', 'ZLIB', # DIRECTORY: panda/src/pipeline/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/pipeline', 'BUILDING:PANDA'] - TargetAdd('p3pipeline_composite1.obj', opts=OPTS, input='p3pipeline_composite1.cxx') - TargetAdd('p3pipeline_composite2.obj', opts=OPTS, input='p3pipeline_composite2.cxx') - TargetAdd('p3pipeline_contextSwitch.obj', opts=OPTS, input='contextSwitch.c') +OPTS=['DIR:panda/src/pipeline', 'BUILDING:PANDA'] +TargetAdd('p3pipeline_composite1.obj', opts=OPTS, input='p3pipeline_composite1.cxx') +TargetAdd('p3pipeline_composite2.obj', opts=OPTS, input='p3pipeline_composite2.cxx') +TargetAdd('p3pipeline_contextSwitch.obj', opts=OPTS, input='contextSwitch.c') - OPTS=['DIR:panda/src/pipeline'] - IGATEFILES=GetDirectoryContents('panda/src/pipeline', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3pipeline.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3pipeline.in', opts=['IMOD:panda3d.core', 'ILIB:libp3pipeline', 'SRCDIR:panda/src/pipeline']) - PyTargetAdd('p3pipeline_pythonThread.obj', opts=OPTS, input='pythonThread.cxx') +OPTS=['DIR:panda/src/pipeline'] +IGATEFILES=GetDirectoryContents('panda/src/pipeline', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3pipeline.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3pipeline.in', opts=['IMOD:panda3d.core', 'ILIB:libp3pipeline', 'SRCDIR:panda/src/pipeline']) +PyTargetAdd('p3pipeline_pythonThread.obj', opts=OPTS, input='pythonThread.cxx') # # DIRECTORY: panda/src/linmath/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/linmath', 'BUILDING:PANDA'] - TargetAdd('p3linmath_composite1.obj', opts=OPTS, input='p3linmath_composite1.cxx') - TargetAdd('p3linmath_composite2.obj', opts=OPTS, input='p3linmath_composite2.cxx') +OPTS=['DIR:panda/src/linmath', 'BUILDING:PANDA'] +TargetAdd('p3linmath_composite1.obj', opts=OPTS, input='p3linmath_composite1.cxx') +TargetAdd('p3linmath_composite2.obj', opts=OPTS, input='p3linmath_composite2.cxx') - OPTS=['DIR:panda/src/linmath'] - IGATEFILES=GetDirectoryContents('panda/src/linmath', ["*.h", "*_composite*.cxx"]) - for ifile in IGATEFILES[:]: - if "_src." in ifile: - IGATEFILES.remove(ifile) - IGATEFILES.remove('cast_to_double.h') - IGATEFILES.remove('lmat_ops.h') - IGATEFILES.remove('cast_to_float.h') - TargetAdd('libp3linmath.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3linmath.in', opts=['IMOD:panda3d.core', 'ILIB:libp3linmath', 'SRCDIR:panda/src/linmath']) +OPTS=['DIR:panda/src/linmath'] +IGATEFILES=GetDirectoryContents('panda/src/linmath', ["*.h", "*_composite*.cxx"]) +for ifile in IGATEFILES[:]: + if "_src." in ifile: + IGATEFILES.remove(ifile) +IGATEFILES.remove('cast_to_double.h') +IGATEFILES.remove('lmat_ops.h') +IGATEFILES.remove('cast_to_float.h') +TargetAdd('libp3linmath.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3linmath.in', opts=['IMOD:panda3d.core', 'ILIB:libp3linmath', 'SRCDIR:panda/src/linmath']) # # DIRECTORY: panda/src/putil/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/putil', 'BUILDING:PANDA', 'ZLIB'] - TargetAdd('p3putil_composite1.obj', opts=OPTS, input='p3putil_composite1.cxx') - TargetAdd('p3putil_composite2.obj', opts=OPTS, input='p3putil_composite2.cxx') +OPTS=['DIR:panda/src/putil', 'BUILDING:PANDA', 'ZLIB'] +TargetAdd('p3putil_composite1.obj', opts=OPTS, input='p3putil_composite1.cxx') +TargetAdd('p3putil_composite2.obj', opts=OPTS, input='p3putil_composite2.cxx') - OPTS=['DIR:panda/src/putil', 'ZLIB'] - IGATEFILES=GetDirectoryContents('panda/src/putil', ["*.h", "*_composite*.cxx"]) - IGATEFILES.remove("test_bam.h") - IGATEFILES.remove("config_util.h") - TargetAdd('libp3putil.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3putil.in', opts=['IMOD:panda3d.core', 'ILIB:libp3putil', 'SRCDIR:panda/src/putil']) - PyTargetAdd('p3putil_ext_composite.obj', opts=OPTS, input='p3putil_ext_composite.cxx') +OPTS=['DIR:panda/src/putil', 'ZLIB'] +IGATEFILES=GetDirectoryContents('panda/src/putil', ["*.h", "*_composite*.cxx"]) +IGATEFILES.remove("test_bam.h") +IGATEFILES.remove("config_util.h") +TargetAdd('libp3putil.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3putil.in', opts=['IMOD:panda3d.core', 'ILIB:libp3putil', 'SRCDIR:panda/src/putil']) +PyTargetAdd('p3putil_ext_composite.obj', opts=OPTS, input='p3putil_ext_composite.cxx') # # DIRECTORY: panda/src/audio/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/audio', 'BUILDING:PANDA'] - TargetAdd('p3audio_composite1.obj', opts=OPTS, input='p3audio_composite1.cxx') +OPTS=['DIR:panda/src/audio', 'BUILDING:PANDA'] +TargetAdd('p3audio_composite1.obj', opts=OPTS, input='p3audio_composite1.cxx') - OPTS=['DIR:panda/src/audio'] - IGATEFILES=["audio.h"] - TargetAdd('libp3audio.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3audio.in', opts=['IMOD:panda3d.core', 'ILIB:libp3audio', 'SRCDIR:panda/src/audio']) +OPTS=['DIR:panda/src/audio'] +IGATEFILES=["audio.h"] +TargetAdd('libp3audio.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3audio.in', opts=['IMOD:panda3d.core', 'ILIB:libp3audio', 'SRCDIR:panda/src/audio']) # # DIRECTORY: panda/src/event/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/event', 'BUILDING:PANDA'] - TargetAdd('p3event_composite1.obj', opts=OPTS, input='p3event_composite1.cxx') - TargetAdd('p3event_composite2.obj', opts=OPTS, input='p3event_composite2.cxx') +OPTS=['DIR:panda/src/event', 'BUILDING:PANDA'] +TargetAdd('p3event_composite1.obj', opts=OPTS, input='p3event_composite1.cxx') +TargetAdd('p3event_composite2.obj', opts=OPTS, input='p3event_composite2.cxx') - OPTS=['DIR:panda/src/event'] - PyTargetAdd('p3event_asyncFuture_ext.obj', opts=OPTS, input='asyncFuture_ext.cxx') - PyTargetAdd('p3event_pythonTask.obj', opts=OPTS, input='pythonTask.cxx') - IGATEFILES=GetDirectoryContents('panda/src/event', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3event.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3event.in', opts=['IMOD:panda3d.core', 'ILIB:libp3event', 'SRCDIR:panda/src/event']) +OPTS=['DIR:panda/src/event'] +PyTargetAdd('p3event_asyncFuture_ext.obj', opts=OPTS, input='asyncFuture_ext.cxx') +PyTargetAdd('p3event_pythonTask.obj', opts=OPTS, input='pythonTask.cxx') +IGATEFILES=GetDirectoryContents('panda/src/event', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3event.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3event.in', opts=['IMOD:panda3d.core', 'ILIB:libp3event', 'SRCDIR:panda/src/event']) # # DIRECTORY: panda/src/mathutil/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/mathutil', 'BUILDING:PANDA', 'FFTW'] - TargetAdd('p3mathutil_composite1.obj', opts=OPTS, input='p3mathutil_composite1.cxx') - TargetAdd('p3mathutil_composite2.obj', opts=OPTS, input='p3mathutil_composite2.cxx') +OPTS=['DIR:panda/src/mathutil', 'BUILDING:PANDA', 'FFTW'] +TargetAdd('p3mathutil_composite1.obj', opts=OPTS, input='p3mathutil_composite1.cxx') +TargetAdd('p3mathutil_composite2.obj', opts=OPTS, input='p3mathutil_composite2.cxx') - OPTS=['DIR:panda/src/mathutil', 'FFTW'] - IGATEFILES=GetDirectoryContents('panda/src/mathutil', ["*.h", "*_composite*.cxx"]) - for ifile in IGATEFILES[:]: - if "_src." in ifile: - IGATEFILES.remove(ifile) - TargetAdd('libp3mathutil.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3mathutil.in', opts=['IMOD:panda3d.core', 'ILIB:libp3mathutil', 'SRCDIR:panda/src/mathutil']) +OPTS=['DIR:panda/src/mathutil', 'FFTW'] +IGATEFILES=GetDirectoryContents('panda/src/mathutil', ["*.h", "*_composite*.cxx"]) +for ifile in IGATEFILES[:]: + if "_src." in ifile: + IGATEFILES.remove(ifile) +TargetAdd('libp3mathutil.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3mathutil.in', opts=['IMOD:panda3d.core', 'ILIB:libp3mathutil', 'SRCDIR:panda/src/mathutil']) # # DIRECTORY: panda/src/gsgbase/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/gsgbase', 'BUILDING:PANDA'] - TargetAdd('p3gsgbase_composite1.obj', opts=OPTS, input='p3gsgbase_composite1.cxx') +OPTS=['DIR:panda/src/gsgbase', 'BUILDING:PANDA'] +TargetAdd('p3gsgbase_composite1.obj', opts=OPTS, input='p3gsgbase_composite1.cxx') - OPTS=['DIR:panda/src/gsgbase'] - IGATEFILES=GetDirectoryContents('panda/src/gsgbase', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3gsgbase.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3gsgbase.in', opts=['IMOD:panda3d.core', 'ILIB:libp3gsgbase', 'SRCDIR:panda/src/gsgbase']) +OPTS=['DIR:panda/src/gsgbase'] +IGATEFILES=GetDirectoryContents('panda/src/gsgbase', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3gsgbase.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3gsgbase.in', opts=['IMOD:panda3d.core', 'ILIB:libp3gsgbase', 'SRCDIR:panda/src/gsgbase']) # # DIRECTORY: panda/src/pnmimage/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/pnmimage', 'BUILDING:PANDA', 'ZLIB'] - TargetAdd('p3pnmimage_composite1.obj', opts=OPTS, input='p3pnmimage_composite1.cxx') - TargetAdd('p3pnmimage_composite2.obj', opts=OPTS, input='p3pnmimage_composite2.cxx') - TargetAdd('p3pnmimage_convert_srgb_sse2.obj', opts=OPTS+['SSE2'], input='convert_srgb_sse2.cxx') +OPTS=['DIR:panda/src/pnmimage', 'BUILDING:PANDA', 'ZLIB'] +TargetAdd('p3pnmimage_composite1.obj', opts=OPTS, input='p3pnmimage_composite1.cxx') +TargetAdd('p3pnmimage_composite2.obj', opts=OPTS, input='p3pnmimage_composite2.cxx') +TargetAdd('p3pnmimage_convert_srgb_sse2.obj', opts=OPTS+['SSE2'], input='convert_srgb_sse2.cxx') - OPTS=['DIR:panda/src/pnmimage', 'ZLIB'] - IGATEFILES=GetDirectoryContents('panda/src/pnmimage', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3pnmimage.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3pnmimage.in', opts=['IMOD:panda3d.core', 'ILIB:libp3pnmimage', 'SRCDIR:panda/src/pnmimage']) - PyTargetAdd('p3pnmimage_pfmFile_ext.obj', opts=OPTS, input='pfmFile_ext.cxx') +OPTS=['DIR:panda/src/pnmimage', 'ZLIB'] +IGATEFILES=GetDirectoryContents('panda/src/pnmimage', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3pnmimage.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3pnmimage.in', opts=['IMOD:panda3d.core', 'ILIB:libp3pnmimage', 'SRCDIR:panda/src/pnmimage']) +PyTargetAdd('p3pnmimage_pfmFile_ext.obj', opts=OPTS, input='pfmFile_ext.cxx') # # DIRECTORY: panda/src/nativenet/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/nativenet', 'OPENSSL', 'BUILDING:PANDA'] - TargetAdd('p3nativenet_composite1.obj', opts=OPTS, input='p3nativenet_composite1.cxx') +OPTS=['DIR:panda/src/nativenet', 'OPENSSL', 'BUILDING:PANDA'] +TargetAdd('p3nativenet_composite1.obj', opts=OPTS, input='p3nativenet_composite1.cxx') - OPTS=['DIR:panda/src/nativenet', 'OPENSSL'] - IGATEFILES=GetDirectoryContents('panda/src/nativenet', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3nativenet.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3nativenet.in', opts=['IMOD:panda3d.core', 'ILIB:libp3nativenet', 'SRCDIR:panda/src/nativenet']) +OPTS=['DIR:panda/src/nativenet', 'OPENSSL'] +IGATEFILES=GetDirectoryContents('panda/src/nativenet', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3nativenet.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3nativenet.in', opts=['IMOD:panda3d.core', 'ILIB:libp3nativenet', 'SRCDIR:panda/src/nativenet']) # # DIRECTORY: panda/src/net/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/net', 'BUILDING:PANDA'] - TargetAdd('p3net_composite1.obj', opts=OPTS, input='p3net_composite1.cxx') - TargetAdd('p3net_composite2.obj', opts=OPTS, input='p3net_composite2.cxx') +OPTS=['DIR:panda/src/net', 'BUILDING:PANDA'] +TargetAdd('p3net_composite1.obj', opts=OPTS, input='p3net_composite1.cxx') +TargetAdd('p3net_composite2.obj', opts=OPTS, input='p3net_composite2.cxx') - OPTS=['DIR:panda/src/net'] - IGATEFILES=GetDirectoryContents('panda/src/net', ["*.h", "*_composite*.cxx"]) - IGATEFILES.remove("datagram_ui.h") - TargetAdd('libp3net.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3net.in', opts=['IMOD:panda3d.core', 'ILIB:libp3net', 'SRCDIR:panda/src/net']) +OPTS=['DIR:panda/src/net'] +IGATEFILES=GetDirectoryContents('panda/src/net', ["*.h", "*_composite*.cxx"]) +IGATEFILES.remove("datagram_ui.h") +TargetAdd('libp3net.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3net.in', opts=['IMOD:panda3d.core', 'ILIB:libp3net', 'SRCDIR:panda/src/net']) # # DIRECTORY: panda/src/pstatclient/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/pstatclient', 'BUILDING:PANDA'] - TargetAdd('p3pstatclient_composite1.obj', opts=OPTS, input='p3pstatclient_composite1.cxx') - TargetAdd('p3pstatclient_composite2.obj', opts=OPTS, input='p3pstatclient_composite2.cxx') +OPTS=['DIR:panda/src/pstatclient', 'BUILDING:PANDA'] +TargetAdd('p3pstatclient_composite1.obj', opts=OPTS, input='p3pstatclient_composite1.cxx') +TargetAdd('p3pstatclient_composite2.obj', opts=OPTS, input='p3pstatclient_composite2.cxx') - OPTS=['DIR:panda/src/pstatclient'] - IGATEFILES=GetDirectoryContents('panda/src/pstatclient', ["*.h", "*_composite*.cxx"]) - IGATEFILES.remove("config_pstats.h") - TargetAdd('libp3pstatclient.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3pstatclient.in', opts=['IMOD:panda3d.core', 'ILIB:libp3pstatclient', 'SRCDIR:panda/src/pstatclient']) +OPTS=['DIR:panda/src/pstatclient'] +IGATEFILES=GetDirectoryContents('panda/src/pstatclient', ["*.h", "*_composite*.cxx"]) +IGATEFILES.remove("config_pstats.h") +TargetAdd('libp3pstatclient.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3pstatclient.in', opts=['IMOD:panda3d.core', 'ILIB:libp3pstatclient', 'SRCDIR:panda/src/pstatclient']) # # DIRECTORY: panda/src/gobj/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/gobj', 'BUILDING:PANDA', 'NVIDIACG', 'ZLIB', 'SQUISH'] - TargetAdd('p3gobj_composite1.obj', opts=OPTS, input='p3gobj_composite1.cxx') - TargetAdd('p3gobj_composite2.obj', opts=OPTS+['BIGOBJ'], input='p3gobj_composite2.cxx') +OPTS=['DIR:panda/src/gobj', 'BUILDING:PANDA', 'NVIDIACG', 'ZLIB', 'SQUISH'] +TargetAdd('p3gobj_composite1.obj', opts=OPTS, input='p3gobj_composite1.cxx') +TargetAdd('p3gobj_composite2.obj', opts=OPTS+['BIGOBJ'], input='p3gobj_composite2.cxx') - OPTS=['DIR:panda/src/gobj', 'NVIDIACG', 'ZLIB', 'SQUISH'] - IGATEFILES=GetDirectoryContents('panda/src/gobj', ["*.h", "*_composite*.cxx"]) - if ("cgfx_states.h" in IGATEFILES): IGATEFILES.remove("cgfx_states.h") - TargetAdd('libp3gobj.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3gobj.in', opts=['IMOD:panda3d.core', 'ILIB:libp3gobj', 'SRCDIR:panda/src/gobj']) - PyTargetAdd('p3gobj_ext_composite.obj', opts=OPTS, input='p3gobj_ext_composite.cxx') +OPTS=['DIR:panda/src/gobj', 'NVIDIACG', 'ZLIB', 'SQUISH'] +IGATEFILES=GetDirectoryContents('panda/src/gobj', ["*.h", "*_composite*.cxx"]) +if ("cgfx_states.h" in IGATEFILES): IGATEFILES.remove("cgfx_states.h") +TargetAdd('libp3gobj.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3gobj.in', opts=['IMOD:panda3d.core', 'ILIB:libp3gobj', 'SRCDIR:panda/src/gobj']) +PyTargetAdd('p3gobj_ext_composite.obj', opts=OPTS, input='p3gobj_ext_composite.cxx') # # DIRECTORY: panda/src/pgraphnodes/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/pgraphnodes', 'BUILDING:PANDA'] - TargetAdd('p3pgraphnodes_composite1.obj', opts=OPTS, input='p3pgraphnodes_composite1.cxx') - TargetAdd('p3pgraphnodes_composite2.obj', opts=OPTS, input='p3pgraphnodes_composite2.cxx') +OPTS=['DIR:panda/src/pgraphnodes', 'BUILDING:PANDA'] +TargetAdd('p3pgraphnodes_composite1.obj', opts=OPTS, input='p3pgraphnodes_composite1.cxx') +TargetAdd('p3pgraphnodes_composite2.obj', opts=OPTS, input='p3pgraphnodes_composite2.cxx') - OPTS=['DIR:panda/src/pgraphnodes'] - IGATEFILES=GetDirectoryContents('panda/src/pgraphnodes', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3pgraphnodes.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3pgraphnodes.in', opts=['IMOD:panda3d.core', 'ILIB:libp3pgraphnodes', 'SRCDIR:panda/src/pgraphnodes']) +OPTS=['DIR:panda/src/pgraphnodes'] +IGATEFILES=GetDirectoryContents('panda/src/pgraphnodes', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3pgraphnodes.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3pgraphnodes.in', opts=['IMOD:panda3d.core', 'ILIB:libp3pgraphnodes', 'SRCDIR:panda/src/pgraphnodes']) # # DIRECTORY: panda/src/pgraph/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/pgraph', 'BUILDING:PANDA'] - TargetAdd('p3pgraph_nodePath.obj', opts=OPTS, input='nodePath.cxx') - TargetAdd('p3pgraph_composite1.obj', opts=OPTS, input='p3pgraph_composite1.cxx') - TargetAdd('p3pgraph_composite2.obj', opts=OPTS, input='p3pgraph_composite2.cxx') - TargetAdd('p3pgraph_composite3.obj', opts=OPTS, input='p3pgraph_composite3.cxx') - TargetAdd('p3pgraph_composite4.obj', opts=OPTS, input='p3pgraph_composite4.cxx') +OPTS=['DIR:panda/src/pgraph', 'BUILDING:PANDA'] +TargetAdd('p3pgraph_nodePath.obj', opts=OPTS, input='nodePath.cxx') +TargetAdd('p3pgraph_composite1.obj', opts=OPTS, input='p3pgraph_composite1.cxx') +TargetAdd('p3pgraph_composite2.obj', opts=OPTS, input='p3pgraph_composite2.cxx') +TargetAdd('p3pgraph_composite3.obj', opts=OPTS, input='p3pgraph_composite3.cxx') +TargetAdd('p3pgraph_composite4.obj', opts=OPTS, input='p3pgraph_composite4.cxx') - OPTS=['DIR:panda/src/pgraph'] - IGATEFILES=GetDirectoryContents('panda/src/pgraph', ["*.h", "nodePath.cxx", "*_composite*.cxx"]) - TargetAdd('libp3pgraph.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3pgraph.in', opts=['IMOD:panda3d.core', 'ILIB:libp3pgraph', 'SRCDIR:panda/src/pgraph']) - PyTargetAdd('p3pgraph_ext_composite.obj', opts=OPTS, input='p3pgraph_ext_composite.cxx') +OPTS=['DIR:panda/src/pgraph'] +IGATEFILES=GetDirectoryContents('panda/src/pgraph', ["*.h", "nodePath.cxx", "*_composite*.cxx"]) +TargetAdd('libp3pgraph.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3pgraph.in', opts=['IMOD:panda3d.core', 'ILIB:libp3pgraph', 'SRCDIR:panda/src/pgraph']) +PyTargetAdd('p3pgraph_ext_composite.obj', opts=OPTS, input='p3pgraph_ext_composite.cxx') # # DIRECTORY: panda/src/cull/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/cull', 'BUILDING:PANDA'] - TargetAdd('p3cull_composite1.obj', opts=OPTS, input='p3cull_composite1.cxx') - TargetAdd('p3cull_composite2.obj', opts=OPTS, input='p3cull_composite2.cxx') +OPTS=['DIR:panda/src/cull', 'BUILDING:PANDA'] +TargetAdd('p3cull_composite1.obj', opts=OPTS, input='p3cull_composite1.cxx') +TargetAdd('p3cull_composite2.obj', opts=OPTS, input='p3cull_composite2.cxx') - OPTS=['DIR:panda/src/cull'] - IGATEFILES=GetDirectoryContents('panda/src/cull', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3cull.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3cull.in', opts=['IMOD:panda3d.core', 'ILIB:libp3cull', 'SRCDIR:panda/src/cull']) +OPTS=['DIR:panda/src/cull'] +IGATEFILES=GetDirectoryContents('panda/src/cull', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3cull.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3cull.in', opts=['IMOD:panda3d.core', 'ILIB:libp3cull', 'SRCDIR:panda/src/cull']) # # DIRECTORY: panda/src/dgraph/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/dgraph', 'BUILDING:PANDA'] - TargetAdd('p3dgraph_composite1.obj', opts=OPTS, input='p3dgraph_composite1.cxx') - TargetAdd('p3dgraph_composite2.obj', opts=OPTS, input='p3dgraph_composite2.cxx') +OPTS=['DIR:panda/src/dgraph', 'BUILDING:PANDA'] +TargetAdd('p3dgraph_composite1.obj', opts=OPTS, input='p3dgraph_composite1.cxx') +TargetAdd('p3dgraph_composite2.obj', opts=OPTS, input='p3dgraph_composite2.cxx') - OPTS=['DIR:panda/src/dgraph'] - IGATEFILES=GetDirectoryContents('panda/src/dgraph', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3dgraph.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3dgraph.in', opts=['IMOD:panda3d.core', 'ILIB:libp3dgraph', 'SRCDIR:panda/src/dgraph']) +OPTS=['DIR:panda/src/dgraph'] +IGATEFILES=GetDirectoryContents('panda/src/dgraph', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3dgraph.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3dgraph.in', opts=['IMOD:panda3d.core', 'ILIB:libp3dgraph', 'SRCDIR:panda/src/dgraph']) # # DIRECTORY: panda/src/device/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/device', 'BUILDING:PANDA'] - TargetAdd('p3device_composite1.obj', opts=OPTS, input='p3device_composite1.cxx') - TargetAdd('p3device_composite2.obj', opts=OPTS, input='p3device_composite2.cxx') +OPTS=['DIR:panda/src/device', 'BUILDING:PANDA'] +TargetAdd('p3device_composite1.obj', opts=OPTS, input='p3device_composite1.cxx') +TargetAdd('p3device_composite2.obj', opts=OPTS, input='p3device_composite2.cxx') - OPTS=['DIR:panda/src/device'] - IGATEFILES=GetDirectoryContents('panda/src/device', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3device.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3device.in', opts=['IMOD:panda3d.core', 'ILIB:libp3device', 'SRCDIR:panda/src/device']) +OPTS=['DIR:panda/src/device'] +IGATEFILES=GetDirectoryContents('panda/src/device', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3device.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3device.in', opts=['IMOD:panda3d.core', 'ILIB:libp3device', 'SRCDIR:panda/src/device']) # # DIRECTORY: panda/src/display/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/display', 'BUILDING:PANDA'] - TargetAdd('p3display_graphicsStateGuardian.obj', opts=OPTS, input='graphicsStateGuardian.cxx') - TargetAdd('p3display_composite1.obj', opts=OPTS, input='p3display_composite1.cxx') - TargetAdd('p3display_composite2.obj', opts=OPTS, input='p3display_composite2.cxx') +OPTS=['DIR:panda/src/display', 'BUILDING:PANDA', 'X11'] +TargetAdd('p3display_graphicsStateGuardian.obj', opts=OPTS, input='graphicsStateGuardian.cxx') +TargetAdd('p3display_composite1.obj', opts=OPTS, input='p3display_composite1.cxx') +TargetAdd('p3display_composite2.obj', opts=OPTS, input='p3display_composite2.cxx') - OPTS=['DIR:panda/src/display'] - IGATEFILES=GetDirectoryContents('panda/src/display', ["*.h", "*_composite*.cxx"]) - IGATEFILES.remove("renderBuffer.h") - TargetAdd('libp3display.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3display.in', opts=['IMOD:panda3d.core', 'ILIB:libp3display', 'SRCDIR:panda/src/display']) - PyTargetAdd('p3display_ext_composite.obj', opts=OPTS, input='p3display_ext_composite.cxx') - - if RTDIST and GetTarget() == 'darwin': - OPTS=['DIR:panda/src/display'] - TargetAdd('subprocessWindowBuffer.obj', opts=OPTS, input='subprocessWindowBuffer.cxx') - TargetAdd('libp3subprocbuffer.ilb', input='subprocessWindowBuffer.obj') +OPTS=['DIR:panda/src/display', 'X11'] +IGATEFILES=GetDirectoryContents('panda/src/display', ["*.h", "*_composite*.cxx"]) +IGATEFILES.remove("renderBuffer.h") +TargetAdd('libp3display.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3display.in', opts=['IMOD:panda3d.core', 'ILIB:libp3display', 'SRCDIR:panda/src/display']) +PyTargetAdd('p3display_ext_composite.obj', opts=OPTS, input='p3display_ext_composite.cxx') # # DIRECTORY: panda/src/chan/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/chan', 'BUILDING:PANDA'] - TargetAdd('p3chan_composite1.obj', opts=OPTS, input='p3chan_composite1.cxx') - TargetAdd('p3chan_composite2.obj', opts=OPTS, input='p3chan_composite2.cxx') +OPTS=['DIR:panda/src/chan', 'BUILDING:PANDA'] +TargetAdd('p3chan_composite1.obj', opts=OPTS, input='p3chan_composite1.cxx') +TargetAdd('p3chan_composite2.obj', opts=OPTS, input='p3chan_composite2.cxx') - OPTS=['DIR:panda/src/chan'] - IGATEFILES=GetDirectoryContents('panda/src/chan', ["*.h", "*_composite*.cxx"]) - IGATEFILES.remove('movingPart.h') - IGATEFILES.remove('animChannelFixed.h') - TargetAdd('libp3chan.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3chan.in', opts=['IMOD:panda3d.core', 'ILIB:libp3chan', 'SRCDIR:panda/src/chan']) +OPTS=['DIR:panda/src/chan'] +IGATEFILES=GetDirectoryContents('panda/src/chan', ["*.h", "*_composite*.cxx"]) +IGATEFILES.remove('movingPart.h') +IGATEFILES.remove('animChannelFixed.h') +TargetAdd('libp3chan.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3chan.in', opts=['IMOD:panda3d.core', 'ILIB:libp3chan', 'SRCDIR:panda/src/chan']) # DIRECTORY: panda/src/char/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/char', 'BUILDING:PANDA'] - TargetAdd('p3char_composite1.obj', opts=OPTS, input='p3char_composite1.cxx') - TargetAdd('p3char_composite2.obj', opts=OPTS, input='p3char_composite2.cxx') +OPTS=['DIR:panda/src/char', 'BUILDING:PANDA'] +TargetAdd('p3char_composite1.obj', opts=OPTS, input='p3char_composite1.cxx') +TargetAdd('p3char_composite2.obj', opts=OPTS, input='p3char_composite2.cxx') - OPTS=['DIR:panda/src/char'] - IGATEFILES=GetDirectoryContents('panda/src/char', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3char.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3char.in', opts=['IMOD:panda3d.core', 'ILIB:libp3char', 'SRCDIR:panda/src/char']) +OPTS=['DIR:panda/src/char'] +IGATEFILES=GetDirectoryContents('panda/src/char', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3char.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3char.in', opts=['IMOD:panda3d.core', 'ILIB:libp3char', 'SRCDIR:panda/src/char']) # # DIRECTORY: panda/src/pnmtext/ # -if (PkgSkip("FREETYPE")==0 and not RUNTIME): +if (PkgSkip("FREETYPE")==0): OPTS=['DIR:panda/src/pnmtext', 'BUILDING:PANDA', 'FREETYPE'] TargetAdd('p3pnmtext_composite1.obj', opts=OPTS, input='p3pnmtext_composite1.cxx') @@ -4086,126 +3708,117 @@ if (PkgSkip("FREETYPE")==0 and not RUNTIME): # DIRECTORY: panda/src/text/ # -if (not RUNTIME): - if not PkgSkip("HARFBUZZ"): - DefSymbol("HARFBUZZ", "HAVE_HARFBUZZ") +if not PkgSkip("HARFBUZZ"): + DefSymbol("HARFBUZZ", "HAVE_HARFBUZZ") - OPTS=['DIR:panda/src/text', 'BUILDING:PANDA', 'ZLIB', 'FREETYPE', 'HARFBUZZ'] - TargetAdd('p3text_composite1.obj', opts=OPTS, input='p3text_composite1.cxx') - TargetAdd('p3text_composite2.obj', opts=OPTS, input='p3text_composite2.cxx') +OPTS=['DIR:panda/src/text', 'BUILDING:PANDA', 'ZLIB', 'FREETYPE', 'HARFBUZZ'] +TargetAdd('p3text_composite1.obj', opts=OPTS, input='p3text_composite1.cxx') +TargetAdd('p3text_composite2.obj', opts=OPTS, input='p3text_composite2.cxx') - OPTS=['DIR:panda/src/text', 'ZLIB', 'FREETYPE'] - IGATEFILES=GetDirectoryContents('panda/src/text', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3text.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3text.in', opts=['IMOD:panda3d.core', 'ILIB:libp3text', 'SRCDIR:panda/src/text']) +OPTS=['DIR:panda/src/text', 'ZLIB', 'FREETYPE'] +IGATEFILES=GetDirectoryContents('panda/src/text', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3text.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3text.in', opts=['IMOD:panda3d.core', 'ILIB:libp3text', 'SRCDIR:panda/src/text']) # # DIRECTORY: panda/src/movies/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/movies', 'BUILDING:PANDA', 'VORBIS', 'OPUS'] - TargetAdd('p3movies_composite1.obj', opts=OPTS, input='p3movies_composite1.cxx') +OPTS=['DIR:panda/src/movies', 'BUILDING:PANDA', 'VORBIS', 'OPUS'] +TargetAdd('p3movies_composite1.obj', opts=OPTS, input='p3movies_composite1.cxx') - OPTS=['DIR:panda/src/movies', 'VORBIS', 'OPUS'] - IGATEFILES=GetDirectoryContents('panda/src/movies', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3movies.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3movies.in', opts=['IMOD:panda3d.core', 'ILIB:libp3movies', 'SRCDIR:panda/src/movies']) +OPTS=['DIR:panda/src/movies', 'VORBIS', 'OPUS'] +IGATEFILES=GetDirectoryContents('panda/src/movies', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3movies.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3movies.in', opts=['IMOD:panda3d.core', 'ILIB:libp3movies', 'SRCDIR:panda/src/movies']) # # DIRECTORY: panda/src/grutil/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/grutil', 'BUILDING:PANDA'] - TargetAdd('p3grutil_multitexReducer.obj', opts=OPTS, input='multitexReducer.cxx') - TargetAdd('p3grutil_composite1.obj', opts=OPTS, input='p3grutil_composite1.cxx') - TargetAdd('p3grutil_composite2.obj', opts=OPTS, input='p3grutil_composite2.cxx') +OPTS=['DIR:panda/src/grutil', 'BUILDING:PANDA'] +TargetAdd('p3grutil_multitexReducer.obj', opts=OPTS, input='multitexReducer.cxx') +TargetAdd('p3grutil_composite1.obj', opts=OPTS, input='p3grutil_composite1.cxx') +TargetAdd('p3grutil_composite2.obj', opts=OPTS, input='p3grutil_composite2.cxx') - OPTS=['DIR:panda/src/grutil'] - IGATEFILES=GetDirectoryContents('panda/src/grutil', ["*.h", "*_composite*.cxx"]) - if 'convexHull.h' in IGATEFILES: IGATEFILES.remove('convexHull.h') - TargetAdd('libp3grutil.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3grutil.in', opts=['IMOD:panda3d.core', 'ILIB:libp3grutil', 'SRCDIR:panda/src/grutil']) +OPTS=['DIR:panda/src/grutil'] +IGATEFILES=GetDirectoryContents('panda/src/grutil', ["*.h", "*_composite*.cxx"]) +if 'convexHull.h' in IGATEFILES: IGATEFILES.remove('convexHull.h') +TargetAdd('libp3grutil.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3grutil.in', opts=['IMOD:panda3d.core', 'ILIB:libp3grutil', 'SRCDIR:panda/src/grutil']) # # DIRECTORY: panda/src/tform/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/tform', 'BUILDING:PANDA'] - TargetAdd('p3tform_composite1.obj', opts=OPTS, input='p3tform_composite1.cxx') - TargetAdd('p3tform_composite2.obj', opts=OPTS, input='p3tform_composite2.cxx') +OPTS=['DIR:panda/src/tform', 'BUILDING:PANDA'] +TargetAdd('p3tform_composite1.obj', opts=OPTS, input='p3tform_composite1.cxx') +TargetAdd('p3tform_composite2.obj', opts=OPTS, input='p3tform_composite2.cxx') - OPTS=['DIR:panda/src/tform'] - IGATEFILES=GetDirectoryContents('panda/src/tform', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3tform.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3tform.in', opts=['IMOD:panda3d.core', 'ILIB:libp3tform', 'SRCDIR:panda/src/tform']) +OPTS=['DIR:panda/src/tform'] +IGATEFILES=GetDirectoryContents('panda/src/tform', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3tform.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3tform.in', opts=['IMOD:panda3d.core', 'ILIB:libp3tform', 'SRCDIR:panda/src/tform']) # # DIRECTORY: panda/src/collide/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/collide', 'BUILDING:PANDA'] - TargetAdd('p3collide_composite1.obj', opts=OPTS, input='p3collide_composite1.cxx') - TargetAdd('p3collide_composite2.obj', opts=OPTS, input='p3collide_composite2.cxx') +OPTS=['DIR:panda/src/collide', 'BUILDING:PANDA'] +TargetAdd('p3collide_composite1.obj', opts=OPTS, input='p3collide_composite1.cxx') +TargetAdd('p3collide_composite2.obj', opts=OPTS, input='p3collide_composite2.cxx') - OPTS=['DIR:panda/src/collide'] - IGATEFILES=GetDirectoryContents('panda/src/collide', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3collide.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3collide.in', opts=['IMOD:panda3d.core', 'ILIB:libp3collide', 'SRCDIR:panda/src/collide']) +OPTS=['DIR:panda/src/collide'] +IGATEFILES=GetDirectoryContents('panda/src/collide', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3collide.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3collide.in', opts=['IMOD:panda3d.core', 'ILIB:libp3collide', 'SRCDIR:panda/src/collide']) # # DIRECTORY: panda/src/parametrics/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/parametrics', 'BUILDING:PANDA'] - TargetAdd('p3parametrics_composite1.obj', opts=OPTS, input='p3parametrics_composite1.cxx') - TargetAdd('p3parametrics_composite2.obj', opts=OPTS, input='p3parametrics_composite2.cxx') +OPTS=['DIR:panda/src/parametrics', 'BUILDING:PANDA'] +TargetAdd('p3parametrics_composite1.obj', opts=OPTS, input='p3parametrics_composite1.cxx') +TargetAdd('p3parametrics_composite2.obj', opts=OPTS, input='p3parametrics_composite2.cxx') - OPTS=['DIR:panda/src/parametrics'] - IGATEFILES=GetDirectoryContents('panda/src/parametrics', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3parametrics.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3parametrics.in', opts=['IMOD:panda3d.core', 'ILIB:libp3parametrics', 'SRCDIR:panda/src/parametrics']) +OPTS=['DIR:panda/src/parametrics'] +IGATEFILES=GetDirectoryContents('panda/src/parametrics', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3parametrics.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3parametrics.in', opts=['IMOD:panda3d.core', 'ILIB:libp3parametrics', 'SRCDIR:panda/src/parametrics']) # # DIRECTORY: panda/src/pgui/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/pgui', 'BUILDING:PANDA'] - TargetAdd('p3pgui_composite1.obj', opts=OPTS, input='p3pgui_composite1.cxx') - TargetAdd('p3pgui_composite2.obj', opts=OPTS, input='p3pgui_composite2.cxx') +OPTS=['DIR:panda/src/pgui', 'BUILDING:PANDA'] +TargetAdd('p3pgui_composite1.obj', opts=OPTS, input='p3pgui_composite1.cxx') +TargetAdd('p3pgui_composite2.obj', opts=OPTS, input='p3pgui_composite2.cxx') - OPTS=['DIR:panda/src/pgui'] - IGATEFILES=GetDirectoryContents('panda/src/pgui', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3pgui.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3pgui.in', opts=['IMOD:panda3d.core', 'ILIB:libp3pgui', 'SRCDIR:panda/src/pgui']) +OPTS=['DIR:panda/src/pgui'] +IGATEFILES=GetDirectoryContents('panda/src/pgui', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3pgui.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3pgui.in', opts=['IMOD:panda3d.core', 'ILIB:libp3pgui', 'SRCDIR:panda/src/pgui']) # # DIRECTORY: panda/src/pnmimagetypes/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/pnmimagetypes', 'DIR:panda/src/pnmimage', 'BUILDING:PANDA', 'PNG', 'ZLIB', 'JPEG', 'TIFF', 'OPENEXR', 'EXCEPTIONS'] - TargetAdd('p3pnmimagetypes_composite1.obj', opts=OPTS, input='p3pnmimagetypes_composite1.cxx') - TargetAdd('p3pnmimagetypes_composite2.obj', opts=OPTS, input='p3pnmimagetypes_composite2.cxx') +OPTS=['DIR:panda/src/pnmimagetypes', 'DIR:panda/src/pnmimage', 'BUILDING:PANDA', 'PNG', 'ZLIB', 'JPEG', 'TIFF', 'OPENEXR', 'EXCEPTIONS'] +TargetAdd('p3pnmimagetypes_composite1.obj', opts=OPTS, input='p3pnmimagetypes_composite1.cxx') +TargetAdd('p3pnmimagetypes_composite2.obj', opts=OPTS, input='p3pnmimagetypes_composite2.cxx') # # DIRECTORY: panda/src/recorder/ # -if (not RUNTIME): - OPTS=['DIR:panda/src/recorder', 'BUILDING:PANDA'] - TargetAdd('p3recorder_composite1.obj', opts=OPTS, input='p3recorder_composite1.cxx') - TargetAdd('p3recorder_composite2.obj', opts=OPTS, input='p3recorder_composite2.cxx') +OPTS=['DIR:panda/src/recorder', 'BUILDING:PANDA'] +TargetAdd('p3recorder_composite1.obj', opts=OPTS, input='p3recorder_composite1.cxx') +TargetAdd('p3recorder_composite2.obj', opts=OPTS, input='p3recorder_composite2.cxx') - OPTS=['DIR:panda/src/recorder'] - IGATEFILES=GetDirectoryContents('panda/src/recorder', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3recorder.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3recorder.in', opts=['IMOD:panda3d.core', 'ILIB:libp3recorder', 'SRCDIR:panda/src/recorder']) +OPTS=['DIR:panda/src/recorder'] +IGATEFILES=GetDirectoryContents('panda/src/recorder', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3recorder.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3recorder.in', opts=['IMOD:panda3d.core', 'ILIB:libp3recorder', 'SRCDIR:panda/src/recorder']) # # DIRECTORY: panda/src/dxml/ @@ -4217,208 +3830,205 @@ OPTS=['DIR:panda/src/dxml', 'TINYXML'] TargetAdd('tinyxml_composite1.obj', opts=OPTS, input='tinyxml_composite1.cxx') TargetAdd('libp3tinyxml.ilb', input='tinyxml_composite1.obj') -if (not RUNTIME): - OPTS=['DIR:panda/src/dxml', 'BUILDING:PANDA', 'TINYXML'] - TargetAdd('p3dxml_composite1.obj', opts=OPTS, input='p3dxml_composite1.cxx') +OPTS=['DIR:panda/src/dxml', 'BUILDING:PANDA', 'TINYXML'] +TargetAdd('p3dxml_composite1.obj', opts=OPTS, input='p3dxml_composite1.cxx') - OPTS=['DIR:panda/src/dxml', 'TINYXML'] - IGATEFILES=GetDirectoryContents('panda/src/dxml', ["*.h", "p3dxml_composite1.cxx"]) - TargetAdd('libp3dxml.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3dxml.in', opts=['IMOD:panda3d.core', 'ILIB:libp3dxml', 'SRCDIR:panda/src/dxml']) +OPTS=['DIR:panda/src/dxml', 'TINYXML'] +IGATEFILES=GetDirectoryContents('panda/src/dxml', ["*.h", "p3dxml_composite1.cxx"]) +TargetAdd('libp3dxml.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3dxml.in', opts=['IMOD:panda3d.core', 'ILIB:libp3dxml', 'SRCDIR:panda/src/dxml']) # # DIRECTORY: panda/metalibs/panda/ # -if (not RUNTIME): - OPTS=['DIR:panda/metalibs/panda', 'BUILDING:PANDA', 'JPEG', 'PNG', 'HARFBUZZ', - 'TIFF', 'OPENEXR', 'ZLIB', 'OPENSSL', 'FREETYPE', 'FFTW', 'ADVAPI', 'WINSOCK2', - 'SQUISH', 'NVIDIACG', 'VORBIS', 'OPUS', 'WINUSER', 'WINMM', 'WINGDI', 'IPHLPAPI', - 'SETUPAPI'] +OPTS=['DIR:panda/metalibs/panda', 'BUILDING:PANDA', 'JPEG', 'PNG', 'HARFBUZZ', + 'TIFF', 'OPENEXR', 'ZLIB', 'OPENSSL', 'FREETYPE', 'FFTW', 'ADVAPI', 'WINSOCK2', + 'SQUISH', 'NVIDIACG', 'VORBIS', 'OPUS', 'WINUSER', 'WINMM', 'WINGDI', 'IPHLPAPI', + 'SETUPAPI', 'IOKIT'] - TargetAdd('panda_panda.obj', opts=OPTS, input='panda.cxx') +TargetAdd('panda_panda.obj', opts=OPTS, input='panda.cxx') - TargetAdd('libpanda.dll', input='panda_panda.obj') - TargetAdd('libpanda.dll', input='p3recorder_composite1.obj') - TargetAdd('libpanda.dll', input='p3recorder_composite2.obj') - TargetAdd('libpanda.dll', input='p3pgraphnodes_composite1.obj') - TargetAdd('libpanda.dll', input='p3pgraphnodes_composite2.obj') - TargetAdd('libpanda.dll', input='p3pgraph_nodePath.obj') - TargetAdd('libpanda.dll', input='p3pgraph_composite1.obj') - TargetAdd('libpanda.dll', input='p3pgraph_composite2.obj') - TargetAdd('libpanda.dll', input='p3pgraph_composite3.obj') - TargetAdd('libpanda.dll', input='p3pgraph_composite4.obj') - TargetAdd('libpanda.dll', input='p3cull_composite1.obj') - TargetAdd('libpanda.dll', input='p3cull_composite2.obj') - TargetAdd('libpanda.dll', input='p3movies_composite1.obj') - TargetAdd('libpanda.dll', input='p3grutil_multitexReducer.obj') - TargetAdd('libpanda.dll', input='p3grutil_composite1.obj') - TargetAdd('libpanda.dll', input='p3grutil_composite2.obj') - TargetAdd('libpanda.dll', input='p3chan_composite1.obj') - TargetAdd('libpanda.dll', input='p3chan_composite2.obj') - TargetAdd('libpanda.dll', input='p3pstatclient_composite1.obj') - TargetAdd('libpanda.dll', input='p3pstatclient_composite2.obj') - TargetAdd('libpanda.dll', input='p3char_composite1.obj') - TargetAdd('libpanda.dll', input='p3char_composite2.obj') - TargetAdd('libpanda.dll', input='p3collide_composite1.obj') - TargetAdd('libpanda.dll', input='p3collide_composite2.obj') - TargetAdd('libpanda.dll', input='p3device_composite1.obj') - TargetAdd('libpanda.dll', input='p3device_composite2.obj') - TargetAdd('libpanda.dll', input='p3dgraph_composite1.obj') - TargetAdd('libpanda.dll', input='p3dgraph_composite2.obj') - TargetAdd('libpanda.dll', input='p3display_graphicsStateGuardian.obj') - TargetAdd('libpanda.dll', input='p3display_composite1.obj') - TargetAdd('libpanda.dll', input='p3display_composite2.obj') - TargetAdd('libpanda.dll', input='p3pipeline_composite1.obj') - TargetAdd('libpanda.dll', input='p3pipeline_composite2.obj') - TargetAdd('libpanda.dll', input='p3pipeline_contextSwitch.obj') - TargetAdd('libpanda.dll', input='p3event_composite1.obj') - TargetAdd('libpanda.dll', input='p3event_composite2.obj') - TargetAdd('libpanda.dll', input='p3gobj_composite1.obj') - TargetAdd('libpanda.dll', input='p3gobj_composite2.obj') - TargetAdd('libpanda.dll', input='p3gsgbase_composite1.obj') - TargetAdd('libpanda.dll', input='p3linmath_composite1.obj') - TargetAdd('libpanda.dll', input='p3linmath_composite2.obj') - TargetAdd('libpanda.dll', input='p3mathutil_composite1.obj') - TargetAdd('libpanda.dll', input='p3mathutil_composite2.obj') - TargetAdd('libpanda.dll', input='p3parametrics_composite1.obj') - TargetAdd('libpanda.dll', input='p3parametrics_composite2.obj') - TargetAdd('libpanda.dll', input='p3pnmimagetypes_composite1.obj') - TargetAdd('libpanda.dll', input='p3pnmimagetypes_composite2.obj') - TargetAdd('libpanda.dll', input='p3pnmimage_composite1.obj') - TargetAdd('libpanda.dll', input='p3pnmimage_composite2.obj') - TargetAdd('libpanda.dll', input='p3pnmimage_convert_srgb_sse2.obj') - TargetAdd('libpanda.dll', input='p3text_composite1.obj') - TargetAdd('libpanda.dll', input='p3text_composite2.obj') - TargetAdd('libpanda.dll', input='p3tform_composite1.obj') - TargetAdd('libpanda.dll', input='p3tform_composite2.obj') - TargetAdd('libpanda.dll', input='p3putil_composite1.obj') - TargetAdd('libpanda.dll', input='p3putil_composite2.obj') - TargetAdd('libpanda.dll', input='p3audio_composite1.obj') - TargetAdd('libpanda.dll', input='p3pgui_composite1.obj') - TargetAdd('libpanda.dll', input='p3pgui_composite2.obj') - TargetAdd('libpanda.dll', input='p3net_composite1.obj') - TargetAdd('libpanda.dll', input='p3net_composite2.obj') - TargetAdd('libpanda.dll', input='p3nativenet_composite1.obj') - TargetAdd('libpanda.dll', input='p3pandabase_pandabase.obj') - TargetAdd('libpanda.dll', input='libpandaexpress.dll') - TargetAdd('libpanda.dll', input='p3dxml_composite1.obj') - TargetAdd('libpanda.dll', input='libp3dtoolconfig.dll') - TargetAdd('libpanda.dll', input='libp3dtool.dll') +TargetAdd('libpanda.dll', input='panda_panda.obj') +TargetAdd('libpanda.dll', input='p3recorder_composite1.obj') +TargetAdd('libpanda.dll', input='p3recorder_composite2.obj') +TargetAdd('libpanda.dll', input='p3pgraphnodes_composite1.obj') +TargetAdd('libpanda.dll', input='p3pgraphnodes_composite2.obj') +TargetAdd('libpanda.dll', input='p3pgraph_nodePath.obj') +TargetAdd('libpanda.dll', input='p3pgraph_composite1.obj') +TargetAdd('libpanda.dll', input='p3pgraph_composite2.obj') +TargetAdd('libpanda.dll', input='p3pgraph_composite3.obj') +TargetAdd('libpanda.dll', input='p3pgraph_composite4.obj') +TargetAdd('libpanda.dll', input='p3cull_composite1.obj') +TargetAdd('libpanda.dll', input='p3cull_composite2.obj') +TargetAdd('libpanda.dll', input='p3movies_composite1.obj') +TargetAdd('libpanda.dll', input='p3grutil_multitexReducer.obj') +TargetAdd('libpanda.dll', input='p3grutil_composite1.obj') +TargetAdd('libpanda.dll', input='p3grutil_composite2.obj') +TargetAdd('libpanda.dll', input='p3chan_composite1.obj') +TargetAdd('libpanda.dll', input='p3chan_composite2.obj') +TargetAdd('libpanda.dll', input='p3pstatclient_composite1.obj') +TargetAdd('libpanda.dll', input='p3pstatclient_composite2.obj') +TargetAdd('libpanda.dll', input='p3char_composite1.obj') +TargetAdd('libpanda.dll', input='p3char_composite2.obj') +TargetAdd('libpanda.dll', input='p3collide_composite1.obj') +TargetAdd('libpanda.dll', input='p3collide_composite2.obj') +TargetAdd('libpanda.dll', input='p3device_composite1.obj') +TargetAdd('libpanda.dll', input='p3device_composite2.obj') +TargetAdd('libpanda.dll', input='p3dgraph_composite1.obj') +TargetAdd('libpanda.dll', input='p3dgraph_composite2.obj') +TargetAdd('libpanda.dll', input='p3display_graphicsStateGuardian.obj') +TargetAdd('libpanda.dll', input='p3display_composite1.obj') +TargetAdd('libpanda.dll', input='p3display_composite2.obj') +TargetAdd('libpanda.dll', input='p3pipeline_composite1.obj') +TargetAdd('libpanda.dll', input='p3pipeline_composite2.obj') +TargetAdd('libpanda.dll', input='p3pipeline_contextSwitch.obj') +TargetAdd('libpanda.dll', input='p3event_composite1.obj') +TargetAdd('libpanda.dll', input='p3event_composite2.obj') +TargetAdd('libpanda.dll', input='p3gobj_composite1.obj') +TargetAdd('libpanda.dll', input='p3gobj_composite2.obj') +TargetAdd('libpanda.dll', input='p3gsgbase_composite1.obj') +TargetAdd('libpanda.dll', input='p3linmath_composite1.obj') +TargetAdd('libpanda.dll', input='p3linmath_composite2.obj') +TargetAdd('libpanda.dll', input='p3mathutil_composite1.obj') +TargetAdd('libpanda.dll', input='p3mathutil_composite2.obj') +TargetAdd('libpanda.dll', input='p3parametrics_composite1.obj') +TargetAdd('libpanda.dll', input='p3parametrics_composite2.obj') +TargetAdd('libpanda.dll', input='p3pnmimagetypes_composite1.obj') +TargetAdd('libpanda.dll', input='p3pnmimagetypes_composite2.obj') +TargetAdd('libpanda.dll', input='p3pnmimage_composite1.obj') +TargetAdd('libpanda.dll', input='p3pnmimage_composite2.obj') +TargetAdd('libpanda.dll', input='p3pnmimage_convert_srgb_sse2.obj') +TargetAdd('libpanda.dll', input='p3text_composite1.obj') +TargetAdd('libpanda.dll', input='p3text_composite2.obj') +TargetAdd('libpanda.dll', input='p3tform_composite1.obj') +TargetAdd('libpanda.dll', input='p3tform_composite2.obj') +TargetAdd('libpanda.dll', input='p3putil_composite1.obj') +TargetAdd('libpanda.dll', input='p3putil_composite2.obj') +TargetAdd('libpanda.dll', input='p3audio_composite1.obj') +TargetAdd('libpanda.dll', input='p3pgui_composite1.obj') +TargetAdd('libpanda.dll', input='p3pgui_composite2.obj') +TargetAdd('libpanda.dll', input='p3net_composite1.obj') +TargetAdd('libpanda.dll', input='p3net_composite2.obj') +TargetAdd('libpanda.dll', input='p3nativenet_composite1.obj') +TargetAdd('libpanda.dll', input='p3pandabase_pandabase.obj') +TargetAdd('libpanda.dll', input='libpandaexpress.dll') +TargetAdd('libpanda.dll', input='p3dxml_composite1.obj') +TargetAdd('libpanda.dll', input='libp3dtoolconfig.dll') +TargetAdd('libpanda.dll', input='libp3dtool.dll') - if PkgSkip("FREETYPE")==0: - TargetAdd('libpanda.dll', input="p3pnmtext_composite1.obj") +if PkgSkip("FREETYPE")==0: + TargetAdd('libpanda.dll', input="p3pnmtext_composite1.obj") - TargetAdd('libpanda.dll', dep='dtool_have_freetype.dat') - TargetAdd('libpanda.dll', opts=OPTS) +TargetAdd('libpanda.dll', dep='dtool_have_freetype.dat') +TargetAdd('libpanda.dll', opts=OPTS) - PyTargetAdd('core_module.obj', input='libp3dtoolbase.in') - PyTargetAdd('core_module.obj', input='libp3dtoolutil.in') - PyTargetAdd('core_module.obj', input='libp3prc.in') +PyTargetAdd('core_module.obj', input='libp3dtoolbase.in') +PyTargetAdd('core_module.obj', input='libp3dtoolutil.in') +PyTargetAdd('core_module.obj', input='libp3prc.in') - PyTargetAdd('core_module.obj', input='libp3downloader.in') - PyTargetAdd('core_module.obj', input='libp3express.in') +PyTargetAdd('core_module.obj', input='libp3downloader.in') +PyTargetAdd('core_module.obj', input='libp3express.in') - PyTargetAdd('core_module.obj', input='libp3recorder.in') - PyTargetAdd('core_module.obj', input='libp3pgraphnodes.in') - PyTargetAdd('core_module.obj', input='libp3pgraph.in') - PyTargetAdd('core_module.obj', input='libp3cull.in') - PyTargetAdd('core_module.obj', input='libp3grutil.in') - PyTargetAdd('core_module.obj', input='libp3chan.in') - PyTargetAdd('core_module.obj', input='libp3pstatclient.in') - PyTargetAdd('core_module.obj', input='libp3char.in') - PyTargetAdd('core_module.obj', input='libp3collide.in') - PyTargetAdd('core_module.obj', input='libp3device.in') - PyTargetAdd('core_module.obj', input='libp3dgraph.in') - PyTargetAdd('core_module.obj', input='libp3display.in') - PyTargetAdd('core_module.obj', input='libp3pipeline.in') - PyTargetAdd('core_module.obj', input='libp3event.in') - PyTargetAdd('core_module.obj', input='libp3gobj.in') - PyTargetAdd('core_module.obj', input='libp3gsgbase.in') - PyTargetAdd('core_module.obj', input='libp3linmath.in') - PyTargetAdd('core_module.obj', input='libp3mathutil.in') - PyTargetAdd('core_module.obj', input='libp3parametrics.in') - PyTargetAdd('core_module.obj', input='libp3pnmimage.in') - PyTargetAdd('core_module.obj', input='libp3text.in') - PyTargetAdd('core_module.obj', input='libp3tform.in') - PyTargetAdd('core_module.obj', input='libp3putil.in') - PyTargetAdd('core_module.obj', input='libp3audio.in') - PyTargetAdd('core_module.obj', input='libp3nativenet.in') - PyTargetAdd('core_module.obj', input='libp3net.in') - PyTargetAdd('core_module.obj', input='libp3pgui.in') - PyTargetAdd('core_module.obj', input='libp3movies.in') - PyTargetAdd('core_module.obj', input='libp3dxml.in') +PyTargetAdd('core_module.obj', input='libp3recorder.in') +PyTargetAdd('core_module.obj', input='libp3pgraphnodes.in') +PyTargetAdd('core_module.obj', input='libp3pgraph.in') +PyTargetAdd('core_module.obj', input='libp3cull.in') +PyTargetAdd('core_module.obj', input='libp3grutil.in') +PyTargetAdd('core_module.obj', input='libp3chan.in') +PyTargetAdd('core_module.obj', input='libp3pstatclient.in') +PyTargetAdd('core_module.obj', input='libp3char.in') +PyTargetAdd('core_module.obj', input='libp3collide.in') +PyTargetAdd('core_module.obj', input='libp3device.in') +PyTargetAdd('core_module.obj', input='libp3dgraph.in') +PyTargetAdd('core_module.obj', input='libp3display.in') +PyTargetAdd('core_module.obj', input='libp3pipeline.in') +PyTargetAdd('core_module.obj', input='libp3event.in') +PyTargetAdd('core_module.obj', input='libp3gobj.in') +PyTargetAdd('core_module.obj', input='libp3gsgbase.in') +PyTargetAdd('core_module.obj', input='libp3linmath.in') +PyTargetAdd('core_module.obj', input='libp3mathutil.in') +PyTargetAdd('core_module.obj', input='libp3parametrics.in') +PyTargetAdd('core_module.obj', input='libp3pnmimage.in') +PyTargetAdd('core_module.obj', input='libp3text.in') +PyTargetAdd('core_module.obj', input='libp3tform.in') +PyTargetAdd('core_module.obj', input='libp3putil.in') +PyTargetAdd('core_module.obj', input='libp3audio.in') +PyTargetAdd('core_module.obj', input='libp3nativenet.in') +PyTargetAdd('core_module.obj', input='libp3net.in') +PyTargetAdd('core_module.obj', input='libp3pgui.in') +PyTargetAdd('core_module.obj', input='libp3movies.in') +PyTargetAdd('core_module.obj', input='libp3dxml.in') - if PkgSkip("FREETYPE")==0: - PyTargetAdd('core_module.obj', input='libp3pnmtext.in') +if PkgSkip("FREETYPE")==0: + PyTargetAdd('core_module.obj', input='libp3pnmtext.in') - PyTargetAdd('core_module.obj', opts=['IMOD:panda3d.core', 'ILIB:core']) +PyTargetAdd('core_module.obj', opts=['IMOD:panda3d.core', 'ILIB:core']) - PyTargetAdd('core.pyd', input='libp3dtoolbase_igate.obj') - PyTargetAdd('core.pyd', input='p3dtoolbase_typeHandle_ext.obj') - PyTargetAdd('core.pyd', input='libp3dtoolutil_igate.obj') - PyTargetAdd('core.pyd', input='p3dtoolutil_ext_composite.obj') - PyTargetAdd('core.pyd', input='libp3prc_igate.obj') - PyTargetAdd('core.pyd', input='p3prc_ext_composite.obj') +PyTargetAdd('core.pyd', input='libp3dtoolbase_igate.obj') +PyTargetAdd('core.pyd', input='p3dtoolbase_typeHandle_ext.obj') +PyTargetAdd('core.pyd', input='libp3dtoolutil_igate.obj') +PyTargetAdd('core.pyd', input='p3dtoolutil_ext_composite.obj') +PyTargetAdd('core.pyd', input='libp3prc_igate.obj') +PyTargetAdd('core.pyd', input='p3prc_ext_composite.obj') - PyTargetAdd('core.pyd', input='libp3downloader_igate.obj') - PyTargetAdd('core.pyd', input='p3downloader_stringStream_ext.obj') - PyTargetAdd('core.pyd', input='p3express_ext_composite.obj') - PyTargetAdd('core.pyd', input='libp3express_igate.obj') +PyTargetAdd('core.pyd', input='libp3downloader_igate.obj') +PyTargetAdd('core.pyd', input='p3express_ext_composite.obj') +PyTargetAdd('core.pyd', input='libp3express_igate.obj') - PyTargetAdd('core.pyd', input='libp3recorder_igate.obj') - PyTargetAdd('core.pyd', input='libp3pgraphnodes_igate.obj') - PyTargetAdd('core.pyd', input='libp3pgraph_igate.obj') - PyTargetAdd('core.pyd', input='libp3movies_igate.obj') - PyTargetAdd('core.pyd', input='libp3grutil_igate.obj') - PyTargetAdd('core.pyd', input='libp3chan_igate.obj') - PyTargetAdd('core.pyd', input='libp3pstatclient_igate.obj') - PyTargetAdd('core.pyd', input='libp3char_igate.obj') - PyTargetAdd('core.pyd', input='libp3collide_igate.obj') - PyTargetAdd('core.pyd', input='libp3device_igate.obj') - PyTargetAdd('core.pyd', input='libp3dgraph_igate.obj') - PyTargetAdd('core.pyd', input='libp3display_igate.obj') - PyTargetAdd('core.pyd', input='libp3pipeline_igate.obj') - PyTargetAdd('core.pyd', input='libp3event_igate.obj') - PyTargetAdd('core.pyd', input='libp3gobj_igate.obj') - PyTargetAdd('core.pyd', input='libp3gsgbase_igate.obj') - PyTargetAdd('core.pyd', input='libp3linmath_igate.obj') - PyTargetAdd('core.pyd', input='libp3mathutil_igate.obj') - PyTargetAdd('core.pyd', input='libp3parametrics_igate.obj') - PyTargetAdd('core.pyd', input='libp3pnmimage_igate.obj') - PyTargetAdd('core.pyd', input='libp3text_igate.obj') - PyTargetAdd('core.pyd', input='libp3tform_igate.obj') - PyTargetAdd('core.pyd', input='libp3putil_igate.obj') - PyTargetAdd('core.pyd', input='libp3audio_igate.obj') - PyTargetAdd('core.pyd', input='libp3pgui_igate.obj') - PyTargetAdd('core.pyd', input='libp3net_igate.obj') - PyTargetAdd('core.pyd', input='libp3nativenet_igate.obj') - PyTargetAdd('core.pyd', input='libp3dxml_igate.obj') +PyTargetAdd('core.pyd', input='libp3recorder_igate.obj') +PyTargetAdd('core.pyd', input='libp3pgraphnodes_igate.obj') +PyTargetAdd('core.pyd', input='libp3pgraph_igate.obj') +PyTargetAdd('core.pyd', input='libp3movies_igate.obj') +PyTargetAdd('core.pyd', input='libp3grutil_igate.obj') +PyTargetAdd('core.pyd', input='libp3chan_igate.obj') +PyTargetAdd('core.pyd', input='libp3pstatclient_igate.obj') +PyTargetAdd('core.pyd', input='libp3char_igate.obj') +PyTargetAdd('core.pyd', input='libp3collide_igate.obj') +PyTargetAdd('core.pyd', input='libp3device_igate.obj') +PyTargetAdd('core.pyd', input='libp3dgraph_igate.obj') +PyTargetAdd('core.pyd', input='libp3display_igate.obj') +PyTargetAdd('core.pyd', input='libp3pipeline_igate.obj') +PyTargetAdd('core.pyd', input='libp3event_igate.obj') +PyTargetAdd('core.pyd', input='libp3gobj_igate.obj') +PyTargetAdd('core.pyd', input='libp3gsgbase_igate.obj') +PyTargetAdd('core.pyd', input='libp3linmath_igate.obj') +PyTargetAdd('core.pyd', input='libp3mathutil_igate.obj') +PyTargetAdd('core.pyd', input='libp3parametrics_igate.obj') +PyTargetAdd('core.pyd', input='libp3pnmimage_igate.obj') +PyTargetAdd('core.pyd', input='libp3text_igate.obj') +PyTargetAdd('core.pyd', input='libp3tform_igate.obj') +PyTargetAdd('core.pyd', input='libp3putil_igate.obj') +PyTargetAdd('core.pyd', input='libp3audio_igate.obj') +PyTargetAdd('core.pyd', input='libp3pgui_igate.obj') +PyTargetAdd('core.pyd', input='libp3net_igate.obj') +PyTargetAdd('core.pyd', input='libp3nativenet_igate.obj') +PyTargetAdd('core.pyd', input='libp3dxml_igate.obj') - if PkgSkip("FREETYPE")==0: - PyTargetAdd('core.pyd', input="libp3pnmtext_igate.obj") +if PkgSkip("FREETYPE")==0: + PyTargetAdd('core.pyd', input="libp3pnmtext_igate.obj") - PyTargetAdd('core.pyd', input='p3pipeline_pythonThread.obj') - PyTargetAdd('core.pyd', input='p3putil_ext_composite.obj') - PyTargetAdd('core.pyd', input='p3pnmimage_pfmFile_ext.obj') - PyTargetAdd('core.pyd', input='p3event_asyncFuture_ext.obj') - PyTargetAdd('core.pyd', input='p3event_pythonTask.obj') - PyTargetAdd('core.pyd', input='p3gobj_ext_composite.obj') - PyTargetAdd('core.pyd', input='p3pgraph_ext_composite.obj') - PyTargetAdd('core.pyd', input='p3display_ext_composite.obj') +PyTargetAdd('core.pyd', input='p3pipeline_pythonThread.obj') +PyTargetAdd('core.pyd', input='p3putil_ext_composite.obj') +PyTargetAdd('core.pyd', input='p3pnmimage_pfmFile_ext.obj') +PyTargetAdd('core.pyd', input='p3event_asyncFuture_ext.obj') +PyTargetAdd('core.pyd', input='p3event_pythonTask.obj') +PyTargetAdd('core.pyd', input='p3gobj_ext_composite.obj') +PyTargetAdd('core.pyd', input='p3pgraph_ext_composite.obj') +PyTargetAdd('core.pyd', input='p3display_ext_composite.obj') - PyTargetAdd('core.pyd', input='core_module.obj') - if not GetLinkAllStatic() and GetTarget() != 'emscripten': - PyTargetAdd('core.pyd', input='libp3tinyxml.ilb') - PyTargetAdd('core.pyd', input='libp3interrogatedb.dll') - PyTargetAdd('core.pyd', input=COMMON_PANDA_LIBS) - PyTargetAdd('core.pyd', opts=['WINSOCK2']) +PyTargetAdd('core.pyd', input='core_module.obj') +if not GetLinkAllStatic() and GetTarget() != 'emscripten': + PyTargetAdd('core.pyd', input='libp3tinyxml.ilb') +PyTargetAdd('core.pyd', input='libp3interrogatedb.dll') +PyTargetAdd('core.pyd', input=COMMON_PANDA_LIBS) +PyTargetAdd('core.pyd', opts=['WINSOCK2']) # # DIRECTORY: panda/src/vision/ # -if (PkgSkip("VISION") == 0) and (not RUNTIME): +if (PkgSkip("VISION") == 0): # We want to know whether we have ffmpeg so that we can override the .avi association. if not PkgSkip("FFMPEG"): DefSymbol("OPENCV", "HAVE_FFMPEG") @@ -4458,7 +4068,7 @@ if (PkgSkip("VISION") == 0) and (not RUNTIME): # DIRECTORY: panda/src/rocket/ # -if (PkgSkip("ROCKET") == 0) and (not RUNTIME): +if (PkgSkip("ROCKET") == 0): OPTS=['DIR:panda/src/rocket', 'BUILDING:ROCKET', 'ROCKET', 'PYTHON'] TargetAdd('p3rocket_composite1.obj', opts=OPTS, input='p3rocket_composite1.cxx') @@ -4490,7 +4100,7 @@ if (PkgSkip("ROCKET") == 0) and (not RUNTIME): # DIRECTORY: panda/src/p3skel # -if (PkgSkip('SKEL')==0) and (not RUNTIME): +if (PkgSkip('SKEL')==0): OPTS=['DIR:panda/src/skel', 'BUILDING:PANDASKEL', 'ADVAPI'] TargetAdd('p3skel_composite1.obj', opts=OPTS, input='p3skel_composite1.cxx') @@ -4503,7 +4113,7 @@ if (PkgSkip('SKEL')==0) and (not RUNTIME): # DIRECTORY: panda/src/p3skel # -if (PkgSkip('SKEL')==0) and (not RUNTIME): +if (PkgSkip('SKEL')==0): OPTS=['BUILDING:PANDASKEL', 'ADVAPI'] TargetAdd('libpandaskel.dll', input='p3skel_composite1.obj') TargetAdd('libpandaskel.dll', input=COMMON_PANDA_LIBS) @@ -4522,7 +4132,7 @@ if (PkgSkip('SKEL')==0) and (not RUNTIME): # DIRECTORY: panda/src/distort/ # -if (PkgSkip('PANDAFX')==0) and (not RUNTIME): +if (PkgSkip('PANDAFX')==0): OPTS=['DIR:panda/src/distort', 'BUILDING:PANDAFX'] TargetAdd('p3distort_composite1.obj', opts=OPTS, input='p3distort_composite1.cxx') @@ -4535,7 +4145,7 @@ if (PkgSkip('PANDAFX')==0) and (not RUNTIME): # DIRECTORY: panda/metalibs/pandafx/ # -if (PkgSkip('PANDAFX')==0) and (not RUNTIME): +if (PkgSkip('PANDAFX')==0): OPTS=['DIR:panda/metalibs/pandafx', 'DIR:panda/src/distort', 'BUILDING:PANDAFX', 'NVIDIACG'] TargetAdd('pandafx_pandafx.obj', opts=OPTS, input='pandafx.cxx') @@ -4559,7 +4169,7 @@ if (PkgSkip('PANDAFX')==0) and (not RUNTIME): # DIRECTORY: panda/src/vrpn/ # -if (PkgSkip("VRPN")==0 and not RUNTIME): +if (PkgSkip("VRPN")==0): OPTS=['DIR:panda/src/vrpn', 'BUILDING:VRPN', 'VRPN'] TargetAdd('p3vrpn_composite1.obj', opts=OPTS, input='p3vrpn_composite1.cxx') TargetAdd('libp3vrpn.dll', input='p3vrpn_composite1.obj') @@ -4585,7 +4195,7 @@ if (PkgSkip("VRPN")==0 and not RUNTIME): # # DIRECTORY: panda/src/ffmpeg # -if PkgSkip("FFMPEG") == 0 and not RUNTIME: +if PkgSkip("FFMPEG") == 0: if not PkgSkip("SWSCALE"): DefSymbol("FFMPEG", "HAVE_SWSCALE") if not PkgSkip("SWRESAMPLE"): @@ -4603,14 +4213,14 @@ if PkgSkip("FFMPEG") == 0 and not RUNTIME: # DIRECTORY: panda/src/audiotraits/ # -if PkgSkip("FMODEX") == 0 and not RUNTIME: +if PkgSkip("FMODEX") == 0: OPTS=['DIR:panda/src/audiotraits', 'BUILDING:FMOD_AUDIO', 'FMODEX'] TargetAdd('fmod_audio_fmod_audio_composite1.obj', opts=OPTS, input='fmod_audio_composite1.cxx') TargetAdd('libp3fmod_audio.dll', input='fmod_audio_fmod_audio_composite1.obj') TargetAdd('libp3fmod_audio.dll', input=COMMON_PANDA_LIBS) TargetAdd('libp3fmod_audio.dll', opts=['MODULE', 'ADVAPI', 'WINUSER', 'WINMM', 'FMODEX']) -if PkgSkip("OPENAL") == 0 and not RUNTIME: +if PkgSkip("OPENAL") == 0: OPTS=['DIR:panda/src/audiotraits', 'BUILDING:OPENAL_AUDIO', 'OPENAL'] TargetAdd('openal_audio_openal_audio_composite1.obj', opts=OPTS, input='openal_audio_composite1.cxx') TargetAdd('libp3openal_audio.dll', input='openal_audio_openal_audio_composite1.obj') @@ -4621,34 +4231,8 @@ if PkgSkip("OPENAL") == 0 and not RUNTIME: # DIRECTORY: panda/src/downloadertools/ # -if (PkgSkip("OPENSSL")==0 and not RTDIST and not RUNTIME and PkgSkip("DEPLOYTOOLS")==0): - OPTS=['DIR:panda/src/downloadertools', 'OPENSSL', 'ZLIB', 'ADVAPI', 'WINSOCK2', 'WINSHELL', 'WINGDI', 'WINUSER'] - - TargetAdd('apply_patch_apply_patch.obj', opts=OPTS, input='apply_patch.cxx') - TargetAdd('apply_patch.exe', input=['apply_patch_apply_patch.obj']) - TargetAdd('apply_patch.exe', input=COMMON_PANDA_LIBS) - TargetAdd('apply_patch.exe', opts=OPTS) - - TargetAdd('build_patch_build_patch.obj', opts=OPTS, input='build_patch.cxx') - TargetAdd('build_patch.exe', input=['build_patch_build_patch.obj']) - TargetAdd('build_patch.exe', input=COMMON_PANDA_LIBS) - TargetAdd('build_patch.exe', opts=OPTS) - - if not PkgSkip("ZLIB"): - TargetAdd('check_adler_check_adler.obj', opts=OPTS, input='check_adler.cxx') - TargetAdd('check_adler.exe', input=['check_adler_check_adler.obj']) - TargetAdd('check_adler.exe', input=COMMON_PANDA_LIBS) - TargetAdd('check_adler.exe', opts=OPTS) - - TargetAdd('check_crc_check_crc.obj', opts=OPTS, input='check_crc.cxx') - TargetAdd('check_crc.exe', input=['check_crc_check_crc.obj']) - TargetAdd('check_crc.exe', input=COMMON_PANDA_LIBS) - TargetAdd('check_crc.exe', opts=OPTS) - - TargetAdd('check_md5_check_md5.obj', opts=OPTS, input='check_md5.cxx') - TargetAdd('check_md5.exe', input=['check_md5_check_md5.obj']) - TargetAdd('check_md5.exe', input=COMMON_PANDA_LIBS) - TargetAdd('check_md5.exe', opts=OPTS) +if (PkgSkip("OPENSSL")==0 and PkgSkip("DEPLOYTOOLS")==0): + OPTS=['DIR:panda/src/downloadertools', 'OPENSSL', 'ADVAPI', 'WINSOCK2', 'WINSHELL', 'WINGDI', 'WINUSER'] TargetAdd('pdecrypt_pdecrypt.obj', opts=OPTS, input='pdecrypt.cxx') TargetAdd('pdecrypt.exe', input=['pdecrypt_pdecrypt.obj']) @@ -4660,16 +4244,11 @@ if (PkgSkip("OPENSSL")==0 and not RTDIST and not RUNTIME and PkgSkip("DEPLOYTOOL TargetAdd('pencrypt.exe', input=COMMON_PANDA_LIBS) TargetAdd('pencrypt.exe', opts=OPTS) - TargetAdd('show_ddb_show_ddb.obj', opts=OPTS, input='show_ddb.cxx') - TargetAdd('show_ddb.exe', input=['show_ddb_show_ddb.obj']) - TargetAdd('show_ddb.exe', input=COMMON_PANDA_LIBS) - TargetAdd('show_ddb.exe', opts=OPTS) - # # DIRECTORY: panda/src/downloadertools/ # -if (PkgSkip("ZLIB")==0 and not RTDIST and not RUNTIME and PkgSkip("DEPLOYTOOLS")==0): +if (PkgSkip("ZLIB")==0 and PkgSkip("DEPLOYTOOLS")==0): OPTS=['DIR:panda/src/downloadertools', 'ZLIB', 'OPENSSL', 'ADVAPI', 'WINSOCK2', 'WINSHELL', 'WINGDI', 'WINUSER'] TargetAdd('multify_multify.obj', opts=OPTS, input='multify.cxx') @@ -4691,7 +4270,7 @@ if (PkgSkip("ZLIB")==0 and not RTDIST and not RUNTIME and PkgSkip("DEPLOYTOOLS") # DIRECTORY: panda/src/windisplay/ # -if (GetTarget() == 'windows' and not RUNTIME): +if (GetTarget() == 'windows'): OPTS=['DIR:panda/src/windisplay', 'BUILDING:PANDAWIN'] TargetAdd('p3windisplay_composite1.obj', opts=OPTS+["BIGOBJ"], input='p3windisplay_composite1.cxx') TargetAdd('p3windisplay_windetectdx9.obj', opts=OPTS + ["DX9"], input='winDetectDx9.cxx') @@ -4704,7 +4283,7 @@ if (GetTarget() == 'windows' and not RUNTIME): # DIRECTORY: panda/metalibs/pandadx9/ # -if GetTarget() == 'windows' and PkgSkip("DX9")==0 and not RUNTIME: +if GetTarget() == 'windows' and PkgSkip("DX9")==0: OPTS=['DIR:panda/src/dxgsg9', 'BUILDING:PANDADX', 'DX9', 'NVIDIACG', 'CGDX9'] TargetAdd('p3dxgsg9_dxGraphicsStateGuardian9.obj', opts=OPTS, input='dxGraphicsStateGuardian9.cxx') TargetAdd('p3dxgsg9_composite1.obj', opts=OPTS, input='p3dxgsg9_composite1.cxx') @@ -4721,7 +4300,7 @@ if GetTarget() == 'windows' and PkgSkip("DX9")==0 and not RUNTIME: # DIRECTORY: panda/src/egg/ # -if not RUNTIME and not PkgSkip("EGG"): +if not PkgSkip("EGG"): OPTS=['DIR:panda/src/egg', 'BUILDING:PANDAEGG', 'ZLIB', 'BISONPREFIX_eggyy', 'FLEXDASHI'] CreateFile(GetOutputDir()+"/include/parser.h") TargetAdd('p3egg_parser.obj', opts=OPTS, input='parser.yxx') @@ -4741,13 +4320,13 @@ if not RUNTIME and not PkgSkip("EGG"): # DIRECTORY: panda/src/egg2pg/ # -if not RUNTIME and not PkgSkip("EGG"): +if not PkgSkip("EGG"): OPTS=['DIR:panda/src/egg2pg', 'BUILDING:PANDAEGG'] TargetAdd('p3egg2pg_composite1.obj', opts=OPTS, input='p3egg2pg_composite1.cxx') TargetAdd('p3egg2pg_composite2.obj', opts=OPTS, input='p3egg2pg_composite2.cxx') OPTS=['DIR:panda/src/egg2pg'] - IGATEFILES=['load_egg_file.h'] + IGATEFILES=['load_egg_file.h', 'save_egg_file.h'] TargetAdd('libp3egg2pg.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3egg2pg.in', opts=['IMOD:panda3d.egg', 'ILIB:libp3egg2pg', 'SRCDIR:panda/src/egg2pg']) @@ -4755,29 +4334,28 @@ if not RUNTIME and not PkgSkip("EGG"): # DIRECTORY: panda/src/framework/ # -if (not RUNTIME): - deps = [] - # Framework wants to link in a renderer when building statically, so tell it what is available. - if GetLinkAllStatic(): - deps = ['dtool_have_gl.dat', 'dtool_have_tinydisplay.dat', 'dtool_have_egg.dat'] - if not PkgSkip("GL"): - DefSymbol("FRAMEWORK", "HAVE_GL") - if not PkgSkip("TINYDISPLAY"): - DefSymbol("FRAMEWORK", "HAVE_TINYDISPLAY") - if not PkgSkip("EGG"): - DefSymbol("FRAMEWORK", "HAVE_EGG") +deps = [] +# Framework wants to link in a renderer when building statically, so tell it what is available. +if GetLinkAllStatic(): + deps = ['dtool_have_gl.dat', 'dtool_have_tinydisplay.dat', 'dtool_have_egg.dat'] + if not PkgSkip("GL"): + DefSymbol("FRAMEWORK", "HAVE_GL") + if not PkgSkip("TINYDISPLAY"): + DefSymbol("FRAMEWORK", "HAVE_TINYDISPLAY") + if not PkgSkip("EGG"): + DefSymbol("FRAMEWORK", "HAVE_EGG") - OPTS=['DIR:panda/src/framework', 'BUILDING:FRAMEWORK', 'FRAMEWORK'] - TargetAdd('p3framework_composite1.obj', opts=OPTS, input='p3framework_composite1.cxx', dep=deps) - TargetAdd('libp3framework.dll', input='p3framework_composite1.obj') - TargetAdd('libp3framework.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libp3framework.dll', opts=['ADVAPI']) +OPTS=['DIR:panda/src/framework', 'BUILDING:FRAMEWORK', 'FRAMEWORK'] +TargetAdd('p3framework_composite1.obj', opts=OPTS, input='p3framework_composite1.cxx', dep=deps) +TargetAdd('libp3framework.dll', input='p3framework_composite1.obj') +TargetAdd('libp3framework.dll', input=COMMON_PANDA_LIBS) +TargetAdd('libp3framework.dll', opts=['ADVAPI']) # # DIRECTORY: panda/src/glgsg/ # -if (not RUNTIME and PkgSkip("GL")==0): +if (PkgSkip("GL")==0): OPTS=['DIR:panda/src/glgsg', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGL', 'GL', 'NVIDIACG'] TargetAdd('p3glgsg_config_glgsg.obj', opts=OPTS, input='config_glgsg.cxx') TargetAdd('p3glgsg_glgsg.obj', opts=OPTS, input='glgsg.cxx') @@ -4786,7 +4364,7 @@ if (not RUNTIME and PkgSkip("GL")==0): # DIRECTORY: panda/src/glesgsg/ # -if (not RUNTIME and PkgSkip("GLES")==0): +if (PkgSkip("GLES")==0): OPTS=['DIR:panda/src/glesgsg', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGLES', 'GLES'] TargetAdd('p3glesgsg_config_glesgsg.obj', opts=OPTS, input='config_glesgsg.cxx') TargetAdd('p3glesgsg_glesgsg.obj', opts=OPTS, input='glesgsg.cxx') @@ -4795,7 +4373,7 @@ if (not RUNTIME and PkgSkip("GLES")==0): # DIRECTORY: panda/src/gles2gsg/ # -if (not RUNTIME and PkgSkip("GLES2")==0): +if (PkgSkip("GLES2")==0): OPTS=['DIR:panda/src/gles2gsg', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGLES2', 'GLES2'] TargetAdd('p3gles2gsg_config_gles2gsg.obj', opts=OPTS, input='config_gles2gsg.cxx') TargetAdd('p3gles2gsg_gles2gsg.obj', opts=OPTS, input='gles2gsg.cxx') @@ -4804,7 +4382,7 @@ if (not RUNTIME and PkgSkip("GLES2")==0): # DIRECTORY: panda/metalibs/pandaegg/ # -if not RUNTIME and not PkgSkip("EGG"): +if not PkgSkip("EGG"): OPTS=['DIR:panda/metalibs/pandaegg', 'DIR:panda/src/egg', 'BUILDING:PANDAEGG'] TargetAdd('pandaegg_pandaegg.obj', opts=OPTS, input='pandaegg.cxx') @@ -4836,7 +4414,7 @@ if not RUNTIME and not PkgSkip("EGG"): # DIRECTORY: panda/src/x11display/ # -if (GetTarget() not in ['windows', 'darwin'] and PkgSkip("X11")==0 and not RUNTIME): +if (GetTarget() not in ['windows', 'darwin'] and PkgSkip("X11")==0): OPTS=['DIR:panda/src/x11display', 'BUILDING:PANDAX11', 'X11'] TargetAdd('p3x11display_composite1.obj', opts=OPTS, input='p3x11display_composite1.cxx') @@ -4844,7 +4422,7 @@ if (GetTarget() not in ['windows', 'darwin'] and PkgSkip("X11")==0 and not RUNTI # DIRECTORY: panda/src/glxdisplay/ # -if (GetTarget() not in ['windows', 'darwin'] and PkgSkip("GL")==0 and PkgSkip("X11")==0 and not RUNTIME): +if (GetTarget() not in ['windows', 'darwin'] and PkgSkip("GL")==0 and PkgSkip("X11")==0): OPTS=['DIR:panda/src/glxdisplay', 'BUILDING:PANDAGL', 'GL', 'NVIDIACG', 'CGGL'] TargetAdd('p3glxdisplay_composite1.obj', opts=OPTS, input='p3glxdisplay_composite1.cxx') OPTS=['DIR:panda/metalibs/pandagl', 'BUILDING:PANDAGL', 'GL', 'NVIDIACG', 'CGGL'] @@ -4861,7 +4439,7 @@ if (GetTarget() not in ['windows', 'darwin'] and PkgSkip("GL")==0 and PkgSkip("X # DIRECTORY: panda/src/cocoadisplay/ # -if (GetTarget() == 'darwin' and PkgSkip("COCOA")==0 and PkgSkip("GL")==0 and not RUNTIME): +if (GetTarget() == 'darwin' and PkgSkip("COCOA")==0 and PkgSkip("GL")==0): OPTS=['DIR:panda/src/cocoadisplay', 'BUILDING:PANDAGL', 'GL', 'NVIDIACG', 'CGGL'] TargetAdd('p3cocoadisplay_composite1.obj', opts=OPTS, input='p3cocoadisplay_composite1.mm') OPTS=['DIR:panda/metalibs/pandagl', 'BUILDING:PANDAGL', 'GL', 'NVIDIACG', 'CGGL'] @@ -4873,33 +4451,13 @@ if (GetTarget() == 'darwin' and PkgSkip("COCOA")==0 and PkgSkip("GL")==0 and not if (PkgSkip('PANDAFX')==0): TargetAdd('libpandagl.dll', input='libpandafx.dll') TargetAdd('libpandagl.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'COCOA', 'CARBON']) - -# -# DIRECTORY: panda/src/osxdisplay/ -# - -elif (GetTarget() == 'darwin' and PkgSkip("CARBON")==0 and PkgSkip("GL")==0 and not RUNTIME): - OPTS=['DIR:panda/src/osxdisplay', 'BUILDING:PANDAGL', 'GL', 'NVIDIACG', 'CGGL'] - TargetAdd('p3osxdisplay_composite1.obj', opts=OPTS, input='p3osxdisplay_composite1.cxx') - TargetAdd('p3osxdisplay_osxGraphicsWindow.obj', opts=OPTS, input='osxGraphicsWindow.mm') - OPTS=['DIR:panda/metalibs/pandagl', 'BUILDING:PANDAGL', 'GL', 'NVIDIACG', 'CGGL'] - TargetAdd('pandagl_pandagl.obj', opts=OPTS, input='pandagl.cxx') - TargetAdd('libpandagl.dll', input='pandagl_pandagl.obj') - TargetAdd('libpandagl.dll', input='p3glgsg_config_glgsg.obj') - TargetAdd('libpandagl.dll', input='p3glgsg_glgsg.obj') - TargetAdd('libpandagl.dll', input='p3osxdisplay_composite1.obj') - TargetAdd('libpandagl.dll', input='p3osxdisplay_osxGraphicsWindow.obj') - if (PkgSkip('PANDAFX')==0): - TargetAdd('libpandagl.dll', input='libpandafx.dll') - TargetAdd('libpandagl.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'CARBON', 'AGL', 'COCOA']) + TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'COCOA', 'CARBON', 'QUARTZ']) # # DIRECTORY: panda/src/wgldisplay/ # -if (GetTarget() == 'windows' and PkgSkip("GL")==0 and not RUNTIME): +if (GetTarget() == 'windows' and PkgSkip("GL")==0): OPTS=['DIR:panda/src/wgldisplay', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGL', 'NVIDIACG', 'CGGL'] TargetAdd('p3wgldisplay_composite1.obj', opts=OPTS, input='p3wgldisplay_composite1.cxx') OPTS=['DIR:panda/metalibs/pandagl', 'BUILDING:PANDAGL', 'NVIDIACG', 'CGGL'] @@ -4918,7 +4476,7 @@ if (GetTarget() == 'windows' and PkgSkip("GL")==0 and not RUNTIME): # DIRECTORY: panda/src/egldisplay/ # -if (PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and PkgSkip("X11")==0 and not RUNTIME): +if (PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and PkgSkip("X11")==0): DefSymbol('GLES', 'OPENGLES_1', '') OPTS=['DIR:panda/src/egldisplay', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGLES', 'GLES', 'EGL'] TargetAdd('pandagles_egldisplay_composite1.obj', opts=OPTS, input='p3egldisplay_composite1.cxx') @@ -4936,7 +4494,7 @@ if (PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and PkgSkip("X11")==0 and not RUNTI # DIRECTORY: panda/src/egldisplay/ # -if (PkgSkip("EGL")==0 and PkgSkip("GLES2")==0 and PkgSkip("X11")==0 and not RUNTIME): +if (PkgSkip("EGL")==0 and PkgSkip("GLES2")==0 and PkgSkip("X11")==0): DefSymbol('GLES2', 'OPENGLES_2', '') OPTS=['DIR:panda/src/egldisplay', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGLES2', 'GLES2', 'EGL'] TargetAdd('pandagles2_egldisplay_composite1.obj', opts=OPTS, input='p3egldisplay_composite1.cxx') @@ -4953,7 +4511,7 @@ if (PkgSkip("EGL")==0 and PkgSkip("GLES2")==0 and PkgSkip("X11")==0 and not RUNT # # DIRECTORY: panda/src/ode/ # -if (PkgSkip("ODE")==0 and not RUNTIME): +if (PkgSkip("ODE")==0): OPTS=['DIR:panda/src/ode', 'BUILDING:PANDAODE', 'ODE'] TargetAdd('p3ode_composite1.obj', opts=OPTS, input='p3ode_composite1.cxx') TargetAdd('p3ode_composite2.obj', opts=OPTS, input='p3ode_composite2.cxx') @@ -4970,7 +4528,7 @@ if (PkgSkip("ODE")==0 and not RUNTIME): # # DIRECTORY: panda/metalibs/pandaode/ # -if (PkgSkip("ODE")==0 and not RUNTIME): +if (PkgSkip("ODE")==0): OPTS=['DIR:panda/metalibs/pandaode', 'BUILDING:PANDAODE', 'ODE'] TargetAdd('pandaode_pandaode.obj', opts=OPTS, input='pandaode.cxx') @@ -4997,7 +4555,7 @@ if (PkgSkip("ODE")==0 and not RUNTIME): # # DIRECTORY: panda/src/bullet/ # -if (PkgSkip("BULLET")==0 and not RUNTIME): +if (PkgSkip("BULLET")==0): OPTS=['DIR:panda/src/bullet', 'BUILDING:PANDABULLET', 'BULLET'] TargetAdd('p3bullet_composite.obj', opts=OPTS, input='p3bullet_composite.cxx') @@ -5009,7 +4567,7 @@ if (PkgSkip("BULLET")==0 and not RUNTIME): # # DIRECTORY: panda/metalibs/pandabullet/ # -if (PkgSkip("BULLET")==0 and not RUNTIME): +if (PkgSkip("BULLET")==0): OPTS=['DIR:panda/metalibs/pandabullet', 'BUILDING:PANDABULLET', 'BULLET'] TargetAdd('pandabullet_pandabullet.obj', opts=OPTS, input='pandabullet.cxx') @@ -5035,10 +4593,10 @@ if (PkgSkip("BULLET")==0 and not RUNTIME): # if (PkgSkip("PHYSX")==0): - OPTS=['DIR:panda/src/physx', 'BUILDING:PANDAPHYSX', 'PHYSX', 'NOARCH:PPC'] + OPTS=['DIR:panda/src/physx', 'BUILDING:PANDAPHYSX', 'PHYSX'] TargetAdd('p3physx_composite.obj', opts=OPTS, input='p3physx_composite.cxx') - OPTS=['DIR:panda/src/physx', 'PHYSX', 'NOARCH:PPC'] + OPTS=['DIR:panda/src/physx', 'PHYSX'] IGATEFILES=GetDirectoryContents('panda/src/physx', ["*.h", "*_composite*.cxx"]) TargetAdd('libpandaphysx.in', opts=OPTS, input=IGATEFILES) TargetAdd('libpandaphysx.in', opts=['IMOD:panda3d.physx', 'ILIB:libpandaphysx', 'SRCDIR:panda/src/physx']) @@ -5048,15 +4606,15 @@ if (PkgSkip("PHYSX")==0): # if (PkgSkip("PHYSX")==0): - OPTS=['DIR:panda/metalibs/pandaphysx', 'BUILDING:PANDAPHYSX', 'PHYSX', 'NOARCH:PPC', 'PYTHON'] + OPTS=['DIR:panda/metalibs/pandaphysx', 'BUILDING:PANDAPHYSX', 'PHYSX', 'PYTHON'] TargetAdd('pandaphysx_pandaphysx.obj', opts=OPTS, input='pandaphysx.cxx') TargetAdd('libpandaphysx.dll', input='pandaphysx_pandaphysx.obj') TargetAdd('libpandaphysx.dll', input='p3physx_composite.obj') TargetAdd('libpandaphysx.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libpandaphysx.dll', opts=['WINUSER', 'PHYSX', 'NOARCH:PPC', 'PYTHON']) + TargetAdd('libpandaphysx.dll', opts=['WINUSER', 'PHYSX', 'PYTHON']) - OPTS=['DIR:panda/metalibs/pandaphysx', 'PHYSX', 'NOARCH:PPC'] + OPTS=['DIR:panda/metalibs/pandaphysx', 'PHYSX'] PyTargetAdd('physx_module.obj', input='libpandaphysx.in') PyTargetAdd('physx_module.obj', opts=OPTS) PyTargetAdd('physx_module.obj', opts=['IMOD:panda3d.physx', 'ILIB:physx', 'IMPORT:panda3d.core']) @@ -5066,13 +4624,13 @@ if (PkgSkip("PHYSX")==0): PyTargetAdd('physx.pyd', input='libpandaphysx.dll') PyTargetAdd('physx.pyd', input='libp3interrogatedb.dll') PyTargetAdd('physx.pyd', input=COMMON_PANDA_LIBS) - PyTargetAdd('physx.pyd', opts=['WINUSER', 'PHYSX', 'NOARCH:PPC']) + PyTargetAdd('physx.pyd', opts=['WINUSER', 'PHYSX']) # # DIRECTORY: panda/src/physics/ # -if (PkgSkip("PANDAPHYSICS")==0) and (not RUNTIME): +if (PkgSkip("PANDAPHYSICS")==0): OPTS=['DIR:panda/src/physics', 'BUILDING:PANDAPHYSICS'] TargetAdd('p3physics_composite1.obj', opts=OPTS, input='p3physics_composite1.cxx') TargetAdd('p3physics_composite2.obj', opts=OPTS, input='p3physics_composite2.cxx') @@ -5087,7 +4645,7 @@ if (PkgSkip("PANDAPHYSICS")==0) and (not RUNTIME): # DIRECTORY: panda/src/particlesystem/ # -if (PkgSkip("PANDAPHYSICS")==0) and (PkgSkip("PANDAPARTICLESYSTEM")==0) and (not RUNTIME): +if (PkgSkip("PANDAPHYSICS")==0) and (PkgSkip("PANDAPARTICLESYSTEM")==0): OPTS=['DIR:panda/src/particlesystem', 'BUILDING:PANDAPHYSICS'] TargetAdd('p3particlesystem_composite1.obj', opts=OPTS, input='p3particlesystem_composite1.cxx') TargetAdd('p3particlesystem_composite2.obj', opts=OPTS, input='p3particlesystem_composite2.cxx') @@ -5106,7 +4664,7 @@ if (PkgSkip("PANDAPHYSICS")==0) and (PkgSkip("PANDAPARTICLESYSTEM")==0) and (not # DIRECTORY: panda/metalibs/pandaphysics/ # -if (PkgSkip("PANDAPHYSICS")==0) and (not RUNTIME): +if (PkgSkip("PANDAPHYSICS")==0): OPTS=['DIR:panda/metalibs/pandaphysics', 'BUILDING:PANDAPHYSICS'] TargetAdd('pandaphysics_pandaphysics.obj', opts=OPTS, input='pandaphysics.cxx') @@ -5161,7 +4719,7 @@ if (PkgSkip("SPEEDTREE")==0): # DIRECTORY: panda/src/testbed/ # -if (not RTDIST and not RUNTIME and PkgSkip("PVIEW")==0): +if (PkgSkip("PVIEW")==0): OPTS=['DIR:panda/src/testbed'] TargetAdd('pview_pview.obj', opts=OPTS, input='pview.cxx') TargetAdd('pview.exe', input='pview_pview.obj') @@ -5178,7 +4736,7 @@ if (not RTDIST and not RUNTIME and PkgSkip("PVIEW")==0): # DIRECTORY: panda/src/android/ # -if (not RUNTIME and GetTarget() == 'android'): +if (GetTarget() == 'android'): OPTS=['DIR:panda/src/android'] TargetAdd('org/panda3d/android/NativeIStream.class', opts=OPTS, input='NativeIStream.java') TargetAdd('org/panda3d/android/NativeOStream.class', opts=OPTS, input='NativeOStream.java') @@ -5193,7 +4751,7 @@ if (not RUNTIME and GetTarget() == 'android'): TargetAdd('android_native_app_glue.obj', opts=OPTS + ['NOHIDDEN'], input='android_native_app_glue.c') TargetAdd('android_main.obj', opts=OPTS, input='android_main.cxx') - if (not RTDIST and PkgSkip("PVIEW")==0): + if (PkgSkip("PVIEW")==0): TargetAdd('libpview_pview.obj', opts=OPTS, input='pview.cxx') TargetAdd('libpview.dll', input='android_native_app_glue.obj') TargetAdd('libpview.dll', input='android_main.obj') @@ -5205,7 +4763,7 @@ if (not RUNTIME and GetTarget() == 'android'): TargetAdd('libpview.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpview.dll', opts=['MODULE', 'ANDROID']) - if (not RTDIST and PkgSkip("PYTHON")==0): + if (PkgSkip("PYTHON")==0): OPTS += ['PYTHON'] TargetAdd('ppython_ppython.obj', opts=OPTS, input='python_main.cxx') TargetAdd('libppython.dll', input='android_native_app_glue.obj') @@ -5214,13 +4772,13 @@ if (not RUNTIME and GetTarget() == 'android'): TargetAdd('libppython.dll', input='libp3framework.dll') TargetAdd('libppython.dll', input='libp3android.dll') TargetAdd('libppython.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libppython.dll', opts=['MODULE', 'ANDROID']) + TargetAdd('libppython.dll', opts=['MODULE', 'ANDROID', 'PYTHON']) # # DIRECTORY: panda/src/androiddisplay/ # -if (GetTarget() == 'android' and PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and not RUNTIME): +if (GetTarget() == 'android' and PkgSkip("EGL")==0 and PkgSkip("GLES")==0): DefSymbol('GLES', 'OPENGLES_1', '') OPTS=['DIR:panda/src/androiddisplay', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGLES', 'GLES', 'EGL'] TargetAdd('pandagles_androiddisplay_composite1.obj', opts=OPTS, input='p3androiddisplay_composite1.cxx') @@ -5238,8 +4796,8 @@ if (GetTarget() == 'android' and PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and no # DIRECTORY: panda/src/tinydisplay/ # -if (not RUNTIME and (GetTarget() in ('windows', 'darwin') or PkgSkip("X11")==0) and PkgSkip("TINYDISPLAY")==0): - OPTS=['DIR:panda/src/tinydisplay', 'BUILDING:TINYDISPLAY'] +if ((GetTarget() in ('windows', 'darwin') or PkgSkip("X11")==0) and PkgSkip("TINYDISPLAY")==0): + OPTS=['DIR:panda/src/tinydisplay', 'BUILDING:TINYDISPLAY', 'X11'] TargetAdd('p3tinydisplay_composite1.obj', opts=OPTS, input='p3tinydisplay_composite1.cxx') TargetAdd('p3tinydisplay_composite2.obj', opts=OPTS, input='p3tinydisplay_composite2.cxx') TargetAdd('p3tinydisplay_ztriangle_1.obj', opts=OPTS, input='ztriangle_1.cxx') @@ -5247,14 +4805,10 @@ if (not RUNTIME and (GetTarget() in ('windows', 'darwin') or PkgSkip("X11")==0) TargetAdd('p3tinydisplay_ztriangle_3.obj', opts=OPTS, input='ztriangle_3.cxx') TargetAdd('p3tinydisplay_ztriangle_4.obj', opts=OPTS, input='ztriangle_4.cxx') TargetAdd('p3tinydisplay_ztriangle_table.obj', opts=OPTS, input='ztriangle_table.cxx') - if GetTarget() == 'darwin': - TargetAdd('p3tinydisplay_tinyOsxGraphicsWindow.obj', opts=OPTS, input='tinyOsxGraphicsWindow.mm') - TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_tinyOsxGraphicsWindow.obj') - TargetAdd('libp3tinydisplay.dll', opts=['CARBON', 'AGL', 'COCOA']) - elif GetTarget() == 'windows': + if GetTarget() == 'windows': TargetAdd('libp3tinydisplay.dll', input='libp3windisplay.dll') TargetAdd('libp3tinydisplay.dll', opts=['WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM']) - else: + elif GetTarget() != 'darwin': TargetAdd('libp3tinydisplay.dll', input='p3x11display_composite1.obj') TargetAdd('libp3tinydisplay.dll', opts=['X11']) TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite1.obj') @@ -5281,11 +4835,11 @@ if (PkgSkip("DIRECT")==0): if (PkgSkip("DIRECT")==0): OPTS=['DIR:direct/src/dcparser', 'BUILDING:DIRECT_DCPARSER', 'WITHINPANDA', 'BISONPREFIX_dcyy'] CreateFile(GetOutputDir()+"/include/dcParser.h") - PyTargetAdd('p3dcparser_dcParser.obj', opts=OPTS, input='dcParser.yxx') + TargetAdd('p3dcparser_dcParser.obj', opts=OPTS, input='dcParser.yxx') #TargetAdd('dcParser.h', input='p3dcparser_dcParser.obj', opts=['DEPENDENCYONLY']) - PyTargetAdd('p3dcparser_dcLexer.obj', opts=OPTS, input='dcLexer.lxx') - PyTargetAdd('p3dcparser_composite1.obj', opts=OPTS, input='p3dcparser_composite1.cxx') - PyTargetAdd('p3dcparser_composite2.obj', opts=OPTS, input='p3dcparser_composite2.cxx') + TargetAdd('p3dcparser_dcLexer.obj', opts=OPTS, input='dcLexer.lxx') + TargetAdd('p3dcparser_composite1.obj', opts=OPTS, input='p3dcparser_composite1.cxx') + TargetAdd('p3dcparser_composite2.obj', opts=OPTS, input='p3dcparser_composite2.cxx') OPTS=['DIR:direct/src/dcparser', 'WITHINPANDA'] IGATEFILES=GetDirectoryContents('direct/src/dcparser', ["*.h", "*_composite*.cxx"]) @@ -5293,6 +4847,7 @@ if (PkgSkip("DIRECT")==0): if "dcmsgtypes.h" in IGATEFILES: IGATEFILES.remove('dcmsgtypes.h') TargetAdd('libp3dcparser.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3dcparser.in', opts=['IMOD:panda3d.direct', 'ILIB:libp3dcparser', 'SRCDIR:direct/src/dcparser']) + PyTargetAdd('p3dcparser_ext_composite.obj', opts=OPTS, input='p3dcparser_ext_composite.cxx') # # DIRECTORY: direct/src/deadrec/ @@ -5314,13 +4869,13 @@ if (PkgSkip("DIRECT")==0): if (PkgSkip("DIRECT")==0): OPTS=['DIR:direct/src/distributed', 'DIR:direct/src/dcparser', 'WITHINPANDA', 'BUILDING:DIRECT', 'OPENSSL'] TargetAdd('p3distributed_config_distributed.obj', opts=OPTS, input='config_distributed.cxx') - PyTargetAdd('p3distributed_cConnectionRepository.obj', opts=OPTS, input='cConnectionRepository.cxx') - PyTargetAdd('p3distributed_cDistributedSmoothNodeBase.obj', opts=OPTS, input='cDistributedSmoothNodeBase.cxx') OPTS=['DIR:direct/src/distributed', 'WITHINPANDA', 'OPENSSL'] IGATEFILES=GetDirectoryContents('direct/src/distributed', ["*.h", "*.cxx"]) TargetAdd('libp3distributed.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3distributed.in', opts=['IMOD:panda3d.direct', 'ILIB:libp3distributed', 'SRCDIR:direct/src/distributed']) + PyTargetAdd('p3distributed_cConnectionRepository.obj', opts=OPTS, input='cConnectionRepository.cxx') + PyTargetAdd('p3distributed_cDistributedSmoothNodeBase.obj', opts=OPTS, input='cDistributedSmoothNodeBase.cxx') # # DIRECTORY: direct/src/interval/ @@ -5370,10 +4925,15 @@ if (PkgSkip("DIRECT")==0): if (PkgSkip("DIRECT")==0): TargetAdd('libp3direct.dll', input='p3directbase_directbase.obj') + TargetAdd('libp3direct.dll', input='p3dcparser_composite1.obj') + TargetAdd('libp3direct.dll', input='p3dcparser_composite2.obj') + TargetAdd('libp3direct.dll', input='p3dcparser_dcParser.obj') + TargetAdd('libp3direct.dll', input='p3dcparser_dcLexer.obj') TargetAdd('libp3direct.dll', input='p3showbase_showBase.obj') if GetTarget() == 'darwin': TargetAdd('libp3direct.dll', input='p3showbase_showBase_assist.obj') TargetAdd('libp3direct.dll', input='p3deadrec_composite1.obj') + TargetAdd('libp3direct.dll', input='p3distributed_config_distributed.obj') TargetAdd('libp3direct.dll', input='p3interval_composite1.obj') TargetAdd('libp3direct.dll', input='p3motiontrail_config_motiontrail.obj') TargetAdd('libp3direct.dll', input='p3motiontrail_cMotionTrail.obj') @@ -5398,11 +4958,7 @@ if (PkgSkip("DIRECT")==0): # These are part of direct.pyd, not libp3direct.dll, because they rely on # the Python libraries. If a C++ user needs these modules, we can move them # back and filter out the Python-specific code. - PyTargetAdd('direct.pyd', input='p3dcparser_composite1.obj') - PyTargetAdd('direct.pyd', input='p3dcparser_composite2.obj') - PyTargetAdd('direct.pyd', input='p3dcparser_dcParser.obj') - PyTargetAdd('direct.pyd', input='p3dcparser_dcLexer.obj') - PyTargetAdd('direct.pyd', input='p3distributed_config_distributed.obj') + PyTargetAdd('direct.pyd', input='p3dcparser_ext_composite.obj') PyTargetAdd('direct.pyd', input='p3distributed_cConnectionRepository.obj') PyTargetAdd('direct.pyd', input='p3distributed_cDistributedSmoothNodeBase.obj') @@ -5416,326 +4972,13 @@ if (PkgSkip("DIRECT")==0): # DIRECTORY: direct/src/dcparse/ # -if (PkgSkip("PYTHON")==0 and PkgSkip("DIRECT")==0 and not RTDIST and not RUNTIME): +if (PkgSkip("DIRECT")==0): OPTS=['DIR:direct/src/dcparse', 'DIR:direct/src/dcparser', 'WITHINPANDA', 'ADVAPI'] - PyTargetAdd('dcparse_dcparse.obj', opts=OPTS, input='dcparse.cxx') - PyTargetAdd('p3dcparse.exe', input='p3dcparser_composite1.obj') - PyTargetAdd('p3dcparse.exe', input='p3dcparser_composite2.obj') - PyTargetAdd('p3dcparse.exe', input='p3dcparser_dcParser.obj') - PyTargetAdd('p3dcparse.exe', input='p3dcparser_dcLexer.obj') - PyTargetAdd('p3dcparse.exe', input='dcparse_dcparse.obj') - PyTargetAdd('p3dcparse.exe', input='libp3direct.dll') - PyTargetAdd('p3dcparse.exe', input=COMMON_PANDA_LIBS) - PyTargetAdd('p3dcparse.exe', input='libp3pystub.lib') - PyTargetAdd('p3dcparse.exe', opts=['ADVAPI']) - -# -# DIRECTORY: direct/src/plugin/ -# - -if (RTDIST or RUNTIME): - # Explicitly define this as we don't include dtool_config.h here. - if GetTarget() not in ('windows', 'darwin'): - DefSymbol("RUNTIME", "HAVE_X11", "1") - - OPTS=['DIR:direct/src/plugin', 'BUILDING:P3D_PLUGIN', 'RUNTIME', 'OPENSSL'] - TargetAdd('plugin_common.obj', opts=OPTS, input='plugin_common_composite1.cxx') - - OPTS += ['ZLIB', 'MSIMG'] - TargetAdd('plugin_plugin.obj', opts=OPTS, input='p3d_plugin_composite1.cxx') - TargetAdd('plugin_mkdir_complete.obj', opts=OPTS, input='mkdir_complete.cxx') - TargetAdd('plugin_wstring_encode.obj', opts=OPTS, input='wstring_encode.cxx') - TargetAdd('plugin_parse_color.obj', opts=OPTS, input='parse_color.cxx') - TargetAdd('plugin_get_twirl_data.obj', opts=OPTS, input='get_twirl_data.cxx') - TargetAdd('plugin_find_root_dir.obj', opts=OPTS, input='find_root_dir.cxx') - if GetTarget() == 'darwin': - TargetAdd('plugin_find_root_dir_assist.obj', opts=OPTS, input='find_root_dir_assist.mm') - TargetAdd('plugin_binaryXml.obj', opts=OPTS, input='binaryXml.cxx') - TargetAdd('plugin_fileSpec.obj', opts=OPTS, input='fileSpec.cxx') - TargetAdd('plugin_handleStream.obj', opts=OPTS, input='handleStream.cxx') - TargetAdd('plugin_handleStreamBuf.obj', opts=OPTS, input='handleStreamBuf.cxx') - if (RTDIST): - for fname in ("p3d_plugin.dll", "libp3d_plugin_static.ilb"): - TargetAdd(fname, input='plugin_plugin.obj') - TargetAdd(fname, input='plugin_mkdir_complete.obj') - TargetAdd(fname, input='plugin_wstring_encode.obj') - TargetAdd(fname, input='plugin_parse_color.obj') - TargetAdd(fname, input='plugin_find_root_dir.obj') - if GetTarget() == 'darwin': - TargetAdd(fname, input='plugin_find_root_dir_assist.obj') - TargetAdd(fname, input='plugin_fileSpec.obj') - TargetAdd(fname, input='plugin_binaryXml.obj') - TargetAdd(fname, input='plugin_handleStream.obj') - TargetAdd(fname, input='plugin_handleStreamBuf.obj') - TargetAdd(fname, input='libp3tinyxml.ilb') - if GetTarget() == 'darwin': - TargetAdd(fname, input='libp3subprocbuffer.ilb') - TargetAdd(fname, opts=['OPENSSL', 'ZLIB', 'X11', 'ADVAPI', 'WINUSER', 'WINGDI', 'WINSHELL', 'WINCOMCTL', 'WINOLE', 'MSIMG']) - TargetAdd("libp3d_plugin_static.ilb", input='plugin_get_twirl_data.obj') - - if (PkgSkip("PYTHON")==0 and RTDIST): - # Freeze VFSImporter and its dependency modules into p3dpython. - # Mark panda3d.core as a dependency to make sure to build that first. - TargetAdd('p3dpython_frozen.obj', input='VFSImporter.py', opts=['DIR:direct/src/showbase', 'FREEZE_STARTUP']) - TargetAdd('p3dpython_frozen.obj', dep='core.pyd') - - OPTS += ['PYTHON'] - TargetAdd('p3dpython_p3dpython_composite1.obj', opts=OPTS, input='p3dpython_composite1.cxx') - TargetAdd('p3dpython_p3dPythonMain.obj', opts=OPTS, input='p3dPythonMain.cxx') - TargetAdd('p3dpython.exe', input='p3dpython_p3dpython_composite1.obj') - TargetAdd('p3dpython.exe', input='p3dpython_p3dPythonMain.obj') - TargetAdd('p3dpython.exe', input='p3dpython_frozen.obj') - TargetAdd('p3dpython.exe', input=COMMON_PANDA_LIBS) - TargetAdd('p3dpython.exe', input='libp3tinyxml.ilb') - TargetAdd('p3dpython.exe', input='libp3interrogatedb.dll') - TargetAdd('p3dpython.exe', opts=['PYTHON', 'WINUSER']) - - TargetAdd('libp3dpython.dll', input='p3dpython_p3dpython_composite1.obj') - TargetAdd('libp3dpython.dll', input='p3dpython_frozen.obj') - TargetAdd('libp3dpython.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libp3dpython.dll', input='libp3tinyxml.ilb') - TargetAdd('libp3dpython.dll', input='libp3interrogatedb.dll') - TargetAdd('libp3dpython.dll', opts=['PYTHON', 'WINUSER']) - - if GetTarget() == 'windows': - DefSymbol("NON_CONSOLE", "NON_CONSOLE", "") - OPTS.append("NON_CONSOLE") - TargetAdd('p3dpythonw_p3dpython_composite1.obj', opts=OPTS, input='p3dpython_composite1.cxx') - TargetAdd('p3dpythonw_p3dPythonMain.obj', opts=OPTS, input='p3dPythonMain.cxx') - TargetAdd('p3dpythonw.exe', input='p3dpythonw_p3dpython_composite1.obj') - TargetAdd('p3dpythonw.exe', input='p3dpythonw_p3dPythonMain.obj') - TargetAdd('p3dpythonw.exe', input='p3dpython_frozen.obj') - TargetAdd('p3dpythonw.exe', input=COMMON_PANDA_LIBS) - TargetAdd('p3dpythonw.exe', input='libp3tinyxml.ilb') - TargetAdd('p3dpythonw.exe', input='libp3interrogatedb.dll') - TargetAdd('p3dpythonw.exe', opts=['SUBSYSTEM:WINDOWS', 'WINUSER']) - - if (PkgSkip("OPENSSL")==0 and RTDIST and False): - OPTS=['DIR:direct/src/plugin', 'DIR:panda/src/express', 'OPENSSL'] - if GetTarget() == 'darwin': - OPTS += ['OPT:2'] - if (PkgSkip("FLTK")==0): - OPTS.append("FLTK") - TargetAdd('plugin_p3dCert.obj', opts=OPTS, input='p3dCert.cxx') - TargetAdd('plugin_p3dCert_strings.obj', opts=OPTS, input='p3dCert_strings.cxx') - TargetAdd('p3dcert.exe', input='plugin_mkdir_complete.obj') - TargetAdd('p3dcert.exe', input='plugin_wstring_encode.obj') - TargetAdd('p3dcert.exe', input='plugin_p3dCert.obj') - TargetAdd('p3dcert.exe', input='plugin_p3dCert_strings.obj') - OPTS=['SUBSYSTEM:WINDOWS', 'OPENSSL', 'FLTK', 'X11', 'WINCOMCTL', 'WINSOCK', 'WINGDI', 'WINUSER', 'ADVAPI', 'WINOLE', 'WINSHELL', 'SUBSYSTEM:WINDOWS'] - if GetTarget() == 'darwin': - OPTS += ['OPT:2'] - TargetAdd('p3dcert.exe', opts=OPTS) - elif (PkgSkip("WX")==0): - OPTS += ["WX", "RTTI"] - TargetAdd('plugin_p3dCert.obj', opts=OPTS, input='p3dCert_wx.cxx') - TargetAdd('p3dcert.exe', input='plugin_mkdir_complete.obj') - TargetAdd('p3dcert.exe', input='plugin_wstring_encode.obj') - TargetAdd('p3dcert.exe', input='plugin_p3dCert.obj') - OPTS=['SUBSYSTEM:WINDOWS', 'OPENSSL', 'WX', 'CARBON', 'WINOLE', 'WINOLEAUT', 'WINUSER', 'ADVAPI', 'WINSHELL', 'WINCOMCTL', 'WINGDI', 'WINCOMDLG'] - if GetTarget() == "darwin": - OPTS += ['GL', 'OPT:2'] - TargetAdd('p3dcert.exe', opts=OPTS) - -# -# DIRECTORY: direct/src/plugin_npapi/ -# - -if RUNTIME: - OPTS=['DIR:direct/src/plugin_npapi', 'RUNTIME', 'GTK2'] - if GetTarget() == 'windows': - nppanda3d_rc = {"name" : "Panda3D Game Engine Plug-in", - "version" : VERSION, - "description" : "Runs 3-D games and interactive applets", - "filename" : "nppanda3d.dll", - "mimetype" : "application/x-panda3d", - "extension" : "p3d", - "filedesc" : "Panda3D applet"} - TargetAdd('nppanda3d.res', opts=OPTS, winrc=nppanda3d_rc) - elif GetTarget() == 'darwin': - TargetAdd('nppanda3d.rsrc', opts=OPTS, input='nppanda3d.r') - - OPTS += ['GTK2'] - TargetAdd('plugin_npapi_nppanda3d_composite1.obj', opts=OPTS, input='nppanda3d_composite1.cxx') - - TargetAdd('nppanda3d.plugin', input='plugin_common.obj') - TargetAdd('nppanda3d.plugin', input='plugin_parse_color.obj') - TargetAdd('nppanda3d.plugin', input='plugin_get_twirl_data.obj') - TargetAdd('nppanda3d.plugin', input='plugin_wstring_encode.obj') - TargetAdd('nppanda3d.plugin', input='plugin_npapi_nppanda3d_composite1.obj') - if GetTarget() == 'windows': - TargetAdd('nppanda3d.plugin', input='nppanda3d.res') - TargetAdd('nppanda3d.plugin', input='nppanda3d.def', ipath=OPTS) - elif GetTarget() == 'darwin': - TargetAdd('nppanda3d.plugin', input='nppanda3d.rsrc') - TargetAdd('nppanda3d.plugin', input='nppanda3d.plist', ipath=OPTS) - TargetAdd('nppanda3d.plugin', input='plugin_find_root_dir_assist.obj') - TargetAdd('nppanda3d.plugin', input='libp3tinyxml.ilb') - TargetAdd('nppanda3d.plugin', opts=['OPENSSL', 'WINGDI', 'WINUSER', 'WINSHELL', 'WINOLE', 'CARBON']) - -# -# DIRECTORY: direct/src/plugin_activex/ -# - -if (RUNTIME and GetTarget() == 'windows' and PkgSkip("MFC")==0): - OPTS=['DIR:direct/src/plugin_activex', 'RUNTIME', 'ACTIVEX', 'MFC'] - DefSymbol('ACTIVEX', '_USRDLL', '') - DefSymbol('ACTIVEX', '_WINDLL', '') - DefSymbol('ACTIVEX', '_AFXDLL', '') - DefSymbol('ACTIVEX', '_MBCS', '') - TargetAdd('P3DActiveX.tlb', opts=OPTS, input='P3DActiveX.idl') - TargetAdd('P3DActiveX.res', opts=OPTS, input='P3DActiveX.rc') - - TargetAdd('plugin_activex_p3dactivex_composite1.obj', opts=OPTS, input='p3dactivex_composite1.cxx') - - TargetAdd('p3dactivex.ocx', input='plugin_common.obj') - TargetAdd('p3dactivex.ocx', input='plugin_parse_color.obj') - TargetAdd('p3dactivex.ocx', input='plugin_get_twirl_data.obj') - TargetAdd('p3dactivex.ocx', input='plugin_wstring_encode.obj') - TargetAdd('p3dactivex.ocx', input='plugin_activex_p3dactivex_composite1.obj') - TargetAdd('p3dactivex.ocx', input='P3DActiveX.res') - TargetAdd('p3dactivex.ocx', input='P3DActiveX.def', ipath=OPTS) - TargetAdd('p3dactivex.ocx', input='libp3tinyxml.ilb') - TargetAdd('p3dactivex.ocx', opts=['MFC', 'WINSOCK2', 'OPENSSL', 'WINGDI', 'WINUSER']) - -# -# DIRECTORY: direct/src/plugin_standalone/ -# - -if (RUNTIME): - OPTS=['DIR:direct/src/plugin_standalone', 'RUNTIME', 'OPENSSL'] - TargetAdd('plugin_standalone_panda3d.obj', opts=OPTS, input='panda3d.cxx') - TargetAdd('plugin_standalone_panda3dBase.obj', opts=OPTS, input='panda3dBase.cxx') - - if GetTarget() == 'windows': - panda3d_rc = {"name" : "Panda3D Game Engine Plug-in", - "version" : VERSION, - "description" : "Runs 3-D games and interactive applets", - "filename" : "panda3d.exe", - "mimetype" : "application/x-panda3d", - "extension" : "p3d", - "filedesc" : "Panda3D applet", - "icon" : "panda3d.ico"} - TargetAdd('panda3d.res', opts=OPTS, winrc=panda3d_rc) - - TargetAdd('plugin_standalone_panda3dMain.obj', opts=OPTS, input='panda3dMain.cxx') - TargetAdd('panda3d.exe', input='plugin_standalone_panda3d.obj') - TargetAdd('panda3d.exe', input='plugin_standalone_panda3dMain.obj') - TargetAdd('panda3d.exe', input='plugin_standalone_panda3dBase.obj') - TargetAdd('panda3d.exe', input='plugin_common.obj') - TargetAdd('panda3d.exe', input='plugin_wstring_encode.obj') - if GetTarget() == 'darwin': - TargetAdd('panda3d.exe', input='plugin_find_root_dir_assist.obj') - elif GetTarget() == 'windows': - TargetAdd('panda3d.exe', input='panda3d.res') - TargetAdd('panda3d.exe', input='libpandaexpress.dll') - TargetAdd('panda3d.exe', input='libp3dtoolconfig.dll') - TargetAdd('panda3d.exe', input='libp3dtool.dll') - #TargetAdd('panda3d.exe', input='libp3pystub.lib') - TargetAdd('panda3d.exe', input='libp3tinyxml.ilb') - TargetAdd('panda3d.exe', opts=['NOICON', 'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER', 'WINSHELL', 'ADVAPI', 'WINSOCK2', 'WINOLE', 'CARBON']) - - if (GetTarget() == 'darwin'): - TargetAdd('plugin_standalone_panda3dMac.obj', opts=OPTS, input='panda3dMac.cxx') - TargetAdd('Panda3D.app', input='plugin_standalone_panda3d.obj') - TargetAdd('Panda3D.app', input='plugin_standalone_panda3dMac.obj') - TargetAdd('Panda3D.app', input='plugin_standalone_panda3dBase.obj') - TargetAdd('Panda3D.app', input='plugin_common.obj') - TargetAdd('Panda3D.app', input='plugin_find_root_dir_assist.obj') - TargetAdd('Panda3D.app', input='libpandaexpress.dll') - TargetAdd('Panda3D.app', input='libp3dtoolconfig.dll') - TargetAdd('Panda3D.app', input='libp3dtool.dll') - #TargetAdd('Panda3D.app', input='libp3pystub.lib') - TargetAdd('Panda3D.app', input='libp3tinyxml.ilb') - TargetAdd('Panda3D.app', input='panda3d_mac.plist', ipath=OPTS) - TargetAdd('Panda3D.app', input='models/plugin_images/panda3d.icns') - TargetAdd('Panda3D.app', opts=['OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER', 'WINSHELL', 'ADVAPI', 'WINSOCK2', 'WINOLE', 'CARBON']) - elif (GetTarget() == 'windows'): - TargetAdd('plugin_standalone_panda3dWinMain.obj', opts=OPTS, input='panda3dWinMain.cxx') - TargetAdd('panda3dw.exe', input='plugin_standalone_panda3d.obj') - TargetAdd('panda3dw.exe', input='plugin_standalone_panda3dWinMain.obj') - TargetAdd('panda3dw.exe', input='plugin_standalone_panda3dBase.obj') - TargetAdd('panda3dw.exe', input='plugin_wstring_encode.obj') - TargetAdd('panda3dw.exe', input='plugin_common.obj') - TargetAdd('panda3dw.exe', input='libpandaexpress.dll') - TargetAdd('panda3dw.exe', input='libp3dtoolconfig.dll') - TargetAdd('panda3dw.exe', input='libp3dtool.dll') - #TargetAdd('panda3dw.exe', input='libp3pystub.lib') - TargetAdd('panda3dw.exe', input='libp3tinyxml.ilb') - TargetAdd('panda3dw.exe', opts=['SUBSYSTEM:WINDOWS', 'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER', 'WINSHELL', 'ADVAPI', 'WINSOCK2', 'WINOLE', 'CARBON']) - -if (RTDIST): - OPTS=['BUILDING:P3D_PLUGIN', 'DIR:direct/src/plugin_standalone', 'DIR:direct/src/plugin', 'DIR:dtool/src/dtoolbase', 'DIR:dtool/src/dtoolutil', 'DIR:dtool/src/pystub', 'DIR:dtool/src/prc', 'DIR:dtool/src/dconfig', 'DIR:panda/src/express', 'DIR:panda/src/downloader', 'RUNTIME', 'P3DEMBED', 'OPENSSL', 'ZLIB'] - # This is arguably a big fat ugly hack, but doing it otherwise would complicate the build process considerably. - DefSymbol("P3DEMBED", "LINK_ALL_STATIC", "") - TargetAdd('plugin_standalone_panda3dBase.obj', opts=OPTS, input='panda3dBase.cxx') - TargetAdd('plugin_standalone_p3dEmbedMain.obj', opts=OPTS, input='p3dEmbedMain.cxx') - TargetAdd('plugin_standalone_p3dEmbed.obj', opts=OPTS, input='p3dEmbed.cxx') - #TargetAdd('plugin_standalone_pystub.obj', opts=OPTS, input='pystub.cxx') - TargetAdd('plugin_standalone_dtoolbase_composite1.obj', opts=OPTS, input='p3dtoolbase_composite1.cxx') - TargetAdd('plugin_standalone_dtoolbase_composite2.obj', opts=OPTS, input='p3dtoolbase_composite2.cxx') - TargetAdd('plugin_standalone_lookup3.obj', opts=OPTS, input='lookup3.c') - TargetAdd('plugin_standalone_indent.obj', opts=OPTS, input='indent.cxx') - TargetAdd('plugin_standalone_dtoolutil_composite1.obj', opts=OPTS, input='p3dtoolutil_composite1.cxx') - TargetAdd('plugin_standalone_dtoolutil_composite2.obj', opts=OPTS, input='p3dtoolutil_composite2.cxx') - if (GetTarget() == 'darwin'): - TargetAdd('plugin_standalone_dtoolutil_filename_assist.obj', opts=OPTS, input='filename_assist.mm') - TargetAdd('plugin_standalone_prc_composite1.obj', opts=OPTS, input='p3prc_composite1.cxx') - TargetAdd('plugin_standalone_prc_composite2.obj', opts=OPTS, input='p3prc_composite2.cxx') - TargetAdd('plugin_standalone_express_composite1.obj', opts=OPTS, input='p3express_composite1.cxx') - TargetAdd('plugin_standalone_express_composite2.obj', opts=OPTS, input='p3express_composite2.cxx') - TargetAdd('plugin_standalone_downloader_composite1.obj', opts=OPTS, input='p3downloader_composite1.cxx') - TargetAdd('plugin_standalone_downloader_composite2.obj', opts=OPTS, input='p3downloader_composite2.cxx') - TargetAdd('p3dembed.exe', input='plugin_standalone_panda3dBase.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_p3dEmbedMain.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_p3dEmbed.obj') - #TargetAdd('p3dembed.exe', input='plugin_standalone_pystub.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_dtoolbase_composite1.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_dtoolbase_composite2.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_lookup3.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_indent.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_dtoolutil_composite1.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_dtoolutil_composite2.obj') - if GetTarget() == 'darwin': - TargetAdd('p3dembed.exe', input='plugin_standalone_dtoolutil_filename_assist.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_prc_composite1.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_prc_composite2.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_express_composite1.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_express_composite2.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_downloader_composite1.obj') - TargetAdd('p3dembed.exe', input='plugin_standalone_downloader_composite2.obj') - TargetAdd('p3dembed.exe', input='plugin_common.obj') - if GetTarget() == 'darwin': - TargetAdd('p3dembed.exe', input='plugin_find_root_dir_assist.obj') - TargetAdd('p3dembed.exe', input='libp3subprocbuffer.ilb') - TargetAdd('p3dembed.exe', input='libp3tinyxml.ilb') - TargetAdd('p3dembed.exe', input='libp3d_plugin_static.ilb') - TargetAdd('p3dembed.exe', opts=['NOICON', 'WINGDI', 'WINSOCK2', 'ZLIB', 'WINUSER', 'OPENSSL', 'WINOLE', 'CARBON', 'MSIMG', 'WINCOMCTL', 'ADVAPI', 'WINSHELL', 'X11']) - - if GetTarget() == 'windows': - OPTS.append("P3DEMBEDW") - DefSymbol("P3DEMBEDW", "P3DEMBEDW", "") - TargetAdd('plugin_standalone_p3dEmbedWinMain.obj', opts=OPTS, input='p3dEmbedMain.cxx') - TargetAdd('p3dembedw.exe', input='plugin_standalone_panda3dBase.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_p3dEmbedWinMain.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_p3dEmbed.obj') - #TargetAdd('p3dembedw.exe', input='plugin_standalone_pystub.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_dtoolbase_composite1.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_dtoolbase_composite2.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_lookup3.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_indent.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_dtoolutil_composite1.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_dtoolutil_composite2.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_prc_composite1.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_prc_composite2.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_express_composite1.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_express_composite2.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_downloader_composite1.obj') - TargetAdd('p3dembedw.exe', input='plugin_standalone_downloader_composite2.obj') - TargetAdd('p3dembedw.exe', input='plugin_common.obj') - TargetAdd('p3dembedw.exe', input='libp3tinyxml.ilb') - TargetAdd('p3dembedw.exe', input='libp3d_plugin_static.ilb') - TargetAdd('p3dembedw.exe', opts=['SUBSYSTEM:WINDOWS', 'NOICON', 'WINGDI', 'WINSOCK2', 'ZLIB', 'WINUSER', 'OPENSSL', 'WINOLE', 'MSIMG', 'WINCOMCTL', 'ADVAPI', 'WINSHELL']) + TargetAdd('dcparse_dcparse.obj', opts=OPTS, input='dcparse.cxx') + TargetAdd('p3dcparse.exe', input='dcparse_dcparse.obj') + TargetAdd('p3dcparse.exe', input='libp3direct.dll') + TargetAdd('p3dcparse.exe', input=COMMON_PANDA_LIBS) + TargetAdd('p3dcparse.exe', opts=['ADVAPI']) # # DIRECTORY: pandatool/src/pandatoolbase/ @@ -5799,15 +5042,6 @@ if not PkgSkip("PANDATOOL"): TargetAdd('egg2bam.exe', input=COMMON_EGG2X_LIBS) TargetAdd('egg2bam.exe', opts=['ADVAPI', 'FFTW']) -# -# DIRECTORY: pandatool/src/cvscopy/ -# - -if not PkgSkip("PANDATOOL"): - OPTS=['DIR:pandatool/src/cvscopy'] - TargetAdd('p3cvscopy_composite1.obj', opts=OPTS, input='p3cvscopy_composite1.cxx') - TargetAdd('libp3cvscopy.lib', input='p3cvscopy_composite1.obj') - # # DIRECTORY: pandatool/src/daeegg/ # @@ -5825,7 +5059,7 @@ if not PkgSkip("PANDATOOL") and not PkgSkip("ASSIMP"): TargetAdd('p3assimp_composite1.obj', opts=OPTS, input='p3assimp_composite1.cxx') TargetAdd('libp3assimp.dll', input='p3assimp_composite1.obj') TargetAdd('libp3assimp.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libp3assimp.dll', opts=OPTS+['ZLIB']) + TargetAdd('libp3assimp.dll', opts=OPTS+['ZLIB', 'ADVAPI']) # # DIRECTORY: pandatool/src/daeprogs/ @@ -6072,7 +5306,7 @@ if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): # if not PkgSkip("PANDATOOL"): - OPTS=['DIR:pandatool/src/fltprogs', 'DIR:pandatool/src/flt', 'DIR:pandatool/src/cvscopy'] + OPTS=['DIR:pandatool/src/fltprogs', 'DIR:pandatool/src/flt'] TargetAdd('flt-info_fltInfo.obj', opts=OPTS, input='fltInfo.cxx') TargetAdd('flt-info.exe', input='flt-info_fltInfo.obj') TargetAdd('flt-info.exe', input='libp3flt.lib') @@ -6089,15 +5323,6 @@ if not PkgSkip("PANDATOOL"): TargetAdd('flt-trans.exe', input=COMMON_PANDA_LIBS) TargetAdd('flt-trans.exe', opts=['ADVAPI']) - TargetAdd('fltcopy_fltCopy.obj', opts=OPTS, input='fltCopy.cxx') - TargetAdd('fltcopy.exe', input='fltcopy_fltCopy.obj') - TargetAdd('fltcopy.exe', input='libp3cvscopy.lib') - TargetAdd('fltcopy.exe', input='libp3flt.lib') - TargetAdd('fltcopy.exe', input='libp3progbase.lib') - TargetAdd('fltcopy.exe', input='libp3pandatoolbase.lib') - TargetAdd('fltcopy.exe', input=COMMON_PANDA_LIBS) - TargetAdd('fltcopy.exe', opts=['ADVAPI']) - if not PkgSkip("EGG"): TargetAdd('egg2flt_eggToFlt.obj', opts=OPTS, input='eggToFlt.cxx') TargetAdd('egg2flt.exe', input='egg2flt_eggToFlt.obj') @@ -6482,26 +5707,17 @@ if not PkgSkip("PANDATOOL"): for VER in MAYAVERSIONS: VNUM = VER[4:] if not PkgSkip(VER) and not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): - if GetTarget() == 'darwin' and int(VNUM) >= 2012: - ARCH_OPTS = ['NOARCH:PPC', 'NOARCH:I386'] - if len(OSX_ARCHS) != 0 and 'x86_64' not in OSX_ARCHS: - continue - elif GetTarget() == 'darwin' and int(VNUM) >= 2009: - ARCH_OPTS = ['NOARCH:PPC'] - elif GetTarget() == 'darwin': - ARCH_OPTS = ['NOARCH:X86_64'] - else: - ARCH_OPTS = [] + if GetTarget() == 'darwin' and int(VNUM) < 2009: + # No x86_64 support. + continue - OPTS=['DIR:pandatool/src/mayaprogs', 'DIR:pandatool/src/maya', 'DIR:pandatool/src/mayaegg', 'DIR:pandatool/src/cvscopy', 'BUILDING:MISC', VER] + ARCH_OPTS + OPTS=['DIR:pandatool/src/mayaprogs', 'DIR:pandatool/src/maya', 'DIR:pandatool/src/mayaegg', 'BUILDING:MISC', VER] TargetAdd('mayaeggimport'+VNUM+'_mayaeggimport.obj', opts=OPTS, input='mayaEggImport.cxx') TargetAdd('mayaeggimport'+VNUM+'.mll', input='mayaegg'+VNUM+'_loader.obj') TargetAdd('mayaeggimport'+VNUM+'.mll', input='mayaeggimport'+VNUM+'_mayaeggimport.obj') TargetAdd('mayaeggimport'+VNUM+'.mll', input='libpandaegg.dll') TargetAdd('mayaeggimport'+VNUM+'.mll', input=COMMON_PANDA_LIBS) - #if GetTarget() == 'windows': - # TargetAdd('mayaeggimport'+VNUM+'.mll', input='libp3pystub.lib') - TargetAdd('mayaeggimport'+VNUM+'.mll', opts=['ADVAPI', VER]+ARCH_OPTS) + TargetAdd('mayaeggimport'+VNUM+'.mll', opts=['ADVAPI', VER]) TargetAdd('mayaloader'+VNUM+'_config_mayaloader.obj', opts=OPTS, input='config_mayaloader.cxx') TargetAdd('libp3mayaloader'+VNUM+'.dll', input='mayaloader'+VNUM+'_config_mayaloader.obj') @@ -6525,7 +5741,7 @@ for VER in MAYAVERSIONS: TargetAdd('libp3mayaloader'+VNUM+'.dll', input='libp3pandatoolbase.lib') TargetAdd('libp3mayaloader'+VNUM+'.dll', input='libpandaegg.dll') TargetAdd('libp3mayaloader'+VNUM+'.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libp3mayaloader'+VNUM+'.dll', opts=['ADVAPI', VER]+ARCH_OPTS) + TargetAdd('libp3mayaloader'+VNUM+'.dll', opts=['ADVAPI', VER]) TargetAdd('mayapview'+VNUM+'_mayaPview.obj', opts=OPTS, input='mayaPview.cxx') TargetAdd('libmayapview'+VNUM+'.mll', input='mayapview'+VNUM+'_mayaPview.obj') @@ -6536,7 +5752,7 @@ for VER in MAYAVERSIONS: TargetAdd('libmayapview'+VNUM+'.mll', input=COMMON_EGG2X_LIBS) else: TargetAdd('libmayapview'+VNUM+'.mll', input=COMMON_EGG2X_LIBS) - TargetAdd('libmayapview'+VNUM+'.mll', opts=['ADVAPI', VER]+ARCH_OPTS) + TargetAdd('libmayapview'+VNUM+'.mll', opts=['ADVAPI', VER]) TargetAdd('maya2egg'+VNUM+'_mayaToEgg.obj', opts=OPTS, input='mayaToEgg.cxx') TargetAdd('maya2egg'+VNUM+'_bin.exe', input='maya2egg'+VNUM+'_mayaToEgg.obj') @@ -6546,7 +5762,7 @@ for VER in MAYAVERSIONS: TargetAdd('maya2egg'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS) else: TargetAdd('maya2egg'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS) - TargetAdd('maya2egg'+VNUM+'_bin.exe', opts=['ADVAPI', VER]+ARCH_OPTS) + TargetAdd('maya2egg'+VNUM+'_bin.exe', opts=['ADVAPI', VER]) TargetAdd('egg2maya'+VNUM+'_eggToMaya.obj', opts=OPTS, input='eggToMaya.cxx') TargetAdd('egg2maya'+VNUM+'_bin.exe', input='egg2maya'+VNUM+'_eggToMaya.obj') @@ -6556,43 +5772,28 @@ for VER in MAYAVERSIONS: TargetAdd('egg2maya'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS) else: TargetAdd('egg2maya'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS) - TargetAdd('egg2maya'+VNUM+'_bin.exe', opts=['ADVAPI', VER]+ARCH_OPTS) - - TargetAdd('mayacopy'+VNUM+'_mayaCopy.obj', opts=OPTS, input='mayaCopy.cxx') - TargetAdd('mayacopy'+VNUM+'_bin.exe', input='mayacopy'+VNUM+'_mayaCopy.obj') - TargetAdd('mayacopy'+VNUM+'_bin.exe', input='libp3cvscopy.lib') - TargetAdd('mayacopy'+VNUM+'_bin.exe', input='libmaya'+VNUM+'.lib') - if GetTarget() == 'windows': - TargetAdd('mayacopy'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS) - else: - TargetAdd('mayacopy'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS) - TargetAdd('mayacopy'+VNUM+'_bin.exe', opts=['ADVAPI', VER]+ARCH_OPTS) + TargetAdd('egg2maya'+VNUM+'_bin.exe', opts=['ADVAPI', VER]) TargetAdd('mayasavepview'+VNUM+'_mayaSavePview.obj', opts=OPTS, input='mayaSavePview.cxx') TargetAdd('libmayasavepview'+VNUM+'.mll', input='mayasavepview'+VNUM+'_mayaSavePview.obj') - TargetAdd('libmayasavepview'+VNUM+'.mll', opts=['ADVAPI', VER]+ARCH_OPTS) + TargetAdd('libmayasavepview'+VNUM+'.mll', opts=['ADVAPI', VER]) TargetAdd('mayapath'+VNUM+'.obj', opts=OPTS, input='mayapath.cxx') TargetAdd('maya2egg'+VNUM+'.exe', input='mayapath'+VNUM+'.obj') TargetAdd('maya2egg'+VNUM+'.exe', input='libpandaexpress.dll') TargetAdd('maya2egg'+VNUM+'.exe', input=COMMON_DTOOL_LIBS) - TargetAdd('maya2egg'+VNUM+'.exe', opts=['ADVAPI']+ARCH_OPTS) + TargetAdd('maya2egg'+VNUM+'.exe', opts=['ADVAPI']) TargetAdd('egg2maya'+VNUM+'.exe', input='mayapath'+VNUM+'.obj') TargetAdd('egg2maya'+VNUM+'.exe', input='libpandaexpress.dll') TargetAdd('egg2maya'+VNUM+'.exe', input=COMMON_DTOOL_LIBS) - TargetAdd('egg2maya'+VNUM+'.exe', opts=['ADVAPI']+ARCH_OPTS) - - TargetAdd('mayacopy'+VNUM+'.exe', input='mayapath'+VNUM+'.obj') - TargetAdd('mayacopy'+VNUM+'.exe', input='libpandaexpress.dll') - TargetAdd('mayacopy'+VNUM+'.exe', input=COMMON_DTOOL_LIBS) - TargetAdd('mayacopy'+VNUM+'.exe', opts=['ADVAPI']+ARCH_OPTS) + TargetAdd('egg2maya'+VNUM+'.exe', opts=['ADVAPI']) # # DIRECTORY: contrib/src/ai/ # -if (PkgSkip("CONTRIB")==0 and not RUNTIME): +if (PkgSkip("CONTRIB")==0 ): OPTS=['DIR:contrib/src/ai', 'BUILDING:PANDAAI'] TargetAdd('p3ai_composite1.obj', opts=OPTS, input='p3ai_composite1.cxx') TargetAdd('libpandaai.dll', input='p3ai_composite1.obj') @@ -6616,7 +5817,7 @@ if (PkgSkip("CONTRIB")==0 and not RUNTIME): # # DIRECTORY: contrib/src/rplight/ # -if not PkgSkip("CONTRIB") and not PkgSkip("PYTHON") and not RUNTIME: +if not PkgSkip("CONTRIB") and not PkgSkip("PYTHON"): OPTS=['DIR:contrib/src/rplight', 'BUILDING:RPLIGHT'] TargetAdd('p3rplight_composite1.obj', opts=OPTS, input='p3rplight_composite1.cxx') @@ -6645,7 +5846,7 @@ if PkgSkip("PYTHON") == 0: if GetTarget() == 'linux' or GetTarget() == 'freebsd': # Setup rpath so libs can be found in the same directory as the deployed game - LibName('DEPLOYSTUB', "-Wl,-rpath,\$ORIGIN") + LibName('DEPLOYSTUB', "-Wl,-rpath,\\$ORIGIN") LibName('DEPLOYSTUB', "-Wl,-z,origin") LibName('DEPLOYSTUB', "-rdynamic") PyTargetAdd('deploy-stub.exe', input='deploy-stub.obj') @@ -6668,7 +5869,7 @@ if PkgSkip("PYTHON") == 0: # Generate the models directory and samples directory # -if not PkgSkip("DIRECT") and not RUNTIME and not PkgSkip("EGG"): +if not PkgSkip("DIRECT") and not PkgSkip("EGG"): model_extensions = ["*.egg"] # Check if we have access to an flt2egg utility, either self-compiled or on the system. @@ -6676,27 +5877,27 @@ if not PkgSkip("DIRECT") and not RUNTIME and not PkgSkip("EGG"): model_extensions.append("*.flt") for model in GetDirectoryContents("dmodels/src/misc", model_extensions): - if (PkgSkip("ZLIB")==0 and PkgSkip("DEPLOYTOOLS")==0 and not RTDIST): + if (PkgSkip("ZLIB")==0 and PkgSkip("DEPLOYTOOLS")==0): newname = model[:-4] + ".egg.pz" else: newname = model[:-4] + ".egg" TargetAdd(GetOutputDir()+"/models/misc/"+newname, input="dmodels/src/misc/"+model) for model in GetDirectoryContents("dmodels/src/gui", model_extensions): - if (PkgSkip("ZLIB")==0 and PkgSkip("DEPLOYTOOLS")==0 and not RTDIST): + if (PkgSkip("ZLIB")==0 and PkgSkip("DEPLOYTOOLS")==0): newname = model[:-4] + ".egg.pz" else: newname = model[:-4] + ".egg" TargetAdd(GetOutputDir()+"/models/gui/"+newname, input="dmodels/src/gui/"+model) for model in GetDirectoryContents("models", model_extensions): - if (PkgSkip("ZLIB")==0 and PkgSkip("DEPLOYTOOLS")==0 and not RTDIST): + if (PkgSkip("ZLIB")==0 and PkgSkip("DEPLOYTOOLS")==0): newname = model[:-4] + ".egg.pz" else: newname = model[:-4] + ".egg" TargetAdd(GetOutputDir()+"/models/"+newname, input="models/"+model) -if not PkgSkip("DIRECT") and not RUNTIME: +if not PkgSkip("DIRECT"): CopyAllFiles(GetOutputDir()+"/models/audio/sfx/", "dmodels/src/audio/sfx/", ".wav") CopyAllFiles(GetOutputDir()+"/models/icons/", "dmodels/src/icons/", ".gif") @@ -6710,28 +5911,6 @@ if not PkgSkip("DIRECT") and not RUNTIME: CopyAllFiles(GetOutputDir()+"/models/maps/", "dmodels/src/maps/", ".rgb") CopyAllFiles(GetOutputDir()+"/models/maps/", "dmodels/src/maps/", ".rgba") -# -# Build the rtdist. -# - -if (RTDIST): - OPTS=['DIR:direct/src/p3d'] - TargetAdd('_panda3d', opts=OPTS, input='panda3d.pdef') - TargetAdd('_coreapi', opts=OPTS, input='coreapi.pdef') - TargetAdd('_thirdparty', opts=OPTS, input='thirdparty.pdef') - -# -# If we have a host URL and distributor, we can make .p3d deployment tools. -# - -if not PkgSkip("DIRECT") and not PkgSkip("DEPLOYTOOLS") and not RUNTIME and not RTDIST and HOST_URL and DISTRIBUTOR: - OPTS=['DIR:direct/src/p3d'] - - TargetAdd('packp3d.p3d', opts=OPTS, input='panda3d.pdef') - TargetAdd('pdeploy.p3d', opts=OPTS, input='panda3d.pdef') - TargetAdd('pmerge.p3d', opts=OPTS, input='panda3d.pdef') - TargetAdd('ppackage.p3d', opts=OPTS, input='panda3d.pdef') - TargetAdd('ppatcher.p3d', opts=OPTS, input='panda3d.pdef') ########################################################################################## # @@ -6903,7 +6082,7 @@ if INSTALLER: MakeInstaller(version=VERSION, outputdir=GetOutputDir(), optimize=GetOptimize(), compressor=COMPRESSOR, debversion=DEBVERSION, rpmrelease=RPMRELEASE, - runtime=RUNTIME, python_versions=python_versions) + python_versions=python_versions) if WHEEL: ProgressOutput(100.0, "Building wheel") diff --git a/makepanda/makepanda.vcproj b/makepanda/makepanda.vcproj index 03324fb60e..06b9cd8f07 100644 --- a/makepanda/makepanda.vcproj +++ b/makepanda/makepanda.vcproj @@ -86,7 +86,6 @@ - @@ -361,10 +360,6 @@ - - - - @@ -1619,7 +1614,6 @@ - @@ -1627,13 +1621,11 @@ - - @@ -1675,12 +1667,10 @@ - - @@ -2088,16 +2078,10 @@ - - - - - - @@ -2152,7 +2136,6 @@ - @@ -2179,7 +2162,6 @@ - @@ -2211,7 +2193,6 @@ - @@ -2220,10 +2201,7 @@ - - - @@ -2267,7 +2245,6 @@ - @@ -2294,7 +2271,6 @@ - @@ -2302,7 +2278,6 @@ - @@ -3506,20 +3481,6 @@ - - - - - - - - - - - - - - @@ -4591,17 +4552,6 @@ - - - - - - - - - - - @@ -4769,11 +4719,9 @@ - - @@ -4939,11 +4887,9 @@ - - @@ -5356,27 +5302,6 @@ - - - - - - - - - - - - - - - - - - - - - @@ -5455,133 +5380,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -5592,32 +5390,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -5641,22 +5413,6 @@ - - - - - - - - - - - - - - - - diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 8790538395..506e232715 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -78,6 +78,7 @@ MSVCVERSIONINFO = { (12,0): {"vsversion":(12,0), "vsname":"Visual Studio 2013"}, (14,0): {"vsversion":(14,0), "vsname":"Visual Studio 2015"}, (14,1): {"vsversion":(15,0), "vsname":"Visual Studio 2017"}, + (14,2): {"vsversion":(16,0), "vsname":"Visual Studio 2019"}, } ######################################################################## @@ -104,6 +105,7 @@ MAYAVERSIONINFO = [("MAYA6", "6.0"), ("MAYA20165","2016.5"), ("MAYA2017","2017"), ("MAYA2018","2018"), + ("MAYA2019","2019"), ] MAXVERSIONINFO = [("MAX6", "SOFTWARE\\Autodesk\\3DSMAX\\6.0", "installdir", "maxsdk\\cssdk\\include"), @@ -564,11 +566,12 @@ def LocateBinary(binary): p = os.environ["PATH"] pathList = p.split(os.pathsep) + suffixes = [''] if GetHost() == 'windows': - if not binary.endswith('.exe'): + if not binary.lower().endswith('.exe') and not binary.lower().endswith('.bat'): # Append .exe if necessary - binary += '.exe' + suffixes = ['.exe', '.bat'] # On Windows the current directory is always implicitly # searched before anything else on PATH. @@ -576,8 +579,9 @@ def LocateBinary(binary): for path in pathList: binpath = os.path.join(os.path.expanduser(path), binary) - if os.access(binpath, os.X_OK): - return os.path.abspath(os.path.realpath(binpath)) + for suffix in suffixes: + if os.access(binpath + suffix, os.X_OK): + return os.path.abspath(os.path.realpath(binpath + suffix)) return None ######################################################################## @@ -586,20 +590,31 @@ def LocateBinary(binary): ## ######################################################################## -def oscmd(cmd, ignoreError = False): +def oscmd(cmd, ignoreError = False, cwd=None): if VERBOSE: print(GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor()) sys.stdout.flush() if sys.platform == "win32": - exe = cmd.split()[0] + if cmd[0] == '"': + exe = cmd[1 : cmd.index('"', 1)] + else: + exe = cmd.split()[0] exe_path = LocateBinary(exe) if exe_path is None: exit("Cannot find "+exe+" on search path") + + if cwd is not None: + pwd = os.getcwd() + os.chdir(cwd) + res = os.spawnl(os.P_WAIT, exe_path, cmd) + + if cwd is not None: + os.chdir(pwd) else: cmd = cmd.replace(';', '\\;') - res = subprocess.call(cmd, shell=True) + res = subprocess.call(cmd, cwd=cwd, shell=True) sig = res & 0x7F if (GetVerbose() and res != 0): print(ColorText("red", "Process exited with exit status %d and signal code %d" % ((res & 0xFF00) >> 8, sig))) @@ -1091,9 +1106,19 @@ def GetOptimizeOption(opts): ## ######################################################################## -def MakeDirectory(path): - if os.path.isdir(path): return 0 - os.mkdir(path) +def MakeDirectory(path, mode=None, recursive=False): + if os.path.isdir(path): + return + + if recursive: + parent = os.path.dirname(path) + if parent and not os.path.isdir(parent): + MakeDirectory(parent, mode=mode, recursive=True) + + if mode is not None: + os.mkdir(path, mode) + else: + os.mkdir(path) def ReadFile(wfile): try: @@ -2134,7 +2159,7 @@ def SdkLocatePython(prefer_thirdparty_python=False): SDK["PYTHONEXEC"] = tp_python + "/bin/" + SDK["PYTHONVERSION"] SDK["PYTHON"] = tp_python + "/include/" + SDK["PYTHONVERSION"] - elif GetTarget() == 'darwin': + elif GetTarget() == 'darwin' and not PkgHasCustomLocation("PYTHON"): # On macOS, search for the Python framework directory matching the # version number of our current Python version. sysroot = SDK.get("MACOSX", "") @@ -2463,22 +2488,46 @@ def SdkLocateAndroid(): SDK["ANDROID_TRIPLE"] = ANDROID_TRIPLE if GetHost() == 'android': + # Assume we're compiling from termux. + prefix = os.environ.get("PREFIX", "/data/data/com.termux/files/usr") + SDK["ANDROID_JAR"] = prefix + "/share/aapt/android.jar" return - # Determine the NDK installation directory. - if 'NDK_ROOT' not in os.environ: - exit('NDK_ROOT must be set when compiling for Android!') + # Find the location of the Android SDK. + sdk_root = os.environ.get('ANDROID_HOME') + if not sdk_root or not os.path.isdir(sdk_root): + sdk_root = os.environ.get('ANDROID_SDK_ROOT') - ndk_root = os.environ["NDK_ROOT"] - if not os.path.isdir(ndk_root): - exit("Cannot find %s. Please install Android NDK and set NDK_ROOT." % (ndk_root)) + # Try the default installation location on Windows. + if not sdk_root and GetHost() == 'windows': + sdk_root = os.path.expanduser(os.path.join('~', 'AppData', 'Local', 'Android', 'Sdk')) + + if not sdk_root: + exit('ANDROID_SDK_ROOT must be set when compiling for Android!') + elif not os.path.isdir(sdk_root): + exit('Cannot find %s. Please install Android SDK and set ANDROID_SDK_ROOT or ANDROID_HOME.' % (sdk_root)) + + # Determine the NDK installation directory. + if os.environ.get('NDK_ROOT') or os.environ.get('ANDROID_NDK_ROOT'): + # We have an explicit setting from an environment variable. + ndk_root = os.environ.get('ANDROID_NDK_ROOT') + if not ndk_root or not os.path.isdir(ndk_root): + ndk_root = os.environ.get('NDK_ROOT') + if not ndk_root or not os.path.isdir(ndk_root): + exit("Cannot find %s. Please install Android NDK and set ANDROID_NDK_ROOT." % (ndk_root)) + else: + # Often, it's installed in the ndk-bundle subdirectory of the SDK. + ndk_root = os.path.join(sdk_root, 'ndk-bundle') + + if not os.path.isdir(os.path.join(ndk_root, 'toolchains')): + exit('Cannot find the Android NDK. Install it via the SDK manager or set the ANDROID_NDK_ROOT variable if you have installed it in a different location.') SDK["ANDROID_NDK"] = ndk_root # Determine the toolchain location. prebuilt_dir = os.path.join(ndk_root, 'toolchains', 'llvm', 'prebuilt') if not os.path.isdir(prebuilt_dir): - exit('Not found: %s' % (prebuilt_dir)) + exit('Not found: %s (is the Android NDK installed?)' % (prebuilt_dir)) host_tag = GetHost() + '-x86' if host_64: @@ -2527,7 +2576,44 @@ def SdkLocateAndroid(): # STL that ships with Android. support = os.path.join(ndk_root, 'sources', 'android', 'support', 'include') IncDirectory("ALWAYS", support.replace('\\', '/')) - LibName("ALWAYS", "-landroid_support") + if api < 21: + LibName("ALWAYS", "-landroid_support") + + # Determine the location of android.jar. + SDK["ANDROID_JAR"] = os.path.join(sdk_root, 'platforms', 'android-%s' % (api), 'android.jar') + if not os.path.isfile(SDK["ANDROID_JAR"]): + exit("Cannot find %s. Install platform API level %s via the SDK manager or change the targeted API level with --target=android-#" % (SDK["ANDROID_JAR"], api)) + + # Which build tools versions do we have? Pick the latest. + versions = [] + for version in os.listdir(os.path.join(sdk_root, "build-tools")): + match = re.match('([0-9]+)\\.([0-9]+)\\.([0-9]+)', version) + if match: + version_tuple = int(match.group(1)), int(match.group(2)), int(match.group(3)) + versions.append(version_tuple) + + versions.sort() + if versions: + version = versions[-1] + SDK["ANDROID_BUILD_TOOLS"] = os.path.join(sdk_root, "build-tools", "{0}.{1}.{2}".format(*version)) + + # And find the location of the Java compiler. + if GetHost() == "windows": + jdk_home = os.environ.get("JDK_HOME") or os.environ.get("JAVA_HOME") + if not jdk_home: + # Try to use the Java shipped with Android Studio. + studio_path = GetRegistryKey("SOFTWARE\\Android Studio", "Path", override64=False) + if studio_path and os.path.isdir(studio_path): + jdk_home = os.path.join(studio_path, "jre") + + if not jdk_home or not os.path.isdir(jdk_home): + exit("Cannot find JDK. Please set JDK_HOME or JAVA_HOME.") + + javac = os.path.join(jdk_home, "bin", "javac.exe") + if not os.path.isfile(javac): + exit("Cannot find %s. Install the JDK and set JDK_HOME or JAVA_HOME." % (javac)) + + SDK["JDK"] = jdk_home ######################################################################## ## @@ -2703,7 +2789,7 @@ def SetupVisualStudioEnviron(): # Targeting the 7.1 SDK (which is the only way to have Windows XP support) # with Visual Studio 2015 requires use of the Universal CRT. - if winsdk_ver == '7.1' and SDK["VISUALSTUDIO_VERSION"] >= (14,0): + if winsdk_ver in ('7.1', '7.1A') and SDK["VISUALSTUDIO_VERSION"] >= (14,0): win_kit = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10") # Fallback in case we can't read the registry. @@ -2712,8 +2798,14 @@ def SetupVisualStudioEnviron(): elif not win_kit.endswith('\\'): win_kit += '\\' - AddToPathEnv("LIB", win_kit + "Lib\\10.0.10150.0\\ucrt\\" + arch) - AddToPathEnv("INCLUDE", win_kit + "Include\\10.0.10150.0\\ucrt") + for vnum in 10150, 10240, 10586, 14393, 15063, 16299, 17134, 17763: + version = "10.0.{0}.0".format(vnum) + if os.path.isfile(win_kit + "Include\\" + version + "\\ucrt\\assert.h"): + print("Using Universal CRT %s" % (version)) + break + + AddToPathEnv("LIB", "%s\\Lib\\%s\\ucrt\\%s" % (win_kit, version, arch)) + AddToPathEnv("INCLUDE", "%s\\Include\\%s\\ucrt" % (win_kit, version)) # Copy the DLLs to the bin directory. CopyAllFiles(GetOutputDir() + "/bin/", win_kit + "Redist\\ucrt\\DLLs\\" + arch + "\\") @@ -2793,6 +2885,13 @@ def SetupBuildEnvironment(compiler): if GetTarget() == 'android' and GetHost() != 'android': AddToPathEnv("PATH", os.path.join(SDK["ANDROID_TOOLCHAIN"], "bin")) + if "ANDROID_BUILD_TOOLS" in SDK: + AddToPathEnv("PATH", SDK["ANDROID_BUILD_TOOLS"]) + + if "JDK" in SDK: + AddToPathEnv("PATH", os.path.join(SDK["JDK"], "bin")) + os.environ["JAVA_HOME"] = SDK["JDK"] + if compiler == "MSVC": # Add the visual studio tools to PATH et al. SetupVisualStudioEnviron() @@ -2844,8 +2943,22 @@ def SetupBuildEnvironment(compiler): Warn("%s failed" % (cmd)) SYS_LIB_DIRS += [SDK.get("SYSROOT", "") + "/usr/lib"] + # The Android toolchain on Windows doesn't actually add this one. + if target == 'android' and GetHost() == 'windows': + libdir = SDK.get("SYSROOT", "") + "/usr/lib" + if GetTargetArch() == 'x86_64': + libdir += '64' + SYS_LIB_DIRS += [libdir] + # Now extract the preprocessor's include directories. - cmd = GetCXX() + sysroot_flag + " -x c++ -v -E /dev/null" + cmd = GetCXX() + " -x c++ -v -E " + os.devnull + if "ANDROID_NDK" in SDK: + ndk_dir = SDK["ANDROID_NDK"].replace('\\', '/') + cmd += ' -isystem %s/sysroot/usr/include' % (ndk_dir) + cmd += ' -isystem %s/sysroot/usr/include/%s' % (ndk_dir, SDK["ANDROID_TRIPLE"]) + else: + cmd += sysroot_flag + null = open(os.devnull, 'w') handle = subprocess.Popen(cmd, stdout=null, stderr=subprocess.PIPE, shell=True) scanning = False @@ -2858,8 +2971,12 @@ def SetupBuildEnvironment(compiler): scanning = True continue - if not line.startswith(' /'): - continue + if sys.platform == "win32": + if not line.startswith(' '): + continue + else: + if not line.startswith(' /'): + continue line = line.strip() if line.endswith(" (framework directory)"): @@ -3104,32 +3221,6 @@ def ParsePandaVersion(fn): except: pass return "0.0.0" -def ParsePluginVersion(fn): - try: - f = open(fn, "r") - pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+P3D_PLUGIN_VERSION[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)') - for line in f: - match = pattern.match(line,0) - if (match): - f.close() - return match.group(1) + "." + match.group(2) + "." + match.group(3) - f.close() - except: pass - return "0.0.0" - -def ParseCoreapiVersion(fn): - try: - f = open(fn, "r") - pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+P3D_COREAPI_VERSION.*([0-9]+)[ \t]*$') - for line in f: - match = pattern.match(line,0) - if (match): - f.close() - return match.group(1) - f.close() - except: pass - return "0" - ########################################################################################## # # Utility function to generate a resource file @@ -3276,6 +3367,9 @@ def GetPythonABI(): soabi = 'cpython-%d%d' % (sys.version_info[:2]) + if sys.version_info >= (3, 8): + return soabi + debug_flag = sysconfig.get_config_var('Py_DEBUG') if (debug_flag is None and hasattr(sys, 'gettotalrefcount')) or debug_flag: soabi += 'd' @@ -3309,7 +3403,6 @@ def CalcLocation(fn, ipath): if (fn.endswith(".py")): return CxxFindSource(fn, ipath) if (fn.endswith(".yxx")): return CxxFindSource(fn, ipath) if (fn.endswith(".lxx")): return CxxFindSource(fn, ipath) - if (fn.endswith(".pdef")):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 diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py index 18f92379b8..e230ae4049 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -1,10 +1,5 @@ """ Generates a wheel (.whl) file from the output of makepanda. - -Since the wheel requires special linking, this will only work if compiled with -the `--wheel` parameter. - -Please keep this file work with Panda3D 1.9 until that reaches EOL. """ from __future__ import print_function, unicode_literals from distutils.util import get_platform @@ -20,7 +15,7 @@ import tempfile import subprocess from distutils.sysconfig import get_config_var from optparse import OptionParser -from makepandacore import ColorText, LocateBinary, ParsePandaVersion, GetExtensionSuffix, SetVerbose, GetVerbose, GetMetadataValue +from makepandacore import ColorText, LocateBinary, GetExtensionSuffix, SetVerbose, GetVerbose, GetMetadataValue from base64 import urlsafe_b64encode @@ -34,6 +29,9 @@ def get_abi_tag(): soabi = 'cp%d%d' % (sys.version_info[:2]) + if sys.version_info >= (3, 8): + return soabi + debug_flag = get_config_var('Py_DEBUG') if (debug_flag is None and hasattr(sys, 'gettotalrefcount')) or debug_flag: soabi += 'd' @@ -104,7 +102,7 @@ MANYLINUX_LIBS = [ # These are not mentioned in manylinux1 spec but should nonetheless always # be excluded. - "linux-vdso.so.1", "linux-gate.so.1", "ld-linux.so.2", + "linux-vdso.so.1", "linux-gate.so.1", "ld-linux.so.2", "libdrm.so.2", ] # Binaries to never scan for dependencies on non-Windows systems. @@ -118,6 +116,8 @@ Root-Is-Purelib: false Tag: {0}-{1}-{2} """ +PROJECT_URLS = dict([line.split('=', 1) for line in GetMetadataValue('project_urls').strip().splitlines()]) + METADATA = { "license": GetMetadataValue('license'), "name": GetMetadataValue('name'), @@ -126,9 +126,7 @@ METADATA = { "summary": GetMetadataValue('description'), "extensions": { "python.details": { - "project_urls": { - "Home": GetMetadataValue('url'), - }, + "project_urls": dict(PROJECT_URLS, Home=GetMetadataValue('url')), "document_names": { "license": "LICENSE.txt" }, @@ -144,18 +142,47 @@ METADATA = { "classifiers": GetMetadataValue('classifiers'), } +DESCRIPTION = """ +The Panda3D free 3D game engine +=============================== + +Panda3D is a powerful 3D engine written in C++, with a complete set of Python +bindings. Unlike other engines, these bindings are automatically generated, +meaning that they are always up-to-date and complete: all functions of the +engine can be controlled from Python. All major Panda3D applications have been +written in Python, this is the intended way of using the engine. + +Panda3D now supports automatic shader generation, which now means you can use +normal maps, gloss maps, glow maps, HDR, cartoon shading, and the like without +having to write any shaders. + +Panda3D is a modern engine supporting advanced features such as shaders, +stencil, and render-to-texture. Panda3D is unusual in that it emphasizes a +short learning curve, rapid development, and extreme stability and robustness. +Panda3D is free software that runs under Windows, Linux, or macOS. + +The Panda3D team is very concerned with making the engine accessible to new +users. We provide a detailed manual, a complete API reference, and a large +collection of sample programs to help you get started. We have active forums, +with many helpful users, and the developers are regularly online to answer +questions. +""" + PANDA3D_TOOLS_INIT = """import os, sys import panda3d +dir = os.path.dirname(panda3d.__file__) +del panda3d + if sys.platform in ('win32', 'cygwin'): path_var = 'PATH' + if hasattr(os, 'add_dll_directory'): + os.add_dll_directory(dir) elif sys.platform == 'darwin': path_var = 'DYLD_LIBRARY_PATH' else: path_var = 'LD_LIBRARY_PATH' -dir = os.path.dirname(panda3d.__file__) -del panda3d if not os.environ.get(path_var): os.environ[path_var] = dir else: @@ -526,11 +553,6 @@ def makewheel(version, output_dir, platform=None): # Update relevant METADATA entries METADATA['version'] = version - version_classifiers = [ - "Programming Language :: Python :: {0}".format(*sys.version_info), - "Programming Language :: Python :: {0}.{1}".format(*sys.version_info), - ] - METADATA['classifiers'].extend(version_classifiers) # Build out the metadata details = METADATA["extensions"]["python.details"] @@ -544,11 +566,14 @@ def makewheel(version, output_dir, platform=None): "Summary: {summary}\n" \ "License: {license}\n".format(**METADATA), "Home-page: {0}\n".format(homepage), + ] + ["Project-URL: {0}, {1}\n".format(*url) for url in PROJECT_URLS.items()] + [ "Author: {0}\n".format(author), "Author-email: {0}\n".format(email), "Platform: {0}\n".format(platform), ] + ["Classifier: {0}\n".format(c) for c in METADATA['classifiers']]) + metadata += '\n' + DESCRIPTION.strip() + '\n' + # Zip it up and name it the right thing whl = WheelFile('panda3d', version, platform) whl.lib_path = [libs_dir] @@ -573,10 +598,23 @@ def makewheel(version, output_dir, platform=None): # Write the panda3d tree. We use a custom empty __init__ since the # default one adds the bin directory to the PATH, which we don't have. - whl.write_file_data('panda3d/__init__.py', """"Python bindings for the Panda3D libraries" + p3d_init = """"Python bindings for the Panda3D libraries" __version__ = '{0}' -""".format(version)) +""".format(version) + + if '27' in ABI_TAG: + p3d_init += """ +if __debug__: + import sys + if sys.version_info < (3, 0): + sys.stderr.write("WARNING: Python 2.7 will reach EOL after December 31, 2019.\\n") + sys.stderr.write("To suppress this warning, upgrade to Python 3.\\n") + sys.stderr.flush() + del sys +""" + + whl.write_file_data('panda3d/__init__.py', p3d_init) # Copy the extension modules from the panda3d directory. ext_suffix = GetExtensionSuffix() @@ -692,7 +730,7 @@ __version__ = '{0}' if __name__ == "__main__": - version = ParsePandaVersion("dtool/PandaVersion.pp") + version = GetMetadataValue('version') parser = OptionParser() parser.add_option('', '--version', dest = 'version', help = 'Panda3D version number (default: %s)' % (version), default = version) diff --git a/makepanda/test_imports.py b/makepanda/test_imports.py deleted file mode 100644 index a9b22943c8..0000000000 --- a/makepanda/test_imports.py +++ /dev/null @@ -1,334 +0,0 @@ -# This script imports Panda3D modules just to make sure that there are no -# missing imports. It is useful for a quick and dirty test to make sure -# that there are no obvious build issues. -import os, importlib - -# This will print out imports on the command line. -import direct.showbase.VerboseImport - - -import imp -import panda3d -dir = os.path.dirname(panda3d.__file__) - -extensions = set() -for suffix in imp.get_suffixes(): - extensions.add(suffix[0]) - -for basename in os.listdir(dir): - module = basename.split('.', 1)[0] - ext = basename[len(module):] - - if ext in extensions: - importlib.import_module('panda3d.%s' % (module)) - - -import direct.actor.Actor -import direct.actor.DistributedActor -import direct.cluster.ClusterClient -import direct.cluster.ClusterConfig -import direct.cluster.ClusterMsgs -import direct.cluster.ClusterServer -import direct.controls.BattleWalker -import direct.controls.ControlManager -import direct.controls.DevWalker -import direct.controls.GhostWalker -import direct.controls.GravityWalker -import direct.controls.InputState -import direct.controls.NonPhysicsWalker -import direct.controls.ObserverWalker -import direct.controls.PhysicsRoller -import direct.controls.PhysicsWalker -import direct.controls.SwimWalker -import direct.controls.TwoDWalker -import direct.directnotify.DirectNotify -import direct.directnotify.DirectNotifyGlobal -import direct.directnotify.Logger -import direct.directnotify.LoggerGlobal -import direct.directnotify.Notifier -import direct.directnotify.RotatingLog -import direct.directtools.DirectCameraControl -import direct.directtools.DirectGeometry -import direct.directtools.DirectGlobals -import direct.directtools.DirectGrid -import direct.directtools.DirectLights -import direct.directtools.DirectManipulation -import direct.directtools.DirectSelection -import direct.directtools.DirectUtil -import direct.directutil.DeltaProfiler -import direct.directutil.DistributedLargeBlobSender -import direct.directutil.DistributedLargeBlobSenderAI -import direct.directutil.LargeBlobSenderConsts -import direct.directutil.Mopath -import direct.directutil.Verify -import direct.directutil.WeightedChoice -import direct.dist.FreezeTool -import direct.distributed.AsyncRequest -import direct.distributed.CRCache -import direct.distributed.CRDataCache -import direct.distributed.CachedDOData -import direct.distributed.CartesianGridBase -import direct.distributed.ClientRepository -import direct.distributed.ClientRepositoryBase -import direct.distributed.ClockDelta -import direct.distributed.ConnectionRepository -import direct.distributed.DistributedCamera -import direct.distributed.DistributedCameraAI -import direct.distributed.DistributedCameraOV -import direct.distributed.DistributedCartesianGrid -import direct.distributed.DistributedCartesianGridAI -import direct.distributed.DistributedNode -import direct.distributed.DistributedNodeAI -import direct.distributed.DistributedNodeUD -import direct.distributed.DistributedObject -import direct.distributed.DistributedObjectAI -import direct.distributed.DistributedObjectBase -import direct.distributed.DistributedObjectGlobal -import direct.distributed.DistributedObjectGlobalAI -import direct.distributed.DistributedObjectGlobalUD -import direct.distributed.DistributedObjectOV -import direct.distributed.DistributedObjectUD -import direct.distributed.DistributedSmoothNodeAI -import direct.distributed.DistributedSmoothNodeBase -import direct.distributed.DoCollectionManager -import direct.distributed.DoHierarchy -import direct.distributed.DoInterestManager -import direct.distributed.GridChild -import direct.distributed.GridParent -import direct.distributed.InterestWatcher -import direct.distributed.MsgTypes -import direct.distributed.MsgTypesCMU -import direct.distributed.NetMessenger -import direct.distributed.ParentMgr -import direct.distributed.PyDatagram -import direct.distributed.PyDatagramIterator -import direct.distributed.RelatedObjectMgr -import direct.distributed.SampleObject -import direct.distributed.ServerRepository -import direct.distributed.StagedObject -import direct.distributed.TimeManager -import direct.distributed.TimeManagerAI -import direct.extensions_native.extension_native_helpers -import direct.filter.CommonFilters -import direct.filter.FilterManager -import direct.fsm.ClassicFSM -import direct.fsm.FSM -import direct.fsm.FourState -import direct.fsm.FourStateAI -import direct.fsm.SampleFSM -import direct.fsm.State -import direct.fsm.StateData -import direct.fsm.StatePush -import direct.gui.DirectButton -import direct.gui.DirectCheckBox -import direct.gui.DirectCheckButton -import direct.gui.DirectDialog -import direct.gui.DirectEntry -import direct.gui.DirectEntryScroll -import direct.gui.DirectFrame -import direct.gui.DirectGui -import direct.gui.DirectGuiBase -import direct.gui.DirectGuiGlobals -import direct.gui.DirectGuiTest -import direct.gui.DirectLabel -import direct.gui.DirectOptionMenu -import direct.gui.DirectRadioButton -import direct.gui.DirectScrollBar -import direct.gui.DirectScrolledFrame -import direct.gui.DirectScrolledList -import direct.gui.DirectSlider -import direct.gui.DirectWaitBar -import direct.gui.OnscreenGeom -import direct.gui.OnscreenImage -import direct.gui.OnscreenText -import direct.interval.ActorInterval -import direct.interval.AnimControlInterval -import direct.interval.FunctionInterval -import direct.interval.IndirectInterval -import direct.interval.Interval -import direct.interval.IntervalGlobal -import direct.interval.IntervalManager -import direct.interval.IntervalTest -import direct.interval.LerpBlendHelpers -import direct.interval.LerpInterval -import direct.interval.MetaInterval -import direct.interval.MopathInterval -import direct.interval.ParticleInterval -import direct.interval.ProjectileInterval -import direct.interval.ProjectileIntervalTest -import direct.interval.SoundInterval -import direct.interval.TestInterval -import direct.motiontrail.MotionTrail -import direct.p3d.AppRunner -import direct.p3d.DWBPackageInstaller -import direct.p3d.DeploymentTools -import direct.p3d.FileSpec -import direct.p3d.HostInfo -import direct.p3d.InstalledHostData -import direct.p3d.InstalledPackageData -import direct.p3d.JavaScript -import direct.p3d.PackageInfo -import direct.p3d.PackageInstaller -import direct.p3d.PackageMerger -import direct.p3d.Packager -import direct.p3d.PatchMaker -import direct.p3d.ScanDirectoryNode -import direct.p3d.SeqValue -import direct.particles.ForceGroup -import direct.particles.GlobalForceGroup -import direct.particles.ParticleEffect -import direct.particles.ParticleFloorTest -import direct.particles.ParticleManagerGlobal -import direct.particles.ParticleTest -import direct.particles.Particles -import direct.particles.SpriteParticleRendererExt -import direct.physics.FallTest -import direct.physics.RotationTest -import direct.showbase.AppRunnerGlobal -import direct.showbase.Audio3DManager -import direct.showbase.BufferViewer -import direct.showbase.BulletinBoard -import direct.showbase.BulletinBoardGlobal -import direct.showbase.BulletinBoardWatcher -import direct.showbase.ContainerLeakDetector -import direct.showbase.ContainerReport -import direct.showbase.CountedResource -import direct.showbase.DirectObject -import direct.showbase.DistancePhasedNode -import direct.showbase.EventGroup -import direct.showbase.EventManager -import direct.showbase.EventManagerGlobal -import direct.showbase.ExceptionVarDump -import direct.showbase.Factory -import direct.showbase.FindCtaPaths -import direct.showbase.Finder -import direct.showbase.GarbageReport -import direct.showbase.GarbageReportScheduler -import direct.showbase.InputStateGlobal -import direct.showbase.Job -import direct.showbase.JobManager -import direct.showbase.JobManagerGlobal -import direct.showbase.LeakDetectors -import direct.showbase.Loader -import direct.showbase.Messenger -import direct.showbase.MessengerGlobal -import direct.showbase.MessengerLeakDetector -import direct.showbase.MirrorDemo -import direct.showbase.ObjectPool -import direct.showbase.ObjectReport -import direct.showbase.OnScreenDebug -import direct.showbase.PhasedObject -import direct.showbase.PhysicsManagerGlobal -import direct.showbase.Pool -import direct.showbase.ProfileSession -import direct.showbase.PythonUtil -import direct.showbase.RandomNumGen -import direct.showbase.ReferrerSearch -import direct.showbase.SfxPlayer -import direct.showbase.ShadowDemo -import direct.showbase.ShadowPlacer -import direct.showbase.ShowBase -import direct.showbase.TaskThreaded -import direct.showbase.ThreeUpShow -import direct.showbase.Transitions -import direct.showbase.VFSImporter -import direct.showbase.WxGlobal -import direct.showutil.BuildGeometry -import direct.showutil.Effects -import direct.showutil.Rope -import direct.showutil.TexMemWatcher -import direct.showutil.TexViewer -import direct.stdpy.file -import direct.stdpy.glob -#import direct.stdpy.pickle -import direct.stdpy.thread -import direct.stdpy.threading -import direct.stdpy.threading2 -import direct.task.FrameProfiler -import direct.task.MiniTask -import direct.task.Task -import direct.task.TaskManagerGlobal -import direct.task.TaskProfiler -import direct.task.TaskTester -import direct.task.Timer - -try: - import Pmw -except ImportError: - Pmw = None - print("Skipping Tk modules due to missing Pmw.") - -if Pmw: - import direct.showbase.TkGlobal - import direct.tkpanels.AnimPanel - import direct.tkpanels.DirectSessionPanel - import direct.tkpanels.FSMInspector - import direct.tkpanels.Inspector - import direct.tkpanels.MopathRecorder - import direct.tkpanels.NotifyPanel - import direct.tkpanels.ParticlePanel - import direct.tkpanels.Placer - import direct.tkpanels.TaskManagerPanel - import direct.tkwidgets.AppShell - import direct.tkwidgets.Dial - import direct.tkwidgets.EntryScale - import direct.tkwidgets.Floater - import direct.tkwidgets.MemoryExplorer - import direct.tkwidgets.ProgressBar - import direct.tkwidgets.SceneGraphExplorer - import direct.tkwidgets.Slider - import direct.tkwidgets.Tree - import direct.tkwidgets.Valuator - import direct.tkwidgets.VectorWidgets - import direct.tkwidgets.WidgetPropertiesDialog - -try: - import wx -except ImportError: - wx = None - print("Skipping wxPython and leveleditor modules due to missing wx.") - -if wx: - import direct.wxwidgets.ViewPort - import direct.wxwidgets.WxAppShell - import direct.wxwidgets.WxPandaShell - import direct.wxwidgets.WxPandaWindow - import direct.wxwidgets.WxSlider - - import direct.leveleditor.ActionMgr - import direct.leveleditor.AnimControlUI - import direct.leveleditor.AnimGlobals - import direct.leveleditor.AnimMgr - import direct.leveleditor.AnimMgrBase - import direct.leveleditor.CurveAnimUI - import direct.leveleditor.CurveEditor - import direct.leveleditor.FileMgr - import direct.leveleditor.GraphEditorUI - import direct.leveleditor.HotKeyUI - import direct.leveleditor.LayerEditorUI - import direct.leveleditor.LevelEditor - import direct.leveleditor.LevelEditorBase - import direct.leveleditor.LevelEditorStart - import direct.leveleditor.LevelEditorUI - import direct.leveleditor.LevelEditorUIBase - import direct.leveleditor.LevelLoader - import direct.leveleditor.LevelLoaderBase - import direct.leveleditor.MayaConverter - import direct.leveleditor.ObjectGlobals - import direct.leveleditor.ObjectHandler - import direct.leveleditor.ObjectMgr - import direct.leveleditor.ObjectMgrBase - import direct.leveleditor.ObjectPalette - import direct.leveleditor.ObjectPaletteBase - import direct.leveleditor.ObjectPaletteUI - import direct.leveleditor.ObjectPropertyUI - import direct.leveleditor.PaletteTreeCtrl - import direct.leveleditor.ProtoObjs - import direct.leveleditor.ProtoObjsUI - import direct.leveleditor.ProtoPalette - import direct.leveleditor.ProtoPaletteBase - import direct.leveleditor.ProtoPaletteUI - import direct.leveleditor.SceneGraphUI - import direct.leveleditor.SceneGraphUIBase - -print("Finished.") diff --git a/makepanda/test_wheel.py b/makepanda/test_wheel.py index 09b0727fe7..86fb8f4d2f 100755 --- a/makepanda/test_wheel.py +++ b/makepanda/test_wheel.py @@ -17,31 +17,31 @@ from optparse import OptionParser def test_wheel(wheel, verbose=False): envdir = tempfile.mkdtemp(prefix="venv-") print("Setting up virtual environment in {0}".format(envdir)) + sys.stdout.flush() + # Create a virtualenv. if sys.version_info >= (3, 0): - subprocess.call([sys.executable, "-m", "venv", "--clear", envdir]) + subprocess.call([sys.executable, "-B", "-m", "venv", "--clear", envdir]) else: - subprocess.call([sys.executable, "-m", "virtualenv", "--clear", envdir]) + subprocess.call([sys.executable, "-B", "-m", "virtualenv", "--clear", envdir]) - # Make sure pip is up-to-date first. - if subprocess.call([sys.executable, "-m", "pip", "install", "-U", "pip"]) != 0: - shutil.rmtree(envdir) - sys.exit(1) - - # Install pytest into the environment, as well as our wheel. - if sys.platform == "win32": - pip = os.path.join(envdir, "Scripts", "pip.exe") - else: - pip = os.path.join(envdir, "bin", "pip") - if subprocess.call([pip, "install", "pytest", wheel]) != 0: - shutil.rmtree(envdir) - sys.exit(1) - - # Run the test suite. + # Determine the path to the Python interpreter. if sys.platform == "win32": python = os.path.join(envdir, "Scripts", "python.exe") else: python = os.path.join(envdir, "bin", "python") + + # Upgrade pip inside the environment too. + if subprocess.call([python, "-m", "pip", "install", "-U", "pip"]) != 0: + shutil.rmtree(envdir) + sys.exit(1) + + # Install pytest into the environment, as well as our wheel. + if subprocess.call([python, "-m", "pip", "install", "pytest", wheel]) != 0: + shutil.rmtree(envdir) + sys.exit(1) + + # Run the test suite. test_cmd = [python, "-m", "pytest", "tests"] if verbose: test_cmd.append("--verbose") diff --git a/panda/metalibs/pandagl/pandagl.cxx b/panda/metalibs/pandagl/pandagl.cxx index 0e33961c9f..7775d2e7b4 100644 --- a/panda/metalibs/pandagl/pandagl.cxx +++ b/panda/metalibs/pandagl/pandagl.cxx @@ -16,9 +16,6 @@ #if defined(HAVE_COCOA) #include "config_cocoadisplay.h" #include "cocoaGraphicsPipe.h" -#elif defined(HAVE_CARBON) -#include "config_osxdisplay.h" -#include "osxGraphicsPipe.h" #endif #ifdef HAVE_GLX @@ -26,8 +23,8 @@ #include "glxGraphicsPipe.h" #endif -#if !defined(HAVE_WGL) && !defined(HAVE_COCOA) && !defined(HAVE_CARBON) && !defined(HAVE_GLX) -#error One of HAVE_WGL, HAVE_COCOA, HAVE_CARBON or HAVE_GLX must be defined when compiling pandagl! +#if !defined(HAVE_WGL) && !defined(HAVE_COCOA) && !defined(HAVE_GLX) +#error One of HAVE_WGL, HAVE_COCOA or HAVE_GLX must be defined when compiling pandagl! #endif /** @@ -46,8 +43,6 @@ init_libpandagl() { #if defined(HAVE_COCOA) init_libcocoadisplay(); -#elif defined(HAVE_CARBON) - init_libosxdisplay(); #endif #ifdef IS_LINUX @@ -67,8 +62,6 @@ get_pipe_type_pandagl() { #if defined(HAVE_COCOA) return CocoaGraphicsPipe::get_class_type().get_index(); -#elif defined(HAVE_CARBON) - return osxGraphicsPipe::get_class_type().get_index(); #endif #ifdef HAVE_GLX diff --git a/panda/src/android/android_main.cxx b/panda/src/android/android_main.cxx index 2f7d7e3dcc..13895d99f6 100644 --- a/panda/src/android/android_main.cxx +++ b/panda/src/android/android_main.cxx @@ -24,6 +24,7 @@ #include "config_display.h" // #define OPENGLES_1 #include "config_androiddisplay.h" +#include #include #include diff --git a/panda/src/audio/audio.h b/panda/src/audio/audio.h index c498834502..ca1f5bfd94 100644 --- a/panda/src/audio/audio.h +++ b/panda/src/audio/audio.h @@ -11,12 +11,12 @@ * @date 2000-07-06 */ -#ifndef __AUDIO_H__ -#define __AUDIO_H__ +#ifndef AUDIO_H +#define AUDIO_H #include "filterProperties.h" #include "audioLoadRequest.h" #include "audioSound.h" #include "audioManager.h" -#endif /* __AUDIO_H__ */ +#endif /* AUDIO_H */ diff --git a/panda/src/audio/audioManager.cxx b/panda/src/audio/audioManager.cxx index 39d16156ef..cae10e675c 100644 --- a/panda/src/audio/audioManager.cxx +++ b/panda/src/audio/audioManager.cxx @@ -325,11 +325,3 @@ void AudioManager:: write(std::ostream &out) const { out << (*this) << "\n"; } - -/** - * For use only with Miles. - */ -void AudioManager:: -set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2, LVecBase3 *speaker3, LVecBase3 *speaker4, LVecBase3 *speaker5, LVecBase3 *speaker6, LVecBase3 *speaker7, LVecBase3 *speaker8, LVecBase3 *speaker9) { - // intentionally blank -} diff --git a/panda/src/audio/audioManager.h b/panda/src/audio/audioManager.h index 62d9a5d090..bd35da2f5b 100644 --- a/panda/src/audio/audioManager.h +++ b/panda/src/audio/audioManager.h @@ -12,8 +12,8 @@ * Prior system by: cary */ -#ifndef __AUDIO_MANAGER_H__ -#define __AUDIO_MANAGER_H__ +#ifndef AUDIOMANAGER_H +#define AUDIOMANAGER_H #include "config_audio.h" #include "audioSound.h" @@ -178,9 +178,6 @@ PUBLISHED: virtual void output(std::ostream &out) const; virtual void write(std::ostream &out) const; - // set_speaker_configuration is a Miles only method. - virtual void set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2=nullptr, LVecBase3 *speaker3=nullptr, LVecBase3 *speaker4=nullptr, LVecBase3 *speaker5=nullptr, LVecBase3 *speaker6=nullptr, LVecBase3 *speaker7=nullptr, LVecBase3 *speaker8=nullptr, LVecBase3 *speaker9=nullptr); - public: static void register_AudioManager_creator(Create_AudioManager_proc* proc); @@ -222,4 +219,4 @@ operator << (std::ostream &out, const AudioManager &mgr) { #include "audioManager.I" -#endif /* __AUDIO_MANAGER_H__ */ +#endif /* AUDIOMANAGER_H */ diff --git a/panda/src/audio/audioSound.cxx b/panda/src/audio/audioSound.cxx index 19cc27f030..aea4e4f14c 100644 --- a/panda/src/audio/audioSound.cxx +++ b/panda/src/audio/audioSound.cxx @@ -83,23 +83,6 @@ set_speaker_mix(PN_stdfloat frontleft, PN_stdfloat frontright, PN_stdfloat cente // intentionally blank } -/** - * For use only with Miles. - */ -PN_stdfloat AudioSound:: -get_speaker_level(int index) { - // intentionally blank - return 0.0; -} - -/** - * For use only with Miles. - */ -void AudioSound:: -set_speaker_levels(PN_stdfloat level1, PN_stdfloat level2, PN_stdfloat level3, PN_stdfloat level4, PN_stdfloat level5, PN_stdfloat level6, PN_stdfloat level7, PN_stdfloat level8, PN_stdfloat level9) { - // intentionally blank -} - /** * Configure the local DSP filter chain. * diff --git a/panda/src/audio/audioSound.h b/panda/src/audio/audioSound.h index e348ad4daf..c7eb3653da 100644 --- a/panda/src/audio/audioSound.h +++ b/panda/src/audio/audioSound.h @@ -12,8 +12,8 @@ * Prior system by: cary */ -#ifndef __AUDIOSOUND_H__ -#define __AUDIOSOUND_H__ +#ifndef AUDIOSOUND_H +#define AUDIOSOUND_H #include "config_audio.h" #include "typedReferenceCount.h" @@ -104,17 +104,10 @@ PUBLISHED: virtual void set_3d_max_distance(PN_stdfloat dist); virtual PN_stdfloat get_3d_max_distance() const; - // *_speaker_mix and *_speaker_level(s) serve the same purpose. - // *_speaker_mix is for use with FMOD. *_speaker_level(s) is for use with - // Miles. Both interfaces exist because of a significant difference in the - // two APIs. Hopefully the difference can be reconciled into a single - // interface at some point. + // *_speaker_mix is for use with FMOD. virtual PN_stdfloat get_speaker_mix(int speaker); virtual void set_speaker_mix(PN_stdfloat frontleft, PN_stdfloat frontright, PN_stdfloat center, PN_stdfloat sub, PN_stdfloat backleft, PN_stdfloat backright, PN_stdfloat sideleft, PN_stdfloat sideright); - virtual PN_stdfloat get_speaker_level(int index); - virtual void set_speaker_levels(PN_stdfloat level1, PN_stdfloat level2=-1.0f, PN_stdfloat level3=-1.0f, PN_stdfloat level4=-1.0f, PN_stdfloat level5=-1.0f, PN_stdfloat level6=-1.0f, PN_stdfloat level7=-1.0f, PN_stdfloat level8=-1.0f, PN_stdfloat level9=-1.0f); - virtual int get_priority(); virtual void set_priority(int priority); @@ -160,4 +153,4 @@ operator << (std::ostream &out, const AudioSound &sound) { EXPCL_PANDA_AUDIO std::ostream & operator << (std::ostream &out, AudioSound::SoundStatus status); -#endif /* __AUDIOSOUND_H__ */ +#endif /* AUDIOSOUND_H */ diff --git a/panda/src/audio/config_audio.cxx b/panda/src/audio/config_audio.cxx index ce9b1413d3..d483b40d75 100644 --- a/panda/src/audio/config_audio.cxx +++ b/panda/src/audio/config_audio.cxx @@ -100,11 +100,6 @@ ConfigVariableEnum fmod_speaker_mode "Options: raw, mono, stereo, quad, surround, 5.1 and 7.1. ")); -// Config variables for Miles: - -ConfigVariableBool audio_software_midi -("audio-software-midi", true); - ConfigVariableFilename audio_dls_file ("audio-dls-file", Filename(), PRC_DESC("Specifies a DLS file that defines an instrument set to load " @@ -113,24 +108,6 @@ ConfigVariableFilename audio_dls_file "one is available; the likely success of this depends on the " "operating system.")); -ConfigVariableBool audio_play_midi -("audio-play-midi", true); - -ConfigVariableBool audio_play_wave -("audio-play-wave", true); - -ConfigVariableBool audio_play_mp3 -("audio-play-mp3", true); - -ConfigVariableInt audio_output_rate -("audio-output-rate", 22050); - -ConfigVariableInt audio_output_bits -("audio-output-bits", 16); - -ConfigVariableInt audio_output_channels -("audio-output-channels", 2); - ConfigureFn(config_audio) { FilterProperties::init_type(); AudioLoadRequest::init_type(); diff --git a/panda/src/audio/config_audio.h b/panda/src/audio/config_audio.h index 47853ac143..895ee000e4 100644 --- a/panda/src/audio/config_audio.h +++ b/panda/src/audio/config_audio.h @@ -58,6 +58,7 @@ EXPCL_PANDA_AUDIO std::istream &operator >> (std::istream &in, FmodSpeakerMode & extern EXPCL_PANDA_AUDIO ConfigVariableInt fmod_number_of_sound_channels; extern EXPCL_PANDA_AUDIO ConfigVariableBool fmod_use_surround_sound; extern EXPCL_PANDA_AUDIO ConfigVariableEnum fmod_speaker_mode; +extern EXPCL_PANDA_AUDIO ConfigVariableFilename audio_dls_file; // Config vars for OpenAL: @@ -67,19 +68,6 @@ extern EXPCL_PANDA_AUDIO ConfigVariableDouble audio_drop_off_factor; extern EXPCL_PANDA_AUDIO ConfigVariableDouble audio_buffering_seconds; extern EXPCL_PANDA_AUDIO ConfigVariableInt audio_preload_threshold; -// Config vars for Miles: - -extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_software_midi; -extern EXPCL_PANDA_AUDIO ConfigVariableFilename audio_dls_file; -extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_play_midi; -extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_play_wave; -extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_play_mp3; -extern EXPCL_PANDA_AUDIO ConfigVariableInt audio_output_rate; -extern EXPCL_PANDA_AUDIO ConfigVariableInt audio_output_bits; -extern EXPCL_PANDA_AUDIO ConfigVariableInt audio_output_channels; - - - #ifdef NOTIFY_DEBUG //[ // Non-release build: #define audio_debug(msg) \ diff --git a/panda/src/audio/nullAudioManager.h b/panda/src/audio/nullAudioManager.h index a4e2c47511..d043a6aed1 100644 --- a/panda/src/audio/nullAudioManager.h +++ b/panda/src/audio/nullAudioManager.h @@ -12,8 +12,8 @@ * Prior system by: cary */ -#ifndef __NULL_AUDIO_MANAGER_H__ -#define __NULL_AUDIO_MANAGER_H__ +#ifndef NULLAUDIOMANAGER_H +#define NULLAUDIOMANAGER_H #include "audioManager.h" #include "nullAudioSound.h" @@ -21,7 +21,7 @@ class EXPCL_PANDA_AUDIO NullAudioManager : public AudioManager { // All of these methods are stubbed out to some degree. If you're looking // for a starting place for a new AudioManager, please consider looking at - // the milesAudioManager. + // the openalAudioManager. public: NullAudioManager(); @@ -89,4 +89,4 @@ private: static TypeHandle _type_handle; }; -#endif /* __NULL_AUDIO_MANAGER_H__ */ +#endif /* NULLAUDIOMANAGER_H */ diff --git a/panda/src/audio/nullAudioSound.h b/panda/src/audio/nullAudioSound.h index d388493132..8ce58de054 100644 --- a/panda/src/audio/nullAudioSound.h +++ b/panda/src/audio/nullAudioSound.h @@ -12,8 +12,8 @@ * Prior system by: cary */ -#ifndef __NULL_AUDIO_SOUND_H__ -#define __NULL_AUDIO_SOUND_H__ +#ifndef NULLAUDIOSOUND_H +#define NULLAUDIOSOUND_H #include "audioSound.h" @@ -23,7 +23,7 @@ class EXPCL_PANDA_AUDIO NullAudioSound : public AudioSound { // All of these methods are stubbed out to some degree. If you're looking // for a starting place for a new AudioManager, please consider looking at - // the milesAudioManager. + // the openalAudioManager. public: ~NullAudioSound(); @@ -91,4 +91,4 @@ private: friend class NullAudioManager; }; -#endif /* __NULL_AUDIO_SOUND_H__ */ +#endif /* NULLAUDIOSOUND_H */ diff --git a/panda/src/audiotraits/config_milesAudio.cxx b/panda/src/audiotraits/config_milesAudio.cxx deleted file mode 100644 index 87cd36321c..0000000000 --- a/panda/src/audiotraits/config_milesAudio.cxx +++ /dev/null @@ -1,102 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file config_milesAudio.cxx - * @author skyler - */ - -#include "pandabase.h" -#ifdef HAVE_RAD_MSS //[ - -#include "config_milesAudio.h" -#include "milesAudioManager.h" -#include "milesAudioSound.h" -#include "milesAudioSample.h" -#include "milesAudioSequence.h" -#include "milesAudioStream.h" -#include "pandaSystem.h" -#include "dconfig.h" - -#if !defined(CPPPARSER) && !defined(LINK_ALL_STATIC) && !defined(BUILDING_MILES_AUDIO) - #error Buildsystem error: BUILDING_MILES_AUDIO not defined -#endif - -ConfigureDef(config_milesAudio); -NotifyCategoryDef(milesAudio, ":audio"); - -ConfigureFn(config_milesAudio) { - init_libMilesAudio(); -} - -ConfigVariableBool miles_audio_force_midi_reset -("audio-force-midi-reset", true); - -ConfigVariableInt miles_audio_expand_mp3_threshold -("miles-audio-expand-mp3-threshold", 16384, - PRC_DESC("This enables a Miles workaround in which small MP3 files are " - "expanded in-memory at load time into WAV format, which can " - "work around problems with Miles being unable to correctly " - "report the length of, or seek within, a variable bit-rate encoded " - "MP3 file. Any MP3 file whose length in bytes is less than " - "this value will be expanded. This only applies to files " - "within the miles-audio-preload-threshold.")); - -ConfigVariableInt miles_audio_preload_threshold -("miles-audio-preload-threshold", -1, - PRC_DESC("This should be no smaller " - "than miles-audio-expand-mp3-threshold. Files that are smaller " - "than this number of bytes will be preloaded and kept " - "resident in memory, while files that are this size or larger " - "will be streamed from disk. Set this to -1 to preload " - "every file.")); - -ConfigVariableBool miles_audio_panda_threads -("miles-audio-panda-threads", true, - PRC_DESC("Set this true to service Miles background audio via Panda's " - "threading interface, instead of Miles' built-in threading " - "interface. This gives Panda more control over the threading, " - "and ensures better lock protection within Panda. This has " - "no meaning unless Panda is compiled with thread support.")); - -/** - * Initializes the library. This must be called at least once before any of - * the functions or classes in this library can be used. Normally it will be - * called by the static initializers and need not be called explicitly, but - * special cases exist. - */ -void -init_libMilesAudio() { - static bool initialized = false; - if (initialized) { - return; - } - initialized = true; - MilesAudioManager::init_type(); - MilesAudioSound::init_type(); - MilesAudioSample::init_type(); - MilesAudioSequence::init_type(); - MilesAudioStream::init_type(); - - PandaSystem *ps = PandaSystem::get_global_ptr(); - ps->add_system("Miles"); - ps->add_system("audio"); - ps->set_system_tag("audio", "implementation", "Miles"); -} - -/** - * This function is called when the dynamic library is loaded; it should - * return the Create_AudioManager function appropriate to create a - * MilesAudioManager. - */ -Create_AudioManager_proc * -get_audio_manager_func_miles_audio() { - init_libMilesAudio(); - return &Create_MilesAudioManager; -} - -#endif //] diff --git a/panda/src/audiotraits/config_milesAudio.h b/panda/src/audiotraits/config_milesAudio.h deleted file mode 100644 index abcb5b1261..0000000000 --- a/panda/src/audiotraits/config_milesAudio.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file config_milesAudio.h - * @author skyler - */ - -#ifndef CONFIG_MILESAUDIO_H -#define CONFIG_MILESAUDIO_H - -#include "pandabase.h" - -#ifdef HAVE_RAD_MSS //[ -#include "notifyCategoryProxy.h" -#include "configVariableBool.h" -#include "configVariableInt.h" -#include "dconfig.h" -#include "audioManager.h" - -ConfigureDecl(config_milesAudio, EXPCL_MILES_AUDIO, EXPTP_MILES_AUDIO); -NotifyCategoryDecl(milesAudio, EXPCL_MILES_AUDIO, EXPTP_MILES_AUDIO); - -extern ConfigVariableBool miles_audio_force_midi_reset; -extern ConfigVariableInt miles_audio_expand_mp3_threshold; -extern ConfigVariableInt miles_audio_preload_threshold; -extern ConfigVariableBool miles_audio_panda_threads; - -extern EXPCL_MILES_AUDIO void init_libMilesAudio(); -extern "C" EXPCL_MILES_AUDIO Create_AudioManager_proc *get_audio_manager_func_miles_audio(); -#endif //] - -#endif // CONFIG_MILESAUDIO_H diff --git a/panda/src/audiotraits/globalMilesManager.I b/panda/src/audiotraits/globalMilesManager.I deleted file mode 100644 index 2a1ae3028e..0000000000 --- a/panda/src/audiotraits/globalMilesManager.I +++ /dev/null @@ -1,39 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file globalMilesManager.I - * @author drose - * @date 2007-07-30 - */ - -/** - * Returns true if the Miles Sound System is open (and active), false if it is - * not. - */ -INLINE bool GlobalMilesManager:: -is_open() const { - return _is_open; -} - -/** - * Returns the number of sample handles that have been allocated. - */ -INLINE int GlobalMilesManager:: -get_num_samples() const { - LightMutexHolder holder(_samples_lock); - return _samples.size(); -} - -/** - * Returns the number of sequence handles that have been allocated. - */ -INLINE int GlobalMilesManager:: -get_num_sequences() const { - LightMutexHolder holder(_sequences_lock); - return _sequences.size(); -} diff --git a/panda/src/audiotraits/globalMilesManager.cxx b/panda/src/audiotraits/globalMilesManager.cxx deleted file mode 100644 index 796b099425..0000000000 --- a/panda/src/audiotraits/globalMilesManager.cxx +++ /dev/null @@ -1,446 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file globalMilesManager.cxx - * @author drose - * @date 2007-07-26 - */ - -#include "globalMilesManager.h" - -#ifdef HAVE_RAD_MSS //[ - -#include "lightMutexHolder.h" -#include "milesAudioManager.h" -#include "milesAudioSample.h" -#include "milesAudioSequence.h" - -#ifdef WIN32 -// For midiOutReset() -#include -#include -#endif - -using std::istream; -using std::string; - -GlobalMilesManager *GlobalMilesManager::_global_ptr; - -/** - * - */ -GlobalMilesManager:: -GlobalMilesManager() : - _managers_lock("GlobalMilesManager::_managers_lock"), - _samples_lock("GlobalMilesManager::_samples_lock"), - _sequences_lock("GlobalMilesManager::_sequences_lock") -{ - _digital_driver = 0; - _midi_driver = 0; - _dls_device = 0; - _dls_file = 0; - _is_open = false; -} - -/** - * Records a new MilesAudioManager in the world. This will open the Miles API - * when the first audio manager is added. - */ -void GlobalMilesManager:: -add_manager(MilesAudioManager *manager) { - LightMutexHolder holder(_managers_lock); - _managers.insert(manager); - if (!_is_open) { - open_api(); - } -} - -/** - * Records that a MilesAudioManager is destructing. This will clsoe the Miles - * API when the last audio manager is removed. - */ -void GlobalMilesManager:: -remove_manager(MilesAudioManager *manager) { - LightMutexHolder holder(_managers_lock); - _managers.erase(manager); - if (_managers.empty() && _is_open) { - close_api(); - } -} - -/** - * Calls cleanup() on all MilesAudioManagers, to cause a clean shutdown. - */ -void GlobalMilesManager:: -cleanup() { - LightMutexHolder holder(_managers_lock); - Managers::iterator mi; - for (mi = _managers.begin(); mi != _managers.end(); ++mi) { - (*mi)->cleanup(); - } -} - -/** - * Gets a sample handle from the global pool for the digital output device, to - * be used with the indicated AudioSound. - * - * If successful, sets the sample handle and the index (which should later be - * used to release the sample) and returns true. If unsuccessful (because - * there are no more available handles), returns false. - * - * This is a very limited resource; you should only get a sample just before - * playing a sound. - */ -bool GlobalMilesManager:: -get_sample(HSAMPLE &sample, size_t &index, MilesAudioSample *sound) { - LightMutexHolder holder(_samples_lock); - - for (size_t i = 0; i < _samples.size(); ++i) { - SampleData &smp = _samples[i]; - if (AIL_sample_status(smp._sample) == SMP_DONE) { - if (smp._sound != nullptr) { - // Tell the last sound that was using this sample that it's done now. - smp._sound->internal_stop(); - } - smp._sound = sound; - sample = smp._sample; - index = i; - return true; - } - } - - // No more already-allocated samples; get a new one from the system. - sample = AIL_allocate_sample_handle(_digital_driver); - if (sample == 0) { - return false; - } - - AIL_init_sample(sample, DIG_F_STEREO_16, 0); - index = _samples.size(); - - SampleData smp; - smp._sound = sound; - smp._sample = sample; - _samples.push_back(smp); - return true; -} - -/** - * Indicates that the indicated AudioSound no longer needs this sample. - */ -void GlobalMilesManager:: -release_sample(size_t index, MilesAudioSample *sound) { - LightMutexHolder holder(_samples_lock); - nassertv(index < _samples.size()); - - SampleData &smp = _samples[index]; - if (smp._sound == sound) { - smp._sound = nullptr; - } -} - -/** - * Gets a sequence handle from the global pool for the digital output device, - * to be used with the indicated AudioSound. - * - * If successful, sets the sequence handle and the index (which should later - * be used to release the sequence) and returns true. If unsuccessful - * (because there are no more available handles), returns false. - * - * This is a very limited resource; you should only get a sequence just before - * playing a sound. - */ -bool GlobalMilesManager:: -get_sequence(HSEQUENCE &sequence, size_t &index, MilesAudioSequence *sound) { - LightMutexHolder holder(_sequences_lock); - - for (size_t i = 0; i < _sequences.size(); ++i) { - SequenceData &seq = _sequences[i]; - if (AIL_sequence_status(seq._sequence) == SEQ_DONE) { - if (seq._sound != nullptr) { - // Tell the last sound that was using this sequence that it's done - // now. - seq._sound->internal_stop(); - } - seq._sound = sound; - sequence = seq._sequence; - index = i; - return true; - } - } - - // No more already-allocated sequences; get a new one from the system. - sequence = AIL_allocate_sequence_handle(_midi_driver); - if (sequence == 0) { - return false; - } - - index = _sequences.size(); - - SequenceData seq; - seq._sound = sound; - seq._sequence = sequence; - _sequences.push_back(seq); - return true; -} - -/** - * Indicates that the indicated AudioSound no longer needs this sequence. - */ -void GlobalMilesManager:: -release_sequence(size_t index, MilesAudioSequence *sound) { - LightMutexHolder holder(_sequences_lock); - nassertv(index < _sequences.size()); - - SequenceData &seq = _sequences[index]; - if (seq._sound == sound) { - seq._sound = nullptr; - } -} - -/** - * Sometimes Miles seems to leave midi notes hanging, even after stop is - * called, so call this method to perform an explicit reset using winMM.dll - * calls, just to ensure silence. - */ -void GlobalMilesManager:: -force_midi_reset() { - if (!miles_audio_force_midi_reset) { - audio_debug("MilesAudioManager::skipping force_midi_reset"); - return; - } - audio_debug("MilesAudioManager::force_midi_reset"); - -#ifdef WIN32 - if ((_midi_driver!=nullptr) && (_midi_driver->deviceid != MIDI_nullptr_DRIVER) && (_midi_driver->hMidiOut != nullptr)) { - audio_debug("MilesAudioManager::calling midiOutReset"); - midiOutReset(_midi_driver->hMidiOut); - } -#endif -} - -/** - * Returns the pointer to the one GlobalMilesManager object. - */ -GlobalMilesManager *GlobalMilesManager:: -get_global_ptr() { - if (_global_ptr == nullptr) { - _global_ptr = new GlobalMilesManager; - } - return _global_ptr; -} - -/** - * Called internally to initialize the Miles API. - */ -void GlobalMilesManager:: -open_api() { - audio_debug("GlobalMilesManager::open_api()") - nassertv(!_is_open); - - bool use_digital = (audio_play_wave || audio_play_mp3); - if (audio_play_midi && audio_software_midi) { - use_digital = true; - } - -#ifdef IS_OSX - audio_software_midi = true; -#endif - - audio_debug(" use_digital="<resolve_filename(dls_pathname, get_model_path()); - - _dls_data.clear(); - PT(VirtualFile) file = vfs->get_file(dls_pathname); - if (file == nullptr) { - milesAudio_cat.warning() - << "DLS file does not exist: " << dls_pathname << "\n"; - - } else if (!file->read_file(_dls_data, true)) { - milesAudio_cat.warning() - << "Could not read DLS file: " << dls_pathname << "\n"; - - } else if (_dls_data.empty()) { - milesAudio_cat.warning() - << "DLS file is empty: " << dls_pathname << "\n"; - - } else { - _dls_file = AIL_DLS_load_memory(_dls_device, &_dls_data[0], 0); - } - - if (_dls_file == 0) { - audio_error(" Could not get DLS file, switching to hardware MIDI."); - AIL_DLS_close(_dls_device, 0); - _dls_device = 0; - AIL_close_XMIDI_driver(_midi_driver); - _midi_driver = AIL_open_XMIDI_driver(0); - - } else { - audio_info(" using Miles software midi"); - } - } else { - _midi_driver = AIL_open_XMIDI_driver(0); - audio_info(" using Miles hardware midi"); - } - } - - _is_open = true; -} - -/** - * Called internally to shut down the Miles API. - */ -void GlobalMilesManager:: -close_api() { - audio_debug("GlobalMilesManager::close_api()") - nassertv(_is_open); - - Samples::iterator si; - for (si = _samples.begin(); si != _samples.end(); ++si) { - SampleData &smp = (*si); - AIL_release_sample_handle(smp._sample); - } - _samples.clear(); - - Sequences::iterator qi; - for (qi = _sequences.begin(); qi != _sequences.end(); ++qi) { - SequenceData &smp = (*qi); - AIL_release_sequence_handle(smp._sequence); - } - _sequences.clear(); - - if (_dls_file != 0) { - AIL_DLS_unload(_dls_device, _dls_file); - _dls_file = 0; - } - - if (_dls_device != 0) { - AIL_DLS_close(_dls_device, 0); - _dls_device = 0; - } - - if (_midi_driver != 0) { - AIL_close_XMIDI_driver(_midi_driver); - _midi_driver = 0; - } - - if (_digital_driver != 0) { - AIL_close_digital_driver(_digital_driver); - _digital_driver = 0; - } - - AIL_shutdown(); - - _is_open = false; -} - -/** - * This callback function is given to Miles to handle file I/O via the Panda - * VFS. It's only used to implemented streaming audio files, since in all - * other cases we open files directly. - */ -U32 AILCALLBACK GlobalMilesManager:: -open_callback(char const *filename, UINTa *file_handle) { - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - istream *strm = vfs->open_read_file(Filename::binary_filename(string(filename)), true); - if (strm == nullptr) { - // Failure. - return 0; - } - // Success. - (*file_handle) = (UINTa)strm; - return 1; -} - -/** - * This callback function is given to Miles to handle file I/O via the Panda - * VFS. - */ -void AILCALLBACK GlobalMilesManager:: -close_callback(UINTa file_handle) { - istream *strm = (istream *)file_handle; - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - vfs->close_read_file(strm); -} - -/** - * This callback function is given to Miles to handle file I/O via the Panda - * VFS. - */ -S32 AILCALLBACK GlobalMilesManager:: -seek_callback(UINTa file_handle, S32 offset, U32 type) { - istream *strm = (istream *)file_handle; - strm->clear(); - switch (type) { - case AIL_FILE_SEEK_BEGIN: - strm->seekg(offset, std::ios::beg); - break; - - case AIL_FILE_SEEK_CURRENT: - strm->seekg(offset, std::ios::cur); - break; - - case AIL_FILE_SEEK_END: - strm->seekg(offset, std::ios::end); - break; - } - - return strm->tellg(); -} - -/** - * This callback function is given to Miles to handle file I/O via the Panda - * VFS. - */ -U32 AILCALLBACK GlobalMilesManager:: -read_callback(UINTa file_handle, void *buffer, U32 bytes) { - istream *strm = (istream *)file_handle; - strm->read((char *)buffer, bytes); - return strm->gcount(); -} - -#endif //] diff --git a/panda/src/audiotraits/globalMilesManager.h b/panda/src/audiotraits/globalMilesManager.h deleted file mode 100644 index c48d48e5ba..0000000000 --- a/panda/src/audiotraits/globalMilesManager.h +++ /dev/null @@ -1,117 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file globalMilesManager.h - * @author drose - * @date 2007-07-26 - */ - -#ifndef GLOBALMILESMANAGER_H -#define GLOBALMILESMANAGER_H - -#include "pandabase.h" -#ifdef HAVE_RAD_MSS //[ - -#include "pset.h" -#include "lightMutex.h" -#include "lightMutexHolder.h" - -#include - -#ifndef UINTa -#define UINTa U32 -#endif - -#ifndef SINTa -#define SINTa S32 -#endif - -class MilesAudioSample; -class MilesAudioSequence; - -/** - * This is a wrapper around the parts of the Miles API that should only be - * created once. This represents the global data common to all - * MilesAudioManagers. - */ -class EXPCL_MILES_AUDIO GlobalMilesManager { -private: - GlobalMilesManager(); - -public: - void add_manager(MilesAudioManager *manager); - void remove_manager(MilesAudioManager *manager); - void cleanup(); - INLINE bool is_open() const; - - bool get_sample(HSAMPLE &sample, size_t &index, MilesAudioSample *sound); - void release_sample(size_t index, MilesAudioSample *sound); - INLINE int get_num_samples() const; - - bool get_sequence(HSEQUENCE &sequence, size_t &index, MilesAudioSequence *sound); - void release_sequence(size_t index, MilesAudioSequence *sound); - INLINE int get_num_sequences() const; - - void force_midi_reset(); - - static GlobalMilesManager *get_global_ptr(); - -public: - HDIGDRIVER _digital_driver; - HMDIDRIVER _midi_driver; - - // For software MIDI: - HDLSDEVICE _dls_device; - HDLSFILEID _dls_file; - vector_uchar _dls_data; - -private: - void open_api(); - void close_api(); - - static U32 AILCALLBACK open_callback(char const *filename, UINTa *file_handle); - static void AILCALLBACK close_callback(UINTa file_handle); - static S32 AILCALLBACK seek_callback(UINTa file_handle, S32 offset, U32 type); - static U32 AILCALLBACK read_callback(UINTa file_handle, void *buffer, U32 bytes); - - -private: - bool _is_open; - - typedef pset Managers; - Managers _managers; - LightMutex _managers_lock; - - class SampleData { - public: - HSAMPLE _sample; - MilesAudioSample *_sound; - }; - - typedef pvector Samples; - Samples _samples; - LightMutex _samples_lock; - - class SequenceData { - public: - HSEQUENCE _sequence; - MilesAudioSequence *_sound; - }; - - typedef pvector Sequences; - Sequences _sequences; - LightMutex _sequences_lock; - - static GlobalMilesManager *_global_ptr; -}; - -#include "globalMilesManager.I" - -#endif //] - -#endif diff --git a/panda/src/audiotraits/milesAudioManager.cxx b/panda/src/audiotraits/milesAudioManager.cxx deleted file mode 100644 index 23221cc565..0000000000 --- a/panda/src/audiotraits/milesAudioManager.cxx +++ /dev/null @@ -1,1121 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file milesAudioManager.cxx - * @author skyler - * @date 2001-06-06 - * Prior system by: cary - */ - -#include "milesAudioManager.h" - -#ifdef HAVE_RAD_MSS //[ - -#include "milesAudioSound.h" -#include "milesAudioSample.h" -#include "milesAudioStream.h" -#include "globalMilesManager.h" -#include "config_audio.h" -#include "config_putil.h" -#include "config_express.h" -#include "virtualFileSystem.h" -#include "nullAudioSound.h" -#include "string_utils.h" -#include "mutexHolder.h" -#include "lightReMutexHolder.h" - -#include - -using std::string; - - -TypeHandle MilesAudioManager::_type_handle; - -AudioManager *Create_MilesAudioManager() { - audio_debug("Create_MilesAudioManager()"); - return new MilesAudioManager(); -} - -/** - * Create an audio manager. This may open the Miles sound system if there - * were no other MilesAudioManager instances. Subsequent managers may use the - * same Miles resources. - */ -MilesAudioManager:: -MilesAudioManager() : - _lock("MilesAudioManager::_lock"), - _streams_lock("MilesAudioManager::_streams_lock"), - _streams_cvar(_streams_lock) -{ - audio_debug("MilesAudioManager::MilesAudioManager(), this = " - << (void *)this); - GlobalMilesManager::get_global_ptr()->add_manager(this); - audio_debug(" audio_active="<cleanup(); - audio_debug("shutdown() finished"); -} - -/** - * This is mostly for debugging, but it it could be used to detect errors in a - * release build if you don't mind the cpu cost. - */ -bool MilesAudioManager:: -is_valid() { - LightReMutexHolder holder(_lock); - return do_is_valid(); -} - -/** - * - */ -PT(AudioSound) MilesAudioManager:: -get_sound(const Filename &file_name, bool, int) { - LightReMutexHolder holder(_lock); - audio_debug("MilesAudioManager::get_sound(file_name=\""<resolve_filename(path, get_model_path()); - audio_debug("Reading "<= (unsigned int)_cache_limit) { - uncache_a_sound(); - } - // Put it in the pool: The following is roughly like: _sounds[path] = - // sd; But, it gives us an iterator into the map. - std::pair ib - = _sounds.insert(SoundMap::value_type(path, sd)); - if (!ib.second) { - // The insert failed. - audio_debug(" failed map insert of "<_file_type == AILFILETYPE_MIDI || - sd->_file_type == AILFILETYPE_XMIDI || - sd->_file_type == AILFILETYPE_XMIDI_DLS || - sd->_file_type == AILFILETYPE_XMIDI_MLS) { - // MIDI file. - audioSound = new MilesAudioSequence(this, sd, file_name); - - } else if (!sd->_raw_data.empty()) { - // WAV or MP3 file preloaded into memory. - audioSound = new MilesAudioSample(this, sd, file_name); - - } else { - // WAV or MP3 file streamed from disk. - audioSound = new MilesAudioStream(this, file_name, path); - } - - audioSound->set_active(_active); - - bool inserted = _sounds_on_loan.insert((MilesAudioSound *)audioSound.p()).second; - nassertr(inserted, audioSound); - - _hasMidiSounds |= (file_name.find(".mid")!=string::npos); - } else { - // Couldn't load the file; just return a NullAudioSound. - audioSound = new NullAudioSound; - } - - audio_debug(" returning 0x" << (void*)audioSound); - nassertr(do_is_valid(), nullptr); - return audioSound; -} - -/** - * - */ -PT(AudioSound) MilesAudioManager:: -get_sound(MovieAudio *sound, bool, int) { - nassert_raise("Miles audio manager does not support MovieAudio sources."); - return nullptr; -} - -/** - * - */ -void MilesAudioManager:: -uncache_sound(const Filename &file_name) { - audio_debug("MilesAudioManager::uncache_sound(file_name=\"" - <resolve_filename(path, get_model_path()); - - audio_debug(" path=\""< 0); - LRU::iterator lru_i = find(_lru.begin(), _lru.end(), &(i->first)); - nassertv(lru_i != _lru.end()); - _lru.erase(lru_i); - _sounds.erase(i); - } - nassertv(do_is_valid()); -} - -/** - * Clear out the sound cache. - */ -void MilesAudioManager:: -clear_cache() { - audio_debug("MilesAudioManager::clear_cache()"); - LightReMutexHolder holder(_lock); - do_clear_cache(); -} - -/** - * Set the number of sounds that the cache can hold. - */ -void MilesAudioManager:: -set_cache_limit(unsigned int count) { - LightReMutexHolder holder(_lock); - - audio_debug("MilesAudioManager::set_cache_limit(count="< count) { - uncache_a_sound(); - } - _cache_limit=count; - nassertv(do_is_valid()); -} - -/** - * - */ -unsigned int MilesAudioManager:: -get_cache_limit() const { - return _cache_limit; -} - -/** - * set the overall volume - */ -void MilesAudioManager:: -set_volume(PN_stdfloat volume) { - audio_debug("MilesAudioManager::set_volume(volume="<set_volume((*i)->get_volume()); - } - } -} - -/** - * get the overall volume - */ -PN_stdfloat MilesAudioManager:: -get_volume() const { - return _volume; -} - -/** - * set the overall play rate - */ -void MilesAudioManager:: -set_play_rate(PN_stdfloat play_rate) { - audio_debug("MilesAudioManager::set_play_rate(play_rate="<set_play_rate((*i)->get_play_rate()); - } - } -} - -/** - * get the overall speed/pitch/play rate - */ -PN_stdfloat MilesAudioManager:: -get_play_rate() const { - return _play_rate; -} - -/** - * turn on/off - */ -void MilesAudioManager:: -set_active(bool active) { - audio_debug("MilesAudioManager::set_active(flag="<set_active(_active); - } - - if ((!_active) && _hasMidiSounds) { - GlobalMilesManager::get_global_ptr()->force_midi_reset(); - } - } -} - -/** - * - */ -bool MilesAudioManager:: -get_active() const { - return _active; -} - -/** - * - */ -void MilesAudioManager:: -set_concurrent_sound_limit(unsigned int limit) { - LightReMutexHolder holder(_lock); - _concurrent_sound_limit = limit; - do_reduce_sounds_playing_to(_concurrent_sound_limit); -} - -/** - * - */ -unsigned int MilesAudioManager:: -get_concurrent_sound_limit() const { - return _concurrent_sound_limit; -} - -/** - * - */ -void MilesAudioManager:: -reduce_sounds_playing_to(unsigned int count) { - LightReMutexHolder holder(_lock); - do_reduce_sounds_playing_to(count); -} - -/** - * Stop playback on all sounds managed by this manager. - */ -void MilesAudioManager:: -stop_all_sounds() { - audio_debug("MilesAudioManager::stop_all_sounds()"); - reduce_sounds_playing_to(0); -} - -/** - * Set spatial attributes of the listener for 3D sounds. Note that Y and Z - * are switched to translate into Miles's coordinate system. - */ -void MilesAudioManager::audio_3d_set_listener_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz, PN_stdfloat fx, PN_stdfloat fy, PN_stdfloat fz, PN_stdfloat ux, PN_stdfloat uy, PN_stdfloat uz) { - audio_debug("MilesAudioManager::audio_3d_set_listener_attributes()"); - - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - AIL_set_listener_3D_position(mgr->_digital_driver, px, pz, py); - AIL_set_listener_3D_velocity_vector(mgr->_digital_driver, vx, vz, vy); - AIL_set_listener_3D_orientation(mgr->_digital_driver, fx, fz, fy, ux, uz, uy); -} - -/** - * Get spatial attributes of the listener for 3D sounds. Note that Y and Z - * are switched to translate from Miles's coordinate system. - */ -void MilesAudioManager::audio_3d_get_listener_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz, PN_stdfloat *fx, PN_stdfloat *fy, PN_stdfloat *fz, PN_stdfloat *ux, PN_stdfloat *uy, PN_stdfloat *uz) { - audio_debug("MilesAudioManager::audio_3d_get_listener_attributes()"); - - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - float lpx, lpy, lpz, lvx, lvy, lvz, lfx, lfy, lfz, lux, luy, luz; - AIL_listener_3D_position(mgr->_digital_driver, &lpx, &lpz, &lpy); - AIL_listener_3D_velocity(mgr->_digital_driver, &lvx, &lvz, &lvy); - AIL_listener_3D_orientation(mgr->_digital_driver, &lfx, &lfz, &lfy, &lux, &luz, &luy); - - *px = lpx; - *py = lpy; - *pz = lpz; - *vx = lvx; - *vy = lvy; - *vz = lvz; - *fx = lfx; - *fy = lfy; - *fz = lfz; - *ux = lux; - *uy = luy; - *uz = luz; -} - -/** - * Set factor to allow user to easily work in a different scale. 1.0 - * represents meters. - */ -void MilesAudioManager::audio_3d_set_distance_factor(PN_stdfloat factor) { - audio_debug("MilesAudioManager::audio_3d_set_distance_factor( factor= " << factor << ")"); - - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - AIL_set_3D_distance_factor(mgr->_digital_driver, factor); -} - -/** - * Get factor controlling working units. - */ -PN_stdfloat MilesAudioManager::audio_3d_get_distance_factor() const { - audio_debug("MilesAudioManager::audio_3d_get_distance_factor()"); - - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - return AIL_3D_distance_factor(mgr->_digital_driver); -} - -/** - * Exaggerates or diminishes the Doppler effect. Defaults to 1.0 - */ -void MilesAudioManager::audio_3d_set_doppler_factor(PN_stdfloat factor) { - audio_debug("MilesAudioManager::audio_3d_set_doppler_factor(factor="<_digital_driver, factor); -} - -/** - * Get the factor controlling the Doppler effect. - */ -PN_stdfloat MilesAudioManager::audio_3d_get_doppler_factor() const { - audio_debug("MilesAudioManager::audio_3d_get_doppler_factor()"); - - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - return AIL_3D_doppler_factor(mgr->_digital_driver); -} - -/** - * Control the effect distance has on audability. Defaults to 1.0 - */ -void MilesAudioManager::audio_3d_set_drop_off_factor(PN_stdfloat factor) { - audio_debug("MilesAudioManager::audio_3d_set_drop_off_factor("<_digital_driver, factor); -} - -/** - * Get the factor controlling how quickly sound falls off with distance. - */ -PN_stdfloat MilesAudioManager::audio_3d_get_drop_off_factor() const { - audio_debug("MilesAudioManager::audio_3d_get_drop_off_factor()"); - - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - return AIL_3D_rolloff_factor(mgr->_digital_driver); -} - -/** - * Works similarly to MilesAudioSound::set_speaker_levels, but specifies the - * 3D positions of the speakers in space. - * - * Once a NULL value is found for a speaker position, no more speaker - * positions will be used. - * - * Note that Y and Z are switched to translate from Miles's coordinate system. - */ -void MilesAudioManager:: -set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2, LVecBase3 *speaker3, LVecBase3 *speaker4, LVecBase3 *speaker5, LVecBase3 *speaker6, LVecBase3 *speaker7, LVecBase3 *speaker8, LVecBase3 *speaker9) { - audio_debug("MilesAudioManager::set_speaker_configuration()"); - - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - - MSSVECTOR3D speakers[9]; - - if(speaker1 != nullptr) { - speakers[0].x = speaker1->get_x(); - speakers[0].y = speaker1->get_z(); - speakers[0].z = speaker1->get_y(); - } - if(speaker2 != nullptr) { - speakers[1].x = speaker2->get_x(); - speakers[1].y = speaker2->get_z(); - speakers[1].z = speaker2->get_y(); - } - if(speaker3 != nullptr) { - speakers[2].x = speaker3->get_x(); - speakers[2].y = speaker3->get_z(); - speakers[2].z = speaker3->get_y(); - } - if(speaker4 != nullptr) { - speakers[3].x = speaker4->get_x(); - speakers[3].y = speaker4->get_z(); - speakers[3].z = speaker4->get_y(); - } - if(speaker5 != nullptr) { - speakers[4].x = speaker5->get_x(); - speakers[4].y = speaker5->get_z(); - speakers[4].z = speaker5->get_y(); - } - if(speaker6 != nullptr) { - speakers[5].x = speaker6->get_x(); - speakers[5].y = speaker6->get_z(); - speakers[5].z = speaker6->get_y(); - } - if(speaker7 != nullptr) { - speakers[6].x = speaker7->get_x(); - speakers[6].y = speaker7->get_z(); - speakers[6].z = speaker7->get_y(); - } - if(speaker8 != nullptr) { - speakers[7].x = speaker8->get_x(); - speakers[7].y = speaker8->get_z(); - speakers[7].z = speaker8->get_y(); - } - if(speaker9 != nullptr) { - speakers[8].x = speaker9->get_x(); - speakers[8].y = speaker9->get_z(); - speakers[8].z = speaker9->get_y(); - } - - if(speaker1 == nullptr) { - audio_error("No valid speaker positions specified in MilesAudioManager::set_speaker_configuration()."); - } else if(speaker2 == nullptr) { - AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 1, 1.0); - } else if(speaker3 == nullptr) { - AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 2, 1.0); - } else if(speaker4 == nullptr) { - AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 3, 1.0); - } else if(speaker5 == nullptr) { - AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 4, 1.0); - } else if(speaker6 == nullptr) { - AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 5, 1.0); - } else if(speaker7 == nullptr) { - AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 6, 1.0); - } else if(speaker8 == nullptr) { - AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 7, 1.0); - } else if(speaker9 == nullptr) { - AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 8, 1.0); - } else { - AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 9, 1.0); - } -} - -/** - * Must be called every frame. Failure to call this every frame could cause - * problems for some audio managers. - */ -void MilesAudioManager:: -update() { - { - MutexHolder holder(_streams_lock); - if (_stream_thread.is_null() && !_streams.empty()) { - // If we don't have a sub-thread, we have to service the streams in the - // main thread. - do_service_streams(); - } - } - - if (_sounds_finished) { - _sounds_finished = false; - - // If the _sounds_finished flag was set, we should scan our list of - // playing sounds and see if any of them have finished recently. We don't - // do this in the finished callback, because that might have been called - // in a sub-thread (and we may not have threading supported--and mutex - // protection--compiled in). - - SoundsPlaying::iterator si = _sounds_playing.begin(); - while (si != _sounds_playing.end()) { - MilesAudioSound *sound = (*si); - ++si; - - if (sound->status() == AudioSound::READY) { - sound->stop(); - } - } - } -} - -/** - * - */ -void MilesAudioManager:: -release_sound(MilesAudioSound *audioSound) { - audio_debug("MilesAudioManager::release_sound(audioSound=\"" - <get_name()<<"\"), this = " << (void *)this); - LightReMutexHolder holder(_lock); - AudioSet::iterator ai = _sounds_on_loan.find(audioSound); - if (ai != _sounds_on_loan.end()) { - _sounds_on_loan.erase(ai); - } - - audio_debug("MilesAudioManager::release_sound() finished"); -} - -/** - * Shuts down the audio manager and releases any resources associated with it. - * Also cleans up all AudioSounds created via the manager. - */ -void MilesAudioManager:: -cleanup() { - audio_debug("MilesAudioManager::cleanup(), this = " << (void *)this - << ", _cleanup_required = " << _cleanup_required); - LightReMutexHolder holder(_lock); - if (!_cleanup_required) { - return; - } - - // Be sure to cleanup associated sounds before cleaning up the manager: - AudioSet orig_sounds; - orig_sounds.swap(_sounds_on_loan); - AudioSet::iterator ai; - for (ai = orig_sounds.begin(); ai != orig_sounds.end(); ++ai) { - (*ai)->cleanup(); - } - - do_clear_cache(); - - // Now stop the thread, if it has been started. - if (!_stream_thread.is_null()) { - milesAudio_cat.info() - << "Stopping audio streaming thread.\n"; - PT(StreamThread) old_thread; - { - MutexHolder holder(_streams_lock); - nassertv(!_stream_thread.is_null()); - _stream_thread->_keep_running = false; - _streams_cvar.notify(); - old_thread = _stream_thread; - _stream_thread.clear(); - } - old_thread->join(); - } - - _cleanup_required = false; - audio_debug("MilesAudioManager::cleanup() finished"); -} - -/** - * - */ -void MilesAudioManager:: -output(std::ostream &out) const { - LightReMutexHolder holder(_lock); - out << get_type() << ": " << _sounds_playing.size() - << " / " << _sounds_on_loan.size() << " sounds playing / total"; -} - -/** - * - */ -void MilesAudioManager:: -write(std::ostream &out) const { - LightReMutexHolder holder(_lock); - - out << (*this) << "\n"; - AudioSet::const_iterator ai; - for (ai = _sounds_on_loan.begin(); ai != _sounds_on_loan.end(); ++ai) { - MilesAudioSound *sound = (*ai); - out << " " << *sound << "\n"; - } - - size_t total_preload = 0; - size_t num_preloaded = 0; - SoundMap::const_iterator si; - for (si = _sounds.begin(); si != _sounds.end(); ++si) { - if (!(*si).second->_raw_data.empty()) { - ++num_preloaded; - total_preload += (*si).second->_raw_data.size(); - } - } - out << num_preloaded << " of " << _sounds.size() << " sounds preloaded, size used is " << (total_preload + 1023) / 1024 << "K\n"; - - { - MutexHolder holder(_streams_lock); - out << _streams.size() << " streams opened.\n"; - if (!_stream_thread.is_null()) { - out << "(Audio streaming thread has been started.)\n"; - } - } - - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - - int num_samples = mgr->get_num_samples(); - out << num_samples << " sample handles allocated globally.\n"; - - int num_sequences = mgr->get_num_sequences(); - out << num_sequences << " sequence handles allocated globally.\n"; -} - - -/** - * Implementation of is_valid(). Assumes the lock is already held. - */ -bool MilesAudioManager:: -do_is_valid() { - bool check = true; - if (_sounds.size() != _lru.size()) { - audio_debug("-- Error _sounds.size() != _lru.size() --"); - check = false; - - } else { - LRU::const_iterator i = _lru.begin(); - for (; i != _lru.end(); ++i) { - SoundMap::const_iterator smi = _sounds.find(**i); - if (smi == _sounds.end()) { - audio_debug("-- "<<**i<<" in _lru and not in _sounds --"); - check = false; - break; - } - } - } - return _is_valid && check; -} - -/** - * Assumes the lock is already held. - */ -void MilesAudioManager:: -do_reduce_sounds_playing_to(unsigned int count) { - int limit = _sounds_playing.size() - count; - while (limit-- > 0) { - SoundsPlaying::iterator sound = _sounds_playing.begin(); - assert(sound != _sounds_playing.end()); - (**sound).stop(); - } -} - -/** - * Assumes the lock is already held. - */ -void MilesAudioManager:: -do_clear_cache() { - if (_is_valid) { nassertv(do_is_valid()); } - _sounds.clear(); - _lru.clear(); - if (_is_valid) { nassertv(do_is_valid()); } -} - -/** - * Adds the indicated stream to the list of streams to be serviced by a Panda - * sub-thread. This is in lieu of Miles' auto-service-stream mechanism. - */ -void MilesAudioManager:: -start_service_stream(HSTREAM stream) { - MutexHolder holder(_streams_lock); - nassertv(find(_streams.begin(), _streams.end(), stream) == _streams.end()); - _streams.push_back(stream); - _streams_cvar.notify(); - - if (_stream_thread.is_null() && Thread::is_threading_supported()) { - milesAudio_cat.info() - << "Starting audio streaming thread.\n"; - _stream_thread = new StreamThread(this); - _stream_thread->start(TP_low, true); - } -} - -/** - * Removes the indicated stream from the list of streams to be serviced by a - * Panda sub-thread. - */ -void MilesAudioManager:: -stop_service_stream(HSTREAM stream) { - MutexHolder holder(_streams_lock); - Streams::iterator si = find(_streams.begin(), _streams.end(), stream); - if (si != _streams.end()) { - _streams.erase(si); - } -} - - -/** - * Assumes the lock is already held. - */ -void MilesAudioManager:: -most_recently_used(const string &path) { - audio_debug("MilesAudioManager::most_recently_used(path=\"" - <0); - LRU::reference path=_lru.front(); - SoundMap::iterator i = _sounds.find(*path); - assert(i != _sounds.end()); - _lru.pop_front(); - - if (i != _sounds.end()) { - audio_debug(" uncaching \""<first<<"\""); - _sounds.erase(i); - } - nassertv(do_is_valid()); -} - -/** - * Inform the manager that a sound is about to play. - */ -void MilesAudioManager:: -starting_sound(MilesAudioSound *audio) { - LightReMutexHolder holder(_lock); - if (_concurrent_sound_limit) { - do_reduce_sounds_playing_to(_concurrent_sound_limit); - } - _sounds_playing.insert(audio); -} - -/** - * Inform the manager that a sound is finished or someone called stop on the - * sound (this should not be called if a sound is only paused). - */ -void MilesAudioManager:: -stopping_sound(MilesAudioSound *audio) { - LightReMutexHolder holder(_lock); - _sounds_playing.erase(audio); - if (_hasMidiSounds && _sounds_playing.size() == 0) { - GlobalMilesManager::get_global_ptr()->force_midi_reset(); - } -} - -/** - * Reads a sound file and allocates a SoundData pointer for it. Returns NULL - * if the sound file cannot be loaded. - * - * Assumes the lock is already held. - */ -PT(MilesAudioManager::SoundData) MilesAudioManager:: -load(const Filename &file_name) { - PT(SoundData) sd = new SoundData; - - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - PT(VirtualFile) file = vfs->get_file(file_name); - if (file == nullptr) { - milesAudio_cat.warning() - << "No such file: " << file_name << "\n"; - return nullptr; - } - - if (file->get_file_size() == 0) { - milesAudio_cat.warning() - << "File " << file_name << " is empty\n"; - return nullptr; - } - - sd->_basename = file_name.get_basename(); - - string extension = sd->_basename.get_extension(); - if (extension == "pz" || extension == "gz") { - extension = Filename(sd->_basename.get_basename_wo_extension()).get_extension(); - } - - bool is_midi_file = (downcase(extension) == "mid"); - - if ((miles_audio_preload_threshold == -1 || file->get_file_size() < (std::streamsize)miles_audio_preload_threshold) || - is_midi_file) { - // If the file is sufficiently small, we'll preload it into memory. MIDI - // files cannot be streamed, so we always preload them, regardless of - // size. - - if (!file->read_file(sd->_raw_data, true)) { - milesAudio_cat.warning() - << "Unable to read " << file_name << "\n"; - return nullptr; - } - - sd->_file_type = - AIL_file_type(&sd->_raw_data[0], sd->_raw_data.size()); - - if (sd->_file_type == AILFILETYPE_MIDI) { - // A standard MIDI file. We have to convert this to XMIDI for Miles. - void *xmi; - U32 xmi_size; - if (AIL_MIDI_to_XMI(&sd->_raw_data[0], sd->_raw_data.size(), - &xmi, &xmi_size, 0)) { - audio_debug("converted " << sd->_basename << " from standard MIDI (" - << sd->_raw_data.size() << " bytes) to XMIDI (" - << xmi_size << " bytes)"); - - // Copy the data to our own buffer and free the Miles-allocated data. - sd->_raw_data.clear(); - sd->_raw_data.insert(sd->_raw_data.end(), - (unsigned char *)xmi, (unsigned char *)xmi + xmi_size); - AIL_mem_free_lock(xmi); - sd->_file_type = AILFILETYPE_XMIDI; - } else { - milesAudio_cat.warning() - << "Could not convert " << sd->_basename << " to XMIDI.\n"; - } - } - - bool expand_to_wav = false; - - if (sd->_file_type != AILFILETYPE_MPEG_L3_AUDIO) { - audio_debug(sd->_basename << " is not an mp3 file."); - } else if ((int)sd->_raw_data.size() >= miles_audio_expand_mp3_threshold) { - audio_debug(sd->_basename << " is too large to expand in-memory."); - } else { - audio_debug(sd->_basename << " will be expanded in-memory."); - expand_to_wav = true; - } - - if (expand_to_wav) { - // Now convert the file to WAV format in-memory. This is useful to work - // around seek and length problems associated with variable bit-rate MP3 - // encoding. - void *wav_data; - U32 wav_data_size; - if (AIL_decompress_ASI(&sd->_raw_data[0], sd->_raw_data.size(), - sd->_basename.c_str(), &wav_data, &wav_data_size, - nullptr)) { - audio_debug("expanded " << sd->_basename << " from " << sd->_raw_data.size() - << " bytes to " << wav_data_size << " bytes."); - - if (wav_data_size != 0) { - // Now copy the memory into our own buffers, and free the Miles- - // allocated memory. - sd->_raw_data.clear(); - sd->_raw_data.insert(sd->_raw_data.end(), - (unsigned char *)wav_data, (unsigned char *)wav_data + wav_data_size); - sd->_file_type = AILFILETYPE_PCM_WAV; - sd->_basename.set_extension("wav"); - } - AIL_mem_free_lock(wav_data); - - } else { - audio_debug("unable to expand " << sd->_basename); - } - } - - } else { - // If the file is large, we'll stream it from disk instead of preloading - // it. This means we don't need to load any data at this point. - } - - return sd; -} - -/** - * Called to service the streaming audio channels currently playing on the - * audio manager. - */ -void MilesAudioManager:: -thread_main(volatile bool &keep_running) { - MutexHolder holder(_streams_lock); - - while (keep_running) { - if (_streams.empty()) { - // If there are no streams to service, block on the condition variable. - _streams_cvar.wait(); - } else { - do_service_streams(); - } - - // Now yield to be polite to the main application. - _streams_lock.release(); - Thread::force_yield(); - _streams_lock.acquire(); - } -} - -/** - * Internal function to service all the streams. Assumes _streams_lock is - * already held. - */ -void MilesAudioManager:: -do_service_streams() { - size_t i = 0; - while (i < _streams.size()) { - HSTREAM stream = _streams[i]; - - // We must release the lock while we are servicing stream i. - _streams_lock.release(); - AIL_service_stream(stream, 0); - Thread::consider_yield(); - _streams_lock.acquire(); - - ++i; - } -} - -/** - * - */ -MilesAudioManager::StreamThread:: -StreamThread(MilesAudioManager *mgr) : - Thread("StreamThread", "StreamThread"), - _mgr(mgr) -{ - _keep_running = true; -} - -/** - * - */ -void MilesAudioManager::StreamThread:: -thread_main() { - _mgr->thread_main(_keep_running); -} - -/** - * - */ -MilesAudioManager::SoundData:: -SoundData() : - _raw_data(MilesAudioManager::get_class_type()), - _has_length(false), - _length(0.0f) -{ -} - -/** - * - */ -MilesAudioManager::SoundData:: -~SoundData() { -} - -/** - * - */ -PN_stdfloat MilesAudioManager::SoundData:: -get_length() { - if (!_has_length) { - // Time to determine the length of the file. - - if (_raw_data.empty()) { - _length = 0.0f; - _has_length = true; - - } else if (_file_type == AILFILETYPE_MPEG_L3_AUDIO) { - // If it's an mp3 file, we have to calculate its length by walking - // through all of its frames. - audio_debug("Computing length of mp3 file " << _basename); - - MP3_INFO info; - AIL_inspect_MP3(&info, &_raw_data[0], _raw_data.size()); - _length = 0.0f; - while (AIL_enumerate_MP3_frames(&info)) { - _length += info.data_size * 8.0f / info.bit_rate; - } - _has_length = true; - - } else if (_file_type == AILFILETYPE_PCM_WAV || - _file_type == AILFILETYPE_ADPCM_WAV || - _file_type == AILFILETYPE_OTHER_ASI_WAV) { - audio_debug("Getting length of wav file " << _basename); - - AILSOUNDINFO info; - if (AIL_WAV_info(&_raw_data[0], &info)) { - _length = (PN_stdfloat)info.samples / (PN_stdfloat)info.rate; - audio_debug(info.samples << " samples at " << info.rate - << "; length is " << _length << " seconds."); - _has_length = true; - } - } - } - - nassertr(_has_length, 0.0f); - return _length; -} - -/** - * Records the sample length, as determined externally. - */ -void MilesAudioManager::SoundData:: -set_length(PN_stdfloat length) { - _length = length; - _has_length = true; -} - -#endif //] diff --git a/panda/src/audiotraits/milesAudioManager.h b/panda/src/audiotraits/milesAudioManager.h deleted file mode 100644 index b3df5adad8..0000000000 --- a/panda/src/audiotraits/milesAudioManager.h +++ /dev/null @@ -1,198 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file milesAudioManager.h - * @author skyler - * @date 2001-06-06 - * Prior system by: cary - */ - -#ifndef __MILES_AUDIO_MANAGER_H__ //[ -#define __MILES_AUDIO_MANAGER_H__ - -#include "pandabase.h" -#ifdef HAVE_RAD_MSS //[ - -#include "audioManager.h" -#include "pset.h" -#include "pmap.h" -#include "pdeque.h" -#include "pvector.h" -#include "thread.h" -#include "pmutex.h" -#include "lightReMutex.h" -#include "conditionVar.h" -#include "vector_uchar.h" - -#include - -class MilesAudioSound; - -class EXPCL_MILES_AUDIO MilesAudioManager: public AudioManager { -public: - // See AudioManager.h for documentation. - - MilesAudioManager(); - virtual ~MilesAudioManager(); - - virtual void shutdown(); - - virtual bool is_valid(); - - virtual PT(AudioSound) get_sound(const Filename &file_name, bool positional = false, int mode=SM_heuristic); - virtual PT(AudioSound) get_sound(MovieAudio *sound, bool positional = false, int mode=SM_heuristic); - virtual void uncache_sound(const Filename &file_name); - virtual void clear_cache(); - virtual void set_cache_limit(unsigned int count); - virtual unsigned int get_cache_limit() const; - - virtual void set_volume(PN_stdfloat volume); - virtual PN_stdfloat get_volume() const; - - void set_play_rate(PN_stdfloat play_rate); - PN_stdfloat get_play_rate() const; - - virtual void set_active(bool active); - virtual bool get_active() const; - - virtual void set_concurrent_sound_limit(unsigned int limit = 0); - virtual unsigned int get_concurrent_sound_limit() const; - - virtual void reduce_sounds_playing_to(unsigned int count); - virtual void stop_all_sounds(); - - virtual void update(); - - // Tell the manager that the sound dtor was called. - void release_sound(MilesAudioSound *audioSound); - void cleanup(); - - // 3D spatialized sound support. Spatialized sound was originally added for - // FMOD, so there are parts of the interface in the Miles implementation - // that are a little more awkward than they would be otherwise. - virtual void audio_3d_set_listener_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat xy, PN_stdfloat xz, PN_stdfloat fx, PN_stdfloat fy, PN_stdfloat fz, PN_stdfloat ux, PN_stdfloat uy, PN_stdfloat uz); - virtual void audio_3d_get_listener_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz, PN_stdfloat *fx, PN_stdfloat *fy, PN_stdfloat *fz, PN_stdfloat *ux, PN_stdfloat *uy, PN_stdfloat *uz); - virtual void audio_3d_set_distance_factor(PN_stdfloat factor); - virtual PN_stdfloat audio_3d_get_distance_factor() const; - virtual void audio_3d_set_doppler_factor(PN_stdfloat factor); - virtual PN_stdfloat audio_3d_get_doppler_factor() const; - virtual void audio_3d_set_drop_off_factor(PN_stdfloat factor); - virtual PN_stdfloat audio_3d_get_drop_off_factor() const; - virtual void set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2=nullptr, LVecBase3 *speaker3=nullptr, LVecBase3 *speaker4=nullptr, LVecBase3 *speaker5=nullptr, LVecBase3 *speaker6=nullptr, LVecBase3 *speaker7=nullptr, LVecBase3 *speaker8=nullptr, LVecBase3 *speaker9=nullptr); - - virtual void output(std::ostream &out) const; - virtual void write(std::ostream &out) const; - -private: - bool do_is_valid(); - void do_reduce_sounds_playing_to(unsigned int count); - void do_clear_cache(); - - void start_service_stream(HSTREAM stream); - void stop_service_stream(HSTREAM stream); - - void most_recently_used(const std::string &path); - void uncache_a_sound(); - - void starting_sound(MilesAudioSound *audio); - void stopping_sound(MilesAudioSound *audio); - - class SoundData; - PT(SoundData) load(const Filename &file_name); - - void thread_main(volatile bool &keep_running); - void do_service_streams(); - -private: - class StreamThread : public Thread { - public: - StreamThread(MilesAudioManager *mgr); - virtual void thread_main(); - - MilesAudioManager *_mgr; - volatile bool _keep_running; - }; - - // The sound cache: - class SoundData : public ReferenceCount { - public: - SoundData(); - ~SoundData(); - PN_stdfloat get_length(); - void set_length(PN_stdfloat length); - - Filename _basename; - S32 _file_type; - vector_uchar _raw_data; - bool _has_length; - PN_stdfloat _length; // in seconds. - }; - typedef pmap SoundMap; - SoundMap _sounds; - - typedef pset AudioSet; - // The offspring of this manager: - AudioSet _sounds_on_loan; - - typedef pset SoundsPlaying; - // The sounds from this manager that are currently playing: - SoundsPlaying _sounds_playing; - - // The Least Recently Used mechanism: - typedef pdeque LRU; - LRU _lru; - // State: - PN_stdfloat _volume; - PN_stdfloat _play_rate; - bool _active; - int _cache_limit; - bool _cleanup_required; - unsigned int _concurrent_sound_limit; - - bool _is_valid; - bool _hasMidiSounds; - - // This mutex protects everything above. - LightReMutex _lock; - bool _sounds_finished; - - typedef pvector Streams; - PT(StreamThread) _stream_thread; - Streams _streams; - Mutex _streams_lock; - ConditionVar _streams_cvar; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - AudioManager::init_type(); - register_type(_type_handle, "MilesAudioManager", - AudioManager::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; - - friend class MilesAudioSound; - friend class MilesAudioSample; - friend class MilesAudioSequence; - friend class MilesAudioStream; -}; - -EXPCL_MILES_AUDIO AudioManager *Create_MilesAudioManager(); - - -#endif //] - -#endif //] diff --git a/panda/src/audiotraits/milesAudioSample.I b/panda/src/audiotraits/milesAudioSample.I deleted file mode 100644 index 4c7cb37996..0000000000 --- a/panda/src/audiotraits/milesAudioSample.I +++ /dev/null @@ -1,13 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file milesAudioSample.I - * @author skyler - * @date 2001-06-06 - * Prior system by: cary - */ diff --git a/panda/src/audiotraits/milesAudioSample.cxx b/panda/src/audiotraits/milesAudioSample.cxx deleted file mode 100644 index 8eb0ab2d26..0000000000 --- a/panda/src/audiotraits/milesAudioSample.cxx +++ /dev/null @@ -1,530 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file milesAudioSample.cxx - * @author skyler - * @date 2001-06-06 - */ - -#include "milesAudioSample.h" - -#ifdef HAVE_RAD_MSS //[ - -#include "milesAudioManager.h" - - -TypeHandle MilesAudioSample::_type_handle; - -#undef miles_audio_debug - -#ifndef NDEBUG //[ -#define miles_audio_debug(x) \ - audio_debug("MilesAudioSample \""<_raw_data.empty()) { - milesAudio_cat.warning() - << "Could not play " << _file_name << ": no data\n"; - } else { - stop(); - _manager->starting_sound(this); - - nassertv(_sample == 0); - - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - if (!mgr->get_sample(_sample, _sample_index, this)){ - milesAudio_cat.warning() - << "Could not play " << _file_name << ": too many open samples\n"; - _sample = 0; - } else { - AIL_set_named_sample_file(_sample, _sd->_basename.c_str(), - &_sd->_raw_data[0], _sd->_raw_data.size(), - 0); - _original_playback_rate = AIL_sample_playback_rate(_sample); - AIL_set_sample_user_data(_sample, 0, (SINTa)this); - AIL_register_EOS_callback(_sample, finish_callback); - - set_volume(_volume); - set_play_rate(_play_rate); - AIL_set_sample_loop_count(_sample, _loop_count); - - if (_got_start_time) { - do_set_time(_start_time); - AIL_resume_sample(_sample); - } else { - AIL_start_sample(_sample); - } - } - - _got_start_time = false; - } - } else { - // In case _loop_count gets set to forever (zero): - audio_debug(" paused "<<_file_name ); - _paused = true; - } -} - -/** - * - */ -void MilesAudioSample:: -stop() { - if (_manager == nullptr) { - return; - } - - miles_audio_debug("stop()"); - _manager->stopping_sound(this); - // The _paused flag should not be cleared here. _paused is not like the - // Pause button on a cddvd player. It is used as a flag to say that it was - // looping when it was set inactive. There is no need to make this - // symmetrical with play(). set_active() is the 'owner' of _paused. play() - // accesses _paused to help in the situation where someone calls play on an - // inactive sound(). - - // it fixes audio bug, I don't understand the reasoning of the above comment - _paused = false; - - if (_sample != 0) { - AIL_end_sample(_sample); - - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - mgr->release_sample(_sample_index, this); - - _sample = 0; - _sample_index = 0; - } -} - -/** - * - */ -PN_stdfloat MilesAudioSample:: -get_time() const { - if (_sample == 0) { - if (_got_start_time) { - return _start_time; - } - return 0.0f; - } - - S32 current_ms; - AIL_sample_ms_position(_sample, nullptr, ¤t_ms); - PN_stdfloat time = PN_stdfloat(current_ms * 0.001f); - - return time; -} - -/** - * - */ -void MilesAudioSample:: -set_volume(PN_stdfloat volume) { - miles_audio_debug("set_volume(volume="<get_volume(); - - // Change to Miles volume, range 0 to 1.0: - F32 milesVolume = volume; - milesVolume = std::min(milesVolume, 1.0f); - milesVolume = std::max(milesVolume, 0.0f); - - // Convert balance of -1.0..1.0 to 0-1.0: - F32 milesBalance = (F32)((_balance + 1.0f) * 0.5f); - - AIL_set_sample_volume_pan(_sample, milesVolume, milesBalance); - } -} - -/** - * - */ -void MilesAudioSample:: -set_balance(PN_stdfloat balance_right) { - miles_audio_debug("set_balance(balance_right="<get_play_rate(); - - // wave and mp3 use sample rate (e.g. 44100) - S32 speed = (S32)(play_rate * (PN_stdfloat)_original_playback_rate); - AIL_set_sample_playback_rate(_sample, speed); - audio_debug(" play_rate for this wav or mp3 is now " << speed); - } -} - -/** - * - */ -PN_stdfloat MilesAudioSample:: -length() const { - return _sd->get_length(); -} - -/** - * - */ -AudioSound::SoundStatus MilesAudioSample:: -status() const { - if (_sample == 0) { - return AudioSound::READY; - } - switch (AIL_sample_status(_sample)) { - case SMP_DONE: - case SMP_STOPPED: - case SMP_FREE: - return AudioSound::READY; - - case SMP_PLAYING: - case SMP_PLAYINGBUTRELEASED: - return AudioSound::PLAYING; - - default: - return AudioSound::BAD; - } -} - -/** - * Stops the sound from playing and releases any associated resources, in - * preparation for releasing the sound or shutting down the sound system. - */ -void MilesAudioSample:: -cleanup() { - stop(); - set_active(false); - nassertv(_sample == 0); - - if (_manager != nullptr) { - _manager->release_sound(this); - _manager = nullptr; - } -} - -/** - * - */ -void MilesAudioSample:: -output(std::ostream &out) const { - out << get_type() << " " << get_name() << " " << status(); - if (!_sd.is_null()) { - out << " " << (_sd->_raw_data.size() + 1023) / 1024 << "K"; - } -} - -/** - * Set position and velocity of this sound. Note that Y and Z are switched to - * translate from Miles's coordinate system. - */ -void MilesAudioSample::set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz) { - audio_debug("MilesAudioSample::set_3d_attributes() Setting a sound's 3D Coordinates."); - - if(_sample != 0) { - AIL_set_sample_3D_position(_sample, px, pz, py); - AIL_set_sample_3D_velocity_vector(_sample, vx, vz, vy); - } else { - audio_warning("_sample == 0 in MilesAudioSample::set_3d_attributes()."); - } -} - -/** - * Get position and velocity of this sound. - */ -void MilesAudioSample::get_3d_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz) { - audio_debug("MilesAudioSample::get_3d_attributes()."); - - if(_sample != 0) { - float lpx, lpy, lpz, lvx, lvy, lvz; - AIL_sample_3D_position(_sample, &lpx, &lpz, &lpy); - AIL_sample_3D_velocity(_sample, &lvx, &lvz, &lvy); - *px = lpx; - *py = lpy; - *pz = lpz; - *vx = lvx; - *vy = lvy; - *vz = lvz; - } else { - audio_warning("_sample == 0 in MilesAudioSample::get_3d_attributes()."); - } -} - -/** - * Set the distance that this sound begins to fall off. With Miles's default - * falloff behavior, when the distance between the sound and the listener is - * doubled, the volume is halved, and vice versa. - */ -void MilesAudioSample::set_3d_min_distance(PN_stdfloat dist) { - audio_debug("MilesAudioSample::set_3d_min_distance() Setting the sound's 3D min distance ( min= " << dist << " ) "); - - if(_sample != 0) { - // Implementation is awkward, since Miles gets and sets min and max - // distances in a single operation. - float max_dist; - int auto_3D_wet_atten; - AIL_sample_3D_distances(_sample, &max_dist, nullptr, &auto_3D_wet_atten); - - AIL_set_sample_3D_distances(_sample, max_dist, dist, auto_3D_wet_atten); - } else { - audio_warning("_sample == 0 in MilesAudioSample::set_3d_min_distance()."); - } -} - -/** - * Get the distance that this sound begins to fall off. - */ -PN_stdfloat MilesAudioSample::get_3d_min_distance() const { - audio_debug("MilesAudioSample::get_3d_min_distance() "); - - if(_sample != 0) { - float min_dist; - AIL_sample_3D_distances(_sample, nullptr, &min_dist, nullptr); - return (PN_stdfloat)min_dist; - } else { - audio_warning("_sample == 0 in MilesAudioSample::get_3d_min_distance()."); - return -1.0; - } -} - -/** - * Set the distance at which this sound is clipped to silence. Note that this - * value does not affect the rate at which the sound falls off, but only the - * distance at which it gets clipped. - */ -void MilesAudioSample::set_3d_max_distance(PN_stdfloat dist) { - audio_debug("MilesAudioSample::set_3d_max_distance() Setting the sound's 3D max distance ( max= " << dist << " ) "); - - if(_sample != 0) { - // Implementation is awkward, since Miles gets and sets min and max - // distances in a single operation. - float min_dist; - int auto_3D_wet_atten; - AIL_sample_3D_distances(_sample, nullptr, &min_dist, &auto_3D_wet_atten); - - AIL_set_sample_3D_distances(_sample, dist, min_dist, auto_3D_wet_atten); - } else { - audio_warning("_sample == 0 in MilesAudioSample::set_3d_max_distance()."); - } -} - -/** - * Get the distance at which this sound is clipped to silence. - */ -PN_stdfloat MilesAudioSample::get_3d_max_distance() const { - audio_debug("MilesAudioSample::get_3d_max_distance() "); - - if(_sample != 0) { - float max_dist; - AIL_sample_3D_distances(_sample, &max_dist, nullptr, nullptr); - return (PN_stdfloat)max_dist; - } else { - audio_warning("_sample == 0 in MilesAudioSample::get_3d_max_distance()."); - return -1.0; - } -} - -/** - * Get the level of a particular logical channel (speaker). "index" specifies - * which speaker in an array of all the logical channels currently in use to - * retrieve the level of. - * - * For instance, in a standard 4.0 channel setup, speakers are setup as - * [frontLeft, frontRight, backLeft, backRight]. Thus, get_speaker_level(2) - * will retrieve the level of the backLeft speaker. - * - * The order in which speakers appear in the array for standard speaker setups - * is defined to be: - * - * FRONT_LEFT FRONT_RIGHT FRONT_CENTER LOW_FREQUENCY (sub woofer) BACK_LEFT - * BACK_RIGHT FRONT_LEFT_OF_CENTER FRONT_RIGHT_OF_CENTER BACK_CENTER SIDE_LEFT - * SIDE_RIGHT TOP_CENTER TOP_FRONT_LEFT TOP_FRONT_CENTER TOP_FRONT_RIGHT - * TOP_BACK_LEFT TOP_BACK_CENTER TOP_BACK_RIGHT - * - */ -PN_stdfloat MilesAudioSample:: -get_speaker_level(int index) { - audio_debug("MilesAudioSample::get_speaker_level(" << index << ")"); - - if(_sample != 0) { - int numLevels; - float *levels = AIL_sample_channel_levels(_sample, &numLevels); - - if(index < numLevels) { - return (PN_stdfloat)levels[index]; - } else { - audio_error("index out of range in MilesAudioSample::get_speaker_level. numLevels: " << numLevels); - return -1.0; - } - } else { - audio_warning("Warning: MilesAudioSample::get_speaker_level only works for sounds that are currently playing"); - return -1.0; - } -} - -/** - * Set the output levels on the logical channels (speakers) for this sound. - * Values should be in the range 0.0 to 1.0. Levels for up to nine channels - * may be specified. As soon as a level is reached that falls outside the - * range 0.0 to 1.0, the levels specified up to that point will be sent and - * all other levels will be ignored. - * - * The user must know what the current speaker setup is in order to know which - * level corresponds to which speaker. - * - * This method will have no effect if 3D attributes have been set for this - * sound. - * - * The order in which speakers appear in the array for standard speaker setups - * is defined to be: - * - * FRONT_LEFT FRONT_RIGHT FRONT_CENTER LOW_FREQUENCY (sub woofer) BACK_LEFT - * BACK_RIGHT FRONT_LEFT_OF_CENTER FRONT_RIGHT_OF_CENTER BACK_CENTER SIDE_LEFT - * SIDE_RIGHT TOP_CENTER TOP_FRONT_LEFT TOP_FRONT_CENTER TOP_FRONT_RIGHT - * TOP_BACK_LEFT TOP_BACK_CENTER TOP_BACK_RIGHT - * - */ -void MilesAudioSample:: -set_speaker_levels(PN_stdfloat level1, PN_stdfloat level2, PN_stdfloat level3, PN_stdfloat level4, PN_stdfloat level5, PN_stdfloat level6, PN_stdfloat level7, PN_stdfloat level8, PN_stdfloat level9) { - audio_debug("MilesAudioSample::set_speaker_levels()"); - - if(_sample != 0) { - float levels[9] = {level1, level2, level3, level4, level5, level6, level7, level8, level9}; - - if((level1 < 0.0) || (level1 > 1.0)) { - audio_error("No valid levels specified in MilesAudioSample::set_speaker_levels()."); - } else if((level2 < 0.0) || (level2 > 1.0)) { - AIL_set_sample_channel_levels(_sample, levels, 1); - } else if((level3 < 0.0) || (level3 > 1.0)) { - AIL_set_sample_channel_levels(_sample, levels, 2); - } else if((level4 < 0.0) || (level4 > 1.0)) { - AIL_set_sample_channel_levels(_sample, levels, 3); - } else if((level5 < 0.0) || (level5 > 1.0)) { - AIL_set_sample_channel_levels(_sample, levels, 4); - } else if((level6 < 0.0) || (level6 > 1.0)) { - AIL_set_sample_channel_levels(_sample, levels, 5); - } else if((level7 < 0.0) || (level7 > 1.0)) { - AIL_set_sample_channel_levels(_sample, levels, 6); - } else if((level8 < 0.0) || (level8 > 1.0)) { - AIL_set_sample_channel_levels(_sample, levels, 7); - } else if((level9 < 0.0) || (level9 > 1.0)) { - AIL_set_sample_channel_levels(_sample, levels, 8); - } else { - AIL_set_sample_channel_levels(_sample, levels, 9); - } - } else { - audio_warning("Warning: MilesAudioSample::set_speaker_levels only works for sounds that are currently playing"); - } -} - -/** - * Called by the GlobalMilesManager when it is detected that this particular - * sound has already stopped, and its sample handle will be recycled. - */ -void MilesAudioSample:: -internal_stop() { - _sample = 0; - _sample_index = 0; -} - -/** - * This callback is made by Miles (possibly in a sub-thread) when the sample - * finishes. - */ -void AILCALLBACK MilesAudioSample:: -finish_callback(HSAMPLE sample) { - MilesAudioSample *self = (MilesAudioSample *)AIL_sample_user_data(sample, 0); - if (milesAudio_cat.is_debug()) { - milesAudio_cat.debug() - << "finished " << *self << "\n"; - } - if (self->_manager == nullptr) { - return; - } - self->_manager->_sounds_finished = true; -} - -/** - * Sets the start time of an already allocated sample. - */ -void MilesAudioSample:: -do_set_time(PN_stdfloat time) { - miles_audio_debug("do_set_time(time="< max_time) { - milesAudio_cat.warning() - << "set_time(" << time << ") requested for sound of length " - << max_time << "\n"; - time = max_time; - } - - S32 time_ms = (S32)(1000.0f * time); - AIL_set_sample_ms_position(_sample, time_ms); -} - -#endif //] diff --git a/panda/src/audiotraits/milesAudioSample.h b/panda/src/audiotraits/milesAudioSample.h deleted file mode 100644 index d61f4de553..0000000000 --- a/panda/src/audiotraits/milesAudioSample.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file milesAudioSample.h - * @author skyler - * @date 2001-06-06 - * Prior system by: cary - */ - -#ifndef MILESAUDIOSAMPLE_H -#define MILESAUDIOSAMPLE_H - -#include "pandabase.h" -#ifdef HAVE_RAD_MSS //[ - -#include "milesAudioSound.h" -#include "milesAudioManager.h" - -#include - -/** - * A sound file, such as a WAV or MP3 file, that is preloaded into memory and - * played from memory. - */ -class EXPCL_MILES_AUDIO MilesAudioSample : public MilesAudioSound { -private: - MilesAudioSample(MilesAudioManager *manager, - MilesAudioManager::SoundData *sd, - const std::string &file_name); - -public: - virtual ~MilesAudioSample(); - - virtual void play(); - virtual void stop(); - - virtual PN_stdfloat get_time() const; - - virtual void set_volume(PN_stdfloat volume=1.0f); - virtual void set_balance(PN_stdfloat balance_right=0.0f); - virtual void set_play_rate(PN_stdfloat play_rate=1.0f); - - virtual PN_stdfloat length() const; - - virtual AudioSound::SoundStatus status() const; - - virtual void cleanup(); - virtual void output(std::ostream &out) const; - - // 3D spatialized sound support. Spatialized sound was originally added for - // FMOD, so there are parts of the interface in the Miles implementation - // that are a little more awkward than they would be otherwise. - void set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz); - void get_3d_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz); - void set_3d_min_distance(PN_stdfloat dist); - PN_stdfloat get_3d_min_distance() const; - void set_3d_max_distance(PN_stdfloat dist); - PN_stdfloat get_3d_max_distance() const; - - virtual PN_stdfloat get_speaker_level(int index); - virtual void set_speaker_levels(PN_stdfloat level1, PN_stdfloat level2=-1.0f, PN_stdfloat level3=-1.0f, PN_stdfloat level4=-1.0f, PN_stdfloat level5=-1.0f, PN_stdfloat level6=-1.0f, PN_stdfloat level7=-1.0f, PN_stdfloat level8=-1.0f, PN_stdfloat level9=-1.0f); - -private: - void internal_stop(); - static void AILCALLBACK finish_callback(HSAMPLE sample); - void do_set_time(PN_stdfloat time); - - PT(MilesAudioManager::SoundData) _sd; - HSAMPLE _sample; - size_t _sample_index; - S32 _original_playback_rate; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - MilesAudioSound::init_type(); - register_type(_type_handle, "MilesAudioSample", - MilesAudioSound::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; - - friend class GlobalMilesManager; - friend class MilesAudioManager; -}; - -#include "milesAudioSample.I" - -#endif //] - -#endif /* MILESAUDIOSAMPLE_H */ diff --git a/panda/src/audiotraits/milesAudioSequence.I b/panda/src/audiotraits/milesAudioSequence.I deleted file mode 100644 index ea13873c17..0000000000 --- a/panda/src/audiotraits/milesAudioSequence.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file milesAudioSequence.I - * @author drose - * @date 2007-07-31 - */ diff --git a/panda/src/audiotraits/milesAudioSequence.cxx b/panda/src/audiotraits/milesAudioSequence.cxx deleted file mode 100644 index b214644e54..0000000000 --- a/panda/src/audiotraits/milesAudioSequence.cxx +++ /dev/null @@ -1,331 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file milesAudioSequence.cxx - * @author drose - * @date 2007-07-31 - */ - -#include "milesAudioSequence.h" - -#ifdef HAVE_RAD_MSS //[ - -#include "milesAudioManager.h" - - -TypeHandle MilesAudioSequence::_type_handle; - -#undef miles_audio_debug - -#ifndef NDEBUG //[ -#define miles_audio_debug(x) \ - audio_debug("MilesAudioSequence \""<release_sound(this); - miles_audio_debug("~MilesAudioSequence() done"); -} - -/** - * - */ -void MilesAudioSequence:: -play() { - miles_audio_debug("play()"); - if (_active) { - stop(); - - if (_sd->_raw_data.empty()) { - milesAudio_cat.warning() - << "Could not play " << _file_name << ": no data\n"; - } else { - _manager->starting_sound(this); - nassertv(_sequence == 0); - - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - if (!mgr->get_sequence(_sequence, _sequence_index, this)){ - milesAudio_cat.warning() - << "Could not play " << _file_name << ": too many open sequences\n"; - _sequence = 0; - } else { - AIL_init_sequence(_sequence, &_sd->_raw_data[0], 0); - AIL_set_sequence_user_data(_sequence, 0, (SINTa)this); - AIL_register_sequence_callback(_sequence, finish_callback); - - set_volume(_volume); - set_play_rate(_play_rate); - AIL_set_sequence_loop_count(_sequence, _loop_count); - - if (_got_start_time) { - do_set_time(_start_time); - AIL_resume_sequence(_sequence); - } else { - AIL_start_sequence(_sequence); - } - } - - _got_start_time = false; - } - } else { - // In case _loop_count gets set to forever (zero): - audio_debug(" paused "<<_file_name ); - _paused = true; - } -} - -/** - * - */ -void MilesAudioSequence:: -stop() { - miles_audio_debug("stop()"); - _manager->stopping_sound(this); - // The _paused flag should not be cleared here. _paused is not like the - // Pause button on a cddvd player. It is used as a flag to say that it was - // looping when it was set inactive. There is no need to make this - // symmetrical with play(). set_active() is the 'owner' of _paused. play() - // accesses _paused to help in the situation where someone calls play on an - // inactive sound(). - - if (_sequence != 0) { - AIL_end_sequence(_sequence); - - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - mgr->release_sequence(_sequence_index, this); - - _sequence = 0; - _sequence_index = 0; - } -} - -/** - * - */ -PN_stdfloat MilesAudioSequence:: -get_time() const { - if (_sequence == 0) { - if (_got_start_time) { - return _start_time; - } - return 0.0f; - } - - S32 current_ms; - AIL_sequence_ms_position(_sequence, nullptr, ¤t_ms); - PN_stdfloat time = PN_stdfloat(current_ms * 0.001f); - - return time; -} - -/** - * - */ -void MilesAudioSequence:: -set_volume(PN_stdfloat volume) { - miles_audio_debug("set_volume(volume="<get_volume(); - - // Change to Miles volume, range 0 to 127: - S32 milesVolume = (S32)(volume * 127.0f); - milesVolume = std::min(milesVolume, 127); - milesVolume = std::max(milesVolume, 0); - - AIL_set_sequence_volume(_sequence, milesVolume, 0); - } -} - -/** - * - */ -void MilesAudioSequence:: -set_balance(PN_stdfloat balance_right) { - miles_audio_debug("set_balance(balance_right="<get_play_rate(); - - S32 percent = (S32)(play_rate * 100.0f); - AIL_set_sequence_tempo(_sequence, percent, 0); - } -} - -/** - * - */ -PN_stdfloat MilesAudioSequence:: -length() const { - if (_sequence == 0) { - // The MIDI file hasn't been started yet. See if the length is cached in - // the SoundData. - if (!_sd->_has_length) { - // It isn't cached, so load the sequence temporarily to determine its - // length. - ((MilesAudioSequence *)this)->determine_length(); - } - - return _sd->get_length(); - } - - // The MIDI file has already been started, so we can ask it directly. - S32 length_ms; - AIL_sequence_ms_position(_sequence, &length_ms, nullptr); - PN_stdfloat time = (PN_stdfloat)length_ms * 0.001f; - return time; -} - -/** - * - */ -AudioSound::SoundStatus MilesAudioSequence:: -status() const { - if (_sequence == 0) { - return AudioSound::READY; - } - switch (AIL_sequence_status(_sequence)) { - case SEQ_DONE: - case SEQ_STOPPED: - case SEQ_FREE: - return AudioSound::READY; - - case SEQ_PLAYING: - case SEQ_PLAYINGBUTRELEASED: - return AudioSound::PLAYING; - - default: - return AudioSound::BAD; - } -} - -/** - * Stops the sound from playing and releases any associated resources, in - * preparation for releasing the sound or shutting down the sound system. - */ -void MilesAudioSequence:: -cleanup() { - stop(); -} - -/** - * Called by the GlobalMilesManager when it is detected that this particular - * sound has already stopped, and its sequence handle will be recycled. - */ -void MilesAudioSequence:: -internal_stop() { - _sequence = 0; - _sequence_index = 0; -} - -/** - * This callback is made by Miles (possibly in a sub-thread) when the sequence - * finishes. - */ -void AILCALLBACK MilesAudioSequence:: -finish_callback(HSEQUENCE sequence) { - MilesAudioSequence *self = (MilesAudioSequence *)AIL_sequence_user_data(sequence, 0); - if (milesAudio_cat.is_debug()) { - milesAudio_cat.debug() - << "finished " << *self << "\n"; - } - self->_manager->_sounds_finished = true; -} - -/** - * Sets the start time of an already allocated stream. - */ -void MilesAudioSequence:: -do_set_time(PN_stdfloat time) { - miles_audio_debug("do_set_time(time="<get_sequence(_sequence, _sequence_index, this)){ - milesAudio_cat.warning() - << "Could not determine length of " << _file_name << ": too many open sequences\n"; - _sequence = 0; - } else { - AIL_init_sequence(_sequence, &_sd->_raw_data[0], 0); - S32 length_ms; - AIL_sequence_ms_position(_sequence, &length_ms, nullptr); - PN_stdfloat time = (PN_stdfloat)length_ms * 0.001f; - mgr->release_sequence(_sequence_index, this); - _sequence = 0; - _sequence_index = 0; - - _sd->set_length(time); - } -} - -#endif //] diff --git a/panda/src/audiotraits/milesAudioSequence.h b/panda/src/audiotraits/milesAudioSequence.h deleted file mode 100644 index 4ab1fe6d90..0000000000 --- a/panda/src/audiotraits/milesAudioSequence.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file milesAudioSequence.h - * @author drose - * @date 2007-07-31 - */ - -#ifndef MILESAUDIOSEQUENCE_H -#define MILESAUDIOSEQUENCE_H - -#include "pandabase.h" -#ifdef HAVE_RAD_MSS //[ - -#include "milesAudioSound.h" -#include "milesAudioManager.h" - -#include - -/** - * A MIDI file, preloaded and played from a memory buffer. MIDI files cannot - * be streamed. - */ -class EXPCL_MILES_AUDIO MilesAudioSequence : public MilesAudioSound { -private: - MilesAudioSequence(MilesAudioManager *manager, - MilesAudioManager::SoundData *sd, - const std::string &file_name); - -public: - virtual ~MilesAudioSequence(); - - virtual void play(); - virtual void stop(); - - virtual PN_stdfloat get_time() const; - - virtual void set_volume(PN_stdfloat volume=1.0f); - virtual void set_balance(PN_stdfloat balance_right=0.0f); - virtual void set_play_rate(PN_stdfloat play_rate=1.0f); - - virtual PN_stdfloat length() const; - - virtual AudioSound::SoundStatus status() const; - - virtual void cleanup(); - -private: - void internal_stop(); - static void AILCALLBACK finish_callback(HSEQUENCE sequence); - void do_set_time(PN_stdfloat time); - void determine_length(); - - PT(MilesAudioManager::SoundData) _sd; - HSEQUENCE _sequence; - size_t _sequence_index; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - MilesAudioSound::init_type(); - register_type(_type_handle, "MilesAudioSequence", - MilesAudioSound::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; - - friend class GlobalMilesManager; - friend class MilesAudioManager; -}; - -#include "milesAudioSequence.I" - -#endif //] - -#endif /* MILESAUDIOSEQUENCE_H */ diff --git a/panda/src/audiotraits/milesAudioSound.I b/panda/src/audiotraits/milesAudioSound.I deleted file mode 100644 index c1354a737f..0000000000 --- a/panda/src/audiotraits/milesAudioSound.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file milesAudioSound.I - * @author drose - * @date 2007-07-30 - */ diff --git a/panda/src/audiotraits/milesAudioSound.cxx b/panda/src/audiotraits/milesAudioSound.cxx deleted file mode 100644 index 75ca1d012b..0000000000 --- a/panda/src/audiotraits/milesAudioSound.cxx +++ /dev/null @@ -1,205 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file milesAudioSound.cxx - * @author drose - * @date 2007-07-30 - */ - -#include "milesAudioSound.h" -#ifdef HAVE_RAD_MSS //[ - -#include "milesAudioManager.h" - -using std::string; - -TypeHandle MilesAudioSound::_type_handle; - -#undef miles_audio_debug - -#ifndef NDEBUG //[ -#define miles_audio_debug(x) \ - audio_debug("MilesAudioSound \""< - -/** - * The base class for both MilesAudioStream and MilesAudioSample. - */ -class EXPCL_MILES_AUDIO MilesAudioSound : public AudioSound { -protected: - MilesAudioSound(MilesAudioManager *manager, const std::string &file_name); - -public: - virtual void set_loop(bool loop=true); - virtual bool get_loop() const; - - virtual void set_loop_count(unsigned long loop_count=1); - virtual unsigned long get_loop_count() const; - - virtual PN_stdfloat get_volume() const; - virtual PN_stdfloat get_balance() const; - virtual PN_stdfloat get_play_rate() const; - - virtual void set_time(PN_stdfloat start_time=0.0); - - virtual void set_active(bool active=true); - virtual bool get_active() const; - - virtual void set_finished_event(const std::string &event); - virtual const std::string &get_finished_event() const; - - virtual const std::string &get_name() const; - - virtual void cleanup(); - -protected: - PT(MilesAudioManager) _manager; - std::string _file_name; - - PN_stdfloat _volume; // 0..1.0 - PN_stdfloat _balance; // -1..1 - PN_stdfloat _play_rate; // 0..1.0 - unsigned long _loop_count; - - // _active is for things like a 'turn off sound effects' in a preferences - // pannel. _active is not about whether a sound is currently playing. Use - // status() for info on whether the sound is playing. - bool _active; - - // _paused is not like the Pause button on a cddvd player. It is used as a - // flag to say that the sound was looping when it was set inactive. - bool _paused; - - // This is the string that throw_event() will throw when the sound finishes - // playing. It is not triggered when the sound is stopped with stop(). - // Note: no longer implemented. - std::string _finished_event; - - // This is set whenever we call set_time(). Calling play() will respect - // this if it is set, and then reset it. - PN_stdfloat _start_time; - bool _got_start_time; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - AudioSound::init_type(); - register_type(_type_handle, "MilesAudioSound", - AudioSound::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; - - friend class MilesAudioManager; -}; - -#include "milesAudioSound.I" - -#endif //] - -#endif diff --git a/panda/src/audiotraits/milesAudioStream.I b/panda/src/audiotraits/milesAudioStream.I deleted file mode 100644 index 0bb33b8a6e..0000000000 --- a/panda/src/audiotraits/milesAudioStream.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file milesAudioStream.I - * @author drose - * @date 2007-07-26 - */ diff --git a/panda/src/audiotraits/milesAudioStream.cxx b/panda/src/audiotraits/milesAudioStream.cxx deleted file mode 100644 index 480d35c81c..0000000000 --- a/panda/src/audiotraits/milesAudioStream.cxx +++ /dev/null @@ -1,310 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file milesAudioStream.cxx - * @author drose - * @date 2007-07-26 - */ - -#include "milesAudioStream.h" -#ifdef HAVE_RAD_MSS //[ - -#include "milesAudioManager.h" -#include "pnotify.h" - -TypeHandle MilesAudioStream::_type_handle; - -#undef miles_audio_debug - -#ifndef NDEBUG //[ -#define miles_audio_debug(x) \ - audio_debug("MilesAudioStream \""<starting_sound(this); - - if (_stream == 0) { - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - _stream = AIL_open_stream(mgr->_digital_driver, _path.c_str(), 0); - if (_stream == 0) { - milesAudio_cat.warning() - << "Could not play " << _file_name << ": too many open streams\n"; - return; - } - AIL_set_stream_user_data(_stream, 0, (SINTa)this); - AIL_register_stream_callback(_stream, finish_callback); - - } else { - // We already had the stream open. Keep it open; just restart it. - AIL_pause_stream(_stream, 1); - _manager->stop_service_stream(_stream); - } - - // Start playing: - nassertv(_stream != 0); - HSAMPLE sample = AIL_stream_sample_handle(_stream); - nassertv(sample != 0); - - _original_playback_rate = AIL_sample_playback_rate(sample); - set_volume(_volume); - set_play_rate(_play_rate); - - AIL_set_stream_loop_count(_stream, _loop_count); - - AIL_start_stream(_stream); - if (_got_start_time) { - // There's no AIL_resume_stream(), so we start in the middle by starting - // normally, then immediately skipping to the middle. - do_set_time(_start_time); - } - - if (miles_audio_panda_threads) { - AIL_auto_service_stream(_stream, 0); - _manager->start_service_stream(_stream); - } else { - AIL_auto_service_stream(_stream, 1); - } - - _got_start_time = false; - - } else { - // In case _loop_count gets set to forever (zero): - audio_debug(" paused "<<_file_name ); - _paused = true; - } -} - -/** - * - */ -void MilesAudioStream:: -stop() { - if (_manager == nullptr) { - return; - } - miles_audio_debug("stop()"); - _manager->stopping_sound(this); - - // The _paused flag should not be cleared here. _paused is not like the - // Pause button on a cddvd player. It is used as a flag to say that it was - // looping when it was set inactive. There is no need to make this - // symmetrical with play(). set_active() is the 'owner' of _paused. play() - // accesses _paused to help in the situation where someone calls play on an - // inactive sound(). - if (_stream != 0) { - _manager->stop_service_stream(_stream); - - AIL_pause_stream(_stream, 1); - AIL_close_stream(_stream); - _stream = 0; - } -} - -/** - * - */ -PN_stdfloat MilesAudioStream:: -get_time() const { - if (_stream == 0) { - if (_got_start_time) { - return _start_time; - } - return 0.0f; - } - - S32 current_ms; - AIL_stream_ms_position(_stream, nullptr, ¤t_ms); - PN_stdfloat time = PN_stdfloat(current_ms * 0.001f); - - return time; -} - -/** - * - */ -void MilesAudioStream:: -set_volume(PN_stdfloat volume) { - _volume = volume; - - if (_stream != 0) { - HSAMPLE sample = AIL_stream_sample_handle(_stream); - nassertv(sample != 0); - - volume *= _manager->get_volume(); - - // Change to Miles volume, range 0 to 1.0: - F32 milesVolume = volume; - milesVolume = std::min(milesVolume, 1.0f); - milesVolume = std::max(milesVolume, 0.0f); - - // Convert balance of -1.0..1.0 to 0-1.0: - F32 milesBalance = (F32)((_balance + 1.0f) * 0.5f); - - AIL_set_sample_volume_pan(sample, milesVolume, milesBalance); - } -} - -/** - * - */ -void MilesAudioStream:: -set_balance(PN_stdfloat balance_right) { - _balance = balance_right; - - // Call set_volume to effect the change: - set_volume(_volume); -} - -/** - * - */ -void MilesAudioStream:: -set_play_rate(PN_stdfloat play_rate) { - _play_rate = play_rate; - - if (_stream != 0) { - HSAMPLE sample = AIL_stream_sample_handle(_stream); - nassertv(sample != 0); - - play_rate *= _manager->get_play_rate(); - - // wave and mp3 use sample rate (e.g. 44100) - S32 speed = (S32)(play_rate * (PN_stdfloat)_original_playback_rate); - AIL_set_sample_playback_rate(sample, speed); - audio_debug(" play_rate for this wav or mp3 is now " << speed); - } -} - -/** - * - */ -PN_stdfloat MilesAudioStream:: -length() const { - if (!_got_length) { - if (_stream == 0) { - GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); - ((MilesAudioStream *)this)->_stream = AIL_open_stream(mgr->_digital_driver, _path.c_str(), 0); - } - - S32 length_ms; - AIL_stream_ms_position(_stream, &length_ms, nullptr); - _length = (PN_stdfloat)length_ms * 0.001f; - _got_length = true; - } - - return _length; -} - -/** - * - */ -AudioSound::SoundStatus MilesAudioStream:: -status() const { - if (!_stream) { - return AudioSound::READY; - } - - switch (AIL_stream_status(_stream)) { - case SMP_STOPPED: - case SMP_DONE: - return AudioSound::READY; - case SMP_PLAYING: - case SMP_PLAYINGBUTRELEASED: - return AudioSound::PLAYING; - default: - return AudioSound::BAD; - } -} - -/** - * Called to release any resources associated with the sound. - */ -void MilesAudioStream:: -cleanup() { - if (_stream) { - stop(); - } - set_active(false); - nassertv(_stream == 0); - - if (_manager != nullptr) { - _manager->release_sound(this); - _manager = nullptr; - } -} - -/** - * This callback is made by Miles (possibly in a sub-thread) when the stream - * finishes. - */ -void AILCALLBACK MilesAudioStream:: -finish_callback(HSTREAM stream) { - MilesAudioStream *self = (MilesAudioStream *)AIL_stream_user_data(stream, 0); - if (milesAudio_cat.is_debug()) { - milesAudio_cat.debug() - << "finished " << *self << "\n"; - } - if (self->_manager == nullptr) { - return; - } - self->_manager->_sounds_finished = true; -} - -/** - * Sets the start time of an already allocated stream. - */ -void MilesAudioStream:: -do_set_time(PN_stdfloat time) { - nassertv(_stream != 0); - - S32 time_ms = (S32)(1000.0f * time); - - // Ensure we don't inadvertently run off the end of the sound. - S32 length_ms; - AIL_stream_ms_position(_stream, &length_ms, nullptr); - time_ms = std::min(time_ms, length_ms); - - AIL_set_stream_ms_position(_stream, time_ms); -} - - -#endif //] diff --git a/panda/src/audiotraits/milesAudioStream.h b/panda/src/audiotraits/milesAudioStream.h deleted file mode 100644 index 15eac4f285..0000000000 --- a/panda/src/audiotraits/milesAudioStream.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file milesAudioStream.h - * @author drose - * @date 2007-07-26 - */ - -#ifndef MILESAUDIOSTREAM_H -#define MILESAUDIOSTREAM_H - -#include "pandabase.h" -#ifdef HAVE_RAD_MSS //[ - -#include "milesAudioSound.h" -#include "milesAudioManager.h" - -#include - -/** - * This represents a sound file played by the Miles Sound System, similar to - * MilesAudioSample, except that it is streamed from disk instead of preloaded - * into memory. - */ -class EXPCL_MILES_AUDIO MilesAudioStream : public MilesAudioSound { -private: - MilesAudioStream(MilesAudioManager *manager, const std::string &file_name, - const Filename &path); - -public: - virtual ~MilesAudioStream(); - - virtual void play(); - virtual void stop(); - - virtual PN_stdfloat get_time() const; - - virtual void set_volume(PN_stdfloat volume=1.0f); - virtual void set_balance(PN_stdfloat balance_right=0.0f); - virtual void set_play_rate(PN_stdfloat play_rate=1.0f); - - virtual PN_stdfloat length() const; - - virtual AudioSound::SoundStatus status() const; - - virtual void cleanup(); - -private: - static void AILCALLBACK finish_callback(HSTREAM stream); - void do_set_time(PN_stdfloat time); - - Filename _path; - HSTREAM _stream; - S32 _original_playback_rate; - mutable PN_stdfloat _length; - mutable bool _got_length; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - MilesAudioSound::init_type(); - register_type(_type_handle, "MilesAudioStream", - MilesAudioSound::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; - - friend class MilesAudioManager; -}; - -#include "milesAudioStream.I" - -#endif //] - -#endif diff --git a/panda/src/audiotraits/miles_audio_composite1.cxx b/panda/src/audiotraits/miles_audio_composite1.cxx deleted file mode 100644 index e8dbf2fa8c..0000000000 --- a/panda/src/audiotraits/miles_audio_composite1.cxx +++ /dev/null @@ -1,9 +0,0 @@ - -#include "config_milesAudio.cxx" -#include "milesAudioManager.cxx" -#include "milesAudioSample.cxx" -#include "milesAudioSequence.cxx" -#include "milesAudioSound.cxx" -#include "milesAudioStream.cxx" -#include "globalMilesManager.cxx" - diff --git a/panda/src/audiotraits/openalAudioManager.cxx b/panda/src/audiotraits/openalAudioManager.cxx index edf7dfe222..ea03b0c0c7 100644 --- a/panda/src/audiotraits/openalAudioManager.cxx +++ b/panda/src/audiotraits/openalAudioManager.cxx @@ -252,7 +252,9 @@ select_audio_device() { devices = (const char *)alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); if (devices) { - audio_cat.debug() << "All OpenAL devices:\n"; + if (audio_cat.is_debug()) { + audio_cat.debug() << "All OpenAL devices:\n"; + } while (*devices) { string device(devices); @@ -280,7 +282,9 @@ select_audio_device() { devices = (const char *)alcGetString(nullptr, ALC_DEVICE_SPECIFIER); if (devices) { - audio_cat.debug() << "OpenAL drivers:\n"; + if (audio_cat.is_debug()) { + audio_cat.debug() << "OpenAL drivers:\n"; + } while (*devices) { string device(devices); @@ -442,7 +446,7 @@ get_sound_data(MovieAudio *movie, int mode) { int channels = stream->audio_channels(); int samples = (int)(stream->length() * stream->audio_rate()); int16_t *data = new int16_t[samples * channels]; - stream->read_samples(samples, data); + samples = stream->read_samples(samples, data); alBufferData(sd->_sample, (channels>1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16, data, samples * channels * 2, stream->audio_rate()); diff --git a/panda/src/audiotraits/openalAudioSound.cxx b/panda/src/audiotraits/openalAudioSound.cxx index 9244b7509f..5676e63f80 100644 --- a/panda/src/audiotraits/openalAudioSound.cxx +++ b/panda/src/audiotraits/openalAudioSound.cxx @@ -373,7 +373,6 @@ read_stream_data(int bytelen, unsigned char *buffer) { nassertr(has_sound_data(), 0); MovieAudioCursor *cursor = _sd->_stream; - double length = cursor->length(); int channels = cursor->audio_channels(); int rate = cursor->audio_rate(); int space = bytelen / (channels * 2); @@ -381,7 +380,7 @@ read_stream_data(int bytelen, unsigned char *buffer) { while (space && (_loops_completed < _playing_loops)) { double t = cursor->tell(); - double remain = length - t; + double remain = cursor->length() - t; if (remain > 60.0) { remain = 60.0; } @@ -403,9 +402,20 @@ read_stream_data(int bytelen, unsigned char *buffer) { if (samples > _sd->_stream->ready()) { samples = _sd->_stream->ready(); } - cursor->read_samples(samples, (int16_t *)buffer); - size_t hval = AddHash::add_hash(0, (uint8_t*)buffer, samples*channels*2); - audio_debug("Streaming " << cursor->get_source()->get_name() << " at " << t << " hash " << hval); + samples = cursor->read_samples(samples, (int16_t *)buffer); + if (audio_cat.is_debug()) { + size_t hval = AddHash::add_hash(0, (uint8_t*)buffer, samples*channels*2); + audio_debug("Streaming " << cursor->get_source()->get_name() << " at " << t << " hash " << hval); + } + if (samples == 0) { + _loops_completed += 1; + cursor->seek(0.0); + if (_playing_loops >= 1000000000) { + // Prevent infinite loop if endlessly looping empty sound + return fill; + } + continue; + } fill += samples; space -= samples; buffer += (samples * channels * 2); diff --git a/panda/src/bullet/bulletBodyNode.cxx b/panda/src/bullet/bulletBodyNode.cxx index cb9d7ed9c0..6e258c47d0 100644 --- a/panda/src/bullet/bulletBodyNode.cxx +++ b/panda/src/bullet/bulletBodyNode.cxx @@ -353,9 +353,17 @@ do_add_shape(BulletShape *bullet_shape, const TransformState *ts) { // Reset the shape scaling before we add a shape, and remember the current // Scale so we can restore it later... - NodePath np = NodePath::any_path((PandaNode *)this); - LVector3 scale = np.get_scale(); - np.set_scale(1.0); + CPT(TransformState) prev_transform = get_transform(); + bool scale_changed = false; + if (!prev_transform->is_identity() && prev_transform->get_scale() != LVecBase3(1.0, 1.0, 1.0)) { + // As a hack, temporarily release the lock, since transform_changed will + // otherwise deadlock trying to grab it again. See GitHub issue #689. + LightMutex &lock = BulletWorld::get_global_lock(); + lock.release(); + set_transform(prev_transform->set_scale(LVecBase3(1.0, 1.0, 1.0))); + lock.acquire(); + scale_changed = true; + } // Root shape btCollisionShape *previous = get_object()->getCollisionShape(); @@ -417,7 +425,13 @@ do_add_shape(BulletShape *bullet_shape, const TransformState *ts) { _shapes.push_back(bullet_shape); // Restore the local scaling again - np.set_scale(scale); + if (scale_changed) { + CPT(TransformState) transform = get_transform()->set_scale(prev_transform->get_scale()); + LightMutex &lock = BulletWorld::get_global_lock(); + lock.release(); + set_transform(std::move(transform)); + lock.acquire(); + } do_shape_changed(); } diff --git a/panda/src/bullet/bulletContactCallbacks.h b/panda/src/bullet/bulletContactCallbacks.h index e9891fda8a..b2027ddff6 100644 --- a/panda/src/bullet/bulletContactCallbacks.h +++ b/panda/src/bullet/bulletContactCallbacks.h @@ -61,7 +61,9 @@ contact_added_callback(btManifoldPoint &cp, PT(PandaNode) node1 = (PandaNode *)obj1->getUserPointer(); #endif - bullet_cat.debug() << "contact added: " << cp.m_userPersistentData << std::endl; + if (bullet_cat.is_debug()) { + bullet_cat.debug() << "contact added: " << cp.m_userPersistentData << std::endl; + } // Gather persistent data UserPersistentData *data = new UserPersistentData(); @@ -123,8 +125,9 @@ contact_processed_callback(btManifoldPoint &cp, */ static bool contact_destroyed_callback(void *userPersistentData) { - - bullet_cat.debug() << "contact removed: " << userPersistentData << std::endl; + if (bullet_cat.is_debug()) { + bullet_cat.debug() << "contact removed: " << userPersistentData << std::endl; + } UserPersistentData *data = (UserPersistentData *)userPersistentData; diff --git a/panda/src/bullet/bulletDebugNode.cxx b/panda/src/bullet/bulletDebugNode.cxx index 2f221eb447..8b758c59ca 100644 --- a/panda/src/bullet/bulletDebugNode.cxx +++ b/panda/src/bullet/bulletDebugNode.cxx @@ -384,8 +384,9 @@ drawTriangle(const btVector3 &v0, const btVector3 &v1, const btVector3 &v2, cons */ void BulletDebugNode::DebugDraw:: drawTriangle(const btVector3 &v0, const btVector3 &v1, const btVector3 &v2, const btVector3 &n0, const btVector3 &n1, const btVector3 &n2, const btVector3 &color, btScalar alpha) { - - bullet_cat.debug() << "drawTriangle(2) - not yet implemented!" << std::endl; + if (bullet_cat.is_debug()) { + bullet_cat.debug() << "drawTriangle(2) - not yet implemented!" << std::endl; + } } /** @@ -405,8 +406,9 @@ drawContactPoint(const btVector3 &point, const btVector3 &normal, btScalar dista */ void BulletDebugNode::DebugDraw:: draw3dText(const btVector3 &location, const char *text) { - - bullet_cat.debug() << "draw3dText - not yet implemented!" << std::endl; + if (bullet_cat.is_debug()) { + bullet_cat.debug() << "draw3dText - not yet implemented!" << std::endl; + } } /** diff --git a/panda/src/bullet/bulletRigidBodyNode.cxx b/panda/src/bullet/bulletRigidBodyNode.cxx index 6f5bff0d46..acd7e96e79 100644 --- a/panda/src/bullet/bulletRigidBodyNode.cxx +++ b/panda/src/bullet/bulletRigidBodyNode.cxx @@ -120,7 +120,7 @@ do_set_mass(PN_stdfloat mass) { btScalar bt_mass = mass; btVector3 bt_inertia(0.0, 0.0, 0.0); - if (bt_mass > 0.0) { + if (bt_mass > 0.0 && !_shapes.empty()) { _rigid->getCollisionShape()->calculateLocalInertia(bt_mass, bt_inertia); } @@ -359,6 +359,20 @@ do_transform_changed() { } } +/** + * + */ +void BulletRigidBodyNode:: +parents_changed() { + + if (_motion.sync_disabled()) return; + + if (get_num_parents() > 0) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + do_transform_changed(); + } +} + /** * */ diff --git a/panda/src/bullet/bulletRigidBodyNode.h b/panda/src/bullet/bulletRigidBodyNode.h index d0e11da6cf..246dec3e6c 100644 --- a/panda/src/bullet/bulletRigidBodyNode.h +++ b/panda/src/bullet/bulletRigidBodyNode.h @@ -112,6 +112,7 @@ public: void do_sync_b2p(); protected: + virtual void parents_changed(); virtual void transform_changed(); private: diff --git a/panda/src/bullet/bulletVehicle.cxx b/panda/src/bullet/bulletVehicle.cxx index ba7ee35d6d..adb5d6c692 100644 --- a/panda/src/bullet/bulletVehicle.cxx +++ b/panda/src/bullet/bulletVehicle.cxx @@ -171,14 +171,14 @@ set_pitch_control(PN_stdfloat pitch) { * Factory method for creating wheels for this vehicle instance. */ BulletWheel BulletVehicle:: -create_wheel() { +create_wheel(PN_stdfloat suspension_rest_length) { LightMutexHolder holder(BulletWorld::get_global_lock()); btVector3 pos(0.0, 0.0, 0.0); btVector3 direction = get_axis(_vehicle->getUpAxis()); btVector3 axle = get_axis(_vehicle->getRightAxis()); - btScalar suspension(0.4); + btScalar suspension(suspension_rest_length); btScalar radius(0.3); btWheelInfo &info = _vehicle->addWheel(pos, direction, axle, suspension, radius, _tuning._, false); diff --git a/panda/src/bullet/bulletVehicle.h b/panda/src/bullet/bulletVehicle.h index 9f9da751c3..486e38a049 100644 --- a/panda/src/bullet/bulletVehicle.h +++ b/panda/src/bullet/bulletVehicle.h @@ -85,7 +85,7 @@ PUBLISHED: void apply_engine_force(PN_stdfloat force, int idx); // Wheels - BulletWheel create_wheel(); + BulletWheel create_wheel(PN_stdfloat suspension_rest_length=0.4); int get_num_wheels() const; BulletWheel get_wheel(int idx) const; diff --git a/panda/src/bullet/bulletWorld.cxx b/panda/src/bullet/bulletWorld.cxx index 930fe10111..a76b0e8007 100644 --- a/panda/src/bullet/bulletWorld.cxx +++ b/panda/src/bullet/bulletWorld.cxx @@ -222,6 +222,8 @@ int BulletWorld:: do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) { LightMutexHolder holder(get_global_lock()); + bullet_contact_added_callback = _contact_added_callback_obj; + _pstat_physics.start(); int num_substeps = clamp(int(dt / stepsize), 1, max_substeps); @@ -249,6 +251,8 @@ do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) { _pstat_physics.stop(); + bullet_contact_added_callback.clear(); + return n; } @@ -363,7 +367,7 @@ remove(TypedObject *object) { } /** - * Deprecated! Please use BulletWorld::attach + * @deprecated Please use BulletWorld::attach */ void BulletWorld:: attach_rigid_body(BulletRigidBodyNode *node) { @@ -373,7 +377,7 @@ attach_rigid_body(BulletRigidBodyNode *node) { } /** - * Deprecated.! Please use BulletWorld::remove + * @deprecated Please use BulletWorld::remove */ void BulletWorld:: remove_rigid_body(BulletRigidBodyNode *node) { @@ -383,7 +387,7 @@ remove_rigid_body(BulletRigidBodyNode *node) { } /** - * Deprecated! Please use BulletWorld::attach + * @deprecated Please use BulletWorld::attach */ void BulletWorld:: attach_soft_body(BulletSoftBodyNode *node) { @@ -393,7 +397,7 @@ attach_soft_body(BulletSoftBodyNode *node) { } /** - * Deprecated.! Please use BulletWorld::remove + * @deprecated Please use BulletWorld::remove */ void BulletWorld:: remove_soft_body(BulletSoftBodyNode *node) { @@ -403,7 +407,7 @@ remove_soft_body(BulletSoftBodyNode *node) { } /** - * Deprecated! Please use BulletWorld::attach + * @deprecated Please use BulletWorld::attach */ void BulletWorld:: attach_ghost(BulletGhostNode *node) { @@ -413,7 +417,7 @@ attach_ghost(BulletGhostNode *node) { } /** - * Deprecated.! Please use BulletWorld::remove + * @deprecated Please use BulletWorld::remove */ void BulletWorld:: remove_ghost(BulletGhostNode *node) { @@ -423,7 +427,7 @@ remove_ghost(BulletGhostNode *node) { } /** - * Deprecated! Please use BulletWorld::attach + * @deprecated Please use BulletWorld::attach */ void BulletWorld:: attach_character(BulletBaseCharacterControllerNode *node) { @@ -433,7 +437,7 @@ attach_character(BulletBaseCharacterControllerNode *node) { } /** - * Deprecated.! Please use BulletWorld::remove + * @deprecated Please use BulletWorld::remove */ void BulletWorld:: remove_character(BulletBaseCharacterControllerNode *node) { @@ -443,7 +447,7 @@ remove_character(BulletBaseCharacterControllerNode *node) { } /** - * Deprecated! Please use BulletWorld::attach + * @deprecated Please use BulletWorld::attach */ void BulletWorld:: attach_vehicle(BulletVehicle *vehicle) { @@ -453,7 +457,7 @@ attach_vehicle(BulletVehicle *vehicle) { } /** - * Deprecated.! Please use BulletWorld::remove + * @deprecated Please use BulletWorld::remove */ void BulletWorld:: remove_vehicle(BulletVehicle *vehicle) { @@ -474,7 +478,7 @@ attach_constraint(BulletConstraint *constraint, bool linked_collision) { } /** - * Deprecated.! Please use BulletWorld::remove + * @deprecated Please use BulletWorld::remove */ void BulletWorld:: remove_constraint(BulletConstraint *constraint) { @@ -1146,7 +1150,7 @@ set_contact_added_callback(CallbackObject *obj) { _world->getSolverInfo().m_solverMode |= SOLVER_USE_2_FRICTION_DIRECTIONS; _world->getSolverInfo().m_solverMode |= SOLVER_ENABLE_FRICTION_DIRECTION_CACHING; - bullet_contact_added_callback = obj; + _contact_added_callback_obj = obj; } /** @@ -1160,7 +1164,7 @@ clear_contact_added_callback() { _world->getSolverInfo().m_solverMode &= ~SOLVER_USE_2_FRICTION_DIRECTIONS; _world->getSolverInfo().m_solverMode &= ~SOLVER_ENABLE_FRICTION_DIRECTION_CACHING; - bullet_contact_added_callback = nullptr; + _contact_added_callback_obj = nullptr; } /** diff --git a/panda/src/bullet/bulletWorld.h b/panda/src/bullet/bulletWorld.h index 9c709c1e91..3218f92742 100644 --- a/panda/src/bullet/bulletWorld.h +++ b/panda/src/bullet/bulletWorld.h @@ -43,7 +43,7 @@ class BulletPersistentManifold; class BulletShape; class BulletSoftBodyWorldInfo; -extern PT(CallbackObject) bullet_contact_added_callback; +extern EXPCL_PANDABULLET PT(CallbackObject) bullet_contact_added_callback; /** * @@ -58,7 +58,7 @@ PUBLISHED: void set_gravity(PN_stdfloat gx, PN_stdfloat gy, PN_stdfloat gz); const LVector3 get_gravity() const; - int do_physics(PN_stdfloat dt, int max_substeps=1, PN_stdfloat stepsize=1.0f/60.0f); + BLOCKING int do_physics(PN_stdfloat dt, int max_substeps=1, PN_stdfloat stepsize=1.0f/60.0f); BulletSoftBodyWorldInfo get_world_info(); @@ -272,6 +272,7 @@ private: btOverlapFilterCallback *_filter_cb; PT(CallbackObject) _tick_callback_obj; + PT(CallbackObject) _contact_added_callback_obj; PT(BulletDebugNode) _debug; diff --git a/panda/src/bullet/config_bullet.cxx b/panda/src/bullet/config_bullet.cxx index f9eed1b6b8..140c7d0efc 100644 --- a/panda/src/bullet/config_bullet.cxx +++ b/panda/src/bullet/config_bullet.cxx @@ -207,7 +207,9 @@ init_libbullet() { // Initialize notification category bullet_cat.init(); - bullet_cat.debug() << "initialize module" << std::endl; + if (bullet_cat.is_debug()) { + bullet_cat.debug() << "initialize module" << std::endl; + } // Register the Bullet system PandaSystem *ps = PandaSystem::get_global_ptr(); diff --git a/panda/src/chan/animControl.h b/panda/src/chan/animControl.h index d52b2e4835..7b9f973cd7 100644 --- a/panda/src/chan/animControl.h +++ b/panda/src/chan/animControl.h @@ -24,7 +24,7 @@ #include "typedReferenceCount.h" #include "namable.h" #include "pmutex.h" -#include "conditionVarFull.h" +#include "conditionVar.h" class PartBundle; class AnimChannelBase; @@ -79,7 +79,7 @@ private: bool _pending; std::string _pending_done_event; Mutex _pending_lock; // protects the above two. - ConditionVarFull _pending_cvar; // signals when _pending goes true. + ConditionVar _pending_cvar; // signals when _pending goes true. // This is a PT(PartGroup) instead of a PT(PartBundle), just because we // can't include partBundle.h for circular reasons. But it actually keeps a diff --git a/panda/src/chan/movingPartBase.I b/panda/src/chan/movingPartBase.I index 182f597b67..223a92a7ec 100644 --- a/panda/src/chan/movingPartBase.I +++ b/panda/src/chan/movingPartBase.I @@ -17,7 +17,6 @@ INLINE MovingPartBase:: MovingPartBase(const MovingPartBase ©) : PartGroup(copy), - _num_effective_channels(0), _effective_control(nullptr), _forced_channel(copy._forced_channel) { diff --git a/panda/src/chan/movingPartBase.cxx b/panda/src/chan/movingPartBase.cxx index e4016edd2c..563c8aa794 100644 --- a/panda/src/chan/movingPartBase.cxx +++ b/panda/src/chan/movingPartBase.cxx @@ -28,7 +28,6 @@ TypeHandle MovingPartBase::_type_handle; MovingPartBase:: MovingPartBase(PartGroup *parent, const std::string &name) : PartGroup(parent, name), - _num_effective_channels(0), _effective_control(nullptr) { } @@ -38,7 +37,6 @@ MovingPartBase(PartGroup *parent, const std::string &name) : */ MovingPartBase:: MovingPartBase() : - _num_effective_channels(0), _effective_control(nullptr) { } @@ -305,7 +303,6 @@ void MovingPartBase:: determine_effective_channels(const CycleData *root_cdata) { _effective_control = nullptr; _effective_channel = nullptr; - _num_effective_channels = 0; AnimControl *effective_control = nullptr; AnimChannelBase *effective_channel = nullptr; @@ -327,7 +324,6 @@ determine_effective_channels(const CycleData *root_cdata) { } } - _num_effective_channels = num_effective_channels; if (num_effective_channels == 1) { _effective_control = effective_control; _effective_channel = effective_channel; diff --git a/panda/src/chan/movingPartBase.h b/panda/src/chan/movingPartBase.h index c7b09c8acf..7dac536516 100644 --- a/panda/src/chan/movingPartBase.h +++ b/panda/src/chan/movingPartBase.h @@ -78,13 +78,9 @@ protected: typedef pvector< PT(AnimChannelBase) > Channels; Channels _channels; - // This is the number of channels in the above _channels vector that - // actually have an effect on this part. - int _num_effective_channels; - // This is the single channel that has an effect on this part, as determined // by determine_effective_channels(). It is only set if there is exactly - // one channel that affects this part (i.e. _num_effective_channels is 1). + // one channel that affects this part (i.e. num_effective_channels is 1). // If there are multiple channels, or no channels at all, it is NULL. AnimControl *_effective_control; PT(AnimChannelBase) _effective_channel; diff --git a/panda/src/chan/partBundle.cxx b/panda/src/chan/partBundle.cxx index 9d960f3e7d..ddd31898ee 100644 --- a/panda/src/chan/partBundle.cxx +++ b/panda/src/chan/partBundle.cxx @@ -201,7 +201,6 @@ clear_control_effects() { if (!cdata->_blend.empty()) { CDWriter cdataw(_cycler, cdata); cdataw->_blend.clear(); - cdataw->_net_blend = 0.0f; cdataw->_anim_changed = true; determine_effective_channels(cdataw); } @@ -556,7 +555,6 @@ control_removed(AnimControl *control) { CDStageWriter cdata(_cycler, pipeline_stage); ChannelBlend::iterator cbi = cdata->_blend.find(control); if (cbi != cdata->_blend.end()) { - cdata->_net_blend -= cbi->second; cdata->_blend.erase(cbi); cdata->_anim_changed = true; @@ -678,7 +676,7 @@ do_set_control_effect(AnimControl *control, PN_stdfloat effect, CData *cdata) { cdata->_last_control_set = control; } - recompute_net_blend(cdata); + determine_effective_channels(cdata); } /** @@ -697,23 +695,6 @@ do_get_control_effect(AnimControl *control, const CData *cdata) const { } } - -/** - * Recomputes the total blending amount after a control effect has been - * adjusted. This value must be kept up-to-date so we can normalize the - * blending amounts. - */ -void PartBundle:: -recompute_net_blend(CData *cdata) { - cdata->_net_blend = 0.0f; - - ChannelBlend::const_iterator bti; - for (bti = cdata->_blend.begin(); bti != cdata->_blend.end(); ++bti) { - cdata->_net_blend += (*bti).second; - } - determine_effective_channels(cdata); -} - /** * Removes and stops all the currently activated AnimControls that animate * some joints also animated by the indicated AnimControl. This is a special @@ -722,7 +703,6 @@ recompute_net_blend(CData *cdata) { */ void PartBundle:: clear_and_stop_intersecting(AnimControl *control, CData *cdata) { - double new_net_blend = 0.0f; ChannelBlend new_blend; bool any_changed = false; @@ -734,7 +714,6 @@ clear_and_stop_intersecting(AnimControl *control, CData *cdata) { // Save this control--it's either the target control, or it has no // joints in common with the target control. new_blend.insert(new_blend.end(), (*cbi)); - new_net_blend += (*cbi).second; } else { // Remove and stop this control. ac->stop(); @@ -743,7 +722,6 @@ clear_and_stop_intersecting(AnimControl *control, CData *cdata) { } if (any_changed) { - cdata->_net_blend = new_net_blend; cdata->_blend.swap(new_blend); cdata->_anim_changed = true; determine_effective_channels(cdata); @@ -840,7 +818,6 @@ CData() { _frame_blend_flag = interpolate_frames; _root_xform = LMatrix4::ident_mat(); _last_control_set = nullptr; - _net_blend = 0.0f; _anim_changed = false; _last_update = 0.0; } @@ -856,7 +833,6 @@ CData(const PartBundle::CData ©) : _root_xform(copy._root_xform), _last_control_set(copy._last_control_set), _blend(copy._blend), - _net_blend(copy._net_blend), _anim_changed(copy._anim_changed), _last_update(copy._last_update) { diff --git a/panda/src/chan/partBundle.h b/panda/src/chan/partBundle.h index 7e9d21259d..aae12f7780 100644 --- a/panda/src/chan/partBundle.h +++ b/panda/src/chan/partBundle.h @@ -164,7 +164,6 @@ private: void do_set_control_effect(AnimControl *control, PN_stdfloat effect, CData *cdata); PN_stdfloat do_get_control_effect(AnimControl *control, const CData *cdata) const; - void recompute_net_blend(CData *cdata); void clear_and_stop_intersecting(AnimControl *control, CData *cdata); COWPT(AnimPreloadTable) _anim_preload; @@ -196,7 +195,6 @@ private: LMatrix4 _root_xform; AnimControl *_last_control_set; ChannelBlend _blend; - PN_stdfloat _net_blend; bool _anim_changed; double _last_update; }; diff --git a/panda/src/char/character.cxx b/panda/src/char/character.cxx index 0e2655de54..eb894fe99c 100644 --- a/panda/src/char/character.cxx +++ b/panda/src/char/character.cxx @@ -243,7 +243,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any, * pointer within this node will be replaced with the new_bundle pointer, and * all geometry within this node will be updated to reference new_bundle. * - * This method is deprecated. Use the newer version of this method, below. + * @deprecated Use the newer version of this method, below. */ void Character:: merge_bundles(PartBundle *old_bundle, PartBundle *new_bundle) { @@ -417,7 +417,7 @@ write_part_values(std::ostream &out) const { * character's position to the current frame, regardless of whether the * character is currently onscreen and animating. * - * This method is deprecated. Call update() instead. + * @deprecated Call update() instead. */ void Character:: update_to_now() { diff --git a/panda/src/cocoadisplay/cocoaGraphicsBuffer.h b/panda/src/cocoadisplay/cocoaGraphicsBuffer.h index 3167d2c02f..8485540fdc 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsBuffer.h +++ b/panda/src/cocoadisplay/cocoaGraphicsBuffer.h @@ -21,7 +21,7 @@ * This is a light wrapper around GLGraphicsBuffer (ie. FBOs) to interface * with Cocoa contexts, so that it can be used without a host window. */ -class CocoaGraphicsBuffer : public GLGraphicsBuffer { +class EXPCL_PANDA_COCOADISPLAY CocoaGraphicsBuffer : public GLGraphicsBuffer { public: CocoaGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe, const std::string &name, diff --git a/panda/src/cocoadisplay/cocoaGraphicsPipe.h b/panda/src/cocoadisplay/cocoaGraphicsPipe.h index cc8c8142d5..902d673047 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsPipe.h +++ b/panda/src/cocoadisplay/cocoaGraphicsPipe.h @@ -33,7 +33,7 @@ class FrameBufferProperties; * This graphics pipe represents the interface for creating OpenGL graphics * windows on a Cocoa-based (e.g. Mac OS X) client. */ -class CocoaGraphicsPipe : public GraphicsPipe { +class EXPCL_PANDA_COCOADISPLAY CocoaGraphicsPipe : public GraphicsPipe { public: CocoaGraphicsPipe(CGDirectDisplayID display = CGMainDisplayID()); virtual ~CocoaGraphicsPipe(); diff --git a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm index 91bd0fbbb9..4f08044a85 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm @@ -53,8 +53,10 @@ CocoaGraphicsPipe(CGDirectDisplayID display) : _display(display) { _display_height = CGDisplayPixelsHigh(_display); load_display_information(); - cocoadisplay_cat.debug() - << "Creating CocoaGraphicsPipe for display ID " << _display << "\n"; + if (cocoadisplay_cat.is_debug()) { + cocoadisplay_cat.debug() + << "Creating CocoaGraphicsPipe for display ID " << _display << "\n"; + } } /** diff --git a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h index c8820a73d3..0463372b8e 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h +++ b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h @@ -26,7 +26,7 @@ * A tiny specialization on GLGraphicsStateGuardian to add some Cocoa-specific * information. */ -class CocoaGraphicsStateGuardian : public GLGraphicsStateGuardian { +class EXPCL_PANDA_COCOADISPLAY CocoaGraphicsStateGuardian : public GLGraphicsStateGuardian { public: INLINE const FrameBufferProperties &get_fb_properties() const; void get_properties(FrameBufferProperties &properties, diff --git a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm index aac8beb033..7ffdc23729 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm @@ -248,8 +248,17 @@ choose_pixel_format(const FrameBufferProperties &properties, attribs.push_back(aux_buffers); attribs.push_back(NSOpenGLPFAColorSize); attribs.push_back(properties.get_color_bits()); + + // Set the depth buffer bits to 24 manually when 1 is requested. + // This prevents getting a depth buffer of only 16 bits when requesting 1. attribs.push_back(NSOpenGLPFADepthSize); - attribs.push_back(properties.get_depth_bits()); + if (properties.get_depth_bits() == 1) { + attribs.push_back(24); + } + else { + attribs.push_back(properties.get_depth_bits()); + } + attribs.push_back(NSOpenGLPFAStencilSize); attribs.push_back(properties.get_stencil_bits()); @@ -314,8 +323,10 @@ choose_pixel_format(const FrameBufferProperties &properties, } // For now, I'm just using the first virtual screen. - cocoadisplay_cat.debug() << - "Pixel format has " << [format numberOfVirtualScreens] << " virtual screens.\n"; + if (cocoadisplay_cat.is_debug()) { + cocoadisplay_cat.debug() << + "Pixel format has " << [format numberOfVirtualScreens] << " virtual screens.\n"; + } get_properties(_fbprops, format, 0); // Don't enable sRGB unless it was explicitly requested. @@ -337,8 +348,10 @@ choose_pixel_format(const FrameBufferProperties &properties, GLint swap = 0; [_context setValues:&swap forParameter:NSOpenGLCPSwapInterval]; - cocoadisplay_cat.debug() - << "Created context " << _context << ": " << _fbprops << "\n"; + if (cocoadisplay_cat.is_debug()) { + cocoadisplay_cat.debug() + << "Created context " << _context << ": " << _fbprops << "\n"; + } } /** diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.h b/panda/src/cocoadisplay/cocoaGraphicsWindow.h index a053cb11b9..99e8814f63 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.h +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.h @@ -29,7 +29,7 @@ * An interface to the Cocoa system for managing OpenGL windows under Mac OS * X. */ -class CocoaGraphicsWindow : public GraphicsWindow { +class EXPCL_PANDA_COCOADISPLAY CocoaGraphicsWindow : public GraphicsWindow { public: CocoaGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, const std::string &name, diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index c3f23d4013..7b09d3424a 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -194,8 +194,10 @@ begin_frame(FrameMode mode, Thread *current_thread) { _context_needs_update = true; [cocoagsg->_context setView:_view]; - cocoadisplay_cat.spam() - << "Switching context to view " << _view << "\n"; + if (cocoadisplay_cat.is_spam()) { + cocoadisplay_cat.spam() + << "Switching context to view " << _view << "\n"; + } } } @@ -711,8 +713,11 @@ close_window() { } if (_window != nil) { - [_window setReleasedWhenClosed: YES]; [_window close]; + + // Process events once more so any pending NSEvents are cleared. Not doing + // this causes the window to stick around after calling [_window close]. + process_events(); _window = nil; } @@ -915,8 +920,11 @@ set_properties_now(WindowProperties &properties) { frame.origin.x = x; frame.origin.y = container.size.height - y - frame.size.height; - cocoadisplay_cat.debug() - << "Setting window content origin to " << frame.origin.x << ", " << frame.origin.y << "\n"; + if (cocoadisplay_cat.is_debug()) { + cocoadisplay_cat.debug() + << "Setting window content origin to " + << frame.origin.x << ", " << frame.origin.y << "\n"; + } if (_window != nil) { [_window setFrame:[_window frameRectForContentRect:frame] display:NO]; @@ -1469,16 +1477,20 @@ handle_close_request() { // and process it directly. throw_event(close_request_event); - cocoadisplay_cat.debug() - << "Window requested close. Rejecting, throwing event " - << close_request_event << " instead\n"; + if (cocoadisplay_cat.is_debug()) { + cocoadisplay_cat.debug() + << "Window requested close. Rejecting, throwing event " + << close_request_event << " instead\n"; + } // Prevent the operating system from closing the window. return false; } - cocoadisplay_cat.debug() - << "Window requested close, accepting\n"; + if (cocoadisplay_cat.is_debug()) { + cocoadisplay_cat.debug() + << "Window requested close, accepting\n"; + } // Let the operating system close the window normally. return true; @@ -1489,7 +1501,9 @@ handle_close_request() { */ void CocoaGraphicsWindow:: handle_close_event() { - cocoadisplay_cat.debug() << "Window is about to close\n"; + if (cocoadisplay_cat.is_debug()) { + cocoadisplay_cat.debug() << "Window is about to close\n"; + } _window = nil; @@ -1615,7 +1629,7 @@ handle_key_event(NSEvent *event) { if ([event type] == NSKeyDown) { // Translate it to a unicode character for keystrokes. I would use // interpretKeyEvents and insertText, but that doesn't handle dead keys. - TISInputSourceRef input_source = TISCopyCurrentKeyboardInputSource(); + TISInputSourceRef input_source = TISCopyCurrentKeyboardLayoutInputSource(); CFDataRef layout_data = (CFDataRef)TISGetInputSourceProperty(input_source, kTISPropertyUnicodeKeyLayoutData); const UCKeyboardLayout *layout = (const UCKeyboardLayout *)CFDataGetBytePtr(layout_data); @@ -1704,17 +1718,17 @@ handle_mouse_button_event(int button, bool down) { if (down) { _input->button_down(MouseButton::button(button)); -#ifndef NDEBUG - cocoadisplay_cat.spam() - << "Mouse button " << button << " down\n"; -#endif + if (cocoadisplay_cat.is_spam()) { + cocoadisplay_cat.spam() + << "Mouse button " << button << " down\n"; + } } else { _input->button_up(MouseButton::button(button)); -#ifndef NDEBUG - cocoadisplay_cat.spam() - << "Mouse button " << button << " up\n"; -#endif + if (cocoadisplay_cat.is_spam()) { + cocoadisplay_cat.spam() + << "Mouse button " << button << " up\n"; + } } } @@ -1792,8 +1806,10 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) { */ void CocoaGraphicsWindow:: handle_wheel_event(double x, double y) { - cocoadisplay_cat.spam() - << "Wheel delta " << x << ", " << y << "\n"; + if (cocoadisplay_cat.is_spam()) { + cocoadisplay_cat.spam() + << "Wheel delta " << x << ", " << y << "\n"; + } if (y > 0.0) { _input->button_down(MouseButton::wheel_up()); @@ -1824,7 +1840,7 @@ get_keyboard_map() const { const UCKeyboardLayout *layout; // Get the current keyboard layout data. - input_source = TISCopyCurrentKeyboardInputSource(); + input_source = TISCopyCurrentKeyboardLayoutInputSource(); layout_data = (CFDataRef) TISGetInputSourceProperty(input_source, kTISPropertyUnicodeKeyLayoutData); layout = (const UCKeyboardLayout *)CFDataGetBytePtr(layout_data); diff --git a/panda/src/cocoadisplay/cocoaPandaView.mm b/panda/src/cocoadisplay/cocoaPandaView.mm index 6a3f48b39a..bd06a8df90 100644 --- a/panda/src/cocoadisplay/cocoaPandaView.mm +++ b/panda/src/cocoadisplay/cocoaPandaView.mm @@ -30,8 +30,10 @@ // the redrawing since we're doing things our own way. self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever; - cocoadisplay_cat.debug() - << "Created CocoaPandaView " << self << " for GraphicsWindow " << window << "\n"; + if (cocoadisplay_cat.is_debug()) { + cocoadisplay_cat.debug() + << "Created CocoaPandaView " << self << " for GraphicsWindow " << window << "\n"; + } _graphicsWindow = window; return self; diff --git a/panda/src/cocoadisplay/cocoaPandaWindowDelegate.h b/panda/src/cocoadisplay/cocoaPandaWindowDelegate.h index 1a80ef056e..93d5b7b35b 100644 --- a/panda/src/cocoadisplay/cocoaPandaWindowDelegate.h +++ b/panda/src/cocoadisplay/cocoaPandaWindowDelegate.h @@ -34,7 +34,6 @@ class CocoaGraphicsWindow; - (void)windowDidBecomeKey:(NSNotification *)notification; - (void)windowDidResignKey:(NSNotification *)notification; - (BOOL)windowShouldClose:(id)sender; -- (void)windowWillClose:(NSNotification *)notification; // TODO: handle fullscreen on Lion. diff --git a/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm b/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm index 6b93410538..7e39748bd1 100644 --- a/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm +++ b/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm @@ -50,11 +50,11 @@ } - (BOOL) windowShouldClose:(id)sender { - return _graphicsWindow->handle_close_request(); -} - -- (void) windowWillClose:(NSNotification *)notification { - _graphicsWindow->handle_close_event(); + bool should_close = _graphicsWindow->handle_close_request(); + if (should_close) { + _graphicsWindow->handle_close_event(); + } + return should_close; } @end diff --git a/panda/src/collide/collisionBox.cxx b/panda/src/collide/collisionBox.cxx index 8528e7bc27..84a8e39817 100644 --- a/panda/src/collide/collisionBox.cxx +++ b/panda/src/collide/collisionBox.cxx @@ -16,6 +16,7 @@ #include "collisionRay.h" #include "collisionSphere.h" #include "collisionSegment.h" +#include "collisionParabola.h" #include "collisionCapsule.h" #include "collisionHandler.h" #include "collisionEntry.h" @@ -186,6 +187,7 @@ get_test_pcollector() { */ void CollisionBox:: output(std::ostream &out) const { + out << "box, (" << get_min() << ") to (" << get_max() << ")"; } /** @@ -492,6 +494,82 @@ test_intersection_from_ray(const CollisionEntry &entry) const { return new_entry; } +PT(CollisionEntry) CollisionBox:: +test_intersection_from_parabola(const CollisionEntry &entry) const { + const CollisionParabola *parabola; + DCAST_INTO_R(parabola, entry.get_from(), nullptr); + + const LMatrix4 &wrt_mat = entry.get_wrt_mat(); + + // Convert the parabola into local coordinate space. + LParabola local_p(parabola->get_parabola()); + local_p.xform(wrt_mat); + + PN_stdfloat t = INT_MAX; + PN_stdfloat t1, t2; + int intersecting_face = -1; + for (int i = 0; i < get_num_planes(); i++) { + LPlane face = get_plane(i); + if (!face.intersects_parabola(t1, t2, local_p)) { + // the parabola does not intersect this face, skip to the next one + continue; + } + PN_stdfloat ts[2] = {t1, t2}; + // iterate through the t values to see if each of them are within our + // parabola and the intersection point is behind all other faces + for (int j = 0; j < 2; j++) { + PN_stdfloat cur_t = ts[j]; + if (cur_t > t) { + // we are looking for the earliest t value + // if this t value is greater, don't bother checking it + continue; + } + if (cur_t >= parabola->get_t1() && cur_t <= parabola->get_t2()) { + // the parabola does intersect this plane, now we check + // if the intersection point is behind all other planes + bool behind = true; + for (int k = 0; k < get_num_planes(); k++) { + if (k == i) { + // no need to check the intersecting face + continue; + } + if (get_plane(k).dist_to_plane(local_p.calc_point(cur_t)) > 0.0f) { + // our point is in front of this face, turns out the parabola + // does not collide with the box at this point + behind = false; + break; + } + } + if (behind) { + // the parabola does indeed collide with the box at this point + t = cur_t; + intersecting_face = i; + } + } + } + } + + if (intersecting_face != -1) { + if (collide_cat.is_debug()) { + collide_cat.debug() + << "intersection detected from " << entry.get_from_node_path() + << " into " << entry.get_into_node_path() << "\n"; + } + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + + LPlane face = get_plane(intersecting_face); + + LPoint3 into_intersection_point = local_p.calc_point(t); + LVector3 normal = (has_effective_normal() && parabola->get_respect_effective_normal()) ? get_effective_normal() : face.get_normal(); + + new_entry->set_surface_point(into_intersection_point); + new_entry->set_surface_normal(normal); + return new_entry; + } else { + return nullptr; + } +} + /** * Double dispatch point for segment as a FROM object */ diff --git a/panda/src/collide/collisionBox.h b/panda/src/collide/collisionBox.h index db9d2c2aa9..4bf96f0212 100644 --- a/panda/src/collide/collisionBox.h +++ b/panda/src/collide/collisionBox.h @@ -81,6 +81,8 @@ protected: test_intersection_from_ray(const CollisionEntry &entry) const; virtual PT(CollisionEntry) test_intersection_from_segment(const CollisionEntry &entry) const; + virtual PT(CollisionEntry) + test_intersection_from_parabola(const CollisionEntry &entry) const; virtual PT(CollisionEntry) test_intersection_from_capsule(const CollisionEntry &entry) const; virtual PT(CollisionEntry) @@ -139,7 +141,6 @@ public: INLINE void calc_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const; INLINE void rederive_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const; INLINE static LPoint3 to_3d(const LVecBase2 &point2d, const LMatrix4 &to_3d_mat); - LPoint3 legacy_to_3d(const LVecBase2 &point2d, int axis) const; bool clip_polygon(Points &new_points, const Points &source_points, const LPlane &plane,int plane_no) const; bool apply_clip_plane(Points &new_points, const ClipPlaneAttrib *cpa, diff --git a/panda/src/collide/collisionCapsule.cxx b/panda/src/collide/collisionCapsule.cxx index 91ab319735..3eed758863 100644 --- a/panda/src/collide/collisionCapsule.cxx +++ b/panda/src/collide/collisionCapsule.cxx @@ -12,6 +12,7 @@ */ #include "collisionCapsule.h" +#include "collisionBox.h" #include "collisionSphere.h" #include "collisionLine.h" #include "collisionRay.h" @@ -140,6 +141,87 @@ compute_internal_bounds() const { return bound; } +/** + * + */ +PT(CollisionEntry) CollisionCapsule:: +test_intersection_from_box(const CollisionEntry &entry) const { + const CollisionBox *box; + DCAST_INTO_R(box, entry.get_from(), nullptr); + + const LMatrix4 wrt_mat = entry.get_wrt_mat(); + const LMatrix4 inv_wrt_mat = entry.get_inv_wrt_mat(); + + // We find the closest point on the box to the line + // segment defined by the endpoints of the capsule. To do + // this, convert the capsule into the box's coordinate space. + LPoint3 box_min = box->get_min(); + LPoint3 box_max = box->get_max(); + LPoint3 a = _a * inv_wrt_mat; + LPoint3 b = _b * inv_wrt_mat; + LVector3 delta = b - a; + + double min_dist = DBL_MAX; + PN_stdfloat t; + LPoint3 closest_point; + // Iterate through the 6 planes of the box + for (int i = 0; i < 6; i++) { + if (!box->get_plane(i).intersects_line(t, a, delta)) { + continue; + } + if (t > 1.0) t = 1.0; + if (t < 0.0) t = 0.0; + if (t == min_dist) continue; + LPoint3 intersection_point = a + delta * t; + // Find the closest point on the box to the intersection point + LPoint3 p = intersection_point.fmax(box_min).fmin(box_max); + double dist = (p - intersection_point).length_squared(); + if (dist < min_dist) { + min_dist = dist; + closest_point = p; + } + } + // Convert the closest point to the capsule's coordinate + // space where the capsule is aligned with the y axis. + closest_point = closest_point * wrt_mat * _inv_mat; + a = _a * _inv_mat; + b = _b * _inv_mat; + + // Find the closest point on the line segment + LPoint3 point_on_segment; + point_on_segment[0] = a[0]; + point_on_segment[2] = a[2]; + if (closest_point[1] < std::min(a[1], b[1])) { + point_on_segment[1] = std::min(a[1], b[1]); + } else if (closest_point[1] > std::max(a[1], b[1])) { + point_on_segment[1] = std::max(a[1], b[1]); + } else { + point_on_segment[1] = closest_point[1]; + } + + if ((closest_point - point_on_segment).length_squared() > _radius * _radius) { + // No intersection. + return nullptr; + } + + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + LVector3 normal; + if (point_on_segment == closest_point) { + // If the box directly intersects the line segment, use + // the box's center to determine the surface normal. + normal = (box->get_center() * wrt_mat * _inv_mat) - point_on_segment; + if (closest_point != a && closest_point != b) normal[1] = 0; + } else { + normal = (closest_point - point_on_segment); + } + normal.normalize(); + LPoint3 surface_point = point_on_segment + normal * _radius; + new_entry->set_interior_point(_mat.xform_point(closest_point)); + new_entry->set_surface_point(_mat.xform_point(surface_point)); + new_entry->set_surface_normal(_mat.xform_vec(normal)); + return new_entry; +} + /** * */ diff --git a/panda/src/collide/collisionCapsule.h b/panda/src/collide/collisionCapsule.h index b6d375ff44..0680125591 100644 --- a/panda/src/collide/collisionCapsule.h +++ b/panda/src/collide/collisionCapsule.h @@ -74,6 +74,8 @@ protected: virtual PT(BoundingVolume) compute_internal_bounds() const; protected: + virtual PT(CollisionEntry) + test_intersection_from_box(const CollisionEntry &entry) const; virtual PT(CollisionEntry) test_intersection_from_sphere(const CollisionEntry &entry) const; virtual PT(CollisionEntry) @@ -154,6 +156,7 @@ private: static TypeHandle _type_handle; friend class CollisionBox; + friend class CollisionPolygon; }; #include "collisionCapsule.I" diff --git a/panda/src/collide/collisionHandlerEvent.I b/panda/src/collide/collisionHandlerEvent.I index d5b6327c69..13303de921 100644 --- a/panda/src/collide/collisionHandlerEvent.I +++ b/panda/src/collide/collisionHandlerEvent.I @@ -80,6 +80,8 @@ add_in_pattern(const std::string &in_pattern) { /** * This method is deprecated; it completely replaces all the in patterns that * have previously been set with the indicated pattern. + * + * @deprecated Use add_in_pattern() instead. */ INLINE void CollisionHandlerEvent:: set_in_pattern(const std::string &in_pattern) { @@ -133,6 +135,8 @@ add_again_pattern(const std::string &again_pattern) { /** * This method is deprecated; it completely replaces all the in patterns that * have previously been set with the indicated pattern. + * + * @deprecated Use add_again_pattern() instead. */ INLINE void CollisionHandlerEvent:: set_again_pattern(const std::string &again_pattern) { @@ -184,6 +188,8 @@ add_out_pattern(const std::string &out_pattern) { /** * This method is deprecated; it completely replaces all the in patterns that * have previously been set with the indicated pattern. + * + * @deprecated Use add_out_pattern() instead. */ INLINE void CollisionHandlerEvent:: set_out_pattern(const std::string &out_pattern) { diff --git a/panda/src/collide/collisionInvSphere.cxx b/panda/src/collide/collisionInvSphere.cxx index 8a75135e55..3abd9ae451 100644 --- a/panda/src/collide/collisionInvSphere.cxx +++ b/panda/src/collide/collisionInvSphere.cxx @@ -12,8 +12,11 @@ */ #include "collisionInvSphere.h" + #include "collisionSphere.h" +#include "collisionCapsule.h" #include "collisionLine.h" +#include "collisionParabola.h" #include "collisionRay.h" #include "collisionSegment.h" #include "collisionHandler.h" @@ -288,6 +291,231 @@ test_intersection_from_segment(const CollisionEntry &entry) const { return new_entry; } + +PT(CollisionEntry) CollisionInvSphere:: +test_intersection_from_parabola(const CollisionEntry &entry) const { + const CollisionParabola *parabola; + DCAST_INTO_R(parabola, entry.get_from(), nullptr); + + const LMatrix4 &wrt_mat = entry.get_wrt_mat(); + + // Convert the parabola into local coordinate space + LParabola local_p(parabola->get_parabola()); + local_p.xform(wrt_mat); + + double t; + LPoint3 into_intersection_point; + if (!intersects_parabola(t, local_p, parabola->get_t1(), parabola->get_t2(), + local_p.calc_point(parabola->get_t1()), + local_p.calc_point(parabola->get_t2()), into_intersection_point)) { + // No intersection. + return nullptr; + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "intersection detected from " << entry.get_from_node_path() + << " into " << entry.get_into_node_path() << "\n"; + } + + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + + LVector3 normal = into_intersection_point - get_center(); + normal.normalize(); + if (has_effective_normal() && parabola->get_respect_effective_normal()) { + new_entry->set_surface_normal(get_effective_normal()); + } else { + new_entry->set_surface_normal(-normal); + } + + LPoint3 surface_point = normal * get_radius() + get_center(); + new_entry->set_surface_point(surface_point); + + return new_entry; +} + + +bool CollisionInvSphere:: +intersects_parabola(double &t, const LParabola ¶bola, + double t1, double t2, + const LPoint3 &p1, const LPoint3 &p2, LPoint3 &into_intersection_point) const { + + /* The method is pretty much identical to CollisionSphere::intersects_parabola: + * recursively divide the parabola into "close enough" line segments, and test + * their intersection with the sphere using tests similar to + * CollisionInvSphere::test_intersection_from_segment. Returns the + * point of intersection via pass-by-reference. + */ + + if (t1 == t2) { + // Special case: a single point. + if ((p1 - get_center()).length_squared() < get_radius() * get_radius()) { + // No intersection. + return false; + } + t = t1; + return true; + } + + double tmid = (t1 + t2) * 0.5; + if (tmid != t1 && tmid != t2) { + LPoint3 pmid = parabola.calc_point(tmid); + LPoint3 pmid2 = (p1 + p2) * 0.5f; + + if ((pmid - pmid2).length_squared() > 0.001f) { + if (intersects_parabola(t, parabola, t1, tmid, p1, pmid, into_intersection_point)) { + return true; + } + return intersects_parabola(t, parabola, tmid, t2, pmid, p2, into_intersection_point); + } + } + + // Line segment is close enough to parabola. Test for intersections + double t1a, t2a; + if (!intersects_line(t1a, t2a, p1, p2 - p1, 0.0f)) { + // segment is somewhere outside the sphere, so + // we have an intersection, simply return the first point + t = 0.0; + } + else { + if (t2a <= 0.0) { + // segment completely below sphere + t = 0.0; + } + else if (t1a >= 1.0) { + // segment acompletely bove sphere + t = 1.0; + } + else if (t2a <= 1.0) { + // bottom edge intersects sphere + t = t2a; + } + else if (t1a >= 0.0) { + // top edge intersects sphere + t = t1a; + } + else { + // completely inside sphere, no intersection + return false; + } + } + into_intersection_point = p1 + t * (p2 - p1); + return true; +} + +/** + * + */ +PT(CollisionEntry) CollisionInvSphere:: +test_intersection_from_capsule(const CollisionEntry &entry) const { + const CollisionCapsule *capsule; + DCAST_INTO_R(capsule, entry.get_from(), nullptr); + + const LMatrix4 &wrt_mat = entry.get_wrt_mat(); + + LPoint3 from_a = capsule->get_point_a() * wrt_mat; + LPoint3 from_b = capsule->get_point_b() * wrt_mat; + + LVector3 from_radius_v = + LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat; + PN_stdfloat from_radius = from_radius_v.length(); + + LPoint3 center = get_center(); + PN_stdfloat radius = get_radius(); + + // Check which one of the points lies furthest inside the sphere. + PN_stdfloat dist_a = (from_a - center).length(); + PN_stdfloat dist_b = (from_b - center).length(); + if (dist_b > dist_a) { + // Store the furthest point into from_a/dist_a. + dist_a = dist_b; + from_a = from_b; + } + + // from_a now contains the furthest point. Is it inside? + if (dist_a < radius - from_radius) { + return nullptr; + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "intersection detected from " << entry.get_from_node_path() + << " into " << entry.get_into_node_path() << "\n"; + } + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + + LVector3 normal = center - from_a; + normal.normalize(); + new_entry->set_surface_point(get_center() - normal * radius); + new_entry->set_interior_point(from_a - normal * from_radius); + + if (has_effective_normal() && capsule->get_respect_effective_normal()) { + new_entry->set_surface_normal(get_effective_normal()); + } else { + new_entry->set_surface_normal(normal); + } + + return new_entry; +} + +/** + * Double dispatch point for box as a FROM object + */ +PT(CollisionEntry) CollisionInvSphere:: +test_intersection_from_box(const CollisionEntry &entry) const { + const CollisionBox *box; + DCAST_INTO_R(box, entry.get_from(), nullptr); + + const LMatrix4 &wrt_mat = entry.get_wrt_mat(); + + LPoint3 center = get_center(); + PN_stdfloat radius_sq = get_radius(); + radius_sq *= radius_sq; + + // Just figure out which box point is furthest from the center. If it + // exceeds the radius, the furthest point wins. + + PN_stdfloat max_dist_sq = -1.0; + LPoint3 deepest_vertex; + + for (int i = 0; i < 8; ++i) { + LPoint3 point = wrt_mat.xform_point(box->get_point(i)); + + PN_stdfloat dist_sq = (point - center).length_squared(); + if (dist_sq > max_dist_sq) { + deepest_vertex = point; + max_dist_sq = dist_sq; + } + } + + if (max_dist_sq < radius_sq) { + // The point furthest away from the center is still inside the sphere. + // Therefore, no collision. + return nullptr; + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "intersection detected from " << entry.get_from_node_path() + << " into " << entry.get_into_node_path() << "\n"; + } + + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + + // The interior point is just the deepest cube vertex. + new_entry->set_interior_point(deepest_vertex); + + // Now extrapolate the surface point and normal from that. + LVector3 normal = center - deepest_vertex; + normal.normalize(); + new_entry->set_surface_point(center - normal * get_radius()); + new_entry->set_surface_normal( + (has_effective_normal() && box->get_respect_effective_normal()) + ? get_effective_normal() : normal); + + return new_entry; +} + /** * Fills the _viz_geom GeomNode up with Geoms suitable for rendering this * solid. diff --git a/panda/src/collide/collisionInvSphere.h b/panda/src/collide/collisionInvSphere.h index fe8b43b10d..0bbc9e2121 100644 --- a/panda/src/collide/collisionInvSphere.h +++ b/panda/src/collide/collisionInvSphere.h @@ -55,9 +55,20 @@ protected: test_intersection_from_ray(const CollisionEntry &entry) const; virtual PT(CollisionEntry) test_intersection_from_segment(const CollisionEntry &entry) const; + virtual PT(CollisionEntry) + test_intersection_from_parabola(const CollisionEntry &entry) const; + virtual PT(CollisionEntry) + test_intersection_from_capsule(const CollisionEntry &entry) const; + virtual PT(CollisionEntry) + test_intersection_from_box(const CollisionEntry &entry) const; virtual void fill_viz_geom(); + protected: + bool intersects_parabola(double &t, const LParabola ¶bola, + double t1, double t2, + const LPoint3 &p1, const LPoint3 &p2, LPoint3 &into_intersection_point) const; + private: static PStatCollector _volume_pcollector; static PStatCollector _test_pcollector; diff --git a/panda/src/collide/collisionNode.cxx b/panda/src/collide/collisionNode.cxx index ce037f917f..1196ba20c7 100644 --- a/panda/src/collide/collisionNode.cxx +++ b/panda/src/collide/collisionNode.cxx @@ -58,6 +58,7 @@ CollisionNode:: CollisionNode(const CollisionNode ©) : PandaNode(copy), _from_collide_mask(copy._from_collide_mask), + _collider_sort(copy._collider_sort), _solids(copy._solids) { } diff --git a/panda/src/collide/collisionPolygon.cxx b/panda/src/collide/collisionPolygon.cxx index 24c8230d2f..c4f6b4e7bb 100644 --- a/panda/src/collide/collisionPolygon.cxx +++ b/panda/src/collide/collisionPolygon.cxx @@ -12,9 +12,11 @@ */ #include "collisionPolygon.h" + #include "collisionHandler.h" #include "collisionEntry.h" #include "collisionSphere.h" +#include "collisionCapsule.h" #include "collisionLine.h" #include "collisionRay.h" #include "collisionSegment.h" @@ -842,6 +844,142 @@ test_intersection_from_parabola(const CollisionEntry &entry) const { return new_entry; } +/** + * This is part of the double-dispatch implementation of test_intersection(). + * It is called when the "from" object is a capsule. + */ +PT(CollisionEntry) CollisionPolygon:: +test_intersection_from_capsule(const CollisionEntry &entry) const { + if (_points.size() < 3) { + return nullptr; + } + + const CollisionCapsule *capsule; + DCAST_INTO_R(capsule, entry.get_from(), nullptr); + + const LMatrix4 &wrt_mat = entry.get_wrt_mat(); + LMatrix4 plane_mat = wrt_mat * _to_2d_mat; + + LPoint3 from_a = capsule->get_point_a() * plane_mat; + LPoint3 from_b = capsule->get_point_b() * plane_mat; + + LVector3 from_radius_v = + LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat; + PN_stdfloat from_radius_sq = from_radius_v.length_squared(); + + // Check if the capsule is colliding with the plane at all. + // Are the points on the same side of the plane? + if ((from_a[1] > 0) == (from_b[1] > 0)) { + // Yes, so calculate the distance of the closest point. + PN_stdfloat dist = min(cabs(from_a[1]), cabs(from_b[1])); + if (dist * dist > from_radius_sq) { + return nullptr; + } + } + + // Order from_a and from_b so that from_a has the deepest point. + bool swapped = (from_a[1] < from_b[1]); + if (swapped) { + std::swap(from_a, from_b); + } + + LPoint3 surface_point, interior_point; + + // Is the projection of from_a onto the plane inside the polygon? + LPoint2 from_a_proj(from_a[0], from_a[2]); + if (point_is_inside(from_a_proj, _points)) { + // Yes, and we already checked the vertical separation earlier on, so we + // know that the capsule is touching the polygon near from_a. + LPoint3 deepest = (swapped ? capsule->get_point_b() : capsule->get_point_a()) * wrt_mat; + PN_stdfloat from_radius = csqrt(from_radius_sq); + surface_point = get_plane().project(deepest); + interior_point = deepest - get_normal() * from_radius; + } + else { + LVector3 from_direction = from_b - from_a; + + // Find the point in the capsule's inner segment with the closest distance + // to the polygon's edges. We effectively test a sphere around that point. + PN_stdfloat min_dist_sq = make_inf((PN_stdfloat)0); + LPoint3 poly_point; + LPoint3 line_point; + + LPoint2 last_point = _points.back()._p; + for (const PointDef &pd : _points) { + LVector2 dir = last_point - pd._p; + last_point = pd._p; + + double t1, t2; + CollisionCapsule::calc_closest_segment_points(t1, t2, + LPoint3(pd._p[0], 0, pd._p[1]), LVector3(dir[0], 0, dir[1]), + from_a, from_direction); + + LPoint3 point1(pd._p[0] + dir[0] * t1, 0, pd._p[1] + dir[1] * t1); + LPoint3 point2 = from_a + from_direction * t2; + PN_stdfloat dist_sq = (point2 - point1).length_squared(); + if (dist_sq < min_dist_sq) { + min_dist_sq = dist_sq; + poly_point = point1; + line_point = point2; + } + } + + // Project the closest point on the segment onto the polygon. Is this point + // inside the polygon? + LPoint2 line_point_proj(line_point[0], line_point[2]); + if (point_is_inside(line_point_proj, _points)) { + // Yes, and we already checked the vertical separation earlier on, so we + // know that the capsule is touching the polygon here. + LMatrix4 to_3d_mat; + rederive_to_3d_mat(to_3d_mat); + + surface_point = to_3d(line_point_proj, to_3d_mat); + + LPoint3 interior; + if (IS_NEARLY_EQUAL(from_a[1], from_b[1])) { + // It's parallel to the polygon; we can use any point on the segment we + // want, so we might as well use the point we determined to be closest. + interior = line_point; + } else { + // Use the deepest point. FIXME: we need something better. This + // pushes the capsule out way too much. + interior = from_a; + } + interior[1] += csqrt(from_radius_sq); + interior_point = interior * to_3d_mat; + } + else if (min_dist_sq < from_radius_sq) { + // No, but it is colliding with an edge. + LMatrix4 to_3d_mat; + rederive_to_3d_mat(to_3d_mat); + + surface_point = poly_point * to_3d_mat; + + // Make sure we calculate an interior point that lies below the polygon. + LVector3 dir = line_point * to_3d_mat - surface_point; + dir.normalize(); + interior_point = surface_point - dir * (csqrt(from_radius_sq) - csqrt(min_dist_sq)); + } + else { + // It is outside the polygon altogether. + return nullptr; + } + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "intersection detected from " << entry.get_from_node_path() + << " into " << entry.get_into_node_path() << "\n"; + } + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + LVector3 normal = (has_effective_normal() && capsule->get_respect_effective_normal()) ? get_effective_normal() : get_normal(); + new_entry->set_surface_normal(normal); + new_entry->set_surface_point(surface_point); + new_entry->set_interior_point(interior_point); + + return new_entry; +} + /** * This is part of the double-dispatch implementation of test_intersection(). * It is called when the "from" object is a box. @@ -1335,37 +1473,6 @@ setup_points(const LPoint3 *begin, const LPoint3 *end) { compute_vectors(_points); } -/** - * Converts the indicated point to 3-d space according to the way - * CollisionPolygons used to be stored in bam files prior to 4.9. - */ -LPoint3 CollisionPolygon:: -legacy_to_3d(const LVecBase2 &point2d, int axis) const { - nassertr(!point2d.is_nan(), LPoint3(0.0f, 0.0f, 0.0f)); - - LVector3 normal = get_normal(); - PN_stdfloat D = get_plane()[3]; - - nassertr(!normal.is_nan(), LPoint3(0.0f, 0.0f, 0.0f)); - nassertr(!cnan(D), LPoint3(0.0f, 0.0f, 0.0f)); - - switch (axis) { - case 0: // AT_x: - return LPoint3(-(normal[1]*point2d[0] + normal[2]*point2d[1] + D)/normal[0], point2d[0], point2d[1]); - - case 1: // AT_y: - return LPoint3(point2d[0], - -(normal[0]*point2d[0] + normal[2]*point2d[1] + D)/normal[1], point2d[1]); - - case 2: // AT_z: - return LPoint3(point2d[0], point2d[1], - -(normal[0]*point2d[0] + normal[1]*point2d[1] + D)/normal[2]); - } - - nassertr(false, LPoint3(0.0f, 0.0f, 0.0f)); - return LPoint3(0.0f, 0.0f, 0.0f); -} - /** * Clips the source_points of the polygon by the indicated clipping plane, and * modifies new_points to reflect the new set of clipped points (but does not diff --git a/panda/src/collide/collisionPolygon.h b/panda/src/collide/collisionPolygon.h index f7fa771bfa..145f35c8a6 100644 --- a/panda/src/collide/collisionPolygon.h +++ b/panda/src/collide/collisionPolygon.h @@ -93,6 +93,8 @@ protected: virtual PT(CollisionEntry) test_intersection_from_parabola(const CollisionEntry &entry) const; virtual PT(CollisionEntry) + test_intersection_from_capsule(const CollisionEntry &entry) const; + virtual PT(CollisionEntry) test_intersection_from_box(const CollisionEntry &entry) const; virtual void fill_viz_geom(); @@ -131,7 +133,6 @@ private: INLINE void calc_to_3d_mat(LMatrix4 &to_3d_mat) const; INLINE void rederive_to_3d_mat(LMatrix4 &to_3d_mat) const; INLINE static LPoint3 to_3d(const LVecBase2 &point2d, const LMatrix4 &to_3d_mat); - LPoint3 legacy_to_3d(const LVecBase2 &point2d, int axis) const; bool clip_polygon(Points &new_points, const Points &source_points, const LPlane &plane) const; diff --git a/panda/src/collide/collisionSphere.cxx b/panda/src/collide/collisionSphere.cxx index aacc43fcb2..0d3275ec36 100644 --- a/panda/src/collide/collisionSphere.cxx +++ b/panda/src/collide/collisionSphere.cxx @@ -620,14 +620,20 @@ intersects_line(double &t1, double &t2, double A = dot(delta, delta); - nassertr(A != 0.0, false); - LVector3 fc = from - get_center(); - double B = 2.0f* dot(delta, fc); double fc_d2 = dot(fc, fc); double radius = get_radius() + inflate_radius; double C = fc_d2 - radius * radius; + if (A == 0.0) { + // Degenerate case where delta is zero. This is effectively a test + // against a point (or sphere, for nonzero inflate_radius). + t1 = 0.0; + t2 = 0.0; + return C < 0.0; + } + + double B = 2.0f * dot(delta, fc); double radical = B*B - 4.0*A*C; if (IS_NEARLY_ZERO(radical)) { diff --git a/panda/src/collide/collisionTraverser.h b/panda/src/collide/collisionTraverser.h index 8a6c912aaf..a51a1bd474 100644 --- a/panda/src/collide/collisionTraverser.h +++ b/panda/src/collide/collisionTraverser.h @@ -49,7 +49,7 @@ PUBLISHED: INLINE void set_respect_prev_transform(bool flag); INLINE bool get_respect_prev_transform() const; - MAKE_PROPERTY(respect_preV_transform, get_respect_prev_transform, + MAKE_PROPERTY(respect_prev_transform, get_respect_prev_transform, set_respect_prev_transform); void add_collider(const NodePath &collider, CollisionHandler *handler); diff --git a/panda/src/device/evdevInputDevice.cxx b/panda/src/device/evdevInputDevice.cxx index b137888d6d..69d2979f7f 100644 --- a/panda/src/device/evdevInputDevice.cxx +++ b/panda/src/device/evdevInputDevice.cxx @@ -62,6 +62,15 @@ enum QuirkBits { // We only connect it if it is reporting any events, because when Steam is // running, the Steam controller is muted in favour of a dummy Xbox device. QB_steam_controller = 32, + + // Axes on the right stick are swapped, using x for y and vice versa. + QB_right_axes_swapped = 64, + + // Has no trigger axes. + QB_no_analog_triggers = 128, + + // Alternate button mapping. + QB_alt_button_mapping = 256, }; static const struct DeviceMapping { @@ -81,9 +90,15 @@ static const struct DeviceMapping { // Steam Controller (wireless) {0x28de, 0x1142, InputDevice::DeviceClass::unknown, QB_steam_controller}, // Jess Tech Colour Rumble Pad - {0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, 0}, + {0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, QB_rstick_from_z | QB_right_axes_swapped}, // Trust GXT 24 - {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, 0}, + {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers | QB_alt_button_mapping}, + // 8bitdo N30 Pro Controller + {0x2dc8, 0x9001, InputDevice::DeviceClass::gamepad, QB_rstick_from_z}, + // Generic gamepad + {0x0810, 0x0001, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers | QB_alt_button_mapping | QB_rstick_from_z | QB_right_axes_swapped}, + // Generic gamepad without sticks + {0x0810, 0xe501, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers | QB_alt_button_mapping}, // 3Dconnexion Space Traveller 3D Mouse {0x046d, 0xc623, InputDevice::DeviceClass::spatial_mouse, 0}, // 3Dconnexion Space Pilot 3D Mouse @@ -486,10 +501,16 @@ init_device() { break; case ABS_Z: if (quirks & QB_rstick_from_z) { - axis = InputDevice::Axis::right_x; + if (quirks & QB_right_axes_swapped) { + axis = InputDevice::Axis::right_y; + } else { + axis = InputDevice::Axis::right_x; + } } else if (_device_class == DeviceClass::gamepad) { - axis = InputDevice::Axis::left_trigger; - have_analog_triggers = true; + if ((quirks & QB_no_analog_triggers) == 0) { + axis = InputDevice::Axis::left_trigger; + have_analog_triggers = true; + } } else if (_device_class == DeviceClass::spatial_mouse) { axis = InputDevice::Axis::z; } else { @@ -512,10 +533,19 @@ init_device() { break; case ABS_RZ: if (quirks & QB_rstick_from_z) { - axis = InputDevice::Axis::right_y; + if (quirks & QB_right_axes_swapped) { + axis = InputDevice::Axis::right_x; + } else { + axis = InputDevice::Axis::right_y; + } } else if (_device_class == DeviceClass::gamepad) { - axis = InputDevice::Axis::right_trigger; - have_analog_triggers = true; + if ((quirks & QB_no_analog_triggers) == 0) { + axis = InputDevice::Axis::right_trigger; + have_analog_triggers = true; + } else { + // Special weird case for Trust GXT 24 + axis = InputDevice::Axis::right_y; + } } else { axis = InputDevice::Axis::yaw; } @@ -535,8 +565,10 @@ init_device() { break; case ABS_GAS: if (_device_class == DeviceClass::gamepad) { - axis = InputDevice::Axis::right_trigger; - have_analog_triggers = true; + if ((quirks & QB_no_analog_triggers) == 0) { + axis = InputDevice::Axis::right_trigger; + have_analog_triggers = true; + } } else { axis = InputDevice::Axis::accelerator; } @@ -961,6 +993,26 @@ map_button(int code, DeviceClass device_class, int quirks) { // BTN_THUMB and BTN_THUMB2 detect touching the touchpads. return ButtonHandle::none(); + } else if (device_class == DeviceClass::gamepad && + (quirks & QB_alt_button_mapping) != 0) { + static const ButtonHandle mapping[] = { + GamepadButton::face_y(), + GamepadButton::face_b(), + GamepadButton::face_a(), + GamepadButton::face_x(), + GamepadButton::lshoulder(), + GamepadButton::rshoulder(), + GamepadButton::ltrigger(), + GamepadButton::rtrigger(), + GamepadButton::back(), + GamepadButton::start(), + GamepadButton::lstick(), + GamepadButton::rstick(), + }; + if ((code & 0xf) < 12) { + return mapping[code & 0xf]; + } + } else if (device_class == DeviceClass::gamepad) { // Based on "Jess Tech Colour Rumble Pad" static const ButtonHandle mapping[] = { diff --git a/panda/src/device/evdevInputDevice.h b/panda/src/device/evdevInputDevice.h index ffc3503ff0..ce9e152cdc 100644 --- a/panda/src/device/evdevInputDevice.h +++ b/panda/src/device/evdevInputDevice.h @@ -24,6 +24,8 @@ class LinuxInputDeviceManager; * This is a type of device that uses the Linux /dev/input/event# API to read * data from a raw mouse or other input device. Unlike the joystick API, the * evdev API supports sending force feedback (ie. rumble) events. + * + * @since 1.10.0 */ class EXPCL_PANDA_DEVICE EvdevInputDevice : public InputDevice { public: diff --git a/panda/src/device/inputDevice.cxx b/panda/src/device/inputDevice.cxx index 130854834e..cce2f975b4 100644 --- a/panda/src/device/inputDevice.cxx +++ b/panda/src/device/inputDevice.cxx @@ -404,19 +404,19 @@ output(std::ostream &out) const { << (_axes.size() != 1 ? 'e' : 'i') << 's'; } - if (_features & (unsigned int)Feature::pointer) { + if (_features & (1 << (unsigned int)Feature::pointer)) { out << ", pointer"; } - if (_features & (unsigned int)Feature::keyboard) { + if (_features & (1 << (unsigned int)Feature::keyboard)) { out << ", keyboard"; } - if (_features & (unsigned int)Feature::tracker) { + if (_features & (1 << (unsigned int)Feature::tracker)) { out << ", tracker"; } - if (_features & (unsigned int)Feature::vibration) { + if (_features & (1 << (unsigned int)Feature::vibration)) { out << ", vibration"; } - if (_features & (unsigned int)Feature::battery) { + if (_features & (1 << (unsigned int)Feature::battery)) { out << ", battery"; if (_battery_data.level > 0 && _battery_data.max_level > 0) { diff --git a/panda/src/device/inputDevice.h b/panda/src/device/inputDevice.h index 3e4e37313e..f3f500e378 100644 --- a/panda/src/device/inputDevice.h +++ b/panda/src/device/inputDevice.h @@ -47,6 +47,8 @@ * There is the DeviceType enumeration, however, which will (if known) contain * identification of the general category of devices this fits in, such as * keyboard, mouse, gamepad, or flight stick. + * + * @since 1.10.0 */ class EXPCL_PANDA_DEVICE InputDevice : public TypedReferenceCount { PUBLISHED: diff --git a/panda/src/device/inputDeviceManager.h b/panda/src/device/inputDeviceManager.h index 7fd69b804e..66381c79e0 100644 --- a/panda/src/device/inputDeviceManager.h +++ b/panda/src/device/inputDeviceManager.h @@ -27,6 +27,8 @@ class WinRawInputDevice; /** * This class keeps track of all the devices on a system, and sends out events * when a device has been hot-plugged. + * + * @since 1.10.0 */ class EXPCL_PANDA_DEVICE InputDeviceManager { protected: diff --git a/panda/src/device/inputDeviceSet.I b/panda/src/device/inputDeviceSet.I old mode 100755 new mode 100644 diff --git a/panda/src/device/inputDeviceSet.cxx b/panda/src/device/inputDeviceSet.cxx old mode 100755 new mode 100644 diff --git a/panda/src/device/inputDeviceSet.h b/panda/src/device/inputDeviceSet.h old mode 100755 new mode 100644 diff --git a/panda/src/device/ioKitInputDevice.cxx b/panda/src/device/ioKitInputDevice.cxx index da1369932e..5ff35c4ab3 100644 --- a/panda/src/device/ioKitInputDevice.cxx +++ b/panda/src/device/ioKitInputDevice.cxx @@ -15,15 +15,20 @@ #if defined(__APPLE__) && !defined(CPPPARSER) +#include #include +#include "inputDeviceManager.h" #include "keyboardButton.h" #include "gamepadButton.h" #include "mouseButton.h" static void removal_callback(void *ctx, IOReturn result, void *sender) { - IOKitInputDevice *input_device = (IOKitInputDevice *)ctx; + // We need to hold a reference to this because it may otherwise be destroyed + // during the call to on_remove(). + PT(IOKitInputDevice) input_device = (IOKitInputDevice *)ctx; nassertv(input_device != nullptr); + nassertv(input_device->test_ref_count_integrity()); input_device->on_remove(); } diff --git a/panda/src/device/linuxInputDeviceManager.cxx b/panda/src/device/linuxInputDeviceManager.cxx index 8eed3e1cc3..7b9b9fdf6f 100644 --- a/panda/src/device/linuxInputDeviceManager.cxx +++ b/panda/src/device/linuxInputDeviceManager.cxx @@ -61,6 +61,9 @@ LinuxInputDeviceManager() { // We'll want to sort the devices by index, since the order may be // meaningful (eg. for the Xbox wireless receiver). + if (indices.empty()) { + return; + } std::sort(indices.begin(), indices.end()); _evdev_devices.resize(indices.back() + 1, nullptr); diff --git a/panda/src/device/linuxJoystickDevice.h b/panda/src/device/linuxJoystickDevice.h index 61d05cd3e8..cf56e27b58 100644 --- a/panda/src/device/linuxJoystickDevice.h +++ b/panda/src/device/linuxJoystickDevice.h @@ -23,6 +23,8 @@ class LinuxInputDeviceManager; /** * This is a type of device that uses the Linux /dev/input/js# API to read * data from a game controller. + * + * @since 1.10.0 */ class EXPCL_PANDA_DEVICE LinuxJoystickDevice : public InputDevice { PUBLISHED: diff --git a/panda/src/device/phidsdi.h b/panda/src/device/phidsdi.h new file mode 100644 index 0000000000..647b3ed322 --- /dev/null +++ b/panda/src/device/phidsdi.h @@ -0,0 +1,166 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file phidsdi.h + * @author rdb + * @date 2019-02-05 + */ + +#ifndef PHIDSDI_H +#define PHIDSDI_H + +#if defined(_WIN32) && !defined(CPPPARSER) + +// Copy definitions from hidusage.h, until we can drop support for the 7.1 SDK +typedef USHORT USAGE, *PUSAGE; + +#define HID_USAGE_PAGE_UNDEFINED ((USAGE) 0x00) +#define HID_USAGE_PAGE_GENERIC ((USAGE) 0x01) +#define HID_USAGE_PAGE_SIMULATION ((USAGE) 0x02) +#define HID_USAGE_PAGE_VR ((USAGE) 0x03) +#define HID_USAGE_PAGE_SPORT ((USAGE) 0x04) +#define HID_USAGE_PAGE_GAME ((USAGE) 0x05) +#define HID_USAGE_PAGE_KEYBOARD ((USAGE) 0x07) +#define HID_USAGE_PAGE_LED ((USAGE) 0x08) +#define HID_USAGE_PAGE_BUTTON ((USAGE) 0x09) + +#define HID_USAGE_GENERIC_POINTER ((USAGE) 0x01) +#define HID_USAGE_GENERIC_MOUSE ((USAGE) 0x02) +#define HID_USAGE_GENERIC_JOYSTICK ((USAGE) 0x04) +#define HID_USAGE_GENERIC_GAMEPAD ((USAGE) 0x05) +#define HID_USAGE_GENERIC_KEYBOARD ((USAGE) 0x06) +#define HID_USAGE_GENERIC_KEYPAD ((USAGE) 0x07) +#define HID_USAGE_GENERIC_SYSTEM_CTL ((USAGE) 0x80) + +#define HID_USAGE_GENERIC_X ((USAGE) 0x30) +#define HID_USAGE_GENERIC_Y ((USAGE) 0x31) +#define HID_USAGE_GENERIC_Z ((USAGE) 0x32) +#define HID_USAGE_GENERIC_RX ((USAGE) 0x33) +#define HID_USAGE_GENERIC_RY ((USAGE) 0x34) +#define HID_USAGE_GENERIC_RZ ((USAGE) 0x35) +#define HID_USAGE_GENERIC_SLIDER ((USAGE) 0x36) +#define HID_USAGE_GENERIC_DIAL ((USAGE) 0x37) +#define HID_USAGE_GENERIC_WHEEL ((USAGE) 0x38) +#define HID_USAGE_GENERIC_HATSWITCH ((USAGE) 0x39) + +// Copy definitions from hidpi.h, until we can drop support for the 7.1 SDK +#define HIDP_STATUS_SUCCESS ((NTSTATUS)(0x11 << 16)) + +typedef enum _HIDP_REPORT_TYPE { + HidP_Input, + HidP_Output, + HidP_Feature +} HIDP_REPORT_TYPE; + +typedef struct _HIDP_BUTTON_CAPS { + USAGE UsagePage; + UCHAR ReportID; + BOOLEAN IsAlias; + USHORT BitField; + USHORT LinkCollection; + USAGE LinkUsage; + USAGE LinkUsagePage; + BOOLEAN IsRange; + BOOLEAN IsStringRange; + BOOLEAN IsDesignatorRange; + BOOLEAN IsAbsolute; + ULONG Reserved[10]; + union { + struct { + USAGE UsageMin, UsageMax; + USHORT StringMin, StringMax; + USHORT DesignatorMin, DesignatorMax; + USHORT DataIndexMin, DataIndexMax; + } Range; + struct { + USAGE Usage, Reserved1; + USHORT StringIndex, Reserved2; + USHORT DesignatorIndex, Reserved3; + USHORT DataIndex, Reserved4; + } NotRange; + }; +} HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS; + +typedef struct _HIDP_VALUE_CAPS { + USAGE UsagePage; + UCHAR ReportID; + BOOLEAN IsAlias; + USHORT BitField; + USHORT LinkCollection; + USAGE LinkUsage; + USAGE LinkUsagePage; + BOOLEAN IsRange; + BOOLEAN IsStringRange; + BOOLEAN IsDesignatorRange; + BOOLEAN IsAbsolute; + BOOLEAN HasNull; + UCHAR Reserved; + USHORT BitSize; + USHORT ReportCount; + USHORT Reserved2[5]; + ULONG UnitsExp; + ULONG Units; + LONG LogicalMin, LogicalMax; + LONG PhysicalMin, PhysicalMax; + union { + struct { + USAGE UsageMin, UsageMax; + USHORT StringMin, StringMax; + USHORT DesignatorMin, DesignatorMax; + USHORT DataIndexMin, DataIndexMax; + } Range; + struct { + USAGE Usage, Reserved1; + USHORT StringIndex, Reserved2; + USHORT DesignatorIndex, Reserved3; + USHORT DataIndex, Reserved4; + } NotRange; + }; +} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS; + +typedef PUCHAR PHIDP_REPORT_DESCRIPTOR; +typedef struct _HIDP_PREPARSED_DATA *PHIDP_PREPARSED_DATA; + +typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + +typedef struct _HIDP_DATA { + USHORT DataIndex; + USHORT Reserved; + union { + ULONG RawValue; + BOOLEAN On; + }; +} HIDP_DATA, *PHIDP_DATA; + +typedef LONG NTSTATUS; +typedef NTSTATUS (__stdcall *pHidP_GetCaps)(PHIDP_PREPARSED_DATA, PHIDP_CAPS); +typedef NTSTATUS (__stdcall *pHidP_GetButtonCaps)(HIDP_REPORT_TYPE, PHIDP_BUTTON_CAPS, PUSHORT, PHIDP_PREPARSED_DATA); +typedef NTSTATUS (__stdcall *pHidP_GetValueCaps)(HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PUSHORT, PHIDP_PREPARSED_DATA); +typedef NTSTATUS (__stdcall *pHidP_GetData)(HIDP_REPORT_TYPE, PHIDP_DATA, PULONG, PHIDP_PREPARSED_DATA, PCHAR, ULONG); +typedef ULONG (__stdcall *pHidP_MaxDataListLength)(HIDP_REPORT_TYPE, PHIDP_PREPARSED_DATA); + +#endif + +#endif diff --git a/panda/src/device/winInputDeviceManager.cxx b/panda/src/device/winInputDeviceManager.cxx index 033a174f6b..2ca4540ae6 100644 --- a/panda/src/device/winInputDeviceManager.cxx +++ b/panda/src/device/winInputDeviceManager.cxx @@ -17,6 +17,22 @@ #if defined(_WIN32) && !defined(CPPPARSER) +#ifdef HAVE_THREADS +/** + * + */ +class InputThread : public Thread { +public: + InputThread(WinInputDeviceManager *manager) : + Thread("input", "input"), _manager(manager) {} + +private: + virtual void thread_main(); + + WinInputDeviceManager *_manager; +}; +#endif + /** * Initializes the input device manager by scanning which devices are currently * connected and setting up any platform-dependent structures necessary for @@ -37,7 +53,7 @@ WinInputDeviceManager() : _xinput_device2.local_object(); _xinput_device3.local_object(); -// This function is only available in Vista and later, so we use a wrapper. + // This function is only available in Vista and later, so we use a wrapper. HMODULE module = LoadLibraryA("cfgmgr32.dll"); if (module) { _CM_Get_DevNode_PropertyW = (pCM_Get_DevNode_Property)GetProcAddress(module, "CM_Get_DevNode_PropertyW"); @@ -45,83 +61,18 @@ WinInputDeviceManager() : _CM_Get_DevNode_PropertyW = nullptr; } - // Now create a message-only window for the raw input. - WNDCLASSEX wc = {}; - wc.cbSize = sizeof(WNDCLASSEX); - wc.lpfnWndProc = window_proc; - wc.hInstance = GetModuleHandle(nullptr); - wc.lpszClassName = "InputDeviceManager"; - if (!RegisterClassEx(&wc)) { - device_cat.warning() - << "Failed to register message-only window class.\n"; - return; - } - - _message_hwnd = CreateWindowEx(0, wc.lpszClassName, "InputDeviceManager", 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr); - if (!_message_hwnd) { - device_cat.warning() - << "Failed to create message-only window.\n"; - return; - } - - // Now listen for raw input devices using the created message loop. - RAWINPUTDEVICE rid[3]; - rid[0].usUsagePage = 1; - rid[0].usUsage = 4; // Joysticks - rid[0].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; - rid[0].hwndTarget = _message_hwnd; - rid[1].usUsagePage = 1; - rid[1].usUsage = 5; // Gamepads - rid[1].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; - rid[1].hwndTarget = _message_hwnd; - rid[2].usUsagePage = 1; - rid[2].usUsage = 8; // Multi-axis controllers (including 3D mice) - rid[2].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; - rid[2].hwndTarget = _message_hwnd; - if (!RegisterRawInputDevices(rid, 3, sizeof(RAWINPUTDEVICE))) { - device_cat.warning() - << "Failed to register raw input devices.\n"; - } - - // Do we have any XInput devices plugged in now? - int num_xinput = 0; - HANDLE xinput_handle; - RAWINPUTDEVICELIST devices[64]; - UINT num_devices = 64; - num_devices = GetRawInputDeviceList(devices, &num_devices, sizeof(RAWINPUTDEVICELIST)); - if (num_devices == (UINT)-1) { - return; - } - for (UINT i = 0; i < num_devices; ++i) { - if (devices[i].dwType != RIM_TYPEHID) { - continue; - } - HANDLE handle = devices[i].hDevice; - UINT size; - if (GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, nullptr, &size) != 0) { - continue; - } - - char *path = (char *)alloca(size); - if (path == nullptr || - GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, (void *)path, &size) < 0) { - continue; - } - - if (strstr(path, "&IG_") != nullptr) { - xinput_handle = handle; - ++num_xinput; - } - } - if (num_xinput == 1) { - // There's only one XInput device, so we know which one it is. - on_input_device_arrival(xinput_handle); - } else if (num_xinput > 0) { - // Just poll all the XInput devices. - _xinput_device0.detect(this); - _xinput_device1.detect(this); - _xinput_device2.detect(this); - _xinput_device3.detect(this); + // If we have threading enabled, start a thread with a message-only window + // loop to listen for input events. We can't actually just let this be + // handled by the main window loop, because the main window might actually + // have been created in a different thread. +#ifdef HAVE_THREADS + if (Thread::is_threading_supported()) { + PT(Thread) thread = new InputThread(this); + thread->start(TP_normal, false); + } else +#endif + { + setup_message_loop(); } } @@ -131,8 +82,17 @@ WinInputDeviceManager() : WinInputDeviceManager:: ~WinInputDeviceManager() { if (_message_hwnd != nullptr) { - DestroyWindow(_message_hwnd); - _message_hwnd = nullptr; +#ifdef HAVE_THREADS + if (Thread::is_threading_supported()) { + HWND hwnd = _message_hwnd; + if (hwnd) { + SendMessage(hwnd, WM_QUIT, 0, 0); + } + } else +#endif + { + destroy_message_loop(); + } } } @@ -289,8 +249,8 @@ on_input_device_arrival(HANDLE handle) { // Some devices insert quite some trailing space here. wchar_t *wbuffer = (wchar_t *)buffer; - size_t wlen = wcslen(wbuffer); - while (iswspace(wbuffer[wlen - 1])) { + size_t wlen = wcsnlen_s(wbuffer, sizeof(buffer) / sizeof(wchar_t)); + while (wlen > 0 && iswspace(wbuffer[wlen - 1])) { wbuffer[--wlen] = 0; } TextEncoder encoder; @@ -315,7 +275,8 @@ on_input_device_arrival(HANDLE handle) { // Is this an XInput device? If so, handle it via XInput, which allows us // to handle independent left/right triggers as well as vibration output. - if (info.dwType == RIM_TYPEHID && strstr(path, "&IG_") != nullptr) { + if (info.dwType == RIM_TYPEHID && strstr(path, "&IG_") != nullptr && + XInputDevice::init_xinput()) { // This is a device we should handle via the XInput API. Check which of // the four players was the lucky one. if (_xinput_device0.check_arrival(info, inst, name, manufacturer)) { @@ -390,6 +351,136 @@ on_input_device_removal(HANDLE handle) { } } +/** + * Polls the system to see if there are any new devices. In some + * implementations this is a no-op. + */ +void WinInputDeviceManager:: +update() { +} + +/** + * Sets up a Windows message loop. Should be called from the thread that will + * be handling the messages. + */ +HWND WinInputDeviceManager:: +setup_message_loop() { + _message_hwnd = 0; + + // Now create a message-only window for the raw input. + WNDCLASSEX wc = {}; + wc.cbSize = sizeof(WNDCLASSEX); + wc.lpfnWndProc = window_proc; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpszClassName = "InputDeviceManager"; + if (!RegisterClassEx(&wc)) { + device_cat.warning() + << "Failed to register message-only window class for input device detection.\n"; + } else { + _message_hwnd = CreateWindowEx(0, wc.lpszClassName, "InputDeviceManager", 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr); + if (!_message_hwnd) { + device_cat.warning() + << "Failed to create message-only window for input device detection.\n"; + } + } + + // Now listen for raw input devices using the created message loop. + RAWINPUTDEVICE rid[3]; + rid[0].usUsagePage = 1; + rid[0].usUsage = 4; // Joysticks + rid[0].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; + rid[0].hwndTarget = _message_hwnd; + rid[1].usUsagePage = 1; + rid[1].usUsage = 5; // Gamepads + rid[1].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; + rid[1].hwndTarget = _message_hwnd; + rid[2].usUsagePage = 1; + rid[2].usUsage = 8; // Multi-axis controllers (including 3D mice) + rid[2].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; + rid[2].hwndTarget = _message_hwnd; + if (!RegisterRawInputDevices(rid, 3, sizeof(RAWINPUTDEVICE))) { + device_cat.warning() + << "Failed to register raw input devices.\n"; + } + + // Do we have any XInput devices plugged in now? + int num_xinput = 0; + HANDLE xinput_handle; + RAWINPUTDEVICELIST devices[64]; + UINT num_devices = 64; + num_devices = GetRawInputDeviceList(devices, &num_devices, sizeof(RAWINPUTDEVICELIST)); + if (num_devices == (UINT)-1) { + num_devices = 0; + } + for (UINT i = 0; i < num_devices; ++i) { + if (devices[i].dwType != RIM_TYPEHID) { + continue; + } + HANDLE handle = devices[i].hDevice; + UINT size; + if (GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, nullptr, &size) != 0) { + continue; + } + + char *path = (char *)alloca(size); + if (path == nullptr || + GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, (void *)path, &size) < 0) { + continue; + } + + if (strstr(path, "&IG_") != nullptr) { + xinput_handle = handle; + ++num_xinput; + } + } + if (num_xinput == 1) { + // There's only one XInput device, so we know which one it is. + on_input_device_arrival(xinput_handle); + } else if (num_xinput > 0) { + // Just poll all the XInput devices. + _xinput_device0.detect(this); + _xinput_device1.detect(this); + _xinput_device2.detect(this); + _xinput_device3.detect(this); + } + + return _message_hwnd; +} + +/** + * Tears down the message loop. Should be called from the thread that called + * setup_message_loop(). + */ +void WinInputDeviceManager:: +destroy_message_loop() { + HWND hwnd = nullptr; + { + LightMutexHolder holder(_lock); + std::swap(_message_hwnd, hwnd); + } + + if (hwnd) { + DestroyWindow(hwnd); + } +} + +/** + * Sends a signal to the thread input thread, asking it to shut itself down. + */ +void WinInputDeviceManager:: +stop_thread() { +#ifdef HAVE_THREADS + WinInputDeviceManager *mgr = (WinInputDeviceManager *)_global_ptr; + if (mgr != nullptr) { + LightMutexHolder holder(mgr->_lock); + HWND hwnd = mgr->_message_hwnd; + if (hwnd) { + PostMessage(hwnd, WM_QUIT, 0, 0); + } + } +#endif +} + /** * Implementation of the message loop. */ @@ -428,4 +519,52 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { return DefWindowProcW(hwnd, msg, wparam, lparam); } +#ifdef HAVE_THREADS +/** + * Thread entry point for the input listener thread. + */ +void InputThread:: +thread_main() { + WinInputDeviceManager *manager = _manager; + HWND hwnd = manager->setup_message_loop(); + if (!hwnd) { + return; + } + + if (device_cat.is_debug()) { + device_cat.debug() + << "Started input device listener thread.\n"; + } + + MSG msg; +#ifdef SIMPLE_THREADS + // In the simple threading case, we can't block the thread waiting for a + // message; we yield control back if there are no more messages. + while (true) { + if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + break; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } else { + Thread::force_yield(); + } + } +#else + while (GetMessage(&msg, nullptr, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +#endif + + if (device_cat.is_debug()) { + device_cat.debug() + << "Stopping input device listener thread.\n"; + } + + manager->destroy_message_loop(); +} +#endif // HAVE_THREADS + #endif diff --git a/panda/src/device/winInputDeviceManager.h b/panda/src/device/winInputDeviceManager.h index 6c3fefd8c1..13cab3d7c0 100644 --- a/panda/src/device/winInputDeviceManager.h +++ b/panda/src/device/winInputDeviceManager.h @@ -41,6 +41,11 @@ public: void on_input_device_arrival(HANDLE handle); void on_input_device_removal(HANDLE handle); + HWND setup_message_loop(); + void destroy_message_loop(); + + static void stop_thread(); + private: // There are always exactly four of these in existence. XInputDevice _xinput_device0; @@ -52,9 +57,11 @@ private: pmap _raw_devices; pmap _raw_devices_by_path; + virtual void update() override; + static LRESULT WINAPI window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); - typedef CONFIGRET (*pCM_Get_DevNode_Property)(DEVINST, const DEVPROPKEY *, DEVPROPTYPE *, PBYTE, PULONG, ULONG); + typedef CONFIGRET (WINAPI *pCM_Get_DevNode_Property)(DEVINST, const DEVPROPKEY *, DEVPROPTYPE *, PBYTE, PULONG, ULONG); pCM_Get_DevNode_Property _CM_Get_DevNode_PropertyW; friend class InputDeviceManager; diff --git a/panda/src/device/winRawInputDevice.cxx b/panda/src/device/winRawInputDevice.cxx index eeb98f112d..10262d124c 100644 --- a/panda/src/device/winRawInputDevice.cxx +++ b/panda/src/device/winRawInputDevice.cxx @@ -14,156 +14,68 @@ #include "winRawInputDevice.h" #include "gamepadButton.h" #include "mouseButton.h" +#include "buttonRegistry.h" +#include "winInputDeviceManager.h" #if defined(_WIN32) && !defined(CPPPARSER) #include #include +#include "phidsdi.h" -// Copy definitions from hidusage.h, until we can drop support for the 7.1 SDK -typedef USHORT USAGE, *PUSAGE; +enum QuirkBits : int { + // Has no trigger axes. + QB_no_analog_triggers = 1, -#define HID_USAGE_PAGE_UNDEFINED ((USAGE) 0x00) -#define HID_USAGE_PAGE_GENERIC ((USAGE) 0x01) -#define HID_USAGE_PAGE_SIMULATION ((USAGE) 0x02) -#define HID_USAGE_PAGE_VR ((USAGE) 0x03) -#define HID_USAGE_PAGE_SPORT ((USAGE) 0x04) -#define HID_USAGE_PAGE_GAME ((USAGE) 0x05) -#define HID_USAGE_PAGE_KEYBOARD ((USAGE) 0x07) -#define HID_USAGE_PAGE_LED ((USAGE) 0x08) -#define HID_USAGE_PAGE_BUTTON ((USAGE) 0x09) + // Throttle goes from -1 to 1 rather than from 0 to 1. + QB_centered_throttle = 2, -#define HID_USAGE_GENERIC_POINTER ((USAGE) 0x01) -#define HID_USAGE_GENERIC_MOUSE ((USAGE) 0x02) -#define HID_USAGE_GENERIC_JOYSTICK ((USAGE) 0x04) -#define HID_USAGE_GENERIC_GAMEPAD ((USAGE) 0x05) -#define HID_USAGE_GENERIC_KEYBOARD ((USAGE) 0x06) -#define HID_USAGE_GENERIC_KEYPAD ((USAGE) 0x07) -#define HID_USAGE_GENERIC_SYSTEM_CTL ((USAGE) 0x80) + // Throttle is reversed. + QB_reversed_throttle = 4, -#define HID_USAGE_GENERIC_X ((USAGE) 0x30) -#define HID_USAGE_GENERIC_Y ((USAGE) 0x31) -#define HID_USAGE_GENERIC_Z ((USAGE) 0x32) -#define HID_USAGE_GENERIC_RX ((USAGE) 0x33) -#define HID_USAGE_GENERIC_RY ((USAGE) 0x34) -#define HID_USAGE_GENERIC_RZ ((USAGE) 0x35) -#define HID_USAGE_GENERIC_SLIDER ((USAGE) 0x36) -#define HID_USAGE_GENERIC_DIAL ((USAGE) 0x37) -#define HID_USAGE_GENERIC_WHEEL ((USAGE) 0x38) -#define HID_USAGE_GENERIC_HATSWITCH ((USAGE) 0x39) + // Right stick uses Z and Rz inputs. + QB_rstick_from_z = 8, -// Copy definitions from hidpi.h, until we can drop support for the 7.1 SDK -#define HIDP_STATUS_SUCCESS ((NTSTATUS)(0x11 << 16)) + // Axes on the right stick are swapped, using x for y and vice versa. + QB_right_axes_swapped = 64, +}; -typedef enum _HIDP_REPORT_TYPE { - HidP_Input, - HidP_Output, - HidP_Feature -} HIDP_REPORT_TYPE; +// Some nonstandard gamepads have different button mappings. +static const struct DeviceMapping { + unsigned short vendor; + unsigned short product; + InputDevice::DeviceClass device_class; + int quirks; + const char *buttons[16]; +} mapping_presets[] = { + // SNES-style USB gamepad, or cheap unbranded USB gamepad with no sticks + // ABXY are mapped based on their position, not based on their label. + {0x0810, 0xe501, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers, + {"face_y", "face_b", "face_a", "face_x", "lshoulder", "rshoulder", "ltrigger", "rtrigger", "back", "start"} + }, + // Unbranded generic cheap USB gamepad + {0x0810, 0x0001, InputDevice::DeviceClass::gamepad, QB_rstick_from_z | QB_no_analog_triggers | QB_right_axes_swapped, + {"face_y", "face_b", "face_a", "face_x", "lshoulder", "rshoulder", "ltrigger", "rtrigger", "back", "start", "lstick", "rstick"} + }, + // Trust GXT 24 / SPEED Link SL-6535-SBK-01 + {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, QB_rstick_from_z | QB_no_analog_triggers, + {"face_y", "face_b", "face_a", "face_x", "lshoulder", "rshoulder", "ltrigger", "rtrigger", "back", "start", "lstick", "rstick"} + }, + // T.Flight Hotas X + {0x044f, 0xb108, InputDevice::DeviceClass::flight_stick, QB_centered_throttle | QB_reversed_throttle, + {0} + }, + // NVIDIA Shield Controller + {0x0955, 0x7214, InputDevice::DeviceClass::gamepad, 0, + {"face_a", "face_b", 0, "face_x", "face_y", "rshoulder", "lshoulder", "rshoulder", 0, 0, 0, "start", 0, "lstick", "rstick", 0} + }, + {0}, +}; -typedef struct _HIDP_BUTTON_CAPS { - USAGE UsagePage; - UCHAR ReportID; - BOOLEAN IsAlias; - USHORT BitField; - USHORT LinkCollection; - USAGE LinkUsage; - USAGE LinkUsagePage; - BOOLEAN IsRange; - BOOLEAN IsStringRange; - BOOLEAN IsDesignatorRange; - BOOLEAN IsAbsolute; - ULONG Reserved[10]; - union { - struct { - USAGE UsageMin, UsageMax; - USHORT StringMin, StringMax; - USHORT DesignatorMin, DesignatorMax; - USHORT DataIndexMin, DataIndexMax; - } Range; - struct { - USAGE Usage, Reserved1; - USHORT StringIndex, Reserved2; - USHORT DesignatorIndex, Reserved3; - USHORT DataIndex, Reserved4; - } NotRange; - }; -} HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS; - -typedef struct _HIDP_VALUE_CAPS { - USAGE UsagePage; - UCHAR ReportID; - BOOLEAN IsAlias; - USHORT BitField; - USHORT LinkCollection; - USAGE LinkUsage; - USAGE LinkUsagePage; - BOOLEAN IsRange; - BOOLEAN IsStringRange; - BOOLEAN IsDesignatorRange; - BOOLEAN IsAbsolute; - BOOLEAN HasNull; - UCHAR Reserved; - USHORT BitSize; - USHORT ReportCount; - USHORT Reserved2[5]; - ULONG UnitsExp; - ULONG Units; - LONG LogicalMin, LogicalMax; - LONG PhysicalMin, PhysicalMax; - union { - struct { - USAGE UsageMin, UsageMax; - USHORT StringMin, StringMax; - USHORT DesignatorMin, DesignatorMax; - USHORT DataIndexMin, DataIndexMax; - } Range; - struct { - USAGE Usage, Reserved1; - USHORT StringIndex, Reserved2; - USHORT DesignatorIndex, Reserved3; - USHORT DataIndex, Reserved4; - } NotRange; - }; -} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS; - -typedef PUCHAR PHIDP_REPORT_DESCRIPTOR; -typedef struct _HIDP_PREPARSED_DATA *PHIDP_PREPARSED_DATA; - -typedef struct _HIDP_CAPS { - USAGE Usage; - USAGE UsagePage; - USHORT InputReportByteLength; - USHORT OutputReportByteLength; - USHORT FeatureReportByteLength; - USHORT Reserved[17]; - USHORT NumberLinkCollectionNodes; - USHORT NumberInputButtonCaps; - USHORT NumberInputValueCaps; - USHORT NumberInputDataIndices; - USHORT NumberOutputButtonCaps; - USHORT NumberOutputValueCaps; - USHORT NumberOutputDataIndices; - USHORT NumberFeatureButtonCaps; - USHORT NumberFeatureValueCaps; - USHORT NumberFeatureDataIndices; -} HIDP_CAPS, *PHIDP_CAPS; - -typedef struct _HIDP_DATA { - USHORT DataIndex; - USHORT Reserved; - union { - ULONG RawValue; - BOOLEAN On; - }; -} HIDP_DATA, *PHIDP_DATA; - -typedef LONG NTSTATUS; -typedef NTSTATUS (*pHidP_GetCaps)(PHIDP_PREPARSED_DATA, PHIDP_CAPS); -typedef NTSTATUS (*pHidP_GetButtonCaps)(HIDP_REPORT_TYPE, PHIDP_BUTTON_CAPS, PUSHORT, PHIDP_PREPARSED_DATA); -typedef NTSTATUS (*pHidP_GetValueCaps)(HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PUSHORT, PHIDP_PREPARSED_DATA); -typedef NTSTATUS (*pHidP_GetData)(HIDP_REPORT_TYPE, PHIDP_DATA, PULONG, PHIDP_PREPARSED_DATA, PCHAR, ULONG); -typedef ULONG (*pHidP_MaxDataListLength)(HIDP_REPORT_TYPE, PHIDP_PREPARSED_DATA); +// This is our fallback button mapping, used with Xbox 360 and other devices. +static const char *default_gamepad_mapping[16] = { + "face_a", "face_b", "face_x", "face_y", "lshoulder", "rshoulder", "back", "start", "lstick", "rstick" +}; static pHidP_GetCaps _HidP_GetCaps = nullptr; static pHidP_GetButtonCaps _HidP_GetButtonCaps = nullptr; @@ -246,6 +158,9 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { _name = std::move(name); + int quirks = 0; + const char *const *gamepad_buttons = default_gamepad_mapping; + switch (info.dwType) { case RIM_TYPEMOUSE: _device_class = DeviceClass::mouse; @@ -264,7 +179,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { info.hid.usUsage == HID_USAGE_GENERIC_GAMEPAD) { _device_class = DeviceClass::gamepad; - // Flight sticks + // Various game controllers, incl. flight sticks and some gamepads } else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC && info.hid.usUsage == HID_USAGE_GENERIC_JOYSTICK) { _device_class = DeviceClass::flight_stick; @@ -273,11 +188,6 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { // Well, it claims to be a gamepad... _device_class = DeviceClass::gamepad; } - //TODO: better solution for this - if (_vendor_id == 0x0079 && _product_id == 0x0006) { - // Trust GXT 24 - _device_class = DeviceClass::gamepad; - } // Mice } else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC && @@ -306,6 +216,28 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { return false; } + if (_device_class == DeviceClass::gamepad || + _device_class == DeviceClass::flight_stick) { + // Do we have a built-in mapping? + const DeviceMapping *mapping = mapping_presets; + while (mapping->vendor != 0) { + if (info.hid.dwVendorId == mapping->vendor && + info.hid.dwProductId == mapping->product) { + _device_class = mapping->device_class; + gamepad_buttons = mapping->buttons; + quirks = mapping->quirks; + if (device_cat.is_debug()) { + device_cat.debug() + << "Using preset mapping for " << mapping->device_class + << " with VID=" << std::hex << mapping->vendor + << " PID=" << mapping->product << std::dec << "\n"; + } + break; + } + ++mapping; + } + } + // Initialize hid.dll, which provides the HID parser functions. static bool hid_initialized = false; if (!hid_initialized) { @@ -334,39 +266,16 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { return false; } - // Do we have a button mapping? - static const ButtonHandle gamepad_buttons_common[] = { - ButtonHandle::none(), - GamepadButton::face_a(), - GamepadButton::face_b(), - GamepadButton::face_x(), - GamepadButton::face_y(), - GamepadButton::lshoulder(), - GamepadButton::rshoulder(), - GamepadButton::start(), - GamepadButton::back(), - GamepadButton::lstick(), - GamepadButton::rstick(), - }; - const ButtonHandle *gamepad_buttons = gamepad_buttons_common; - if (_vendor_id == 0x0810 && _product_id == 0xe501) { - // SNES-style USB gamepad - static const ButtonHandle gamepad_buttons_snes[] = { - ButtonHandle::none(), - GamepadButton::face_x(), - GamepadButton::face_a(), - GamepadButton::face_b(), - GamepadButton::face_y(), - GamepadButton::lshoulder(), - GamepadButton::rshoulder(), - ButtonHandle::none(), - ButtonHandle::none(), - GamepadButton::back(), - GamepadButton::start(), - }; - gamepad_buttons = gamepad_buttons_snes; + if (device_cat.is_debug()) { + device_cat.debug() + << "Found " << _device_class << " device \"" << _name << "\" with " + << caps.NumberInputDataIndices << " data indices, " + << caps.NumberInputButtonCaps << " button caps, " + << caps.NumberInputValueCaps << " value caps\n"; } + ButtonRegistry *registry = ButtonRegistry::ptr(); + // Prepare a mapping of data indices to button/axis indices. _indices.resize(caps.NumberInputDataIndices); @@ -374,8 +283,11 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { _axes.clear(); USHORT num_button_caps = caps.NumberInputButtonCaps; - PHIDP_BUTTON_CAPS button_caps = (PHIDP_BUTTON_CAPS)alloca(num_button_caps * sizeof(HIDP_BUTTON_CAPS)); - _HidP_GetButtonCaps(HidP_Input, button_caps, &num_button_caps, buffer); + PHIDP_BUTTON_CAPS button_caps; + if (num_button_caps > 0u) { + button_caps = (PHIDP_BUTTON_CAPS)alloca(num_button_caps * sizeof(HIDP_BUTTON_CAPS)); + _HidP_GetButtonCaps(HidP_Input, button_caps, &num_button_caps, buffer); + } for (USHORT i = 0; i < num_button_caps; ++i) { HIDP_BUTTON_CAPS &cap = button_caps[i]; @@ -396,13 +308,15 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { if (device_cat.is_debug()) { device_cat.debug() << "Found button: DataIndex=" << dec << cap.NotRange.DataIndex - << ", ReportID=" << dec << (int)cap.ReportID - << ", UsagePage=0x" << cap.UsagePage + << ", ReportID=" << (int)cap.ReportID + << ", UsagePage=0x" << hex << cap.UsagePage << ", Usage=0x" << cap.NotRange.Usage << dec << "\n"; } } + nassertd(cap.Range.DataIndexMin + upper < (int)_indices.size()) continue; + // Windows will only tell us which buttons in a report are "on", so we // need to keep track of which buttons exist in which report so that we // can figure out which ones are off. @@ -416,8 +330,10 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { switch (cap.UsagePage) { case HID_USAGE_PAGE_BUTTON: if (_device_class == DeviceClass::gamepad) { - if (usage < sizeof(gamepad_buttons_common) / sizeof(ButtonHandle)) { - handle = gamepad_buttons[usage]; + if (usage > 0 && usage - 1 < _countof(default_gamepad_mapping)) { + if (gamepad_buttons[usage - 1] != nullptr) { + handle = registry->find_button(gamepad_buttons[usage - 1]); + } } } else if (_device_class == DeviceClass::flight_stick) { if (usage > 0) { @@ -429,6 +345,9 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { handle = MouseButton::button(button); } break; + + default: + continue; } int button_index = _buttons.size(); @@ -439,8 +358,11 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { } USHORT num_value_caps = caps.NumberInputValueCaps; - PHIDP_VALUE_CAPS value_caps = (PHIDP_VALUE_CAPS)alloca(num_value_caps * sizeof(HIDP_VALUE_CAPS)); - _HidP_GetValueCaps(HidP_Input, value_caps, &num_value_caps, buffer); + PHIDP_VALUE_CAPS value_caps; + if (num_value_caps > 0u) { + value_caps = (PHIDP_VALUE_CAPS)alloca(num_value_caps * sizeof(HIDP_VALUE_CAPS)); + _HidP_GetValueCaps(HidP_Input, value_caps, &num_value_caps, buffer); + } _hat_data_index = -1; @@ -464,7 +386,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { if (device_cat.is_debug()) { device_cat.debug() << "Found value: DataIndex=" << dec << cap.NotRange.DataIndex - << ", ReportID=" << dec << (int)cap.ReportID + << ", ReportID=" << (int)cap.ReportID << ", UsagePage=0x" << hex << cap.UsagePage << ", Usage=0x" << cap.NotRange.Usage << dec << ", LogicalMin=" << cap.LogicalMin @@ -472,6 +394,8 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { } } + nassertd(cap.Range.DataIndexMin + upper < (int)_indices.size()) continue; + for (int j = 0; j <= upper; ++j) { USAGE usage = j + cap.Range.UsageMin; USHORT data_index = j + cap.Range.DataIndexMin; @@ -509,9 +433,24 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { break; case HID_USAGE_GENERIC_Z: if (_device_class == DeviceClass::gamepad) { - axis = Axis::left_trigger; + if (quirks & QB_rstick_from_z) { + if (quirks & QB_right_axes_swapped) { + axis = InputDevice::Axis::right_y; + swap(cap.LogicalMin, cap.LogicalMax); + } else { + axis = InputDevice::Axis::right_x; + } + } else if ((quirks & QB_no_analog_triggers) == 0) { + axis = Axis::left_trigger; + } } else if (_device_class == DeviceClass::flight_stick) { axis = Axis::throttle; + if ((quirks & QB_reversed_throttle) != 0) { + std::swap(cap.LogicalMin, cap.LogicalMax); + } + if ((quirks & QB_centered_throttle) != 0) { + is_signed = false; + } } else { axis = Axis::z; swap(cap.LogicalMin, cap.LogicalMax); @@ -534,7 +473,16 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { break; case HID_USAGE_GENERIC_RZ: if (_device_class == DeviceClass::gamepad) { - axis = Axis::right_trigger; + if (quirks & QB_rstick_from_z) { + if (quirks & QB_right_axes_swapped) { + axis = InputDevice::Axis::right_x; + } else { + axis = InputDevice::Axis::right_y; + swap(cap.LogicalMin, cap.LogicalMax); + } + } else if ((quirks & QB_no_analog_triggers) == 0) { + axis = Axis::right_trigger; + } } else { // Flip to match Panda's convention for heading. axis = Axis::yaw; @@ -558,11 +506,18 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { break; } + // If this axis already exists, don't double-map it, but take the first + // one. This is important for the Trust GXT 24 / SL-6535-SBK-01 which + // have a weird extra Z axis with DataIndex 2 that should be ignored. + for (size_t i = 0; i < _axes.size(); ++i) { + if (_axes[i].axis == axis) { + axis = Axis::none; + break; + } + } + int axis_index; - if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::throttle) { - // T.Flight Hotas X throttle is reversed and can go backwards. - axis_index = add_axis(axis, cap.LogicalMax, cap.LogicalMin, true); - } else if (!is_signed) { + if (!is_signed) { // All axes on the weird XInput-style mappings go from -1 to 1 axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax, true); } else { @@ -590,6 +545,8 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { _max_data_count = _HidP_MaxDataListLength(HidP_Input, buffer); + nassertr_always(_max_data_count >= 0, false); + _handle = handle; _is_connected = true; return true; @@ -619,6 +576,10 @@ on_input(PRAWINPUT input) { nassertv(input != nullptr); nassertv(_preparsed != nullptr); + if (_max_data_count == 0) { + return; + } + BYTE *ptr = input->data.hid.bRawData; if (input->data.hid.dwSizeHid == 0) { return; @@ -626,6 +587,12 @@ on_input(PRAWINPUT input) { LightMutexHolder holder(_lock); + if (device_cat.is_spam()) { + device_cat.spam() + << _name << " received " << input->data.hid.dwCount << " reports of size " + << input->data.hid.dwSizeHid << "\n"; + } + for (DWORD i = 0; i < input->data.hid.dwCount; ++i) { process_report((PCHAR)ptr, input->data.hid.dwSizeHid); ptr += input->data.hid.dwSizeHid; @@ -654,6 +621,7 @@ process_report(PCHAR ptr, size_t size) { if (status == HIDP_STATUS_SUCCESS) { for (ULONG di = 0; di < count; ++di) { if (data[di].DataIndex != _hat_data_index) { + nassertd(data[di].DataIndex < _indices.size()) continue; const Index &idx = _indices[data[di].DataIndex]; if (idx._axis >= 0) { if (idx._signed) { diff --git a/panda/src/device/winRawInputDevice.h b/panda/src/device/winRawInputDevice.h index ce4fe9a19c..bea8066b85 100644 --- a/panda/src/device/winRawInputDevice.h +++ b/panda/src/device/winRawInputDevice.h @@ -25,6 +25,8 @@ class WinInputDeviceManager; /** * This implementation of InputDevice uses the Win32 raw input API and the HID * parser library to support a wide range of devices. + * + * @since 1.10.0 */ class EXPCL_PANDA_DEVICE WinRawInputDevice final : public InputDevice { public: diff --git a/panda/src/device/xInputDevice.cxx b/panda/src/device/xInputDevice.cxx index 4621d467b9..b86a5cd90b 100644 --- a/panda/src/device/xInputDevice.cxx +++ b/panda/src/device/xInputDevice.cxx @@ -99,12 +99,12 @@ typedef struct _XINPUT_CAPABILITIES_EX { WORD Unknown2; } XINPUT_CAPABILITIES_EX; -typedef DWORD (*pXInputGetState)(DWORD, XINPUT_STATE *); -typedef DWORD (*pXInputSetState)(DWORD, XINPUT_VIBRATION *); -typedef DWORD (*pXInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES *); -typedef DWORD (*pXInputGetCapabilitiesEx)(DWORD, DWORD, DWORD, XINPUT_CAPABILITIES_EX *); -typedef DWORD (*pXInputGetBatteryInformation)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *); -typedef DWORD (*pXInputGetBaseBusInformation)(DWORD, XINPUT_BUSINFO *); +typedef DWORD (WINAPI *pXInputGetState)(DWORD, XINPUT_STATE *); +typedef DWORD (WINAPI *pXInputSetState)(DWORD, XINPUT_VIBRATION *); +typedef DWORD (WINAPI *pXInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES *); +typedef DWORD (WINAPI *pXInputGetCapabilitiesEx)(DWORD, DWORD, DWORD, XINPUT_CAPABILITIES_EX *); +typedef DWORD (WINAPI *pXInputGetBatteryInformation)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *); +typedef DWORD (WINAPI *pXInputGetBaseBusInformation)(DWORD, XINPUT_BUSINFO *); static pXInputGetState get_state = nullptr; static pXInputSetState set_state = nullptr; @@ -249,6 +249,10 @@ detect(InputDeviceManager *mgr) { */ bool XInputDevice:: init_xinput() { + if (_initialized) { + return true; + } + if (device_cat.is_debug()) { device_cat.debug() << "Initializing XInput library.\n"; } diff --git a/panda/src/device/xInputDevice.h b/panda/src/device/xInputDevice.h index 0bf3900ec4..2088c19182 100644 --- a/panda/src/device/xInputDevice.h +++ b/panda/src/device/xInputDevice.h @@ -31,6 +31,8 @@ typedef struct tagRID_DEVICE_INFO RID_DEVICE_INFO; /** * This implementation of InputDevice uses Microsoft's XInput library to * interface with an Xbox 360 game controller. + * + * @since 1.10.0 */ class EXPCL_PANDA_DEVICE XInputDevice final : public InputDevice { public: diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index ae1052ba1d..39a1e73aa2 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -48,6 +48,11 @@ #include "displayRegionDrawCallbackData.h" #include "callbackGraphicsWindow.h" #include "depthTestAttrib.h" +#include "unionBoundingVolume.h" + +#if defined(_WIN32) && defined(HAVE_THREADS) && defined(SIMPLE_THREADS) +#include "winInputDeviceManager.h" +#endif #if defined(WIN32) #define WINDOWS_LEAN_AND_MEAN @@ -593,7 +598,6 @@ remove_all_windows() { // appeared to be happening, and this worked around it. Windows old_windows; old_windows.swap(_windows); - Windows::iterator wi; for (GraphicsOutput *win : old_windows) { nassertv(win != nullptr); do_remove_window(win, current_thread); @@ -638,6 +642,12 @@ remove_all_windows() { PStatClient::get_global_pstats()->disconnect(); #endif +#if defined(_WIN32) && defined(HAVE_THREADS) && defined(SIMPLE_THREADS) + // Send a message to the input message pump asking it to shut itself down. + // If we don't do that, the next call will deadlock. + WinInputDeviceManager::stop_thread(); +#endif + // Well, and why not clean up all threads here? Thread::prepare_for_exit(); } @@ -1309,32 +1319,6 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup, CullTraverser *trav = dr->get_cull_traverser(); trav->set_cull_handler(cull_handler); trav->set_scene(scene_setup, gsg, dr->get_incomplete_render()); - - trav->set_view_frustum(nullptr); - if (view_frustum_cull) { - // If we're to be performing view-frustum culling, determine the bounding - // volume associated with the current viewing frustum. - - // First, we have to get the current viewing frustum, which comes from the - // lens. - PT(BoundingVolume) bv = scene_setup->get_cull_bounds(); - - if (bv != nullptr && !bv->is_infinite() && - bv->as_geometric_bounding_volume() != nullptr) { - // Transform it into the appropriate coordinate space. - PT(GeometricBoundingVolume) local_frustum; - local_frustum = bv->make_copy()->as_geometric_bounding_volume(); - nassertv(!local_frustum.is_null()); - - NodePath scene_parent = scene_setup->get_scene_root().get_parent(current_thread); - CPT(TransformState) cull_center_transform = - scene_setup->get_cull_center().get_transform(scene_parent, current_thread); - local_frustum->xform(cull_center_transform->get_mat()); - - trav->set_view_frustum(local_frustum); - } - } - trav->traverse(scene_setup->get_scene_root()); trav->end_traverse(); } @@ -1546,6 +1530,11 @@ cull_to_bins(GraphicsEngine::Windows wlist, Thread *current_thread) { typedef pmap AlreadyCulled; AlreadyCulled already_culled; + // We cull shadow passes last; whether we cull them depends on whether their + // respective frusta are in view of a "normal" camera. + pvector shadow_passes; + pmap non_shadow_bounds; + size_t wlist_size = wlist.size(); for (size_t wi = 0; wi < wlist_size; ++wi) { GraphicsOutput *win = wlist[wi]; @@ -1572,6 +1561,27 @@ cull_to_bins(GraphicsEngine::Windows wlist, Thread *current_thread) { key._lens_index = dr_reader.get_lens_index(); } + // If this is a shadow pass, postpone culling it until we've culled + // all the other passes, and collected their bounding volumes. + Light *light = nullptr; + if (!key._camera.is_empty()) { + light = key._camera.node()->as_light(); + } + if (light != nullptr) { + shadow_passes.push_back(std::move(scene_setup)); + continue; + } + else if (!shadow_passes.empty()) { + NodePath scene_root = scene_setup->get_scene_root(); + const GeometricBoundingVolume *gbv = scene_setup->get_view_frustum(); + UnionBoundingVolume &bounds = non_shadow_bounds[scene_root]; + if (gbv == nullptr || gbv->is_infinite()) { + bounds.set_infinite(); + } else { + bounds.add_component(gbv); + } + } + AlreadyCulled::iterator aci = already_culled.insert(AlreadyCulled::value_type(std::move(key), nullptr)).first; if ((*aci).second == nullptr) { // We have not used this camera already in this thread. Perform @@ -1598,11 +1608,46 @@ cull_to_bins(GraphicsEngine::Windows wlist, Thread *current_thread) { } // Save the results for next frame. - dr->set_cull_result(std::move(cull_result), MOVE(scene_setup), current_thread); + dr->set_cull_result(std::move(cull_result), std::move(scene_setup), current_thread); } } } } + + // Now cull all the shadow passes if their cull bounds are in view. + // We don't bother checking the AlreadyCulled list, because we know there is + // only one output per GSG+light combination. + for (PT(SceneSetup) &scene_setup : shadow_passes) { + DisplayRegion *dr = scene_setup->get_display_region(); + GraphicsOutput *win = dr->get_window(); + PStatTimer timer(win->get_cull_window_pcollector(), current_thread); + + PT(CullResult) cull_result; + + // Are the cull bounds in view of another camera? + GeometricBoundingVolume *frustum = scene_setup->get_view_frustum(); + if (frustum == nullptr || + non_shadow_bounds[scene_setup->get_scene_root()].contains(frustum)) { + GraphicsStateGuardian *gsg = win->get_gsg(); + cull_result = dr->get_cull_result(current_thread); + if (cull_result != nullptr) { + cull_result = cull_result->make_next(); + } else { + // This DisplayRegion has no cull results; draw it. + cull_result = new CullResult(gsg, dr->get_draw_region_pcollector()); + } + cull_to_bins(win, gsg, dr, scene_setup, cull_result, current_thread); + } + else if (display_cat.is_spam()) { + display_cat.spam() + << *scene_setup->get_camera_node() + << " frustum is not in view, skipping shadow pass\n"; + } + + // Even save the results if null, to tell the draw pass that we don't want + // to draw this at all. + dr->set_cull_result(std::move(cull_result), std::move(scene_setup), current_thread); + } } /** @@ -2009,6 +2054,29 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) { CPT(TransformState) cs_world_transform = cs_transform->compose(world_transform); scene_setup->set_cs_world_transform(cs_world_transform); + if (view_frustum_cull) { + // If we're to be performing view-frustum culling, determine the bounding + // volume associated with the current viewing frustum. + + // First, we have to get the current viewing frustum, which comes from the + // lens. + PT(BoundingVolume) bv = scene_setup->get_cull_bounds(); + + if (bv != nullptr && !bv->is_infinite() && + bv->as_geometric_bounding_volume() != nullptr) { + // Transform it into the appropriate coordinate space. + PT(GeometricBoundingVolume) local_frustum; + local_frustum = bv->make_copy()->as_geometric_bounding_volume(); + nassertr(!local_frustum.is_null(), nullptr); + + CPT(TransformState) cull_center_transform = + scene_setup->get_cull_center().get_transform(scene_parent, current_thread); + local_frustum->xform(cull_center_transform->get_mat()); + + scene_setup->set_view_frustum(local_frustum); + } + } + return scene_setup; } diff --git a/panda/src/display/graphicsOutput.cxx b/panda/src/display/graphicsOutput.cxx index daf9b217b1..850faca771 100644 --- a/panda/src/display/graphicsOutput.cxx +++ b/panda/src/display/graphicsOutput.cxx @@ -375,6 +375,8 @@ add_render_texture(Texture *tex, RenderTextureMode mode, * This is a deprecated interface that made sense back when GraphicsOutputs * could only render into one texture at a time. From now on, use * clear_render_textures and add_render_texture instead. + * + * @deprecated Use add_render_texture() instead. */ void GraphicsOutput:: setup_render_texture(Texture *tex, bool allow_bind, bool to_ram) { diff --git a/panda/src/display/graphicsPipeSelection.h b/panda/src/display/graphicsPipeSelection.h index c3b61317e9..ee9db2ded4 100644 --- a/panda/src/display/graphicsPipeSelection.h +++ b/panda/src/display/graphicsPipeSelection.h @@ -22,7 +22,6 @@ #include "lightMutex.h" #include "vector_string.h" -class HardwareChannel; class GraphicsWindow; /** diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 93a3f36651..851ca112ea 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -301,9 +301,9 @@ GraphicsStateGuardian:: // Note that if uniquify-states is false, we can't iterate over all the // states, and some GSGs will linger. Let's hope this isn't a problem. LightReMutexHolder holder(*RenderState::_states_lock); - size_t size = RenderState::_states->get_num_entries(); + size_t size = RenderState::_states.get_num_entries(); for (size_t si = 0; si < size; ++si) { - const RenderState *state = RenderState::_states->get_key(si); + const RenderState *state = RenderState::_states.get_key(si); state->_mungers.remove(_id); state->_munged_states.remove(_id); } @@ -1433,12 +1433,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, // Apply the default OpenGL lights otherwise. // Special exception for light 0, which defaults to white. string basename = name->get_basename(); - if (basename == "color" || basename == "diffuse") { - t.set_row(3, _light_color_scale); - return &t; - } else if (basename == "specular") { - return &LMatrix4::ones_mat(); - } + return &LMatrix4::ones_mat(); } return fetch_specified_member(NodePath(), name, t); } @@ -1494,7 +1489,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, } else if (index == 0) { // Apply the default OpenGL lights otherwise. // Special exception for light 0, which defaults to white. - t.set_row(0, _light_color_scale); + t.set_row(0, LVecBase4(1, 1, 1, 1)); } return &t; } @@ -1539,7 +1534,6 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) Light *light = node->as_light(); nassertr(light != nullptr, &LMatrix4::ident_mat()); LColor c = light->get_color(); - c.componentwise_mult(_light_color_scale); t.set_row(3, c); return &t; @@ -1551,7 +1545,6 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) nassertr(light != nullptr, &LMatrix4::ident_mat()); if (node->is_ambient_light()) { LColor c = light->get_color(); - c.componentwise_mult(_light_color_scale); t.set_row(3, c); } else { // Non-ambient lights don't currently have an ambient color in Panda3D. @@ -1570,7 +1563,6 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) t.set_row(3, LColor(0.0f, 0.0f, 0.0f, 1.0f)); } else { LColor c = light->get_color(); - c.componentwise_mult(_light_color_scale); t.set_row(3, c); } return &t; @@ -1909,6 +1901,14 @@ fetch_ptr_parameter(const Shader::ShaderPtrSpec& spec) { return (_target_shader->get_shader_input_ptr(spec._arg)); } +/** + * + */ +bool GraphicsStateGuardian:: +fetch_ptr_parameter(const Shader::ShaderPtrSpec& spec, Shader::ShaderPtrData &data) { + return _target_shader->get_shader_input_ptr(spec._arg, data); +} + /** * Makes the specified DisplayRegion current. All future drawing and clear * operations will be constrained within the given DisplayRegion. @@ -2823,6 +2823,12 @@ do_issue_light() { } } else { + // Don't forget to still enable lighting if we have only an ambient light. + if (!_lighting_enabled) { + enable_lighting(true); + _lighting_enabled = true; + } + set_ambient_light(target_light->get_ambient_contribution()); } diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index e1c9435fef..cb556499b2 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -339,6 +339,7 @@ public: PT(Texture) fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler, int &view); const Shader::ShaderPtrData *fetch_ptr_parameter(const Shader::ShaderPtrSpec& spec); + bool fetch_ptr_parameter(const Shader::ShaderPtrSpec &spec, Shader::ShaderPtrData &data); virtual void prepare_display_region(DisplayRegionPipelineReader *dr); virtual void clear_before_callback(); diff --git a/panda/src/display/subprocessWindowBuffer.h b/panda/src/display/subprocessWindowBuffer.h index 2cadcee8f5..a6c00db7c3 100644 --- a/panda/src/display/subprocessWindowBuffer.h +++ b/panda/src/display/subprocessWindowBuffer.h @@ -25,8 +25,7 @@ * between processes, as well as appropriate synchronization primitives. * * It's designed to be compiled outside of Panda, so that code that doesn't - * link with Panda (in particular, the Panda3D plugin core API) may still link - * with this and use it. + * link with Panda may still link with this and use it. * * At the moment, and maybe indefinitely, it is only compiled on OSX, and only * when we are building support for the plugin; because it is only needed diff --git a/panda/src/distort/nonlinearImager.cxx b/panda/src/distort/nonlinearImager.cxx index e0f86670a2..7ffc290621 100644 --- a/panda/src/distort/nonlinearImager.cxx +++ b/panda/src/distort/nonlinearImager.cxx @@ -49,6 +49,8 @@ NonlinearImager:: /** * This version of this method is deprecated and will soon be removed. Use * the version that takes two parameters instead. + * + * @deprecated Use the version that takes two parameters instead. */ int NonlinearImager:: add_screen(ProjectionScreen *screen) { diff --git a/panda/src/distort/oSphereLens.cxx b/panda/src/distort/oSphereLens.cxx index 7b8bd35591..9175453fd9 100644 --- a/panda/src/distort/oSphereLens.cxx +++ b/panda/src/distort/oSphereLens.cxx @@ -48,7 +48,7 @@ do_extrude(const Lens::CData *lens_cdata, LPoint3 f = point2d * do_get_film_mat_inv(lens_cdata); PN_stdfloat focal_length = do_get_focal_length(lens_cdata); - PN_stdfloat angle = f[0] * cylindrical_k / focal_length; + PN_stdfloat angle = f[0] * ospherical_k / focal_length; PN_stdfloat sinAngle, cosAngle; csincos(deg_2_rad(angle), &sinAngle, &cosAngle); diff --git a/panda/src/downloader/decompressor.cxx b/panda/src/downloader/decompressor.cxx index 272279047d..bc4822ee91 100644 --- a/panda/src/downloader/decompressor.cxx +++ b/panda/src/downloader/decompressor.cxx @@ -183,7 +183,7 @@ decompress(const Filename &source_file) { return false; int ch = _decompress->get(); - while (!_decompress->eof() && !_decompress->fail()) { + while (ch != EOF && !_decompress->fail()) { _dest->put(ch); ch = _decompress->get(); } @@ -207,7 +207,7 @@ decompress(Ramfile &source_and_dest_file) { IDecompressStream decompress(&source, false); int ch = decompress.get(); - while (!decompress.eof() && !decompress.fail()) { + while (ch != EOF && !decompress.fail()) { dest.put(ch); ch = decompress.get(); } diff --git a/panda/src/downloader/documentSpec.cxx b/panda/src/downloader/documentSpec.cxx index 6ca2a9b260..a27ebe0338 100644 --- a/panda/src/downloader/documentSpec.cxx +++ b/panda/src/downloader/documentSpec.cxx @@ -66,7 +66,7 @@ input(std::istream &in) { // Scan the tag, up to but not including the closing paren. std::string tag; in >> ch; - while (!in.fail() && !in.eof() && ch != ')') { + while (!in.fail() && ch != ')') { tag += ch; // We want to include embedded whitespace, so we use get(). ch = in.get(); @@ -81,7 +81,7 @@ input(std::istream &in) { // Scan the date, up to but not including the closing bracket. if (ch != ']') { std::string date; - while (!in.fail() && !in.eof() && ch != ']') { + while (!in.fail() && ch != ']') { date += ch; ch = in.get(); } diff --git a/panda/src/downloader/downloadDb.cxx b/panda/src/downloader/downloadDb.cxx index 0679614c8b..be12e9fa0a 100644 --- a/panda/src/downloader/downloadDb.cxx +++ b/panda/src/downloader/downloadDb.cxx @@ -58,9 +58,10 @@ back_to_front_slash(const string &str) { */ DownloadDb:: DownloadDb(Ramfile &server_file, Filename &client_file) { - if (downloader_cat.is_debug()) + if (downloader_cat.is_debug()) { downloader_cat.debug() << "DownloadDb constructor called" << endl; + } _client_db = read_db(client_file, 0); _client_db._filename = client_file; _server_db = read_db(server_file, 1); @@ -71,9 +72,10 @@ DownloadDb(Ramfile &server_file, Filename &client_file) { */ DownloadDb:: DownloadDb(Filename &server_file, Filename &client_file) { - if (downloader_cat.is_debug()) + if (downloader_cat.is_debug()) { downloader_cat.debug() << "DownloadDb constructor called" << endl; + } _client_db = read_db(client_file, 0); _client_db._filename = client_file; _server_db = read_db(server_file, 1); @@ -94,9 +96,10 @@ DownloadDb() { */ DownloadDb:: ~DownloadDb() { - if (downloader_cat.is_debug()) + if (downloader_cat.is_debug()) { downloader_cat.debug() << "DownloadDb destructor called" << endl; + } } @@ -330,8 +333,10 @@ write_db(Filename &file, Db db, bool want_server_info) { return false; } - downloader_cat.spam() - << "Writing to file: " << file << endl; + if (downloader_cat.is_spam()) { + downloader_cat.spam() + << "Writing to file: " << file << endl; + } StreamWriter sw(write_stream); @@ -594,8 +599,10 @@ parse_header(Datagram dg) { // Make sure we have a good header DatagramIterator di(dg); uint32_t magic_number = di.get_uint32(); - downloader_cat.debug() - << "Parsed magic number: " << magic_number << endl; + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Parsed magic number: " << magic_number << endl; + } // If the magic number is equal to the bogus magic number it signifies that // the previous write was interrupted if (magic_number == _bogus_magic_number) { @@ -614,8 +621,10 @@ parse_header(Datagram dg) { } int32_t num_multifiles = di.get_int32(); - downloader_cat.debug() - << "Parsed number of multifiles: " << num_multifiles << endl; + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Parsed number of multifiles: " << num_multifiles << endl; + } // If we got all the way here, must be done return num_multifiles; @@ -631,8 +640,10 @@ int DownloadDb::Db:: parse_record_header(Datagram dg) { DatagramIterator di(dg); int32_t record_length = di.get_int32(); - downloader_cat.spam() - << "Parsed record header length: " << record_length << endl; + if (downloader_cat.is_spam()) { + downloader_cat.spam() + << "Parsed record header length: " << record_length << endl; + } // If we got all the way here, must be done return record_length; @@ -662,10 +673,12 @@ parse_mfr(Datagram dg) { // Read the hash value mfr->_hash.read_datagram(di); - downloader_cat.debug() - << "Parsed multifile record: " << mfr->_name << " phase: " << mfr->_phase - << " size: " << mfr->_size - << " status: " << mfr->_status << " num_files: " << mfr->_num_files << endl; + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Parsed multifile record: " << mfr->_name << " phase: " << mfr->_phase + << " size: " << mfr->_size << " status: " << mfr->_status + << " num_files: " << mfr->_num_files << endl; + } // Return the new MultifileRecord return mfr; @@ -690,8 +703,10 @@ parse_fr(Datagram dg) { // convert any old records we might read. fr->_name = back_to_front_slash(fr->_name); - downloader_cat.spam() - << "Parsed file record: " << fr->_name << endl; + if (downloader_cat.is_spam()) { + downloader_cat.spam() + << "Parsed file record: " << fr->_name << endl; + } // Return the new MultifileRecord return fr; @@ -1022,16 +1037,21 @@ int DownloadDb:: get_version(const Filename &name, const HashVal &hash) const { VersionMap::const_iterator vmi = _versions.find(name); if (vmi == _versions.end()) { - downloader_cat.debug() - << "DownloadDb::get_version() - can't find: " << name << endl; + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "DownloadDb::get_version() - can't find: " << name << endl; + } return -1; } const VectorHash &vhash = (*vmi).second; VectorHash::const_iterator i = find(vhash.begin(), vhash.end(), hash); - if (i != vhash.end()) + if (i != vhash.end()) { return (i - vhash.begin() + 1); - downloader_cat.debug() - << "DownloadDb::get_version() - can't find hash: " << hash << endl; + } + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "DownloadDb::get_version() - can't find hash: " << hash << endl; + } return -1; } @@ -1072,9 +1092,11 @@ write_version_map(StreamWriter &sw) { sw.add_int32(_versions.size()); for (vmi = _versions.begin(); vmi != _versions.end(); ++vmi) { name = (*vmi).first; - downloader_cat.spam() - << "DownloadDb::write_version_map() - writing file: " - << name << " of length: " << name.length() << endl; + if (downloader_cat.is_spam()) { + downloader_cat.spam() + << "DownloadDb::write_version_map() - writing file: " + << name << " of length: " << name.length() << endl; + } sw.add_int32(name.length()); sw.append_data(name); sw.add_int32((*vmi).second.size()); @@ -1099,17 +1121,21 @@ read_version_map(StreamReader &sr) { // Get the file name string name = sr.get_string32(); - downloader_cat.spam() - << "DownloadDb::read_version_map() - name: " << name << endl; + if (downloader_cat.is_spam()) { + downloader_cat.spam() + << "DownloadDb::read_version_map() - name: " << name << endl; + } // Get number of hash values for name int length = sr.get_int32(); if (sr.get_istream()->fail()) { return false; } - downloader_cat.spam() - << "DownloadDb::read_version_map() - number of values: " << length - << endl; + if (downloader_cat.is_spam()) { + downloader_cat.spam() + << "DownloadDb::read_version_map() - number of values: " << length + << endl; + } for (int j = 0; j < length; j++) { HashVal hash; diff --git a/panda/src/downloader/httpChannel.cxx b/panda/src/downloader/httpChannel.cxx index 17c12d806a..365c819b0f 100644 --- a/panda/src/downloader/httpChannel.cxx +++ b/panda/src/downloader/httpChannel.cxx @@ -2762,7 +2762,7 @@ bool HTTPChannel:: server_getline(string &str) { nassertr(!_source.is_null(), false); int ch = (*_source)->get(); - while (!(*_source)->eof() && !(*_source)->fail()) { + while (ch != EOF && !(*_source)->fail()) { switch (ch) { case '\n': // end-of-line character, we're done. @@ -2850,7 +2850,7 @@ bool HTTPChannel:: server_get(string &str, size_t num_bytes) { nassertr(!_source.is_null(), false); int ch = (*_source)->get(); - while (!(*_source)->eof() && !(*_source)->fail()) { + while (ch != EOF && !(*_source)->fail()) { _working_get += (char)ch; if (_working_get.length() >= num_bytes) { str = _working_get; diff --git a/panda/src/downloader/httpDate.cxx b/panda/src/downloader/httpDate.cxx index a651b50f89..e17c6c06d6 100644 --- a/panda/src/downloader/httpDate.cxx +++ b/panda/src/downloader/httpDate.cxx @@ -279,7 +279,7 @@ input(std::istream &in) { string date; ch = in.get(); - while (!in.fail() && !in.eof() && ch != '"') { + while (!in.fail() && ch != '"') { date += ch; ch = in.get(); } diff --git a/panda/src/downloader/p3downloader_composite2.cxx b/panda/src/downloader/p3downloader_composite2.cxx index b06e28e4bb..4afed2e40a 100644 --- a/panda/src/downloader/p3downloader_composite2.cxx +++ b/panda/src/downloader/p3downloader_composite2.cxx @@ -13,8 +13,6 @@ #include "multiplexStreamBuf.cxx" #include "patcher.cxx" #include "socketStream.cxx" -#include "stringStreamBuf.cxx" -#include "stringStream.cxx" #include "urlSpec.cxx" #include "virtualFileHTTP.cxx" #include "virtualFileMountHTTP.cxx" diff --git a/panda/src/downloader/socketStream.cxx b/panda/src/downloader/socketStream.cxx index eec65d1857..cd18e7f972 100644 --- a/panda/src/downloader/socketStream.cxx +++ b/panda/src/downloader/socketStream.cxx @@ -56,7 +56,7 @@ do_receive_datagram(Datagram &dg) { // Read the first two bytes: the datagram length. while ((int)_data_so_far.size() < _tcp_header_size) { int ch = _istream->get(); - if (_istream->eof() || _istream->fail()) { + if (ch == EOF || _istream->fail()) { _istream->clear(); return false; } diff --git a/panda/src/downloadertools/apply_patch.cxx b/panda/src/downloadertools/apply_patch.cxx deleted file mode 100644 index 502d67c89b..0000000000 --- a/panda/src/downloadertools/apply_patch.cxx +++ /dev/null @@ -1,46 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file apply_patch.cxx - */ - -#include "pandabase.h" -#include "panda_getopt.h" -#include "preprocess_argv.h" -#include "patchfile.h" -#include "filename.h" - -using std::cerr; -using std::endl; - -int -main(int argc, char **argv) { - preprocess_argv(argc, argv); - - if (argc < 3) { - cerr << "Usage: apply_patch " << endl; - cerr << "Will overwrite old_file" << endl; - return 1; - } - - Filename patch = argv[1]; - patch.set_binary(); - - Filename file = argv[2]; - file.set_binary(); - - Patchfile pfile; - - cerr << "Applying patch file " << patch << " to " << file << endl; - if (pfile.apply(patch, file) == false) { - cerr << "apply patch failed" << endl; - return 1; - } - - return 0; -} diff --git a/panda/src/downloadertools/build_patch.cxx b/panda/src/downloadertools/build_patch.cxx deleted file mode 100644 index 5c705bb38d..0000000000 --- a/panda/src/downloadertools/build_patch.cxx +++ /dev/null @@ -1,125 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file build_patch.cxx - */ - -#include "pandabase.h" -#include "panda_getopt.h" -#include "preprocess_argv.h" -#include "patchfile.h" -#include "filename.h" - -using std::cerr; -using std::endl; - -void -usage() { - cerr << "Usage: build_patch [opts] " << endl; -} - -void -help() { - usage(); - cerr << "\n" - "This program generates a patch file that describes the differences\n" - "between any two source files. The patch file can later be used to\n" - "construct , given . Arbitrary file types, including\n" - "binary files, are supported.\n\n" - - "The patching algorithm can get very slow for very large files. As an\n" - "optimization, if the input files are both Panda Multifiles, the patcher\n" - "will by default patch them on a per-subfile basis, which has the potential\n" - "to be much faster. The -c option will forbid this and force the patcher\n" - "to work on the full file.\n\n" - - "Options:\n\n" - - " -o output_name\n" - " Specify the filename of the patch file to generate.\n\n" - - " -c\n" - " Always generate patches against the complete file, even if the\n" - " input files appear to be multifiles.\n\n" - - " -f footprint_length\n" - " Specify the footprint length for the patching algorithm.\n\n"; -} - -int -main(int argc, char **argv) { - Filename patch_file; - bool complete_file = false; - int footprint_length = 0; - - // extern char *optarg; - extern int optind; - static const char *optflags = "o:cf:h"; - preprocess_argv(argc, argv); - int flag = getopt(argc, argv, optflags); - Filename rel_path; - while (flag != EOF) { - switch (flag) { - case 'o': - patch_file = optarg; - break; - - case 'c': - complete_file = true; - break; - - case 'f': - footprint_length = atoi(optarg); - break; - - case 'h': - help(); - return 1; - case '?': - usage(); - return 1; - default: - cerr << "Unhandled switch: " << flag << endl; - break; - } - flag = getopt(argc, argv, optflags); - } - argc -= (optind - 1); - argv += (optind - 1); - - if (argc < 3) { - usage(); - return 1; - } - - Filename src_file = Filename::from_os_specific(argv[1]); - src_file.set_binary(); - - Filename dest_file = Filename::from_os_specific(argv[2]); - dest_file.set_binary(); - - if (patch_file.empty()) { - patch_file = dest_file.get_fullpath() + ".pch"; - } - Patchfile pfile; - - pfile.set_allow_multifile(!complete_file); - if (footprint_length != 0) { - cerr << "Footprint length is " << footprint_length << "\n"; - pfile.set_footprint_length(footprint_length); - } - - cerr << "Building patch file to convert " << src_file << " to " - << dest_file << endl; - if (pfile.build(src_file, dest_file, patch_file) == false) { - cerr << "build patch failed" << endl; - return 1; - } - - return 0; -} diff --git a/panda/src/downloadertools/check_adler.cxx b/panda/src/downloadertools/check_adler.cxx deleted file mode 100644 index dbea94f8b7..0000000000 --- a/panda/src/downloadertools/check_adler.cxx +++ /dev/null @@ -1,26 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file check_adler.cxx - */ - -#include "download_utils.h" - -int -main(int argc, char *argv[]) { - if (argc < 2) { - std::cerr << "Usage: check_adler " << std::endl; - return 1; - } - - Filename source_file = argv[1]; - - std::cout << check_adler(source_file); - - return 0; -} diff --git a/panda/src/downloadertools/check_crc.cxx b/panda/src/downloadertools/check_crc.cxx deleted file mode 100644 index e98de20b57..0000000000 --- a/panda/src/downloadertools/check_crc.cxx +++ /dev/null @@ -1,26 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file check_crc.cxx - */ - -#include "download_utils.h" - -int -main(int argc, char *argv[]) { - if (argc < 2) { - std::cerr << "Usage: check_crc " << std::endl; - return 1; - } - - Filename source_file = argv[1]; - - std::cout << check_crc(source_file); - - return 0; -} diff --git a/panda/src/downloadertools/check_md5.cxx b/panda/src/downloadertools/check_md5.cxx deleted file mode 100644 index 2cc3ce508e..0000000000 --- a/panda/src/downloadertools/check_md5.cxx +++ /dev/null @@ -1,155 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file check_md5.cxx - */ - -#include "pandabase.h" -#include "hashVal.h" -#include "filename.h" -#include "panda_getopt.h" -#include "preprocess_argv.h" - -using std::cerr; -using std::cout; - -bool output_decimal = false; -bool suppress_filename = false; -pofstream binary_output; - -void -usage() { - cerr << - "\n" - "Usage:\n\n" - "check_md5 [-q] [-d] [-b filename] [-i \"input string\"] [file1 file2 ...]\n" - "check_md5 -h\n\n"; -} - -void -help() { - usage(); - cerr << - "This program outputs the MD5 hash of one or more files (or of a string\n" - "passed on the command line with -i).\n\n" - - "An MD5 hash is a 128-bit value. The output is presented as a 32-digit\n" - "hexadecimal string by default, but with -d, it is presented as four\n" - "big-endian unsigned 32-bit decimal integers. Normally the filename\n" - "of each file is printed along with the hash; -q suppresses this.\n\n" - - "To write the 16 bytes (per input file) of the output directly to a\n" - "binary file, use -b with the name of the file to receive the output.\n"; -} - -void -output_hash(const std::string &filename, const HashVal &hash) { - if (!suppress_filename && !filename.empty()) { - cout << filename << " "; - } - if (output_decimal) { - hash.output_dec(cout); - } else { - hash.output_hex(cout); - } - cout << "\n"; - - // Also output to the binary_output file if it is open. No sweat if it's - // not. - hash.output_binary(binary_output); -} - - -int -main(int argc, char **argv) { - extern char *optarg; - extern int optind; - const char *optstr = "i:db:qh"; - - bool got_input_string = false; - std::string input_string; - Filename binary_output_filename; - - preprocess_argv(argc, argv); - int flag = getopt(argc, argv, optstr); - - while (flag != EOF) { - switch (flag) { - case 'i': - got_input_string = true; - input_string = optarg; - break; - - case 'd': - output_decimal = true; - break; - - case 'b': - binary_output_filename = Filename::binary_filename(std::string(optarg)); - break; - - case 'q': - suppress_filename = true; - break; - - case 'h': - help(); - exit(1); - - default: - exit(1); - } - flag = getopt(argc, argv, optstr); - } - - argc -= (optind-1); - argv += (optind-1); - - if (argc < 2 && !got_input_string) { - usage(); - exit(1); - } - - if (!binary_output_filename.empty()) { - if (!binary_output_filename.open_write(binary_output)) { - cerr << "Unable to open " << binary_output_filename << ".\n"; - exit(1); - } - } - - if (got_input_string) { - HashVal hash; - hash.hash_string(input_string); - output_hash("", hash); - } - - bool okflag = true; - - for (int i = 1; i < argc; i++) { - Filename source_file = Filename::from_os_specific(argv[i]); - - if (!source_file.exists()) { - cerr << source_file << " not found!\n"; - okflag = false; - } else { - HashVal hash; - if (!hash.hash_file(source_file)) { - cerr << "Unable to read " << source_file << "\n"; - okflag = false; - } else { - output_hash(source_file.get_basename(), hash); - } - } - } - - if (!okflag) { - exit(1); - } - - return 0; -} diff --git a/panda/src/downloadertools/show_ddb.cxx b/panda/src/downloadertools/show_ddb.cxx deleted file mode 100644 index 1b1a4abce4..0000000000 --- a/panda/src/downloadertools/show_ddb.cxx +++ /dev/null @@ -1,32 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file show_ddb.cxx - * @author drose - * @date 2002-11-02 - */ - -#include "pandabase.h" -#include "downloadDb.h" -#include "filename.h" - -int -main(int argc, char *argv[]) { - if (argc != 3) { - std::cerr << "Usage: show_ddb server.ddb client.ddb\n"; - return 1; - } - - Filename server_ddb = Filename::from_os_specific(argv[1]); - Filename client_ddb = Filename::from_os_specific(argv[2]); - - DownloadDb db(server_ddb, client_ddb); - db.write(std::cout); - - return 0; -} diff --git a/panda/src/dxgsg9/dxTextureContext9.cxx b/panda/src/dxgsg9/dxTextureContext9.cxx index 70c9db7803..d130acd959 100644 --- a/panda/src/dxgsg9/dxTextureContext9.cxx +++ b/panda/src/dxgsg9/dxTextureContext9.cxx @@ -844,9 +844,11 @@ create_texture(DXScreenData &scrn) { tex->set_anisotropic_degree(aniso_degree); #ifdef _DEBUG - dxgsg9_cat.spam() - << "create_texture: setting aniso degree for " << tex->get_name() - << " to: " << aniso_degree << endl; + if (dxgsg9_cat.is_spam()) { + dxgsg9_cat.spam() + << "create_texture: setting aniso degree for " << tex->get_name() + << " to: " << aniso_degree << endl; + } #endif UINT mip_level_count; diff --git a/panda/src/dxgsg9/wdxGraphicsWindow9.cxx b/panda/src/dxgsg9/wdxGraphicsWindow9.cxx index 07a265a97c..ef70d24879 100644 --- a/panda/src/dxgsg9/wdxGraphicsWindow9.cxx +++ b/panda/src/dxgsg9/wdxGraphicsWindow9.cxx @@ -280,13 +280,17 @@ open_window() { _properties.add_properties(resized_props); } - wdxdisplay9_cat.debug() << "_wcontext._window is " << _wcontext._window << "\n"; + if (wdxdisplay9_cat.is_debug()) { + wdxdisplay9_cat.debug() << "_wcontext._window is " << _wcontext._window << "\n"; + } if (!WinGraphicsWindow::open_window()) { return false; } _wcontext._window = _hWnd; - wdxdisplay9_cat.debug() << "_wcontext._window is " << _wcontext._window << "\n"; + if (wdxdisplay9_cat.is_debug()) { + wdxdisplay9_cat.debug() << "_wcontext._window is " << _wcontext._window << "\n"; + } // Here check if a device already exists. If so, then this open_window call // may be an extension to create multiple windows on same device In that @@ -294,14 +298,18 @@ open_window() { while (true) { if (_dxgsg->get_pipe()->get_device() == nullptr || discard_device) { - wdxdisplay9_cat.debug() << "device is null or fullscreen\n"; + if (wdxdisplay9_cat.is_debug()) { + wdxdisplay9_cat.debug() << "device is null or fullscreen\n"; + } // If device exists, free it if (_dxgsg->get_pipe()->get_device()) { _dxgsg->dx_cleanup(); } - wdxdisplay9_cat.debug() << "device width " << _wcontext._display_mode.Width << "\n"; + if (wdxdisplay9_cat.is_debug()) { + wdxdisplay9_cat.debug() << "device width " << _wcontext._display_mode.Width << "\n"; + } if (!create_screen_buffers_and_device(_wcontext, dx_force_16bpp_zbuffer)) { wdxdisplay9_cat.error() << "Unable to create window with specified parameters.\n"; close_window(); @@ -315,7 +323,9 @@ open_window() { } else { // fill in the DXScreenData from dxdevice here and change the reference // to _window. - wdxdisplay9_cat.debug() << "device is not null\n"; + if (wdxdisplay9_cat.is_debug()) { + wdxdisplay9_cat.debug() << "device is not null\n"; + } dxdev = (DXGraphicsDevice9*)_dxgsg->get_pipe()->get_device(); memcpy(&_wcontext, &dxdev->_Scrn, sizeof(DXScreenData)); @@ -325,7 +335,9 @@ open_window() { _wcontext._presentation_params.BackBufferWidth = _wcontext._display_mode.Width = _properties.get_x_size(); _wcontext._presentation_params.BackBufferHeight = _wcontext._display_mode.Height = _properties.get_y_size(); - wdxdisplay9_cat.debug() << "device width " << _wcontext._presentation_params.BackBufferWidth << "\n"; + if (wdxdisplay9_cat.is_debug()) { + wdxdisplay9_cat.debug() << "device width " << _wcontext._presentation_params.BackBufferWidth << "\n"; + } if (!_dxgsg->create_swap_chain(&_wcontext)) { discard_device = true; continue; // try again @@ -334,7 +346,9 @@ open_window() { break; } } - wdxdisplay9_cat.debug() << "swapchain is " << _wcontext._swap_chain << "\n"; + if (wdxdisplay9_cat.is_debug()) { + wdxdisplay9_cat.debug() << "swapchain is " << _wcontext._swap_chain << "\n"; + } return true; } @@ -349,13 +363,17 @@ reset_window(bool swapchain) { if (swapchain) { if (_wcontext._swap_chain) { _dxgsg->create_swap_chain(&_wcontext); - wdxdisplay9_cat.debug() << "created swapchain " << _wcontext._swap_chain << "\n"; + if (wdxdisplay9_cat.is_debug()) { + wdxdisplay9_cat.debug() << "created swapchain " << _wcontext._swap_chain << "\n"; + } } } else { if (_wcontext._swap_chain) { _dxgsg->release_swap_chain(&_wcontext); - wdxdisplay9_cat.debug() << "released swapchain " << _wcontext._swap_chain << "\n"; + if (wdxdisplay9_cat.is_debug()) { + wdxdisplay9_cat.debug() << "released swapchain " << _wcontext._swap_chain << "\n"; + } } } } @@ -532,7 +550,9 @@ create_screen_buffers_and_device(DXScreenData &display, bool force_16bpp_zbuffer } } - wdxdisplay9_cat.debug() << "Display Width " << dwRenderWidth << " and PresParam Width " << _wcontext._presentation_params.BackBufferWidth << "\n"; + if (wdxdisplay9_cat.is_debug()) { + wdxdisplay9_cat.debug() << "Display Width " << dwRenderWidth << " and PresParam Width " << _wcontext._presentation_params.BackBufferWidth << "\n"; + } // BUGBUG: need to change panda to put frame buffer properties with // GraphicsWindow, not GSG!! Update: Did I fix the bug? - Josh @@ -1164,7 +1184,9 @@ reset_device_resize_window(UINT new_xsize, UINT new_ysize) { if (screen) { _wcontext._swap_chain = screen->_swap_chain; } - wdxdisplay9_cat.debug() << "swapchain is " << _wcontext._swap_chain << "\n"; + if (wdxdisplay9_cat.is_debug()) { + wdxdisplay9_cat.debug() << "swapchain is " << _wcontext._swap_chain << "\n"; + } _gsg->mark_new(); init_resized_window(); return retval; @@ -1275,8 +1297,10 @@ D3DFMT_to_DepthBits(D3DFORMAT fmt) { return 15; default: - wdxdisplay9_cat.debug() - << "D3DFMT_DepthBits: unhandled D3DFMT!\n"; + if (wdxdisplay9_cat.is_debug()) { + wdxdisplay9_cat.debug() + << "D3DFMT_DepthBits: unhandled D3DFMT!\n"; + } return 0; } } diff --git a/panda/src/egg/eggMaterial.I b/panda/src/egg/eggMaterial.I index f12a50c00d..7c49c2bd98 100644 --- a/panda/src/egg/eggMaterial.I +++ b/panda/src/egg/eggMaterial.I @@ -12,7 +12,7 @@ */ /** - * + * @since 1.10.0 */ INLINE void EggMaterial:: set_base(const LColor &base) { @@ -21,7 +21,7 @@ set_base(const LColor &base) { } /** - * + * @since 1.10.0 */ INLINE void EggMaterial:: clear_base() { @@ -29,7 +29,7 @@ clear_base() { } /** - * + * @since 1.10.0 */ INLINE bool EggMaterial:: has_base() const { @@ -39,6 +39,8 @@ has_base() const { /** * It is legal to call this even if has_base() returns false. If so, it * simply returns the default base color. + * + * @since 1.10.0 */ INLINE LColor EggMaterial:: get_base() const { @@ -239,7 +241,7 @@ get_shininess() const { } /** - * + * @since 1.10.0 */ INLINE void EggMaterial:: set_roughness(double roughness) { @@ -248,7 +250,7 @@ set_roughness(double roughness) { } /** - * + * @since 1.10.0 */ INLINE void EggMaterial:: clear_roughness() { @@ -256,7 +258,7 @@ clear_roughness() { } /** - * + * @since 1.10.0 */ INLINE bool EggMaterial:: has_roughness() const { @@ -264,7 +266,7 @@ has_roughness() const { } /** - * + * @since 1.10.0 */ INLINE double EggMaterial:: get_roughness() const { @@ -276,7 +278,7 @@ get_roughness() const { } /** - * + * @since 1.10.0 */ INLINE void EggMaterial:: set_metallic(double metallic) { @@ -285,7 +287,7 @@ set_metallic(double metallic) { } /** - * + * @since 1.10.0 */ INLINE void EggMaterial:: clear_metallic() { @@ -293,7 +295,7 @@ clear_metallic() { } /** - * + * @since 1.10.0 */ INLINE bool EggMaterial:: has_metallic() const { @@ -301,7 +303,7 @@ has_metallic() const { } /** - * + * @since 1.10.0 */ INLINE double EggMaterial:: get_metallic() const { @@ -313,7 +315,7 @@ get_metallic() const { } /** - * + * @since 1.10.0 */ INLINE void EggMaterial:: set_ior(double ior) { @@ -322,7 +324,7 @@ set_ior(double ior) { } /** - * + * @since 1.10.0 */ INLINE void EggMaterial:: clear_ior() { @@ -330,7 +332,7 @@ clear_ior() { } /** - * + * @since 1.10.0 */ INLINE bool EggMaterial:: has_ior() const { @@ -338,7 +340,7 @@ has_ior() const { } /** - * + * @since 1.10.0 */ INLINE double EggMaterial:: get_ior() const { diff --git a/panda/src/egg/eggPrimitive.I b/panda/src/egg/eggPrimitive.I index 13e994acce..b98679ff67 100644 --- a/panda/src/egg/eggPrimitive.I +++ b/panda/src/egg/eggPrimitive.I @@ -109,7 +109,7 @@ get_connected_shading() const { /** * Replaces the current list of textures with the indicated texture. * - * This method is deprecated and is used in support of single-texturing only. + * @deprecated This method is used in support of single-texturing only. * Please use the multitexture variant add_texture instead. */ INLINE void EggPrimitive:: @@ -121,7 +121,7 @@ set_texture(EggTexture *texture) { /** * Returns true if the primitive has any textures specified, false otherwise. * - * This method is deprecated and is used in support of single-texturing only. + * @deprecated This method is used in support of single-texturing only. * New code should be written to use the multitexture variants instead. */ INLINE bool EggPrimitive:: @@ -143,7 +143,7 @@ has_texture(EggTexture *texture) const { * Returns the first texture on the primitive, if any, or NULL if there are no * textures on the primitive. * - * This method is deprecated and is used in support of single-texturing only. + * @deprecated This method is used in support of single-texturing only. * New code should be written to use the multitexture variants instead. */ INLINE EggTexture *EggPrimitive:: diff --git a/panda/src/egg/eggVertexUV.I b/panda/src/egg/eggVertexUV.I index 05f6f8609f..eef97e52e3 100644 --- a/panda/src/egg/eggVertexUV.I +++ b/panda/src/egg/eggVertexUV.I @@ -97,6 +97,14 @@ has_tangent() const { return (_flags & F_has_tangent) != 0; } +/** + * + */ +INLINE bool EggVertexUV:: +has_tangent4() const { + return (_flags & F_has_tangent4) != 0; +} + /** * */ @@ -106,6 +114,19 @@ get_tangent() const { return _tangent; } +/** + * + */ +INLINE LVecBase4d EggVertexUV:: +get_tangent4() const { + LVecBase4d tangent4(_tangent, 1.0); + nassertr_always(has_tangent(), tangent4); + if (_flags & F_flip_computed_binormal) { + tangent4[3] = -1.0; + } + return tangent4; +} + /** * */ @@ -113,6 +134,22 @@ INLINE void EggVertexUV:: set_tangent(const LNormald &tangent) { _tangent = tangent; _flags |= F_has_tangent; + _flags &= ~(F_has_tangent4 | F_flip_computed_binormal); +} + +/** + * Sets the tangent vector, along with a fourth parameter that is multiplied + * with the result of cross(normal, tangent) when computing the binormal. + */ +INLINE void EggVertexUV:: +set_tangent4(const LVecBase4d &tangent) { + _tangent = tangent.get_xyz(); + _flags |= F_has_tangent4 | F_has_tangent; + if (tangent[3] < 0.0) { + _flags |= F_flip_computed_binormal; + } else { + _flags &= ~F_flip_computed_binormal; + } } /** diff --git a/panda/src/egg/eggVertexUV.cxx b/panda/src/egg/eggVertexUV.cxx index 49a3c39ace..bcc0c1055e 100644 --- a/panda/src/egg/eggVertexUV.cxx +++ b/panda/src/egg/eggVertexUV.cxx @@ -145,7 +145,10 @@ write(std::ostream &out, int indent_level) const { } else { indent(out, indent_level+2) << get_uv() << "\n"; } - if (has_tangent()) { + if (has_tangent4()) { + indent(out, indent_level + 2) + << " { " << get_tangent4() << " }\n"; + } else if (has_tangent()) { indent(out, indent_level + 2) << " { " << get_tangent() << " }\n"; } diff --git a/panda/src/egg/eggVertexUV.h b/panda/src/egg/eggVertexUV.h index 2ab0447639..e729f28101 100644 --- a/panda/src/egg/eggVertexUV.h +++ b/panda/src/egg/eggVertexUV.h @@ -45,8 +45,11 @@ PUBLISHED: INLINE void set_uvw(const LTexCoord3d &texCoord); INLINE bool has_tangent() const; + INLINE bool has_tangent4() const; INLINE const LNormald &get_tangent() const; + INLINE LVecBase4d get_tangent4() const; INLINE void set_tangent(const LNormald &tangent); + INLINE void set_tangent4(const LVecBase4d &tangent); INLINE void clear_tangent(); INLINE bool has_binormal() const; @@ -69,6 +72,10 @@ private: F_has_tangent = 0x001, F_has_binormal = 0x002, F_has_w = 0x004, + F_has_tangent4 = 0x008, + + // Only defined temporarily as we can't add a float to this class in 1.10. + F_flip_computed_binormal = 0x010, }; int _flags; diff --git a/panda/src/egg/parser.cxx.prebuilt b/panda/src/egg/parser.cxx.prebuilt index f9a81a30a5..576f851740 100644 --- a/panda/src/egg/parser.cxx.prebuilt +++ b/panda/src/egg/parser.cxx.prebuilt @@ -1,20 +1,19 @@ -/* A Bison parser, made by GNU Bison 2.4.2. */ +/* A Bison parser, made by GNU Bison 3.2.2. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc. -/* Skeleton implementation for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software - Foundation, Inc. - This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program. If not, see . */ @@ -27,7 +26,7 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ @@ -41,11 +40,14 @@ define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.4.2" +#define YYBISON_VERSION "3.2.2" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -59,23 +61,19 @@ /* Pull parsers. */ #define YYPULL 1 -/* Using locations. */ -#define YYLSP_NEEDED 0 /* Substitute the variable and function names. */ #define yyparse eggyyparse #define yylex eggyylex #define yyerror eggyyerror -#define yylval eggyylval -#define yychar eggyychar #define yydebug eggyydebug #define yynerrs eggyynerrs +#define yylval eggyylval +#define yychar eggyychar -/* Copy the first part of user declarations. */ - -/* Line 189 of yacc.c */ -#line 6 "parser.yxx" +/* First part of user prologue. */ +#line 7 "panda/src/egg/parser.yxx" /* yacc.c:338 */ #include "pandabase.h" @@ -170,10 +168,6 @@ static Groups groups; // temporarily. static int vertex_index; -// We need to hold a matrix for a little bit while parsing the -// entries. -static LMatrix3d matrix_2d; - //////////////////////////////////////////////////////////////////// // Defining the interface to the parser. @@ -205,7 +199,7 @@ egg_cleanup_parser() { eggyyerror("Undefined vertex pool " + pool->get_name()); } else { eggyyerror("Undefined vertices in pool " + pool->get_name()); - + egg_cat.error(false) << "Undefined vertex index numbers:"; EggVertexPool::const_iterator vi; @@ -216,7 +210,7 @@ egg_cleanup_parser() { << " " << vertex->get_index(); } } - egg_cat.error(false) + egg_cat.error(false) << "\n"; } } @@ -232,14 +226,18 @@ egg_cleanup_parser() { } - -/* Line 189 of yacc.c */ -#line 234 "y.tab.c" - -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif +#line 234 "built/tmp/parser.yxx.c" /* yacc.c:338 */ +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE @@ -249,108 +247,113 @@ egg_cleanup_parser() { # define YYERROR_VERBOSE 0 #endif -/* Enabling the token table. */ -#ifndef YYTOKEN_TABLE -# define YYTOKEN_TABLE 0 +/* In a future release of Bison, this section will be replaced + by #include "parser.yxx.h". */ +#ifndef YY_EGGYY_BUILT_TMP_PARSER_YXX_H_INCLUDED +# define YY_EGGYY_BUILT_TMP_PARSER_YXX_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int eggyydebug; #endif - -/* Tokens. */ +/* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE - /* Put the tokens into the symbol table, so that GDB and other debuggers - know about them. */ - enum yytokentype { - EGG_NUMBER = 258, - EGG_ULONG = 259, - EGG_STRING = 260, - ANIMPRELOAD = 261, - BEZIERCURVE = 262, - BFACE = 263, - BILLBOARD = 264, - BILLBOARDCENTER = 265, - BINORMAL = 266, - BUNDLE = 267, - CLOSED = 268, - COLLIDE = 269, - COMMENT = 270, - COMPONENT = 271, - COORDSYSTEM = 272, - CV = 273, - DART = 274, - DNORMAL = 275, - DRGBA = 276, - DUV = 277, - DXYZ = 278, - DCS = 279, - DISTANCE = 280, - DTREF = 281, - DYNAMICVERTEXPOOL = 282, - EXTERNAL_FILE = 283, - GROUP = 284, - DEFAULTPOSE = 285, - JOINT = 286, - KNOTS = 287, - INCLUDE = 288, - INSTANCE = 289, - LINE = 290, - LOOP = 291, - MATERIAL = 292, - MATRIX3 = 293, - MATRIX4 = 294, - MODEL = 295, - MREF = 296, - NORMAL = 297, - NURBSCURVE = 298, - NURBSSURFACE = 299, - OBJECTTYPE = 300, - ORDER = 301, - OUTTANGENT = 302, - PATCH = 303, - POINTLIGHT = 304, - POLYGON = 305, - REF = 306, - RGBA = 307, - ROTATE = 308, - ROTX = 309, - ROTY = 310, - ROTZ = 311, - SANIM = 312, - SCALAR = 313, - SCALE = 314, - SEQUENCE = 315, - SHADING = 316, - SWITCH = 317, - SWITCHCONDITION = 318, - TABLE = 319, - TABLE_V = 320, - TAG = 321, - TANGENT = 322, - TEXLIST = 323, - TEXTURE = 324, - TLENGTHS = 325, - TRANSFORM = 326, - TRANSLATE = 327, - TREF = 328, - TRIANGLEFAN = 329, - TRIANGLESTRIP = 330, - TRIM = 331, - TXT = 332, - UKNOTS = 333, - UV = 334, - AUX = 335, - VKNOTS = 336, - VERTEX = 337, - VERTEXANIM = 338, - VERTEXPOOL = 339, - VERTEXREF = 340, - XFMANIM = 341, - XFMSANIM = 342, - START_EGG = 343, - START_GROUP_BODY = 344, - START_TEXTURE_BODY = 345, - START_PRIMITIVE_BODY = 346 - }; + enum yytokentype + { + EGG_NUMBER = 258, + EGG_ULONG = 259, + EGG_STRING = 260, + ANIMPRELOAD = 261, + BEZIERCURVE = 262, + BFACE = 263, + BILLBOARD = 264, + BILLBOARDCENTER = 265, + BINORMAL = 266, + BUNDLE = 267, + CLOSED = 268, + COLLIDE = 269, + COMMENT = 270, + COMPONENT = 271, + COORDSYSTEM = 272, + CV = 273, + DART = 274, + DNORMAL = 275, + DRGBA = 276, + DUV = 277, + DXYZ = 278, + DCS = 279, + DISTANCE = 280, + DTREF = 281, + DYNAMICVERTEXPOOL = 282, + EXTERNAL_FILE = 283, + GROUP = 284, + DEFAULTPOSE = 285, + JOINT = 286, + KNOTS = 287, + INCLUDE = 288, + INSTANCE = 289, + LINE = 290, + LOOP = 291, + MATERIAL = 292, + MATRIX3 = 293, + MATRIX4 = 294, + MODEL = 295, + MREF = 296, + NORMAL = 297, + NURBSCURVE = 298, + NURBSSURFACE = 299, + OBJECTTYPE = 300, + ORDER = 301, + OUTTANGENT = 302, + PATCH = 303, + POINTLIGHT = 304, + POLYGON = 305, + REF = 306, + RGBA = 307, + ROTATE = 308, + ROTX = 309, + ROTY = 310, + ROTZ = 311, + SANIM = 312, + SCALAR = 313, + SCALE = 314, + SEQUENCE = 315, + SHADING = 316, + SWITCH = 317, + SWITCHCONDITION = 318, + TABLE = 319, + TABLE_V = 320, + TAG = 321, + TANGENT = 322, + TEXLIST = 323, + TEXTURE = 324, + TLENGTHS = 325, + TRANSFORM = 326, + TRANSLATE = 327, + TREF = 328, + TRIANGLEFAN = 329, + TRIANGLESTRIP = 330, + TRIM = 331, + TXT = 332, + UKNOTS = 333, + UV = 334, + AUX = 335, + VKNOTS = 336, + VERTEX = 337, + VERTEXANIM = 338, + VERTEXPOOL = 339, + VERTEXREF = 340, + XFMANIM = 341, + XFMSANIM = 342, + START_EGG = 343, + START_GROUP_BODY = 344, + START_TEXTURE_BODY = 345, + START_PRIMITIVE_BODY = 346 + }; #endif /* Tokens. */ #define EGG_NUMBER 258 @@ -443,21 +446,16 @@ egg_cleanup_parser() { #define START_TEXTURE_BODY 345 #define START_PRIMITIVE_BODY 346 +/* Value type. */ +extern YYSTYPE eggyylval; -#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +int eggyyparse (void); -# define yystype YYSTYPE /* obsolescent; will be withdrawn */ -# define YYSTYPE_IS_DECLARED 1 -#endif +#endif /* !YY_EGGYY_BUILT_TMP_PARSER_YXX_H_INCLUDED */ -/* Copy the second part of user declarations. */ - - -/* Line 264 of yacc.c */ -#line 457 "y.tab.c" #ifdef short # undef short @@ -471,23 +469,20 @@ typedef unsigned char yytype_uint8; #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; -#elif (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -typedef signed char yytype_int8; #else -typedef short int yytype_int8; +typedef signed char yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else -typedef unsigned short int yytype_uint16; +typedef unsigned short yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else -typedef short int yytype_int16; +typedef short yytype_int16; #endif #ifndef YYSIZE_T @@ -495,12 +490,11 @@ typedef short int yytype_int16; # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t -# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) +# elif ! defined YYSIZE_T # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else -# define YYSIZE_T unsigned int +# define YYSIZE_T unsigned # endif #endif @@ -510,38 +504,58 @@ typedef short int yytype_int16; # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ -# define YY_(msgid) dgettext ("bison-runtime", msgid) +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ -# define YY_(msgid) msgid +# define YY_(Msgid) Msgid # endif #endif +#ifndef YY_ATTRIBUTE +# if (defined __GNUC__ \ + && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ + || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C +# define YY_ATTRIBUTE(Spec) __attribute__(Spec) +# else +# define YY_ATTRIBUTE(Spec) /* empty */ +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) +#endif + /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ -# define YYUSE(e) ((void) (e)) +# define YYUSE(E) ((void) (E)) #else -# define YYUSE(e) /* empty */ +# define YYUSE(E) /* empty */ #endif -/* Identity function, used to suppress warnings about constant conditions. */ -#ifndef lint -# define YYID(n) (n) +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") #else -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static int -YYID (int yyi) -#else -static int -YYID (yyi) - int yyi; +# define YY_INITIAL_VALUE(Value) Value #endif -{ - return yyi; -} +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + #if ! defined yyoverflow || YYERROR_VERBOSE @@ -560,11 +574,11 @@ YYID (yyi) # define alloca _alloca # else # define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS # include /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # endif @@ -572,8 +586,8 @@ YYID (yyi) # endif # ifdef YYSTACK_ALLOC - /* Pacify GCC's `empty if-body' warning. */ -# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely @@ -587,25 +601,23 @@ YYID (yyi) # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif -# if (defined __cplusplus && ! defined _STDLIB_H \ +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ - && (defined YYFREE || defined free))) + && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc -# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) +# if ! defined malloc && ! defined EXIT_SUCCESS void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free -# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) +# if ! defined free && ! defined EXIT_SUCCESS void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif @@ -615,7 +627,7 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ - || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc @@ -633,64 +645,70 @@ union yyalloc ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) -/* Copy COUNT objects from FROM to TO. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(To, From, Count) \ - __builtin_memcpy (To, From, (Count) * sizeof (*(From))) -# else -# define YYCOPY(To, From, Count) \ - do \ - { \ - YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (To)[yyi] = (From)[yyi]; \ - } \ - while (YYID (0)) -# endif -# endif +# define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ -# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ - Stack = &yyptr->Stack_alloc; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (YYID (0)) +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) #endif +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + /* YYFINAL -- State number of the termination state. */ #define YYFINAL 14 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 1536 +#define YYLAST 1388 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 94 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 127 /* YYNRULES -- Number of rules. */ -#define YYNRULES 277 -/* YYNRULES -- Number of states. */ -#define YYNSTATES 763 +#define YYNRULES 278 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 765 -/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned + by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 346 -#define YYTRANSLATE(YYX) \ - ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) +#define YYTRANSLATE(YYX) \ + ((unsigned) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) -/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, without out-of-bounds checking. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -731,200 +749,41 @@ static const yytype_uint8 yytranslate[] = }; #if YYDEBUG -/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in - YYRHS. */ -static const yytype_uint16 yyprhs[] = -{ - 0, 0, 3, 6, 9, 12, 15, 17, 20, 22, - 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, - 44, 46, 48, 50, 52, 54, 56, 58, 63, 69, - 70, 78, 80, 87, 90, 91, 98, 100, 107, 113, - 120, 121, 128, 130, 133, 134, 140, 141, 148, 150, - 153, 157, 162, 163, 171, 172, 180, 186, 192, 201, - 210, 213, 217, 225, 233, 241, 250, 258, 267, 268, - 273, 277, 286, 295, 300, 310, 320, 321, 328, 329, - 336, 337, 344, 346, 353, 359, 367, 375, 381, 387, - 393, 399, 405, 411, 417, 424, 430, 433, 436, 439, - 442, 448, 451, 453, 455, 458, 459, 465, 466, 472, - 474, 477, 480, 483, 486, 489, 492, 495, 498, 501, - 504, 507, 510, 516, 523, 528, 533, 538, 543, 551, - 557, 564, 569, 574, 576, 586, 591, 593, 610, 620, - 622, 629, 634, 646, 659, 660, 667, 668, 675, 676, - 683, 684, 691, 692, 699, 700, 707, 708, 715, 716, - 723, 725, 731, 737, 739, 740, 748, 754, 760, 766, - 769, 775, 781, 787, 794, 796, 802, 808, 814, 817, - 823, 829, 835, 841, 847, 853, 856, 862, 869, 871, - 877, 883, 889, 892, 898, 904, 910, 916, 922, 929, - 931, 933, 935, 939, 948, 957, 962, 972, 982, 984, - 993, 996, 998, 1000, 1002, 1008, 1010, 1013, 1015, 1017, - 1018, 1025, 1026, 1033, 1035, 1038, 1041, 1044, 1047, 1050, - 1051, 1058, 1060, 1067, 1073, 1074, 1081, 1083, 1090, 1096, - 1097, 1104, 1106, 1113, 1116, 1117, 1124, 1126, 1133, 1135, - 1138, 1140, 1143, 1145, 1147, 1149, 1151, 1153, 1155, 1157, - 1159, 1161, 1163, 1165, 1167, 1169, 1171, 1173, 1175, 1177, - 1180, 1182, 1184, 1186, 1188, 1190, 1192, 1194 -}; - -/* YYRHS -- A `-1'-separated list of the rules' RHS. */ -static const yytype_int16 yyrhs[] = -{ - 95, 0, -1, 88, 96, -1, 89, 126, -1, 90, - 102, -1, 91, 169, -1, 220, -1, 96, 97, -1, - 98, -1, 99, -1, 100, -1, 103, -1, 106, -1, - 107, -1, 120, -1, 122, -1, 124, -1, 152, -1, - 154, -1, 156, -1, 158, -1, 160, -1, 162, -1, - 164, -1, 166, -1, 187, -1, 201, -1, 17, 92, - 212, 93, -1, 15, 211, 92, 215, 93, -1, -1, - 69, 210, 92, 212, 101, 102, 93, -1, 220, -1, - 102, 58, 210, 92, 218, 93, -1, 102, 129, -1, - -1, 37, 210, 92, 104, 105, 93, -1, 220, -1, - 105, 58, 210, 92, 218, 93, -1, 28, 211, 92, - 212, 93, -1, 214, 28, 211, 92, 212, 93, -1, - -1, 84, 210, 108, 92, 109, 93, -1, 220, -1, - 109, 110, -1, -1, 82, 111, 92, 113, 93, -1, - -1, 82, 219, 112, 92, 113, 93, -1, 217, -1, - 217, 217, -1, 217, 217, 217, -1, 217, 217, 217, - 217, -1, -1, 113, 79, 211, 92, 114, 116, 93, - -1, -1, 113, 80, 210, 92, 115, 117, 93, -1, - 113, 42, 92, 118, 93, -1, 113, 52, 92, 119, - 93, -1, 113, 23, 214, 92, 217, 217, 217, 93, - -1, 113, 23, 92, 214, 217, 217, 217, 93, -1, - 217, 217, -1, 217, 217, 217, -1, 116, 67, 92, - 217, 217, 217, 93, -1, 116, 11, 92, 217, 217, - 217, 93, -1, 116, 22, 214, 92, 217, 217, 93, - -1, 116, 22, 214, 92, 217, 217, 217, 93, -1, - 116, 22, 92, 214, 217, 217, 93, -1, 116, 22, - 92, 214, 217, 217, 217, 93, -1, -1, 217, 217, - 217, 217, -1, 217, 217, 217, -1, 118, 20, 214, - 92, 217, 217, 217, 93, -1, 118, 20, 92, 214, - 217, 217, 217, 93, -1, 217, 217, 217, 217, -1, - 119, 21, 214, 92, 217, 217, 217, 217, 93, -1, - 119, 21, 92, 214, 217, 217, 217, 217, 93, -1, - -1, 29, 211, 121, 92, 126, 93, -1, -1, 31, - 211, 123, 92, 126, 93, -1, -1, 34, 211, 125, - 92, 126, 93, -1, 220, -1, 126, 58, 210, 92, - 218, 93, -1, 126, 9, 92, 214, 93, -1, 126, - 10, 92, 217, 217, 217, 93, -1, 126, 14, 211, - 92, 127, 128, 93, -1, 126, 24, 92, 219, 93, - -1, 126, 24, 92, 5, 93, -1, 126, 19, 92, - 219, 93, -1, 126, 19, 92, 5, 93, -1, 126, - 62, 92, 219, 93, -1, 126, 45, 92, 212, 93, - -1, 126, 40, 92, 219, 93, -1, 126, 66, 211, - 92, 215, 93, -1, 126, 68, 92, 219, 93, -1, - 126, 129, -1, 126, 131, -1, 126, 148, -1, 126, - 150, -1, 126, 51, 92, 209, 93, -1, 126, 97, - -1, 214, -1, 220, -1, 128, 214, -1, -1, 71, - 130, 92, 133, 93, -1, -1, 30, 132, 92, 133, - 93, -1, 220, -1, 133, 134, -1, 133, 135, -1, - 133, 136, -1, 133, 137, -1, 133, 138, -1, 133, - 139, -1, 133, 140, -1, 133, 141, -1, 133, 142, - -1, 133, 143, -1, 133, 144, -1, 133, 146, -1, - 72, 92, 217, 217, 93, -1, 72, 92, 217, 217, - 217, 93, -1, 53, 92, 217, 93, -1, 54, 92, - 217, 93, -1, 55, 92, 217, 93, -1, 56, 92, - 217, 93, -1, 53, 92, 217, 217, 217, 217, 93, - -1, 59, 92, 217, 217, 93, -1, 59, 92, 217, - 217, 217, 93, -1, 59, 92, 217, 93, -1, 38, - 92, 145, 93, -1, 220, -1, 217, 217, 217, 217, - 217, 217, 217, 217, 217, -1, 39, 92, 147, 93, - -1, 220, -1, 217, 217, 217, 217, 217, 217, 217, - 217, 217, 217, 217, 217, 217, 217, 217, 217, -1, - 85, 92, 204, 149, 51, 92, 208, 93, 93, -1, - 220, -1, 149, 58, 210, 92, 218, 93, -1, 63, - 92, 151, 93, -1, 25, 92, 217, 217, 82, 92, - 217, 217, 217, 93, 93, -1, 25, 92, 217, 217, - 217, 82, 92, 217, 217, 217, 93, 93, -1, -1, - 50, 211, 153, 92, 169, 93, -1, -1, 74, 211, - 155, 92, 169, 93, -1, -1, 75, 211, 157, 92, - 169, 93, -1, -1, 48, 211, 159, 92, 169, 93, - -1, -1, 49, 211, 161, 92, 169, 93, -1, -1, - 35, 211, 163, 92, 169, 93, -1, -1, 44, 211, - 165, 92, 171, 93, -1, -1, 43, 211, 167, 92, - 172, 93, -1, 220, -1, 168, 42, 92, 176, 93, - -1, 168, 52, 92, 177, 93, -1, 220, -1, -1, - 169, 16, 219, 92, 170, 168, 93, -1, 169, 73, - 92, 173, 93, -1, 169, 69, 92, 174, 93, -1, - 169, 41, 92, 175, 93, -1, 169, 179, -1, 169, - 42, 92, 176, 93, -1, 169, 52, 92, 177, 93, - -1, 169, 8, 92, 178, 93, -1, 169, 58, 210, - 92, 218, 93, -1, 220, -1, 171, 73, 92, 173, - 93, -1, 171, 69, 92, 174, 93, -1, 171, 41, - 92, 175, 93, -1, 171, 179, -1, 171, 42, 92, - 176, 93, -1, 171, 52, 92, 177, 93, -1, 171, - 8, 92, 178, 93, -1, 171, 46, 92, 180, 93, - -1, 171, 78, 92, 181, 93, -1, 171, 81, 92, - 182, 93, -1, 171, 166, -1, 171, 76, 92, 183, - 93, -1, 171, 58, 210, 92, 218, 93, -1, 220, - -1, 172, 73, 92, 173, 93, -1, 172, 69, 92, - 174, 93, -1, 172, 41, 92, 175, 93, -1, 172, - 179, -1, 172, 42, 92, 176, 93, -1, 172, 52, - 92, 177, 93, -1, 172, 8, 92, 178, 93, -1, - 172, 46, 92, 185, 93, -1, 172, 32, 92, 186, - 93, -1, 172, 58, 210, 92, 218, 93, -1, 206, - -1, 210, -1, 207, -1, 217, 217, 217, -1, 176, - 20, 214, 92, 217, 217, 217, 93, -1, 176, 20, - 92, 214, 217, 217, 217, 93, -1, 217, 217, 217, - 217, -1, 177, 21, 214, 92, 217, 217, 217, 217, - 93, -1, 177, 21, 92, 214, 217, 217, 217, 217, - 93, -1, 219, -1, 85, 92, 204, 51, 92, 208, - 93, 93, -1, 219, 219, -1, 205, -1, 205, -1, - 220, -1, 183, 36, 92, 184, 93, -1, 220, -1, - 184, 166, -1, 219, -1, 205, -1, -1, 64, 211, - 188, 92, 191, 93, -1, -1, 12, 211, 190, 92, - 191, 93, -1, 220, -1, 191, 187, -1, 191, 189, - -1, 191, 192, -1, 191, 195, -1, 191, 198, -1, - -1, 57, 211, 193, 92, 194, 93, -1, 220, -1, - 194, 58, 210, 92, 218, 93, -1, 194, 65, 92, - 205, 93, -1, -1, 86, 211, 196, 92, 197, 93, - -1, 220, -1, 197, 58, 210, 92, 218, 93, -1, - 197, 65, 92, 205, 93, -1, -1, 87, 211, 199, - 92, 200, 93, -1, 220, -1, 200, 58, 210, 92, - 218, 93, -1, 200, 192, -1, -1, 6, 211, 202, - 92, 203, 93, -1, 220, -1, 203, 58, 210, 92, - 218, 93, -1, 220, -1, 204, 219, -1, 220, -1, - 205, 217, -1, 210, -1, 210, -1, 210, -1, 210, - -1, 220, -1, 214, -1, 213, -1, 220, -1, 214, - -1, 220, -1, 214, -1, 3, -1, 4, -1, 5, - -1, 220, -1, 216, -1, 214, -1, 216, 214, -1, - 3, -1, 4, -1, 3, -1, 4, -1, 5, -1, - 3, -1, 4, -1, -1 -}; - -/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 234, 234, 235, 236, 237, 248, 249, 265, 266, - 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, - 277, 278, 279, 280, 281, 282, 283, 294, 317, 332, - 331, 359, 360, 706, 718, 717, 744, 745, 840, 847, - 868, 867, 907, 908, 920, 919, 931, 930, 976, 980, - 984, 988, 993, 992, 1008, 1007, 1022, 1023, 1024, 1032, - 1051, 1055, 1059, 1067, 1075, 1083, 1091, 1099, 1116, 1117, - 1131, 1135, 1143, 1161, 1165, 1173, 1192, 1191, 1217, 1216, - 1238, 1237, 1262, 1263, 1408, 1420, 1425, 1432, 1438, 1451, - 1458, 1471, 1477, 1483, 1489, 1494, 1500, 1501, 1502, 1503, - 1504, 1513, 1527, 1557, 1558, 1581, 1580, 1597, 1596, 1617, - 1618, 1619, 1620, 1621, 1622, 1623, 1624, 1625, 1626, 1627, - 1628, 1629, 1633, 1640, 1647, 1654, 1661, 1668, 1675, 1682, - 1689, 1696, 1703, 1707, 1708, 1720, 1724, 1725, 1747, 1780, - 1784, 1809, 1821, 1826, 1844, 1843, 1863, 1862, 1882, 1881, - 1901, 1900, 1920, 1919, 1939, 1938, 1958, 1957, 1977, 1976, - 1996, 1997, 1998, 2009, 2011, 2010, 2031, 2032, 2033, 2034, - 2035, 2036, 2037, 2038, 2113, 2114, 2115, 2116, 2117, 2118, - 2119, 2120, 2121, 2122, 2123, 2124, 2130, 2131, 2197, 2198, - 2199, 2200, 2201, 2202, 2203, 2204, 2205, 2206, 2207, 2278, - 2295, 2335, 2352, 2356, 2364, 2382, 2386, 2394, 2412, 2428, - 2459, 2477, 2497, 2517, 2522, 2533, 2539, 2558, 2574, 2596, - 2595, 2619, 2618, 2639, 2640, 2644, 2648, 2652, 2656, 2672, - 2671, 2692, 2693, 2705, 2720, 2719, 2740, 2741, 2758, 2773, - 2772, 2793, 2794, 2809, 2825, 2824, 2845, 2846, 2870, 2874, - 2888, 2892, 2906, 2927, 2948, 2973, 2994, 2999, 3011, 3023, - 3028, 3039, 3043, 3057, 3061, 3065, 3078, 3082, 3098, 3102, - 3116, 3117, 3132, 3138, 3144, 3161, 3169, 3175 + 0, 239, 239, 240, 241, 242, 253, 254, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 299, 322, 337, + 336, 364, 365, 720, 732, 731, 758, 759, 880, 887, + 908, 907, 947, 948, 960, 959, 971, 970, 1016, 1020, + 1024, 1028, 1033, 1032, 1048, 1047, 1062, 1063, 1064, 1072, + 1091, 1095, 1099, 1107, 1115, 1123, 1131, 1139, 1147, 1164, + 1165, 1179, 1183, 1191, 1209, 1213, 1221, 1240, 1239, 1265, + 1264, 1286, 1285, 1310, 1311, 1459, 1471, 1476, 1483, 1489, + 1502, 1509, 1522, 1528, 1534, 1540, 1545, 1551, 1552, 1553, + 1554, 1555, 1564, 1578, 1608, 1609, 1632, 1631, 1648, 1647, + 1668, 1669, 1670, 1671, 1672, 1673, 1674, 1675, 1676, 1677, + 1678, 1679, 1680, 1684, 1691, 1698, 1705, 1712, 1719, 1726, + 1733, 1740, 1747, 1754, 1758, 1759, 1771, 1775, 1776, 1798, + 1831, 1835, 1860, 1872, 1877, 1895, 1894, 1914, 1913, 1933, + 1932, 1952, 1951, 1971, 1970, 1990, 1989, 2009, 2008, 2028, + 2027, 2047, 2048, 2049, 2060, 2062, 2061, 2082, 2083, 2084, + 2085, 2086, 2087, 2088, 2089, 2164, 2165, 2166, 2167, 2168, + 2169, 2170, 2171, 2172, 2173, 2174, 2175, 2181, 2182, 2248, + 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258, + 2329, 2346, 2386, 2403, 2407, 2415, 2433, 2437, 2445, 2463, + 2479, 2510, 2528, 2548, 2568, 2573, 2584, 2590, 2609, 2625, + 2647, 2646, 2670, 2669, 2690, 2691, 2695, 2699, 2703, 2707, + 2723, 2722, 2743, 2744, 2756, 2771, 2770, 2791, 2792, 2809, + 2824, 2823, 2844, 2845, 2860, 2876, 2875, 2896, 2897, 2921, + 2925, 2939, 2943, 2957, 2978, 2999, 3024, 3045, 3050, 3062, + 3074, 3079, 3090, 3094, 3108, 3112, 3116, 3129, 3133, 3149, + 3153, 3167, 3168, 3183, 3189, 3195, 3212, 3220, 3226 }; #endif -#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +#if YYDEBUG || YYERROR_VERBOSE || 0 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = @@ -972,13 +831,13 @@ static const char *const yytname[] = "texture_name", "material_name", "vertex_pool_name", "group_name", "required_name", "optional_name", "required_string", "optional_string", "string", "repeated_string", "repeated_string_body", "real", - "real_or_string", "integer", "empty", 0 + "real_or_string", "integer", "empty", YY_NULLPTR }; #endif # ifdef YYPRINT -/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to - token YYLEX-NUM. */ +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, @@ -994,157 +853,202 @@ static const yytype_uint16 yytoknum[] = }; # endif -/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ -static const yytype_uint8 yyr1[] = +#define YYPACT_NINF -430 + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-430))) + +#define YYTABLE_NINF -1 + +#define yytable_value_is_error(Yytable_value) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = { - 0, 94, 95, 95, 95, 95, 96, 96, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 98, 99, 101, - 100, 102, 102, 102, 104, 103, 105, 105, 106, 106, - 108, 107, 109, 109, 111, 110, 112, 110, 113, 113, - 113, 113, 114, 113, 115, 113, 113, 113, 113, 113, - 116, 116, 116, 116, 116, 116, 116, 116, 117, 117, - 118, 118, 118, 119, 119, 119, 121, 120, 123, 122, - 125, 124, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 127, 128, 128, 130, 129, 132, 131, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 134, 135, 136, 137, 138, 139, 140, 141, - 142, 143, 144, 145, 145, 146, 147, 147, 148, 149, - 149, 150, 151, 151, 153, 152, 155, 154, 157, 156, - 159, 158, 161, 160, 163, 162, 165, 164, 167, 166, - 168, 168, 168, 169, 170, 169, 169, 169, 169, 169, - 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, - 171, 171, 171, 171, 171, 171, 171, 171, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 173, - 174, 175, 176, 176, 176, 177, 177, 177, 178, 179, - 180, 181, 182, 183, 183, 184, 184, 185, 186, 188, - 187, 190, 189, 191, 191, 191, 191, 191, 191, 193, - 192, 194, 194, 194, 196, 195, 197, 197, 197, 199, - 198, 200, 200, 200, 202, 201, 203, 203, 204, 204, - 205, 205, 206, 207, 208, 209, 210, 210, 211, 212, - 212, 213, 213, 214, 214, 214, 215, 215, 216, 216, - 217, 217, 218, 218, 218, 219, 219, 220 + 211, -430, -430, -430, -430, 14, 1060, -430, 1206, -430, + 141, -430, 495, -430, -430, -430, -430, -430, 329, 329, + -55, 329, 329, 329, 329, 329, 329, 329, 329, 329, + 329, 329, 329, 329, 329, 329, 329, -430, -430, -430, + -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, + -430, -430, -430, -430, -430, -430, -430, 70, 10, 22, + 329, 68, 114, -430, 119, 125, 134, 329, 150, 152, + 329, 164, -430, 169, -430, -430, -430, -430, -430, 329, + -430, 172, 185, 174, 175, 180, 329, 182, 191, 192, + -430, -430, -430, -430, -430, 203, 329, 215, -430, -430, + -430, -430, 220, -430, -430, -430, -430, -430, -430, -430, + -430, 226, -430, -430, -430, 329, 329, 255, 228, 347, + 374, 230, 185, 329, 329, 235, 185, 99, 236, 185, + 243, -430, 244, 185, -430, -430, 247, 329, 255, 255, + 249, 329, 329, -430, 253, 329, 57, -430, -430, 329, + 261, 273, 274, 279, -430, 283, 290, 300, 303, 313, + 323, 329, 326, 331, 333, 334, 184, -430, -430, 255, + 329, 188, 210, 251, 254, -430, 262, 287, 328, -430, + 385, 335, 337, 338, 329, 345, -430, 185, -430, 385, + 346, -430, -430, 348, -430, -430, 29, 255, 23, 255, + 385, 349, -430, 352, -430, -430, 131, -430, -430, 353, + 329, -430, -430, 356, -430, -430, -430, -430, -430, -430, + -430, -430, -430, -430, -430, -430, -430, -430, -430, 329, + -430, 255, -430, -430, -430, -430, -430, -430, 381, -430, + -430, -430, -430, -430, -430, -430, 361, -430, 255, -430, + 362, -430, 441, 170, -430, -430, 365, -430, -430, -430, + 69, -430, 255, 73, -430, 255, 366, -430, -430, 369, + 74, -430, -430, -430, -430, 789, 983, 1133, 221, 140, + -430, 145, -430, 167, -430, 288, 375, 435, 189, -430, + -430, 827, 864, 75, -430, 376, 378, 4, -430, 380, + 383, 386, 389, 391, 393, 396, 397, -430, -430, -430, + -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, + -430, 255, -430, -430, 398, 329, -430, 59, -430, 329, + 399, -430, 329, 400, 255, -430, 329, 329, -430, -430, + -430, -430, -430, 329, -430, 406, 407, 409, 414, 418, + 420, 329, 425, 426, -430, -430, 427, 429, 434, 437, + 438, 329, 439, 440, 443, 446, 447, -430, -430, -430, + -430, -430, -430, 329, 329, 329, 329, -430, -430, -430, + -430, -430, -430, 123, -430, -430, 185, -430, -430, -430, + -430, -430, -430, 255, 255, 255, 255, 255, 255, 255, + 255, 76, 329, 448, 449, 450, -430, 255, 255, 255, + 255, -430, 451, -430, 453, 454, 185, -430, 329, 255, + 185, 255, 456, 329, 329, 185, 329, 255, 185, 255, + 457, 329, 329, -430, -430, -430, -430, -430, -430, -430, + -430, 458, -430, 459, 255, -430, 461, 255, -430, 7, + 462, 463, 469, 44, 255, 471, 80, 472, 385, 255, + 255, 255, 255, 255, 255, 476, 385, 385, 477, 478, + 255, -430, 479, 32, 480, -430, 86, 385, 483, 485, + 488, 490, 43, 491, 185, 92, 385, 492, 493, 53, + -430, 494, 255, 497, 255, 499, 500, 501, 506, 255, + 508, -430, 255, -430, 255, -430, 255, -430, -430, -430, + -430, 47, 52, 255, 512, 513, 514, 55, 97, 255, + 255, 255, 255, -430, 515, 516, -430, -430, -430, -430, + -430, -430, -430, 518, -430, -430, -430, -430, -430, -430, + -430, -430, 519, -430, -430, 522, -430, -430, -430, -430, + -430, -430, -430, 263, 255, 255, 255, 255, 255, -430, + 523, -430, 524, 255, 255, -430, -430, -430, -430, 525, + 526, 255, 255, -430, -430, -430, -430, -430, 223, 157, + -430, 162, -430, 138, -430, 79, 528, 531, 329, 329, + -430, 255, 269, 255, 255, 533, -430, -430, 255, 255, + -430, -430, 536, 537, 61, -430, -430, 329, 532, -430, + 329, 540, -430, 329, -430, -430, 329, 541, 255, 255, + 542, 543, 255, -430, 255, 255, -430, 544, 255, -430, + -430, -430, -430, 546, -430, 547, -430, 548, 255, 255, + 83, 255, 100, 255, -430, -430, -430, 255, 255, 550, + 552, 385, 54, 385, 56, 385, 255, 255, 82, -430, + 255, 88, -430, 255, 255, 255, 255, 255, -430, 554, + 555, -430, 556, -430, 560, 255, 255, 329, 564, -430, + 329, 565, 255, 77, 255, 568, 255, 255, 255, -430, + -430, -430, -430, 569, 571, 255, 255, 255, 255, -430, + 566, 91, 573, -430, 255, -430, 255, -430, 255, -430, + -430, 255, 255, 255, 255, 255, 329, 580, 255, -430, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + -430, 255, 584, 587, 255, 255, 255, 255, 255, 255, + 255, -430, -430, 588, 589, 593, 58, 63, 66, 255, + -430, -430, -430, -430, 595, -430, 599, -430, 600, 255, + -430, -430, -430, 255, -430 }; -/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ -static const yytype_uint8 yyr2[] = -{ - 0, 2, 2, 2, 2, 2, 1, 2, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 4, 5, 0, - 7, 1, 6, 2, 0, 6, 1, 6, 5, 6, - 0, 6, 1, 2, 0, 5, 0, 6, 1, 2, - 3, 4, 0, 7, 0, 7, 5, 5, 8, 8, - 2, 3, 7, 7, 7, 8, 7, 8, 0, 4, - 3, 8, 8, 4, 9, 9, 0, 6, 0, 6, - 0, 6, 1, 6, 5, 7, 7, 5, 5, 5, - 5, 5, 5, 5, 6, 5, 2, 2, 2, 2, - 5, 2, 1, 1, 2, 0, 5, 0, 5, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 5, 6, 4, 4, 4, 4, 7, 5, - 6, 4, 4, 1, 9, 4, 1, 16, 9, 1, - 6, 4, 11, 12, 0, 6, 0, 6, 0, 6, - 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, - 1, 5, 5, 1, 0, 7, 5, 5, 5, 2, - 5, 5, 5, 6, 1, 5, 5, 5, 2, 5, - 5, 5, 5, 5, 5, 2, 5, 6, 1, 5, - 5, 5, 2, 5, 5, 5, 5, 5, 6, 1, - 1, 1, 3, 8, 8, 4, 9, 9, 1, 8, - 2, 1, 1, 1, 5, 1, 2, 1, 1, 0, - 6, 0, 6, 1, 2, 2, 2, 2, 2, 0, - 6, 1, 6, 5, 0, 6, 1, 6, 5, 0, - 6, 1, 6, 2, 0, 6, 1, 6, 1, 2, - 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, - 1, 1, 1, 1, 1, 1, 1, 0 -}; - -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero - means the default is an error. */ + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ static const yytype_uint16 yydefact[] = { - 0, 277, 277, 277, 277, 0, 2, 6, 3, 82, - 4, 31, 5, 163, 1, 263, 264, 265, 277, 277, - 0, 277, 277, 277, 277, 277, 277, 277, 277, 277, - 277, 277, 277, 277, 277, 277, 277, 7, 8, 9, + 0, 278, 278, 278, 278, 0, 2, 6, 3, 83, + 4, 31, 5, 164, 1, 264, 265, 266, 278, 278, + 0, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 278, 278, 278, 278, 278, 278, 278, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 0, 0, 0, - 277, 0, 0, 107, 0, 0, 0, 277, 0, 0, - 277, 0, 105, 0, 101, 96, 97, 98, 99, 277, - 33, 0, 0, 0, 0, 0, 277, 0, 0, 0, - 169, 244, 258, 262, 261, 0, 277, 0, 76, 78, - 80, 154, 0, 257, 256, 158, 156, 150, 152, 144, - 219, 0, 146, 148, 40, 277, 0, 0, 0, 0, - 0, 0, 0, 277, 277, 0, 0, 0, 0, 0, - 0, 277, 0, 0, 275, 276, 0, 277, 0, 0, - 0, 277, 277, 277, 0, 277, 0, 260, 259, 277, + 278, 0, 0, 108, 0, 0, 0, 278, 0, 0, + 278, 0, 106, 0, 102, 97, 98, 99, 100, 278, + 33, 0, 0, 0, 0, 0, 278, 0, 0, 0, + 170, 245, 259, 263, 262, 0, 278, 0, 77, 79, + 81, 155, 0, 258, 257, 159, 157, 151, 153, 145, + 220, 0, 147, 149, 40, 278, 0, 0, 0, 0, + 0, 0, 0, 278, 278, 0, 0, 0, 0, 0, + 0, 278, 0, 0, 276, 277, 0, 278, 0, 0, + 0, 278, 278, 278, 0, 278, 0, 261, 260, 278, 0, 0, 0, 0, 34, 0, 0, 0, 0, 0, - 0, 277, 0, 0, 0, 0, 0, 270, 271, 0, - 0, 0, 0, 0, 0, 277, 0, 0, 0, 255, - 0, 0, 0, 0, 277, 0, 277, 277, 248, 0, - 0, 208, 164, 0, 201, 253, 0, 0, 0, 0, - 0, 0, 200, 0, 199, 252, 0, 277, 268, 0, - 267, 266, 27, 0, 277, 277, 277, 277, 277, 277, - 277, 277, 277, 277, 277, 29, 277, 277, 277, 277, - 84, 0, 277, 102, 90, 89, 88, 87, 0, 109, - 93, 92, 100, 272, 273, 274, 0, 91, 0, 141, - 0, 95, 0, 0, 249, 139, 0, 172, 277, 168, - 0, 170, 0, 0, 171, 0, 0, 167, 166, 0, - 0, 246, 28, 269, 38, 0, 0, 0, 0, 0, - 36, 0, 188, 0, 174, 0, 0, 0, 0, 223, - 277, 0, 0, 0, 42, 0, 0, 0, 103, 0, - 0, 0, 0, 0, 0, 0, 0, 108, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, - 83, 0, 94, 106, 0, 277, 32, 0, 160, 0, - 0, 202, 0, 0, 0, 173, 277, 277, 245, 77, - 79, 81, 155, 277, 35, 0, 0, 0, 0, 0, - 0, 277, 0, 0, 159, 192, 0, 0, 0, 0, - 0, 277, 0, 0, 0, 0, 0, 157, 185, 178, - 151, 153, 145, 277, 277, 277, 277, 220, 224, 225, - 226, 227, 228, 0, 147, 149, 44, 41, 43, 39, - 85, 86, 104, 277, 277, 0, 0, 0, 0, 0, - 0, 0, 277, 0, 0, 0, 165, 0, 0, 0, - 0, 205, 0, 254, 0, 0, 0, 277, 277, 0, - 0, 0, 0, 277, 277, 0, 277, 0, 0, 0, - 0, 277, 277, 277, 277, 277, 221, 229, 234, 239, - 30, 0, 46, 0, 0, 133, 0, 0, 136, 0, + 0, 278, 0, 0, 0, 0, 0, 271, 272, 0, + 0, 0, 0, 0, 0, 278, 0, 0, 0, 256, + 0, 0, 0, 0, 278, 0, 278, 278, 249, 0, + 0, 209, 165, 0, 202, 254, 0, 0, 0, 0, + 0, 0, 201, 0, 200, 253, 0, 278, 269, 0, + 268, 267, 27, 0, 278, 278, 278, 278, 278, 278, + 278, 278, 278, 278, 278, 29, 278, 278, 278, 278, + 85, 0, 278, 103, 91, 90, 89, 88, 0, 110, + 94, 93, 101, 273, 274, 275, 0, 92, 0, 142, + 0, 96, 0, 0, 250, 140, 0, 173, 278, 169, + 0, 171, 0, 0, 172, 0, 0, 168, 167, 0, + 0, 247, 28, 270, 38, 0, 0, 0, 0, 0, + 36, 0, 189, 0, 175, 0, 0, 0, 0, 224, + 278, 0, 0, 0, 42, 0, 0, 0, 104, 0, + 0, 0, 0, 0, 0, 0, 0, 109, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 84, 0, 95, 107, 0, 278, 32, 0, 161, 0, + 0, 203, 0, 0, 0, 174, 278, 278, 246, 78, + 80, 82, 156, 278, 35, 0, 0, 0, 0, 0, + 0, 278, 0, 0, 160, 193, 0, 0, 0, 0, + 0, 278, 0, 0, 0, 0, 0, 158, 186, 179, + 152, 154, 146, 278, 278, 278, 278, 221, 225, 226, + 227, 228, 229, 0, 148, 150, 44, 41, 43, 39, + 86, 87, 105, 278, 278, 0, 0, 0, 0, 0, + 0, 0, 278, 0, 0, 0, 166, 0, 0, 0, + 0, 206, 0, 255, 0, 0, 0, 278, 278, 0, + 0, 0, 0, 278, 278, 0, 278, 0, 0, 0, + 0, 278, 278, 278, 278, 278, 222, 230, 235, 240, + 30, 0, 46, 0, 0, 134, 0, 0, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 218, 250, 0, 0, 0, 217, 0, 0, 0, 0, + 219, 251, 0, 0, 0, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 213, 0, 211, 0, 212, 0, 0, 0, 0, 0, - 0, 132, 0, 135, 0, 124, 0, 125, 126, 127, - 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 209, 0, 0, 195, 197, 251, 191, - 193, 196, 194, 0, 190, 189, 181, 177, 179, 182, - 210, 180, 0, 176, 175, 0, 186, 183, 184, 277, - 277, 277, 277, 0, 48, 0, 0, 0, 0, 129, - 0, 122, 0, 0, 0, 138, 140, 161, 162, 0, - 0, 0, 0, 247, 37, 198, 187, 277, 0, 0, - 231, 0, 236, 0, 241, 0, 0, 0, 277, 277, - 45, 49, 0, 0, 0, 0, 130, 123, 0, 0, - 204, 203, 0, 0, 0, 215, 222, 277, 0, 230, - 277, 0, 235, 277, 240, 243, 0, 0, 0, 0, - 0, 0, 50, 47, 0, 0, 128, 0, 0, 207, - 206, 214, 216, 0, 277, 0, 277, 0, 0, 0, + 214, 0, 212, 0, 213, 0, 0, 0, 0, 0, + 0, 133, 0, 136, 0, 125, 0, 126, 127, 128, + 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 210, 0, 0, 196, 198, 252, 192, + 194, 197, 195, 0, 191, 190, 182, 178, 180, 183, + 211, 181, 0, 177, 176, 0, 187, 184, 185, 278, + 278, 278, 278, 0, 48, 0, 0, 0, 0, 130, + 0, 123, 0, 0, 0, 139, 141, 162, 163, 0, + 0, 0, 0, 248, 37, 199, 188, 278, 0, 0, + 232, 0, 237, 0, 242, 0, 0, 0, 278, 278, + 45, 49, 0, 0, 0, 0, 131, 124, 0, 0, + 205, 204, 0, 0, 0, 216, 223, 278, 0, 231, + 278, 0, 236, 278, 241, 244, 0, 0, 0, 0, + 0, 0, 50, 47, 0, 0, 129, 0, 0, 208, + 207, 215, 217, 0, 278, 0, 278, 0, 0, 0, 0, 0, 0, 0, 52, 54, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, - 0, 0, 57, 0, 0, 68, 0, 0, 142, 0, - 0, 233, 0, 238, 0, 0, 0, 0, 0, 70, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, - 232, 237, 242, 0, 0, 0, 0, 0, 0, 73, - 0, 0, 0, 53, 60, 55, 0, 134, 0, 59, + 0, 0, 57, 0, 0, 69, 0, 0, 143, 0, + 0, 234, 0, 239, 0, 0, 0, 0, 0, 71, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, + 233, 238, 243, 0, 0, 0, 0, 0, 0, 74, + 0, 0, 0, 53, 60, 55, 0, 135, 0, 59, 58, 0, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 72, 71, 0, 0, 0, 0, 0, 0, 0, - 75, 74, 63, 66, 0, 64, 0, 62, 0, 67, - 65, 0, 137 + 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 73, 72, 0, 0, 0, 0, 0, 0, 0, + 76, 75, 64, 67, 0, 65, 0, 62, 0, 0, + 68, 66, 63, 0, 138 }; -/* YYDEFGOTO[NTERM-NUM]. */ + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -430, -430, -430, 160, -430, -430, -430, -430, -106, -430, + -430, -430, -430, -430, -430, -430, -430, -430, -430, -242, + -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, + -430, -430, 193, -430, -430, -9, -430, -430, -430, 341, + -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, + -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, + -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, + -430, -430, -279, -430, -430, 137, -430, -430, -430, -200, + -184, -177, -296, -291, -297, -154, -430, -430, -430, -430, + -430, -430, -430, -285, -430, -430, -430, -76, -50, -430, + -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, + 408, -429, -430, -430, 213, -430, 252, 11, -96, -430, + -6, 359, -430, 3, -169, -14, 752 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 5, 6, 74, 38, 39, 40, 290, 10, 41, @@ -1162,431 +1066,297 @@ static const yytype_int16 yydefgoto[] = 103, 209, 210, 528, 246, 191, 104 }; -/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ -#define YYPACT_NINF -430 -static const yytype_int16 yypact[] = -{ - 245, -430, -430, -430, -430, 14, 794, -430, 1451, -430, - 168, -430, 535, -430, -430, -430, -430, -430, 271, 271, - -61, 271, 271, 271, 271, 271, 271, 271, 271, 271, - 271, 271, 271, 271, 271, 271, 271, -430, -430, -430, - -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, - -430, -430, -430, -430, -430, -430, -430, 9, -17, -12, - 271, 6, 10, -430, 12, 42, 46, 271, 74, 106, - 271, 125, -430, 155, -430, -430, -430, -430, -430, 271, - -430, 187, 228, 188, 190, 198, 271, 203, 204, 208, - -430, -430, -430, -430, -430, 209, 271, 219, -430, -430, - -430, -430, 223, -430, -430, -430, -430, -430, -430, -430, - -430, 227, -430, -430, -430, 271, 271, 255, 229, 342, - 345, 230, 228, 271, 271, 235, 228, 137, 236, 228, - 239, -430, 246, 228, -430, -430, 259, 271, 255, 255, - 261, 271, 271, -430, 267, 271, 57, -430, -430, 271, - 272, 273, 274, 279, -430, 281, 283, 284, 287, 288, - 289, 271, 290, 297, 298, 300, 127, -430, -430, 255, - 271, 224, 270, 302, 314, -430, 315, 322, 325, -430, - 353, 330, 301, 332, 271, 333, -430, 228, -430, 353, - 335, -430, -430, 338, -430, -430, 32, 255, 92, 255, - 353, 346, -430, 348, -430, -430, 128, -430, -430, 349, - 271, -430, -430, 352, -430, -430, -430, -430, -430, -430, - -430, -430, -430, -430, -430, -430, -430, -430, -430, 271, - -430, 255, -430, -430, -430, -430, -430, -430, 381, -430, - -430, -430, -430, -430, -430, -430, 356, -430, 255, -430, - 361, -430, 441, 164, -430, -430, 362, -430, -430, -430, - 69, -430, 255, 73, -430, 255, 365, -430, -430, 354, - 53, -430, -430, -430, -430, 975, 1184, 1378, 225, 70, - -430, 145, -430, 167, -430, 247, 336, 375, 95, -430, - -430, 435, 495, 134, -430, 366, 368, 44, -430, 377, - 380, 386, 389, 390, 391, 392, 393, -430, -430, -430, - -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, - -430, 255, -430, -430, 394, 271, -430, 37, -430, 271, - 396, -430, 271, 397, 255, -430, 271, 271, -430, -430, - -430, -430, -430, 271, -430, 398, 399, 400, 406, 407, - 409, 271, 414, 418, -430, -430, 420, 425, 426, 427, - 429, 271, 434, 437, 438, 439, 440, -430, -430, -430, - -430, -430, -430, 271, 271, 271, 271, -430, -430, -430, - -430, -430, -430, 153, -430, -430, 228, -430, -430, -430, - -430, -430, -430, 255, 255, 255, 255, 255, 255, 255, - 255, 66, 271, 443, 446, 447, -430, 255, 255, 255, - 255, -430, 448, -430, 450, 452, 228, -430, 271, 255, - 228, 255, 453, 271, 271, 228, 271, 255, 228, 255, - 454, 271, 271, -430, -430, -430, -430, -430, -430, -430, - -430, 456, -430, 457, 255, -430, 459, 255, -430, 4, - 461, 462, 463, 7, 255, 470, 257, 472, 353, 255, - 255, 255, 255, 255, 255, 476, 353, 353, 477, 478, - 255, -430, 479, 43, 480, -430, 97, 353, 485, 488, - 490, 491, 83, 492, 228, 102, 353, 493, 496, 8, - -430, 497, 255, 498, 255, 471, 500, 503, 506, 255, - 508, -430, 255, -430, 255, -430, 255, -430, -430, -430, - -430, 47, 52, 255, 511, 512, 513, 96, 103, 255, - 255, 255, 255, -430, 514, 516, -430, -430, -430, -430, - -430, -430, -430, 518, -430, -430, -430, -430, -430, -430, - -430, -430, 519, -430, -430, 521, -430, -430, -430, -430, - -430, -430, -430, 250, 255, 255, 255, 255, 255, -430, - 522, -430, 523, 255, 255, -430, -430, -430, -430, 524, - 525, 255, 255, -430, -430, -430, -430, -430, 142, 179, - -430, 191, -430, 100, -430, 79, 527, 531, 271, 271, - -430, 255, 262, 255, 255, 533, -430, -430, 255, 255, - -430, -430, 536, 537, 76, -430, -430, 271, 532, -430, - 271, 540, -430, 271, -430, -430, 271, 541, 255, 255, - 542, 543, 255, -430, 255, 255, -430, 544, 255, -430, - -430, -430, -430, 546, -430, 547, -430, 548, 255, 255, - 101, 255, 108, 255, -430, -430, -430, 255, 255, 550, - 552, 353, 54, 353, 56, 353, 255, 255, 82, -430, - 255, 88, -430, 255, 255, 255, 255, 255, -430, 554, - 555, -430, 556, -430, 560, 255, 255, 271, 562, -430, - 271, 564, 255, 77, 255, 565, 255, 255, 255, -430, - -430, -430, -430, 568, 569, 255, 255, 255, 255, -430, - 572, 91, 573, -430, 255, -430, 255, -430, 255, -430, - -430, 255, 255, 255, 255, 255, 271, 580, 255, -430, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - -430, 255, 582, 583, 255, 255, 255, 255, 255, 255, - 255, -430, -430, 584, 587, 588, 58, 63, 589, 255, - -430, -430, -430, -430, 590, -430, 591, -430, 255, -430, - -430, 255, -430 -}; - -/* YYPGOTO[NTERM-NUM]. */ -static const yytype_int16 yypgoto[] = -{ - -430, -430, -430, 178, -430, -430, -430, -430, 64, -430, - -430, -430, -430, -430, -430, -430, -430, -430, -430, -117, - -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, - -430, -430, 146, -430, -430, -9, -430, -430, -430, 341, - -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, - -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, - -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, - -430, -430, -279, -430, -430, 86, -430, -430, -430, -310, - -263, -165, -292, -179, -204, -19, -430, -430, -430, -430, - -430, -430, -430, -285, -430, -430, -430, -16, -43, -430, - -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, - 530, -429, -430, -430, 147, -430, 760, 11, -96, -430, - -6, 430, -430, 3, -180, -14, 744 -}; - -/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -1 + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_uint16 yytable[] = { - 57, 80, 57, 378, 368, 492, 494, 167, 168, 256, + 57, 80, 57, 378, 368, 492, 494, 15, 16, 17, 167, 168, 93, 93, 14, 93, 93, 93, 93, 93, - 266, 93, 93, 93, 93, 93, 93, 177, 93, 93, - 95, 96, 97, 98, 99, 100, 101, 115, 105, 106, - 107, 108, 109, 110, 545, 112, 113, 15, 16, 17, + 256, 93, 93, 93, 93, 93, 93, 177, 93, 93, + 95, 266, 97, 98, 99, 100, 101, 96, 105, 106, + 107, 108, 109, 110, 263, 112, 113, 167, 168, 260, 167, 168, 260, 213, 93, 167, 168, 167, 168, 167, 168, 167, 168, 260, 93, 225, 167, 168, 136, 167, - 168, 118, 15, 16, 17, 116, 15, 16, 17, 404, - 117, 128, 15, 16, 17, 15, 16, 17, 700, 405, - 147, 15, 16, 17, 15, 16, 17, 505, 119, 701, - 510, 546, 120, 260, 122, 172, 174, 373, 176, 93, - 166, 337, 181, 263, 479, 185, 260, 147, 263, 27, - 169, 658, 488, 263, 263, 261, 165, 473, 343, 661, - 406, 134, 135, 295, 123, 482, 530, 391, 124, 208, - 559, 197, 199, 147, 702, 561, 338, 671, 455, 673, - 212, 753, 374, 345, 373, 147, 755, 374, 613, 32, - 478, 329, 182, 344, 233, 332, 126, 517, 487, 631, - 703, 616, 231, 254, 677, 356, 538, 346, 208, 269, - 680, 375, 376, 716, 37, 264, 347, 348, 377, 567, - 532, 349, 254, 614, 659, 541, 568, 350, 127, 374, - 262, 662, 265, 351, 273, 652, 32, 654, 357, 358, - 27, 79, 468, 359, 352, 324, 386, 129, 353, 360, - 230, 480, 325, 147, 72, 361, 79, 387, 375, 376, - 89, 134, 135, 81, 296, 606, 362, 607, 354, 72, - 363, 82, 476, 364, 608, 365, 440, 131, 366, 610, - 485, 321, 89, 472, 330, 81, 611, 333, 167, 168, - 367, 481, 355, 82, 369, 331, 83, 84, 334, 57, - 57, 57, 609, 585, 15, 16, 17, 85, 516, 133, - 137, 518, 138, 86, 612, 585, 524, 525, 83, 84, - 139, 392, 586, 378, 87, 141, 142, 533, 88, 85, - 143, 145, 587, 278, 586, 86, 542, 285, 286, 287, - 89, 149, 291, 292, 587, 154, 87, 234, 342, 161, - 88, 170, 175, 407, 401, 632, 409, 180, 184, 588, - 589, 186, 89, 1, 2, 3, 4, 411, 189, 514, - 370, 588, 589, 590, 81, 134, 135, 171, 134, 135, - 173, 192, 82, 200, 383, 623, 243, 244, 245, 207, - 275, 276, 277, 235, 214, 215, 216, 93, 93, 93, - 93, 217, 442, 219, 80, 220, 221, 83, 84, 222, - 223, 224, 226, 81, 436, 437, 438, 439, 85, 227, - 228, 82, 229, 248, 86, 236, 444, 447, 449, 450, - 451, 452, 453, 454, 456, 87, 475, 237, 240, 88, - 461, 462, 463, 464, 484, 241, 83, 84, 242, 299, - 300, 89, 197, 247, 199, 249, 251, 85, 257, 371, - 197, 259, 199, 86, 301, 302, 303, 304, 592, 267, - 305, 268, 272, 81, 87, 274, 336, 502, 88, 320, - 504, 82, 506, 306, 322, 326, 511, 512, 335, 389, - 89, 390, 197, 199, 519, 520, 521, 522, 372, 393, - 540, 670, 394, 672, 307, 674, 83, 84, 395, 299, - 300, 396, 397, 398, 399, 400, 402, 85, 408, 410, - 416, 417, 418, 86, 301, 302, 303, 304, 419, 420, - 305, 421, 554, 81, 87, 556, 423, 557, 88, 558, - 424, 82, 425, 306, 560, 562, 563, 426, 427, 428, - 89, 429, 569, 570, 571, 572, 431, 252, 384, 432, - 433, 434, 435, 578, 323, 458, 83, 84, 459, 460, - 615, 465, 466, 81, 467, 477, 486, 85, 499, 457, - 501, 82, 503, 86, 507, 508, 509, 591, 554, 593, - 594, 595, 513, 549, 87, 515, 598, 599, 88, 523, - 526, 527, 529, 531, 602, 603, 83, 84, 534, 617, - 89, 535, 93, 536, 537, 539, 543, 85, 385, 544, - 547, 548, 550, 86, 622, 551, 624, 625, 552, 620, - 555, 627, 628, 564, 87, 565, 566, 573, 88, 574, - 638, 575, 576, 577, 250, 596, 597, 600, 601, 618, - 89, 641, 643, 619, 634, 646, 626, 647, 648, 629, + 168, 118, 15, 16, 17, 260, 15, 16, 17, 167, + 168, 128, 15, 16, 17, 15, 16, 17, 700, 545, + 147, 15, 16, 17, 15, 16, 17, 391, 115, 701, + 505, 404, 116, 658, 27, 172, 174, 263, 176, 93, + 166, 405, 181, 263, 117, 185, 264, 147, 263, 468, + 169, 661, 261, 473, 182, 530, 165, 355, 480, 369, + 476, 482, 337, 295, 134, 135, 538, 510, 485, 208, + 559, 197, 199, 147, 702, 561, 546, 671, 567, 673, + 212, 753, 406, 345, 631, 147, 755, 386, 455, 757, + 119, 329, 514, 517, 233, 332, 37, 338, 387, 518, + 703, 616, 231, 254, 677, 356, 659, 346, 208, 532, + 680, 79, 269, 716, 383, 541, 347, 348, 134, 135, + 568, 349, 254, 662, 72, 374, 613, 350, 343, 79, + 262, 373, 265, 351, 273, 652, 120, 654, 357, 358, + 27, 122, 72, 359, 352, 607, 440, 123, 353, 360, + 610, 324, 608, 147, 479, 361, 124, 611, 325, 81, + 89, 614, 488, 344, 296, 373, 362, 82, 354, 478, + 363, 472, 126, 364, 127, 365, 374, 487, 366, 481, + 609, 321, 89, 32, 330, 612, 129, 333, 167, 168, + 367, 131, 83, 84, 133, 331, 137, 138, 334, 57, + 57, 57, 139, 85, 141, 375, 376, 230, 102, 86, + 374, 234, 377, 142, 143, 111, 585, 32, 114, 516, + 87, 392, 585, 378, 88, 145, 81, 524, 525, 1, + 2, 3, 4, 235, 82, 586, 89, 149, 533, 375, + 376, 586, 154, 592, 342, 587, 606, 542, 161, 125, + 170, 587, 175, 407, 401, 632, 409, 180, 184, 83, + 84, 132, 15, 16, 17, 186, 189, 411, 140, 192, + 85, 200, 588, 589, 236, 207, 86, 237, 588, 589, + 134, 135, 171, 214, 278, 240, 590, 87, 285, 286, + 287, 88, 623, 291, 292, 215, 216, 93, 93, 93, + 93, 217, 442, 89, 80, 219, 179, 134, 135, 173, + 241, 370, 220, 81, 436, 437, 438, 439, 243, 244, + 245, 82, 221, 202, 205, 222, 444, 447, 449, 450, + 451, 452, 453, 454, 456, 223, 475, 275, 276, 277, + 461, 462, 463, 464, 484, 224, 83, 84, 226, 299, + 300, 242, 197, 227, 199, 228, 229, 85, 247, 248, + 197, 249, 199, 86, 301, 302, 303, 304, 251, 257, + 305, 259, 267, 81, 87, 268, 272, 502, 88, 274, + 504, 82, 506, 306, 320, 322, 511, 512, 326, 335, + 89, 336, 197, 199, 519, 520, 521, 522, 371, 389, + 540, 390, 393, 578, 307, 394, 83, 84, 395, 299, + 300, 396, 670, 397, 672, 398, 674, 85, 399, 400, + 402, 408, 410, 86, 301, 302, 303, 304, 416, 417, + 305, 418, 554, 81, 87, 556, 419, 557, 88, 558, + 420, 82, 421, 306, 560, 562, 563, 423, 424, 425, + 89, 426, 569, 570, 571, 572, 427, 252, 372, 428, + 429, 431, 432, 615, 323, 433, 83, 84, 434, 435, + 458, 459, 460, 250, 465, 466, 467, 85, 477, 486, + 499, 206, 501, 86, 503, 507, 508, 591, 554, 593, + 594, 595, 509, 513, 87, 515, 598, 599, 88, 523, + 526, 527, 529, 531, 602, 603, 534, 403, 535, 617, + 89, 536, 93, 537, 539, 543, 544, 547, 413, 414, + 548, 549, 550, 551, 622, 415, 624, 625, 552, 620, + 555, 627, 628, 422, 564, 0, 565, 566, 573, 574, + 638, 575, 576, 430, 577, 457, 596, 597, 600, 601, + 618, 641, 643, 619, 634, 646, 626, 647, 648, 629, 630, 650, 636, 639, 644, 645, 0, 649, 651, 653, 655, 656, 657, 668, 660, 669, 663, 689, 690, 691, - 666, 667, 678, 692, 696, 681, 698, 0, 705, 675, - 676, 709, 710, 679, 715, 718, 682, 684, 686, 687, - 688, 695, 728, 206, 697, 741, 742, 750, 693, 694, - 751, 752, 757, 759, 760, 699, 0, 704, 0, 706, - 707, 708, 0, 0, 0, 717, 0, 0, 711, 712, + 666, 667, 678, 692, 413, 681, 696, 698, 715, 675, + 676, 705, 709, 679, 710, 718, 682, 684, 686, 687, + 688, 695, 728, 0, 697, 202, 205, 741, 693, 694, + 742, 750, 751, 202, 205, 699, 752, 704, 760, 706, + 707, 708, 761, 762, 0, 717, 0, 0, 711, 712, 713, 714, 0, 0, 0, 0, 0, 719, 0, 720, 727, 721, 0, 0, 722, 723, 724, 725, 726, 0, 0, 729, 0, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 0, 740, 0, 0, 743, 744, 745, - 746, 747, 748, 749, 0, 7, 9, 11, 13, 754, - 756, 0, 758, 0, 0, 0, 0, 0, 0, 0, - 0, 761, 94, 94, 762, 94, 94, 94, 94, 94, - 0, 94, 94, 94, 94, 94, 94, 0, 94, 94, - 0, 0, 0, 0, 0, 0, 102, 0, 0, 0, - 0, 0, 0, 111, 0, 0, 114, 15, 16, 17, - 18, 0, 0, 0, 94, 0, 0, 0, 0, 19, - 0, 20, 0, 0, 94, 0, 0, 0, 0, 0, - 0, 0, 21, 22, 0, 23, 0, 125, 24, 25, - 0, 26, 0, 0, 0, 0, 0, 27, 28, 132, - 148, 0, 29, 30, 31, 0, 140, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 32, 94, - 0, 0, 0, 33, 0, 0, 0, 148, 34, 35, - 0, 0, 0, 0, 0, 188, 0, 0, 36, 0, - 0, 0, 0, 0, 179, 0, 0, 188, 0, 211, - 0, 0, 0, 148, 0, 0, 0, 0, 0, 0, - 0, 202, 205, 0, 0, 148, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 239, - 0, 0, 0, 0, 0, 0, 0, 0, 211, 0, - 239, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 746, 747, 748, 749, 0, 0, 0, 0, 0, 754, + 756, 758, 759, 7, 9, 11, 13, 0, 0, 0, + 0, 0, 763, 0, 0, 0, 764, 0, 0, 0, + 94, 94, 0, 94, 94, 94, 94, 94, 0, 94, + 94, 94, 94, 94, 94, 0, 94, 94, 0, 0, + 0, 0, 15, 16, 17, 18, 0, 0, 58, 59, + 0, 0, 0, 60, 19, 0, 20, 0, 61, 0, + 0, 0, 94, 62, 0, 0, 0, 21, 22, 63, + 23, 0, 94, 24, 25, 0, 26, 0, 0, 64, + 0, 0, 27, 28, 65, 81, 0, 29, 30, 31, + 66, 621, 0, 82, 0, 0, 0, 67, 148, 0, + 0, 68, 69, 32, 0, 70, 0, 71, 33, 633, + 72, 0, 635, 34, 35, 637, 0, 94, 83, 84, + 0, 0, 81, 36, 73, 148, 0, 0, 0, 85, + 82, 0, 339, 188, 0, 86, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 188, 87, 211, 0, 0, + 88, 148, 0, 0, 0, 83, 84, 0, 0, 0, + 0, 0, 89, 148, 0, 0, 85, 0, 0, 0, + 384, 0, 86, 0, 0, 0, 0, 239, 0, 0, + 0, 0, 0, 87, 0, 0, 211, 88, 239, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, + 0, 0, 0, 0, 0, 0, 0, 385, 0, 271, + 0, 0, 0, 0, 0, 0, 9, 9, 9, 13, + 280, 282, 284, 13, 13, 13, 289, 0, 13, 13, + 294, 148, 0, 0, 298, 0, 15, 16, 17, 18, + 0, 0, 58, 59, 0, 0, 0, 60, 19, 0, + 20, 0, 61, 0, 0, 0, 0, 62, 0, 0, + 328, 21, 22, 63, 23, 0, 0, 24, 25, 0, + 26, 0, 0, 64, 0, 0, 27, 28, 65, 0, + 0, 29, 30, 31, 66, 0, 0, 0, 0, 0, + 0, 67, 11, 0, 0, 68, 69, 32, 0, 70, + 0, 71, 33, 0, 72, 0, 0, 34, 35, 0, + 0, 0, 0, 15, 16, 17, 18, 36, 73, 0, + 0, 0, 0, 0, 0, 19, 340, 20, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 21, 22, + 0, 23, 0, 0, 24, 25, 0, 26, 0, 0, + 0, 0, 0, 27, 28, 0, 0, 0, 29, 30, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 32, 94, 94, 94, 94, 33, + 0, 0, 0, 0, 34, 35, 15, 16, 17, 18, + 0, 0, 58, 59, 36, 445, 448, 60, 19, 0, + 20, 0, 61, 0, 0, 0, 0, 62, 0, 0, + 0, 21, 22, 63, 23, 0, 0, 24, 25, 471, + 26, 0, 0, 64, 0, 0, 27, 28, 65, 0, + 0, 29, 30, 31, 66, 490, 471, 471, 0, 0, + 0, 67, 0, 0, 0, 68, 69, 32, 0, 70, + 0, 71, 33, 0, 72, 0, 0, 34, 35, 15, + 16, 17, 18, 0, 0, 58, 59, 36, 73, 0, + 60, 19, 0, 20, 0, 61, 341, 0, 0, 0, + 62, 0, 0, 0, 21, 22, 63, 23, 0, 0, + 24, 25, 0, 26, 0, 0, 64, 0, 0, 27, + 28, 65, 0, 0, 29, 30, 31, 66, 0, 0, + 0, 0, 0, 0, 67, 0, 0, 0, 68, 69, + 32, 0, 70, 0, 71, 33, 0, 72, 0, 0, + 34, 35, 0, 0, 0, 0, 0, 0, 0, 0, + 36, 73, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 289, 580, 582, 584, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 271, 0, 0, 0, 0, 0, 0, 9, 9, - 9, 13, 280, 282, 284, 13, 13, 13, 289, 0, - 13, 13, 294, 148, 0, 0, 298, 0, 15, 16, - 17, 18, 0, 0, 58, 59, 0, 0, 0, 60, - 19, 0, 20, 0, 61, 0, 0, 0, 0, 62, - 0, 0, 328, 21, 22, 63, 23, 0, 0, 24, - 25, 0, 26, 0, 0, 64, 0, 0, 27, 28, - 65, 0, 0, 29, 30, 31, 66, 0, 0, 0, - 0, 0, 0, 67, 11, 0, 0, 68, 69, 32, - 0, 70, 0, 71, 33, 0, 72, 0, 0, 34, - 35, 0, 0, 0, 0, 0, 0, 0, 0, 36, - 73, 0, 0, 0, 0, 0, 0, 0, 339, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 605, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 403, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 413, 414, 0, 0, - 0, 0, 0, 415, 0, 0, 0, 0, 0, 0, - 0, 422, 0, 0, 0, 0, 0, 94, 94, 94, - 94, 430, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 445, 448, 0, + 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 471, 413, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 490, 471, 471, - 0, 0, 0, 202, 205, 0, 0, 15, 16, 17, - 18, 202, 205, 58, 59, 0, 0, 0, 60, 19, - 0, 20, 0, 61, 0, 0, 0, 0, 62, 0, - 0, 0, 21, 22, 63, 23, 0, 0, 24, 25, - 0, 26, 0, 0, 64, 0, 0, 27, 28, 65, - 0, 0, 29, 30, 31, 66, 0, 0, 0, 0, - 0, 0, 67, 0, 0, 0, 68, 69, 32, 0, - 70, 0, 71, 33, 0, 72, 0, 0, 34, 35, - 0, 0, 0, 0, 0, 0, 0, 0, 36, 73, - 0, 0, 0, 0, 0, 0, 0, 340, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 289, 580, 582, 584, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 605, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 621, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 633, 0, 0, - 635, 0, 0, 637, 0, 0, 0, 0, 471, 0, - 471, 15, 16, 17, 18, 0, 0, 58, 59, 0, - 0, 0, 60, 19, 0, 20, 0, 61, 0, 0, - 0, 0, 62, 0, 0, 0, 21, 22, 63, 23, - 0, 0, 24, 25, 0, 26, 0, 0, 64, 0, - 0, 27, 28, 65, 0, 0, 29, 30, 31, 66, - 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, - 68, 69, 32, 0, 70, 0, 71, 33, 0, 72, - 0, 0, 34, 35, 15, 16, 17, 18, 0, 0, - 58, 59, 36, 73, 0, 60, 19, 0, 20, 0, - 61, 341, 0, 0, 0, 62, 0, 0, 0, 21, - 22, 63, 23, 0, 0, 24, 25, 0, 26, 0, - 0, 64, 0, 0, 27, 28, 65, 0, 0, 29, - 30, 31, 66, 0, 0, 0, 0, 0, 0, 67, - 0, 0, 0, 68, 69, 32, 0, 70, 0, 71, - 33, 0, 72, 0, 0, 34, 35, 0, 0, 0, - 0, 0, 0, 0, 0, 36, 73 + 0, 0, 0, 0, 0, 0, 471, 0, 471 }; static const yytype_int16 yycheck[] = { - 6, 10, 8, 288, 283, 434, 435, 3, 4, 189, + 6, 10, 8, 288, 283, 434, 435, 3, 4, 5, 3, 4, 18, 19, 0, 21, 22, 23, 24, 25, - 200, 27, 28, 29, 30, 31, 32, 123, 34, 35, - 19, 92, 21, 22, 23, 24, 25, 28, 27, 28, - 29, 30, 31, 32, 36, 34, 35, 3, 4, 5, + 189, 27, 28, 29, 30, 31, 32, 123, 34, 35, + 19, 200, 21, 22, 23, 24, 25, 92, 27, 28, + 29, 30, 31, 32, 21, 34, 35, 3, 4, 20, 3, 4, 20, 149, 60, 3, 4, 3, 4, 3, 4, 3, 4, 20, 70, 161, 3, 4, 82, 3, - 4, 60, 3, 4, 5, 92, 3, 4, 5, 42, - 92, 70, 3, 4, 5, 3, 4, 5, 11, 52, - 96, 3, 4, 5, 3, 4, 5, 93, 92, 22, - 93, 93, 92, 20, 92, 119, 120, 12, 122, 115, - 116, 58, 126, 21, 424, 129, 20, 123, 21, 43, - 117, 20, 432, 21, 21, 93, 115, 419, 58, 21, - 93, 3, 4, 229, 92, 427, 93, 93, 92, 145, - 93, 138, 139, 149, 67, 93, 93, 93, 82, 93, - 93, 93, 57, 8, 12, 161, 93, 57, 58, 64, - 423, 92, 25, 93, 170, 92, 92, 459, 431, 93, - 93, 92, 169, 187, 92, 8, 93, 32, 184, 51, - 92, 86, 87, 92, 6, 93, 41, 42, 93, 93, - 93, 46, 206, 93, 93, 93, 93, 52, 92, 57, - 197, 93, 199, 58, 210, 634, 64, 636, 41, 42, - 43, 58, 416, 46, 69, 51, 82, 92, 73, 52, - 93, 425, 58, 229, 71, 58, 58, 93, 86, 87, - 85, 3, 4, 8, 231, 93, 69, 58, 93, 71, - 73, 16, 421, 76, 65, 78, 93, 92, 81, 58, - 429, 248, 85, 418, 260, 8, 65, 263, 3, 4, - 93, 426, 281, 16, 283, 262, 41, 42, 265, 275, - 276, 277, 93, 23, 3, 4, 5, 52, 458, 92, - 92, 460, 92, 58, 93, 23, 466, 467, 41, 42, - 92, 297, 42, 578, 69, 92, 92, 477, 73, 52, - 92, 92, 52, 217, 42, 58, 486, 221, 222, 223, - 85, 92, 226, 227, 52, 92, 69, 93, 93, 92, - 73, 92, 92, 329, 321, 604, 332, 92, 92, 79, - 80, 92, 85, 88, 89, 90, 91, 334, 92, 82, - 93, 79, 80, 93, 8, 3, 4, 5, 3, 4, - 5, 92, 16, 92, 290, 93, 3, 4, 5, 92, - 214, 215, 216, 93, 92, 92, 92, 373, 374, 375, - 376, 92, 386, 92, 383, 92, 92, 41, 42, 92, - 92, 92, 92, 8, 373, 374, 375, 376, 52, 92, - 92, 16, 92, 92, 58, 93, 393, 394, 395, 396, - 397, 398, 399, 400, 401, 69, 420, 93, 93, 73, - 407, 408, 409, 410, 428, 93, 41, 42, 93, 38, - 39, 85, 419, 93, 421, 93, 93, 52, 93, 93, - 427, 93, 429, 58, 53, 54, 55, 56, 555, 93, - 59, 93, 93, 8, 69, 93, 92, 444, 73, 93, + 4, 60, 3, 4, 5, 20, 3, 4, 5, 3, + 4, 70, 3, 4, 5, 3, 4, 5, 11, 36, + 96, 3, 4, 5, 3, 4, 5, 93, 28, 22, + 93, 42, 92, 20, 43, 119, 120, 21, 122, 115, + 116, 52, 126, 21, 92, 129, 93, 123, 21, 416, + 117, 21, 93, 419, 25, 93, 115, 281, 425, 283, + 421, 427, 58, 229, 3, 4, 93, 93, 429, 145, + 93, 138, 139, 149, 67, 93, 93, 93, 93, 93, + 93, 93, 93, 8, 93, 161, 93, 82, 82, 93, + 92, 92, 82, 459, 170, 92, 6, 93, 93, 460, + 93, 92, 169, 187, 92, 8, 93, 32, 184, 93, + 92, 58, 51, 92, 290, 93, 41, 42, 3, 4, + 93, 46, 206, 93, 71, 57, 58, 52, 58, 58, + 197, 12, 199, 58, 210, 634, 92, 636, 41, 42, + 43, 92, 71, 46, 69, 58, 93, 92, 73, 52, + 58, 51, 65, 229, 424, 58, 92, 65, 58, 8, + 85, 93, 432, 93, 231, 12, 69, 16, 93, 423, + 73, 418, 92, 76, 92, 78, 57, 431, 81, 426, + 93, 248, 85, 64, 260, 93, 92, 263, 3, 4, + 93, 92, 41, 42, 92, 262, 92, 92, 265, 275, + 276, 277, 92, 52, 92, 86, 87, 93, 26, 58, + 57, 93, 93, 92, 92, 33, 23, 64, 36, 458, + 69, 297, 23, 578, 73, 92, 8, 466, 467, 88, + 89, 90, 91, 93, 16, 42, 85, 92, 477, 86, + 87, 42, 92, 555, 93, 52, 93, 486, 92, 67, + 92, 52, 92, 329, 321, 604, 332, 92, 92, 41, + 42, 79, 3, 4, 5, 92, 92, 334, 86, 92, + 52, 92, 79, 80, 93, 92, 58, 93, 79, 80, + 3, 4, 5, 92, 217, 93, 93, 69, 221, 222, + 223, 73, 93, 226, 227, 92, 92, 373, 374, 375, + 376, 92, 386, 85, 383, 92, 124, 3, 4, 5, + 93, 93, 92, 8, 373, 374, 375, 376, 3, 4, + 5, 16, 92, 141, 142, 92, 393, 394, 395, 396, + 397, 398, 399, 400, 401, 92, 420, 214, 215, 216, + 407, 408, 409, 410, 428, 92, 41, 42, 92, 38, + 39, 93, 419, 92, 421, 92, 92, 52, 93, 92, + 427, 93, 429, 58, 53, 54, 55, 56, 93, 93, + 59, 93, 93, 8, 69, 93, 93, 444, 73, 93, 447, 16, 449, 72, 93, 93, 453, 454, 93, 93, - 85, 93, 459, 460, 461, 462, 463, 464, 93, 92, - 484, 651, 92, 653, 93, 655, 41, 42, 92, 38, - 39, 92, 92, 92, 92, 92, 92, 52, 92, 92, + 85, 92, 459, 460, 461, 462, 463, 464, 93, 93, + 484, 93, 92, 549, 93, 92, 41, 42, 92, 38, + 39, 92, 651, 92, 653, 92, 655, 52, 92, 92, 92, 92, 92, 58, 53, 54, 55, 56, 92, 92, 59, 92, 499, 8, 69, 502, 92, 504, 73, 506, 92, 16, 92, 72, 511, 512, 513, 92, 92, 92, 85, 92, 519, 520, 521, 522, 92, 186, 93, 92, - 92, 92, 92, 549, 93, 92, 41, 42, 92, 92, - 583, 93, 92, 8, 92, 92, 92, 52, 92, 402, - 93, 16, 93, 58, 93, 93, 93, 554, 555, 556, - 557, 558, 92, 92, 69, 93, 563, 564, 73, 93, - 93, 93, 93, 93, 571, 572, 41, 42, 93, 585, - 85, 93, 588, 93, 93, 93, 93, 52, 93, 93, - 93, 93, 92, 58, 591, 92, 593, 594, 92, 588, - 92, 598, 599, 92, 69, 93, 93, 93, 73, 93, - 616, 93, 93, 92, 184, 93, 93, 93, 93, 92, - 85, 618, 619, 92, 92, 622, 93, 624, 625, 93, + 92, 92, 92, 583, 93, 92, 41, 42, 92, 92, + 92, 92, 92, 184, 93, 92, 92, 52, 92, 92, + 92, 143, 93, 58, 93, 93, 93, 554, 555, 556, + 557, 558, 93, 92, 69, 93, 563, 564, 73, 93, + 93, 93, 93, 93, 571, 572, 93, 325, 93, 585, + 85, 93, 588, 93, 93, 93, 93, 93, 336, 337, + 93, 92, 92, 92, 591, 343, 593, 594, 92, 588, + 92, 598, 599, 351, 92, -1, 93, 93, 93, 93, + 616, 93, 93, 361, 92, 402, 93, 93, 93, 93, + 92, 618, 619, 92, 92, 622, 93, 624, 625, 93, 93, 628, 92, 92, 92, 92, -1, 93, 92, 92, 92, 638, 639, 93, 641, 93, 643, 93, 93, 93, - 647, 648, 658, 93, 92, 661, 92, -1, 93, 656, - 657, 93, 93, 660, 92, 92, 663, 664, 665, 666, - 667, 677, 92, 143, 680, 93, 93, 93, 675, 676, - 93, 93, 93, 93, 93, 682, -1, 684, -1, 686, - 687, 688, -1, -1, -1, 701, -1, -1, 695, 696, + 647, 648, 658, 93, 402, 661, 92, 92, 92, 656, + 657, 93, 93, 660, 93, 92, 663, 664, 665, 666, + 667, 677, 92, -1, 680, 423, 424, 93, 675, 676, + 93, 93, 93, 431, 432, 682, 93, 684, 93, 686, + 687, 688, 93, 93, -1, 701, -1, -1, 695, 696, 697, 698, -1, -1, -1, -1, -1, 704, -1, 706, 716, 708, -1, -1, 711, 712, 713, 714, 715, -1, -1, 718, -1, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, -1, 731, -1, -1, 734, 735, 736, - 737, 738, 739, 740, -1, 1, 2, 3, 4, 746, - 747, -1, 749, -1, -1, -1, -1, -1, -1, -1, - -1, 758, 18, 19, 761, 21, 22, 23, 24, 25, - -1, 27, 28, 29, 30, 31, 32, -1, 34, 35, - -1, -1, -1, -1, -1, -1, 26, -1, -1, -1, - -1, -1, -1, 33, -1, -1, 36, 3, 4, 5, - 6, -1, -1, -1, 60, -1, -1, -1, -1, 15, - -1, 17, -1, -1, 70, -1, -1, -1, -1, -1, - -1, -1, 28, 29, -1, 31, -1, 67, 34, 35, - -1, 37, -1, -1, -1, -1, -1, 43, 44, 79, - 96, -1, 48, 49, 50, -1, 86, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 64, 115, - -1, -1, -1, 69, -1, -1, -1, 123, 74, 75, - -1, -1, -1, -1, -1, 131, -1, -1, 84, -1, - -1, -1, -1, -1, 124, -1, -1, 143, -1, 145, - -1, -1, -1, 149, -1, -1, -1, -1, -1, -1, - -1, 141, 142, -1, -1, 161, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 175, - -1, -1, -1, -1, -1, -1, -1, -1, 184, -1, - 186, 187, -1, -1, -1, -1, -1, -1, -1, -1, + 737, 738, 739, 740, -1, -1, -1, -1, -1, 746, + 747, 748, 749, 1, 2, 3, 4, -1, -1, -1, + -1, -1, 759, -1, -1, -1, 763, -1, -1, -1, + 18, 19, -1, 21, 22, 23, 24, 25, -1, 27, + 28, 29, 30, 31, 32, -1, 34, 35, -1, -1, + -1, -1, 3, 4, 5, 6, -1, -1, 9, 10, + -1, -1, -1, 14, 15, -1, 17, -1, 19, -1, + -1, -1, 60, 24, -1, -1, -1, 28, 29, 30, + 31, -1, 70, 34, 35, -1, 37, -1, -1, 40, + -1, -1, 43, 44, 45, 8, -1, 48, 49, 50, + 51, 589, -1, 16, -1, -1, -1, 58, 96, -1, + -1, 62, 63, 64, -1, 66, -1, 68, 69, 607, + 71, -1, 610, 74, 75, 613, -1, 115, 41, 42, + -1, -1, 8, 84, 85, 123, -1, -1, -1, 52, + 16, -1, 93, 131, -1, 58, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 143, 69, 145, -1, -1, + 73, 149, -1, -1, -1, 41, 42, -1, -1, -1, + -1, -1, 85, 161, -1, -1, 52, -1, -1, -1, + 93, -1, 58, -1, -1, -1, -1, 175, -1, -1, + -1, -1, -1, 69, -1, -1, 184, 73, 186, 187, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 85, + -1, -1, -1, -1, -1, -1, -1, 93, -1, 207, + -1, -1, -1, -1, -1, -1, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, -1, 226, 227, + 228, 229, -1, -1, 232, -1, 3, 4, 5, 6, + -1, -1, 9, 10, -1, -1, -1, 14, 15, -1, + 17, -1, 19, -1, -1, -1, -1, 24, -1, -1, + 258, 28, 29, 30, 31, -1, -1, 34, 35, -1, + 37, -1, -1, 40, -1, -1, 43, 44, 45, -1, + -1, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, 58, 290, -1, -1, 62, 63, 64, -1, 66, + -1, 68, 69, -1, 71, -1, -1, 74, 75, -1, + -1, -1, -1, 3, 4, 5, 6, 84, 85, -1, + -1, -1, -1, -1, -1, 15, 93, 17, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 28, 29, + -1, 31, -1, -1, 34, 35, -1, 37, -1, -1, + -1, -1, -1, 43, 44, -1, -1, -1, 48, 49, + 50, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 64, 373, 374, 375, 376, 69, + -1, -1, -1, -1, 74, 75, 3, 4, 5, 6, + -1, -1, 9, 10, 84, 393, 394, 14, 15, -1, + 17, -1, 19, -1, -1, -1, -1, 24, -1, -1, + -1, 28, 29, 30, 31, -1, -1, 34, 35, 417, + 37, -1, -1, 40, -1, -1, 43, 44, 45, -1, + -1, 48, 49, 50, 51, 433, 434, 435, -1, -1, + -1, 58, -1, -1, -1, 62, 63, 64, -1, 66, + -1, 68, 69, -1, 71, -1, -1, 74, 75, 3, + 4, 5, 6, -1, -1, 9, 10, 84, 85, -1, + 14, 15, -1, 17, -1, 19, 93, -1, -1, -1, + 24, -1, -1, -1, 28, 29, 30, 31, -1, -1, + 34, 35, -1, 37, -1, -1, 40, -1, -1, 43, + 44, 45, -1, -1, 48, 49, 50, 51, -1, -1, + -1, -1, -1, -1, 58, -1, -1, -1, 62, 63, + 64, -1, 66, -1, 68, 69, -1, 71, -1, -1, + 74, 75, -1, -1, -1, -1, -1, -1, -1, -1, + 84, 85, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 549, 550, 551, 552, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 207, -1, -1, -1, -1, -1, -1, 214, 215, - 216, 217, 218, 219, 220, 221, 222, 223, 224, -1, - 226, 227, 228, 229, -1, -1, 232, -1, 3, 4, - 5, 6, -1, -1, 9, 10, -1, -1, -1, 14, - 15, -1, 17, -1, 19, -1, -1, -1, -1, 24, - -1, -1, 258, 28, 29, 30, 31, -1, -1, 34, - 35, -1, 37, -1, -1, 40, -1, -1, 43, 44, - 45, -1, -1, 48, 49, 50, 51, -1, -1, -1, - -1, -1, -1, 58, 290, -1, -1, 62, 63, 64, - -1, 66, -1, 68, 69, -1, 71, -1, -1, 74, - 75, -1, -1, -1, -1, -1, -1, -1, -1, 84, - 85, -1, -1, -1, -1, -1, -1, -1, 93, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 577, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 325, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 336, 337, -1, -1, - -1, -1, -1, 343, -1, -1, -1, -1, -1, -1, - -1, 351, -1, -1, -1, -1, -1, 373, 374, 375, - 376, 361, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 393, 394, -1, + 588, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 417, 402, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 433, 434, 435, - -1, -1, -1, 423, 424, -1, -1, 3, 4, 5, - 6, 431, 432, 9, 10, -1, -1, -1, 14, 15, - -1, 17, -1, 19, -1, -1, -1, -1, 24, -1, - -1, -1, 28, 29, 30, 31, -1, -1, 34, 35, - -1, 37, -1, -1, 40, -1, -1, 43, 44, 45, - -1, -1, 48, 49, 50, 51, -1, -1, -1, -1, - -1, -1, 58, -1, -1, -1, 62, 63, 64, -1, - 66, -1, 68, 69, -1, 71, -1, -1, 74, 75, - -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, - -1, -1, -1, -1, -1, -1, -1, 93, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 549, 550, 551, 552, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 577, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 588, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 589, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 607, -1, -1, - 610, -1, -1, 613, -1, -1, -1, -1, 634, -1, - 636, 3, 4, 5, 6, -1, -1, 9, 10, -1, - -1, -1, 14, 15, -1, 17, -1, 19, -1, -1, - -1, -1, 24, -1, -1, -1, 28, 29, 30, 31, - -1, -1, 34, 35, -1, 37, -1, -1, 40, -1, - -1, 43, 44, 45, -1, -1, 48, 49, 50, 51, - -1, -1, -1, -1, -1, -1, 58, -1, -1, -1, - 62, 63, 64, -1, 66, -1, 68, 69, -1, 71, - -1, -1, 74, 75, 3, 4, 5, 6, -1, -1, - 9, 10, 84, 85, -1, 14, 15, -1, 17, -1, - 19, 93, -1, -1, -1, 24, -1, -1, -1, 28, - 29, 30, 31, -1, -1, 34, 35, -1, 37, -1, - -1, 40, -1, -1, 43, 44, 45, -1, -1, 48, - 49, 50, 51, -1, -1, -1, -1, -1, -1, 58, - -1, -1, -1, 62, 63, 64, -1, 66, -1, 68, - 69, -1, 71, -1, -1, 74, 75, -1, -1, -1, - -1, -1, -1, -1, -1, 84, 85 + -1, -1, -1, -1, -1, -1, 634, -1, 636 }; -/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing - symbol of state STATE-NUM. */ + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 88, 89, 90, 91, 95, 96, 220, 126, 220, @@ -1664,108 +1434,111 @@ static const yytype_uint8 yystos[] = 217, 217, 217, 217, 217, 217, 217, 214, 92, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 93, 93, 217, 217, 217, 217, 217, 217, 217, - 93, 93, 93, 93, 217, 93, 217, 93, 217, 93, - 93, 217, 217 + 93, 93, 93, 93, 217, 93, 217, 93, 217, 217, + 93, 93, 93, 217, 217 }; -#define yyerrok (yyerrstatus = 0) -#define yyclearin (yychar = YYEMPTY) -#define YYEMPTY (-2) -#define YYEOF 0 + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 94, 95, 95, 95, 95, 96, 96, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 98, 99, 101, + 100, 102, 102, 102, 104, 103, 105, 105, 106, 106, + 108, 107, 109, 109, 111, 110, 112, 110, 113, 113, + 113, 113, 114, 113, 115, 113, 113, 113, 113, 113, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 117, + 117, 118, 118, 118, 119, 119, 119, 121, 120, 123, + 122, 125, 124, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 127, 128, 128, 130, 129, 132, 131, + 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, + 133, 133, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 145, 146, 147, 147, 148, + 149, 149, 150, 151, 151, 153, 152, 155, 154, 157, + 156, 159, 158, 161, 160, 163, 162, 165, 164, 167, + 166, 168, 168, 168, 169, 170, 169, 169, 169, 169, + 169, 169, 169, 169, 169, 171, 171, 171, 171, 171, + 171, 171, 171, 171, 171, 171, 171, 171, 171, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 173, 174, 175, 176, 176, 176, 177, 177, 177, 178, + 179, 180, 181, 182, 183, 183, 184, 184, 185, 186, + 188, 187, 190, 189, 191, 191, 191, 191, 191, 191, + 193, 192, 194, 194, 194, 196, 195, 197, 197, 197, + 199, 198, 200, 200, 200, 202, 201, 203, 203, 204, + 204, 205, 205, 206, 207, 208, 209, 210, 210, 211, + 212, 212, 213, 213, 214, 214, 214, 215, 215, 216, + 216, 217, 217, 218, 218, 218, 219, 219, 220 +}; -#define YYACCEPT goto yyacceptlab -#define YYABORT goto yyabortlab -#define YYERROR goto yyerrorlab + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 2, 2, 2, 2, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4, 5, 0, + 7, 1, 6, 2, 0, 6, 1, 6, 5, 6, + 0, 6, 1, 2, 0, 5, 0, 6, 1, 2, + 3, 4, 0, 7, 0, 7, 5, 5, 8, 8, + 2, 3, 7, 8, 7, 7, 8, 7, 8, 0, + 4, 3, 8, 8, 4, 9, 9, 0, 6, 0, + 6, 0, 6, 1, 6, 5, 7, 7, 5, 5, + 5, 5, 5, 5, 5, 6, 5, 2, 2, 2, + 2, 5, 2, 1, 1, 2, 0, 5, 0, 5, + 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 5, 6, 4, 4, 4, 4, 7, + 5, 6, 4, 4, 1, 9, 4, 1, 16, 9, + 1, 6, 4, 11, 12, 0, 6, 0, 6, 0, + 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, + 6, 1, 5, 5, 1, 0, 7, 5, 5, 5, + 2, 5, 5, 5, 6, 1, 5, 5, 5, 2, + 5, 5, 5, 5, 5, 5, 2, 5, 6, 1, + 5, 5, 5, 2, 5, 5, 5, 5, 5, 6, + 1, 1, 1, 3, 8, 8, 4, 9, 9, 1, + 8, 2, 1, 1, 1, 5, 1, 2, 1, 1, + 0, 6, 0, 6, 1, 2, 2, 2, 2, 2, + 0, 6, 1, 6, 5, 0, 6, 1, 6, 5, + 0, 6, 1, 6, 2, 0, 6, 1, 6, 1, + 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 1, 0 +}; -/* Like YYERROR except do call yyerror. This remains here temporarily - to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. However, - YYFAIL appears to be in use. Nevertheless, it is formally deprecated - in Bison 2.4.2's NEWS entry, where a plan to phase it out is - discussed. */ +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab -#define YYFAIL goto yyerrlab -#if defined YYFAIL - /* This is here to suppress warnings from the GCC cpp's - -Wunused-macros. Normally we don't worry about that warning, but - some users do, and we want to make it easy for users to remove - YYFAIL uses, which will produce warnings from Bison 2.5. */ -#endif #define YYRECOVERING() (!!yyerrstatus) -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY && yylen == 1) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - yytoken = YYTRANSLATE (yychar); \ - YYPOPSTACK (1); \ - goto yybackup; \ - } \ - else \ - { \ +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ yyerror (YY_("syntax error: cannot back up")); \ - YYERROR; \ - } \ -while (YYID (0)) + YYERROR; \ + } \ +while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 -#define YYTERROR 1 -#define YYERRCODE 256 - - -/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. - If N is 0, then set CURRENT to the empty location which ends - the previous symbol: RHS[0] (always defined). */ - -#define YYRHSLOC(Rhs, K) ((Rhs)[K]) -#ifndef YYLLOC_DEFAULT -# define YYLLOC_DEFAULT(Current, Rhs, N) \ - do \ - if (YYID (N)) \ - { \ - (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ - (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ - (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ - (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ - } \ - else \ - { \ - (Current).first_line = (Current).last_line = \ - YYRHSLOC (Rhs, 0).last_line; \ - (Current).first_column = (Current).last_column = \ - YYRHSLOC (Rhs, 0).last_column; \ - } \ - while (YYID (0)) -#endif - - -/* YY_LOCATION_PRINT -- Print the location on the stream. - This macro was not mandated originally: define only if we know - we won't break user code: when these are the locations we know. */ - -#ifndef YY_LOCATION_PRINT -# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL -# define YY_LOCATION_PRINT(File, Loc) \ - fprintf (File, "%d.%d-%d.%d", \ - (Loc).first_line, (Loc).first_column, \ - (Loc).last_line, (Loc).last_column) -# else -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif -#endif - - -/* YYLEX -- calling `yylex' with the right arguments. */ - -#ifdef YYLEX_PARAM -# define YYLEX yylex (YYLEX_PARAM) -#else -# define YYLEX yylex () -#endif /* Enable debugging if requested. */ #if YYDEBUG @@ -1775,80 +1548,61 @@ while (YYID (0)) # define YYFPRINTF fprintf # endif -# define YYDPRINTF(Args) \ -do { \ - if (yydebug) \ - YYFPRINTF Args; \ -} while (YYID (0)) +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) -# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ -do { \ - if (yydebug) \ - { \ - YYFPRINTF (stderr, "%s ", Title); \ - yy_symbol_print (stderr, \ - Type, Value); \ - YYFPRINTF (stderr, "\n"); \ - } \ -} while (YYID (0)) - - -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ - -/*ARGSUSED*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) -#else -static void -yy_symbol_value_print (yyoutput, yytype, yyvaluep) - FILE *yyoutput; - int yytype; - YYSTYPE const * const yyvaluep; +/* This macro is provided for backward compatibility. */ +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep) { + FILE *yyoutput = yyo; + YYUSE (yyoutput); if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) - YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); -# else - YYUSE (yyoutput); + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); # endif - switch (yytype) - { - default: - break; - } + YYUSE (yytype); } -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) static void -yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) -#else -static void -yy_symbol_print (yyoutput, yytype, yyvaluep) - FILE *yyoutput; - int yytype; - YYSTYPE const * const yyvaluep; -#endif +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep) { - if (yytype < YYNTOKENS) - YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); - else - YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); - yy_symbol_value_print (yyoutput, yytype, yyvaluep); - YYFPRINTF (yyoutput, ")"); + yy_symbol_value_print (yyo, yytype, yyvaluep); + YYFPRINTF (yyo, ")"); } /*------------------------------------------------------------------. @@ -1856,16 +1610,8 @@ yy_symbol_print (yyoutput, yytype, yyvaluep) | TOP (included). | `------------------------------------------------------------------*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) -#else -static void -yy_stack_print (yybottom, yytop) - yytype_int16 *yybottom; - yytype_int16 *yytop; -#endif { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) @@ -1876,49 +1622,42 @@ yy_stack_print (yybottom, yytop) YYFPRINTF (stderr, "\n"); } -# define YY_STACK_PRINT(Bottom, Top) \ -do { \ - if (yydebug) \ - yy_stack_print ((Bottom), (Top)); \ -} while (YYID (0)) +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) static void -yy_reduce_print (YYSTYPE *yyvsp, int yyrule) -#else -static void -yy_reduce_print (yyvsp, yyrule) - YYSTYPE *yyvsp; - int yyrule; -#endif +yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule) { + unsigned long yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; - unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", - yyrule - 1, yylno); + yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); - yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], - &(yyvsp[(yyi + 1) - (yynrhs)]) - ); + yy_symbol_print (stderr, + yystos[yyssp[yyi + 1 - yynrhs]], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); YYFPRINTF (stderr, "\n"); } } -# define YY_REDUCE_PRINT(Rule) \ -do { \ - if (yydebug) \ - yy_reduce_print (yyvsp, Rule); \ -} while (YYID (0)) +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule); \ +} while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ @@ -1932,7 +1671,7 @@ int yydebug; /* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH +#ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif @@ -1947,7 +1686,6 @@ int yydebug; # define YYMAXDEPTH 10000 #endif - #if YYERROR_VERBOSE @@ -1956,15 +1694,8 @@ int yydebug; # define yystrlen strlen # else /* Return the length of YYSTR. */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) -#else -static YYSIZE_T -yystrlen (yystr) - const char *yystr; -#endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) @@ -1980,16 +1711,8 @@ yystrlen (yystr) # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) -#else -static char * -yystpcpy (yydest, yysrc) - char *yydest; - const char *yysrc; -#endif { char *yyd = yydest; const char *yys = yysrc; @@ -2019,192 +1742,192 @@ yytnamerr (char *yyres, const char *yystr) char const *yyp = yystr; for (;;) - switch (*++yyp) - { - case '\'': - case ',': - goto do_not_strip_quotes; + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; - case '\\': - if (*++yyp != '\\') - goto do_not_strip_quotes; - /* Fall through. */ - default: - if (yyres) - yyres[yyn] = *yyp; - yyn++; - break; + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; - case '"': - if (yyres) - yyres[yyn] = '\0'; - return yyn; - } + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); - return yystpcpy (yyres, yystr) - yyres; + return (YYSIZE_T) (yystpcpy (yyres, yystr) - yyres); } # endif -/* Copy into YYRESULT an error message about the unexpected token - YYCHAR while in state YYSTATE. Return the number of bytes copied, - including the terminating null byte. If YYRESULT is null, do not - copy anything; just return the number of bytes that would be - copied. As a special case, return 0 if an ordinary "syntax error" - message will do. Return YYSIZE_MAXIMUM if overflow occurs during - size calculation. */ -static YYSIZE_T -yysyntax_error (char *yyresult, int yystate, int yychar) +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) { - int yyn = yypact[yystate]; + YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; - if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) - return 0; - else + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) { - int yytype = YYTRANSLATE (yychar); - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - int yysize_overflow = 0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - int yyx; + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; -# if 0 - /* This is so xgettext sees the translatable formats that are - constructed on the fly. */ - YY_("syntax error, unexpected %s"); - YY_("syntax error, unexpected %s, expecting %s"); - YY_("syntax error, unexpected %s, expecting %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); -# endif - char *yyfmt; - char const *yyf; - static char const yyunexpected[] = "syntax error, unexpected %s"; - static char const yyexpecting[] = ", expecting %s"; - static char const yyor[] = " or %s"; - char yyformat[sizeof yyunexpected - + sizeof yyexpecting - 1 - + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) - * (sizeof yyor - 1))]; - char const *yyprefix = yyexpecting; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 1; - - yyarg[0] = yytname[yytype]; - yyfmt = yystpcpy (yyformat, yyunexpected); - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - yyformat[sizeof yyunexpected - 1] = '\0'; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - yyfmt = yystpcpy (yyfmt, yyprefix); - yyprefix = yyor; - } - - yyf = YY_(yyformat); - yysize1 = yysize + yystrlen (yyf); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - - if (yysize_overflow) - return YYSIZE_MAXIMUM; - - if (yyresult) - { - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - char *yyp = yyresult; - int yyi = 0; - while ((*yyp = *yyf) != '\0') - { - if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyf += 2; - } - else - { - yyp++; - yyf++; - } - } - } - return yysize; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; } #endif /* YYERROR_VERBOSE */ - /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ -/*ARGSUSED*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) -#else -static void -yydestruct (yymsg, yytype, yyvaluep) - const char *yymsg; - int yytype; - YYSTYPE *yyvaluep; -#endif { YYUSE (yyvaluep); - if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); - switch (yytype) - { - - default: - break; - } + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END } -/* Prevent warnings from -Wmissing-prototypes. */ -#ifdef YYPARSE_PARAM -#if defined __STDC__ || defined __cplusplus -int yyparse (void *YYPARSE_PARAM); -#else -int yyparse (); -#endif -#else /* ! YYPARSE_PARAM */ -#if defined __STDC__ || defined __cplusplus -int yyparse (void); -#else -int yyparse (); -#endif -#endif /* ! YYPARSE_PARAM */ + /* The lookahead symbol. */ @@ -2212,49 +1935,26 @@ int yychar; /* The semantic value of the lookahead symbol. */ YYSTYPE yylval; - /* Number of syntax errors so far. */ int yynerrs; +/*----------. +| yyparse. | +`----------*/ -/*-------------------------. -| yyparse or yypush_parse. | -`-------------------------*/ - -#ifdef YYPARSE_PARAM -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -int -yyparse (void *YYPARSE_PARAM) -#else -int -yyparse (YYPARSE_PARAM) - void *YYPARSE_PARAM; -#endif -#else /* ! YYPARSE_PARAM */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) int yyparse (void) -#else -int -yyparse () - -#endif -#endif { - - int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: - `yyss': related to states. - `yyvs': related to semantic values. + 'yyss': related to states. + 'yyvs': related to semantic values. - Refer to the stacks thru separate pointers, to allow yyoverflow + Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ @@ -2272,7 +1972,7 @@ yyparse () int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ - int yytoken; + int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; @@ -2290,9 +1990,8 @@ yyparse () Keep to zero when no symbol should be popped. */ int yylen = 0; - yytoken = 0; - yyss = yyssa; - yyvs = yyvsa; + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); @@ -2301,14 +2000,6 @@ yyparse () yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ - - /* Initialize stack pointers. - Waste one element of value and location stack - so that they stay on the same level as the state stack. - The wasted elements are never initialized. */ - yyssp = yyss; - yyvsp = yyvs; - goto yysetstate; /*------------------------------------------------------------. @@ -2320,32 +2011,31 @@ yyparse () yyssp++; yysetstate: - *yyssp = yystate; + *yyssp = (yytype_int16) yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ - YYSIZE_T yysize = yyssp - yyss + 1; + YYSIZE_T yysize = (YYSIZE_T) (yyssp - yyss + 1); #ifdef yyoverflow { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - YYSTYPE *yyvs1 = yyvs; - yytype_int16 *yyss1 = yyss; + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. This used to be a - conditional around just the two extra args, but that might - be undefined if yyoverflow is a macro. */ - yyoverflow (YY_("memory exhausted"), - &yyss1, yysize * sizeof (*yyssp), - &yyvs1, yysize * sizeof (*yyvsp), - &yystacksize); - - yyss = yyss1; - yyvs = yyvs1; + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE @@ -2353,22 +2043,22 @@ yyparse () # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) - goto yyexhaustedlab; + goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) - yystacksize = YYMAXDEPTH; + yystacksize = YYMAXDEPTH; { - yytype_int16 *yyss1 = yyss; - union yyalloc *yyptr = - (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); - if (! yyptr) - goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss_alloc, yyss); - YYSTACK_RELOCATE (yyvs_alloc, yyvs); + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE - if (yyss1 != yyssa) - YYSTACK_FREE (yyss1); + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ @@ -2377,10 +2067,10 @@ yyparse () yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", - (unsigned long int) yystacksize)); + (unsigned long) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) - YYABORT; + YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); @@ -2400,7 +2090,7 @@ yybackup: /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; - if (yyn == YYPACT_NINF) + if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ @@ -2409,7 +2099,7 @@ yybackup: if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); - yychar = YYLEX; + yychar = yylex (); } if (yychar <= YYEOF) @@ -2431,8 +2121,8 @@ yybackup: yyn = yytable[yyn]; if (yyn <= 0) { - if (yyn == 0 || yyn == YYTABLE_NINF) - goto yyerrlab; + if (yytable_value_is_error (yyn)) + goto yyerrlab; yyn = -yyn; goto yyreduce; } @@ -2449,7 +2139,9 @@ yybackup: yychar = YYEMPTY; yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END goto yynewstate; @@ -2472,7 +2164,7 @@ yyreduce: yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: - `$$ = $1'. + '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison @@ -2486,21 +2178,18 @@ yyreduce: switch (yyn) { case 7: - -/* Line 1464 of yacc.c */ -#line 250 "parser.yxx" +#line 255 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { assert(!egg_stack.empty()); - DCAST(EggData, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[(2) - (2)]._egg))); + DCAST(EggData, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[0]._egg))); } +#line 2191 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 27: - -/* Line 1464 of yacc.c */ -#line 295 "parser.yxx" +#line 300 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - string strval = (yyvsp[(3) - (4)]._string); + string strval = (yyvsp[-1]._string); EggCoordinateSystem *cs = new EggCoordinateSystem; CoordinateSystem f = parse_coordinate_system_string(strval); @@ -2511,24 +2200,22 @@ yyreduce: } (yyval._egg) = cs; } +#line 2208 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 28: - -/* Line 1464 of yacc.c */ -#line 318 "parser.yxx" +#line 323 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._egg) = new EggComment((yyvsp[(2) - (5)]._string), (yyvsp[(4) - (5)]._string)); + (yyval._egg) = new EggComment((yyvsp[-3]._string), (yyvsp[-1]._string)); } +#line 2216 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 29: - -/* Line 1464 of yacc.c */ -#line 332 "parser.yxx" +#line 337 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - string tref_name = (yyvsp[(2) - (4)]._string); - Filename filename = (yyvsp[(4) - (4)]._string); + string tref_name = (yyvsp[-2]._string); + Filename filename = (yyvsp[0]._string); EggTexture *texture = new EggTexture(tref_name, filename); if (textures.find(tref_name) != textures.end()) { @@ -2538,27 +2225,25 @@ yyreduce: egg_stack.push_back(texture); } +#line 2233 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 30: - -/* Line 1464 of yacc.c */ -#line 345 "parser.yxx" +#line 350 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { (yyval._egg) = egg_stack.back(); egg_stack.pop_back(); } +#line 2242 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 32: - -/* Line 1464 of yacc.c */ -#line 361 "parser.yxx" +#line 366 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggTexture *texture = DCAST(EggTexture, egg_stack.back()); - string name = (yyvsp[(3) - (6)]._string); - double value = (yyvsp[(5) - (6)]._number); - string strval = (yyvsp[(5) - (6)]._string); + string name = (yyvsp[-3]._string); + double value = (yyvsp[-1]._number); + string strval = (yyvsp[-1]._string); if (cmp_nocase_uh(name, "type") == 0) { EggTexture::TextureType tt = EggTexture::string_texture_type(strval); @@ -2851,7 +2536,7 @@ yyreduce: } } else if (cmp_nocase_uh(name, "depth_write") == 0) { - EggRenderMode::DepthWriteMode m = + EggRenderMode::DepthWriteMode m = EggRenderMode::string_depth_write_mode(strval); if (m == EggRenderMode::DWM_unspecified) { eggyywarning("Unknown depth-write mode " + strval); @@ -2860,7 +2545,7 @@ yyreduce: } } else if (cmp_nocase_uh(name, "depth_test") == 0) { - EggRenderMode::DepthTestMode m = + EggRenderMode::DepthTestMode m = EggRenderMode::string_depth_test_mode(strval); if (m == EggRenderMode::DTM_unspecified) { eggyywarning("Unknown depth-test mode " + strval); @@ -2869,7 +2554,7 @@ yyreduce: } } else if (cmp_nocase_uh(name, "visibility") == 0) { - EggRenderMode::VisibilityMode m = + EggRenderMode::VisibilityMode m = EggRenderMode::string_visibility_mode(strval); if (m == EggRenderMode::VM_unspecified) { eggyywarning("Unknown visibility mode " + strval); @@ -2895,18 +2580,26 @@ yyreduce: } else if (cmp_nocase_uh(name, "read_mipmaps") == 0) { texture->set_read_mipmaps(((int)value) != 0); + } else if (cmp_nocase_uh(name, "min_lod") == 0) { + texture->set_min_lod(value); + + } else if (cmp_nocase_uh(name, "max_lod") == 0) { + texture->set_max_lod(value); + + } else if (cmp_nocase_uh(name, "lod_bias") == 0) { + texture->set_lod_bias(value); + } else { eggyywarning("Unsupported texture scalar: " + name); } } +#line 2601 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 34: - -/* Line 1464 of yacc.c */ -#line 718 "parser.yxx" +#line 732 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - string mref_name = (yyvsp[(2) - (3)]._string); + string mref_name = (yyvsp[-1]._string); EggMaterial *material = new EggMaterial(mref_name); if (materials.find(mref_name) != materials.end()) { @@ -2916,26 +2609,24 @@ yyreduce: egg_stack.push_back(material); } +#line 2617 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 35: - -/* Line 1464 of yacc.c */ -#line 730 "parser.yxx" +#line 744 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { (yyval._egg) = egg_stack.back(); egg_stack.pop_back(); } +#line 2626 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 37: - -/* Line 1464 of yacc.c */ -#line 746 "parser.yxx" +#line 760 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggMaterial *material = DCAST(EggMaterial, egg_stack.back()); - string name = (yyvsp[(3) - (6)]._string); - double value = (yyvsp[(5) - (6)]._number); + string name = (yyvsp[-3]._string); + double value = (yyvsp[-1]._number); if (cmp_nocase_uh(name, "baser") == 0) { LColor base = material->get_base(); @@ -3041,42 +2732,39 @@ yyreduce: eggyywarning("Unsupported material scalar: " + name); } } +#line 2740 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 38: - -/* Line 1464 of yacc.c */ -#line 841 "parser.yxx" +#line 881 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - string node_name = (yyvsp[(2) - (5)]._string); - Filename filename = (yyvsp[(4) - (5)]._string); + string node_name = (yyvsp[-3]._string); + Filename filename = (yyvsp[-1]._string); EggExternalReference *ref = new EggExternalReference(node_name, filename); (yyval._egg) = ref; } +#line 2751 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 39: - -/* Line 1464 of yacc.c */ -#line 848 "parser.yxx" +#line 888 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - if (cmp_nocase_uh((yyvsp[(1) - (6)]._string), "group") != 0) { + if (cmp_nocase_uh((yyvsp[-5]._string), "group") != 0) { eggyyerror("keyword 'group' expected"); } - string node_name = (yyvsp[(3) - (6)]._string); - Filename filename = (yyvsp[(5) - (6)]._string); + string node_name = (yyvsp[-3]._string); + Filename filename = (yyvsp[-1]._string); EggExternalReference *ref = new EggExternalReference(node_name, filename); (yyval._egg) = ref; } +#line 2765 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 40: - -/* Line 1464 of yacc.c */ -#line 868 "parser.yxx" +#line 908 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - string name = (yyvsp[(2) - (2)]._string); - EggVertexPool *pool = NULL; + string name = (yyvsp[0]._string); + EggVertexPool *pool = nullptr; VertexPools::const_iterator vpi = vertex_pools.find(name); if (vpi != vertex_pools.end()) { @@ -3097,45 +2785,41 @@ yyreduce: egg_stack.push_back(pool); } +#line 2793 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 41: - -/* Line 1464 of yacc.c */ -#line 892 "parser.yxx" +#line 932 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { (yyval._egg) = egg_stack.back(); egg_stack.pop_back(); } +#line 2802 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 44: - -/* Line 1464 of yacc.c */ -#line 920 "parser.yxx" +#line 960 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { egg_stack.push_back(new EggVertex); } +#line 2810 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 45: - -/* Line 1464 of yacc.c */ -#line 924 "parser.yxx" +#line 964 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { PT(EggVertex) vtx = DCAST(EggVertex, egg_stack.back()); egg_stack.pop_back(); DCAST(EggVertexPool, egg_stack.back())->add_vertex(vtx); } +#line 2821 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 46: - -/* Line 1464 of yacc.c */ -#line 931 "parser.yxx" +#line 971 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - vertex_index = (int)(yyvsp[(2) - (2)]._number); + vertex_index = (int)(yyvsp[0]._number); EggVertexPool *pool = DCAST(EggVertexPool, egg_stack.back()); if (vertex_index < 0) { @@ -3158,12 +2842,11 @@ yyreduce: egg_stack.push_back(new EggVertex); } +#line 2850 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 47: - -/* Line 1464 of yacc.c */ -#line 956 "parser.yxx" +#line 996 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { PT(EggVertex) vtx = DCAST(EggVertex, egg_stack.back()); egg_stack.pop_back(); @@ -3173,309 +2856,294 @@ yyreduce: pool->add_vertex(vtx, vertex_index); } } +#line 2864 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 48: - -/* Line 1464 of yacc.c */ -#line 977 "parser.yxx" +#line 1017 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggVertex, egg_stack.back())->set_pos((yyvsp[(1) - (1)]._number)); + DCAST(EggVertex, egg_stack.back())->set_pos((yyvsp[0]._number)); } +#line 2872 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 49: - -/* Line 1464 of yacc.c */ -#line 981 "parser.yxx" +#line 1021 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggVertex, egg_stack.back())->set_pos(LPoint2d((yyvsp[(1) - (2)]._number), (yyvsp[(2) - (2)]._number))); + DCAST(EggVertex, egg_stack.back())->set_pos(LPoint2d((yyvsp[-1]._number), (yyvsp[0]._number))); } +#line 2880 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 50: - -/* Line 1464 of yacc.c */ -#line 985 "parser.yxx" +#line 1025 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggVertex, egg_stack.back())->set_pos(LPoint3d((yyvsp[(1) - (3)]._number), (yyvsp[(2) - (3)]._number), (yyvsp[(3) - (3)]._number))); + DCAST(EggVertex, egg_stack.back())->set_pos(LPoint3d((yyvsp[-2]._number), (yyvsp[-1]._number), (yyvsp[0]._number))); } +#line 2888 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 51: - -/* Line 1464 of yacc.c */ -#line 989 "parser.yxx" +#line 1029 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggVertex, egg_stack.back())->set_pos(LPoint4d((yyvsp[(1) - (4)]._number), (yyvsp[(2) - (4)]._number), (yyvsp[(3) - (4)]._number), (yyvsp[(4) - (4)]._number))); + DCAST(EggVertex, egg_stack.back())->set_pos(LPoint4d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number), (yyvsp[0]._number))); } +#line 2896 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 52: - -/* Line 1464 of yacc.c */ -#line 993 "parser.yxx" +#line 1033 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggVertex *vertex = DCAST(EggVertex, egg_stack.back()); - EggVertexUV *uv = new EggVertexUV((yyvsp[(3) - (4)]._string), LTexCoordd::zero()); + EggVertexUV *uv = new EggVertexUV((yyvsp[-1]._string), LTexCoordd::zero()); egg_stack.push_back(uv); - if (vertex->has_uv((yyvsp[(3) - (4)]._string))) { - eggyywarning("Ignoring repeated UV name " + (yyvsp[(3) - (4)]._string)); + if (vertex->has_uv((yyvsp[-1]._string))) { + eggyywarning("Ignoring repeated UV name " + (yyvsp[-1]._string)); } else { vertex->set_uv_obj(uv); } } +#line 2911 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 53: - -/* Line 1464 of yacc.c */ -#line 1004 "parser.yxx" +#line 1044 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { egg_stack.pop_back(); } +#line 2919 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 54: - -/* Line 1464 of yacc.c */ -#line 1008 "parser.yxx" +#line 1048 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggVertex *vertex = DCAST(EggVertex, egg_stack.back()); - EggVertexAux *aux = new EggVertexAux((yyvsp[(3) - (4)]._string), LVecBase4d::zero()); + EggVertexAux *aux = new EggVertexAux((yyvsp[-1]._string), LVecBase4d::zero()); egg_stack.push_back(aux); - if (vertex->has_aux((yyvsp[(3) - (4)]._string))) { - eggyywarning("Ignoring repeated Aux name " + (yyvsp[(3) - (4)]._string)); + if (vertex->has_aux((yyvsp[-1]._string))) { + eggyywarning("Ignoring repeated Aux name " + (yyvsp[-1]._string)); } else { vertex->set_aux_obj(aux); } } +#line 2934 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 55: - -/* Line 1464 of yacc.c */ -#line 1019 "parser.yxx" +#line 1059 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { egg_stack.pop_back(); } +#line 2942 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 58: - -/* Line 1464 of yacc.c */ -#line 1025 "parser.yxx" +#line 1065 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { bool inserted = DCAST(EggVertex, egg_stack.back())->_dxyzs. - insert(EggMorphVertex((yyvsp[(3) - (8)]._string), LVector3d((yyvsp[(5) - (8)]._number), (yyvsp[(6) - (8)]._number), (yyvsp[(7) - (8)]._number)))).second; + insert(EggMorphVertex((yyvsp[-5]._string), LVector3d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number)))).second; if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(3) - (8)]._string)); + eggyywarning("Ignoring repeated morph name " + (yyvsp[-5]._string)); } } +#line 2954 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 59: - -/* Line 1464 of yacc.c */ -#line 1033 "parser.yxx" +#line 1073 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { bool inserted = DCAST(EggVertex, egg_stack.back())->_dxyzs. - insert(EggMorphVertex((yyvsp[(4) - (8)]._string), LVector3d((yyvsp[(5) - (8)]._number), (yyvsp[(6) - (8)]._number), (yyvsp[(7) - (8)]._number)))).second; + insert(EggMorphVertex((yyvsp[-4]._string), LVector3d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number)))).second; if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(4) - (8)]._string)); + eggyywarning("Ignoring repeated morph name " + (yyvsp[-4]._string)); } } +#line 2966 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 60: - -/* Line 1464 of yacc.c */ -#line 1052 "parser.yxx" +#line 1092 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggVertexUV, egg_stack.back())->set_uv(LTexCoordd((yyvsp[(1) - (2)]._number), (yyvsp[(2) - (2)]._number))); + DCAST(EggVertexUV, egg_stack.back())->set_uv(LTexCoordd((yyvsp[-1]._number), (yyvsp[0]._number))); } +#line 2974 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 61: - -/* Line 1464 of yacc.c */ -#line 1056 "parser.yxx" +#line 1096 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggVertexUV, egg_stack.back())->set_uvw(LVecBase3d((yyvsp[(1) - (3)]._number), (yyvsp[(2) - (3)]._number), (yyvsp[(3) - (3)]._number))); + DCAST(EggVertexUV, egg_stack.back())->set_uvw(LVecBase3d((yyvsp[-2]._number), (yyvsp[-1]._number), (yyvsp[0]._number))); } +#line 2982 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 62: - -/* Line 1464 of yacc.c */ -#line 1060 "parser.yxx" +#line 1100 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { if (DCAST(EggVertexUV, egg_stack.back())->has_tangent()) { eggyywarning("Ignoring repeated tangent"); } else { - DCAST(EggVertexUV, egg_stack.back())->set_tangent(LNormald((yyvsp[(4) - (7)]._number), (yyvsp[(5) - (7)]._number), (yyvsp[(6) - (7)]._number))); + DCAST(EggVertexUV, egg_stack.back())->set_tangent(LNormald((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number))); } } +#line 2994 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 63: +#line 1108 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + if (DCAST(EggVertexUV, egg_stack.back())->has_tangent()) { + eggyywarning("Ignoring repeated tangent"); + } else { + DCAST(EggVertexUV, egg_stack.back())->set_tangent4(LVecBase4d((yyvsp[-4]._number), (yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number))); + } +} +#line 3006 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 1068 "parser.yxx" + case 64: +#line 1116 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { if (DCAST(EggVertexUV, egg_stack.back())->has_binormal()) { eggyywarning("Ignoring repeated binormal"); } else { - DCAST(EggVertexUV, egg_stack.back())->set_binormal(LNormald((yyvsp[(4) - (7)]._number), (yyvsp[(5) - (7)]._number), (yyvsp[(6) - (7)]._number))); - } -} - break; - - case 64: - -/* Line 1464 of yacc.c */ -#line 1076 "parser.yxx" - { - bool inserted = DCAST(EggVertexUV, egg_stack.back())->_duvs. - insert(EggMorphTexCoord((yyvsp[(3) - (7)]._string), LVector3d((yyvsp[(5) - (7)]._number), (yyvsp[(6) - (7)]._number), 0.0))).second; - if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(3) - (7)]._string)); + DCAST(EggVertexUV, egg_stack.back())->set_binormal(LNormald((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number))); } } +#line 3018 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 65: - -/* Line 1464 of yacc.c */ -#line 1084 "parser.yxx" +#line 1124 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { bool inserted = DCAST(EggVertexUV, egg_stack.back())->_duvs. - insert(EggMorphTexCoord((yyvsp[(3) - (8)]._string), LVector3d((yyvsp[(5) - (8)]._number), (yyvsp[(6) - (8)]._number), (yyvsp[(7) - (8)]._number)))).second; + insert(EggMorphTexCoord((yyvsp[-4]._string), LVector3d((yyvsp[-2]._number), (yyvsp[-1]._number), 0.0))).second; if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(3) - (8)]._string)); + eggyywarning("Ignoring repeated morph name " + (yyvsp[-4]._string)); } } +#line 3030 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 66: - -/* Line 1464 of yacc.c */ -#line 1092 "parser.yxx" +#line 1132 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { bool inserted = DCAST(EggVertexUV, egg_stack.back())->_duvs. - insert(EggMorphTexCoord((yyvsp[(4) - (7)]._string), LVector3d((yyvsp[(5) - (7)]._number), (yyvsp[(6) - (7)]._number), 0.0))).second; + insert(EggMorphTexCoord((yyvsp[-5]._string), LVector3d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number)))).second; if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(4) - (7)]._string)); + eggyywarning("Ignoring repeated morph name " + (yyvsp[-5]._string)); } } +#line 3042 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 67: - -/* Line 1464 of yacc.c */ -#line 1100 "parser.yxx" +#line 1140 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { bool inserted = DCAST(EggVertexUV, egg_stack.back())->_duvs. - insert(EggMorphTexCoord((yyvsp[(4) - (8)]._string), LVector3d((yyvsp[(5) - (8)]._number), (yyvsp[(6) - (8)]._number), (yyvsp[(7) - (8)]._number)))).second; + insert(EggMorphTexCoord((yyvsp[-3]._string), LVector3d((yyvsp[-2]._number), (yyvsp[-1]._number), 0.0))).second; if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(4) - (8)]._string)); + eggyywarning("Ignoring repeated morph name " + (yyvsp[-3]._string)); } } +#line 3054 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 69: - -/* Line 1464 of yacc.c */ -#line 1118 "parser.yxx" + case 68: +#line 1148 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggVertexAux, egg_stack.back())->set_aux(LVecBase4d((yyvsp[(1) - (4)]._number), (yyvsp[(2) - (4)]._number), (yyvsp[(3) - (4)]._number), (yyvsp[(4) - (4)]._number))); + bool inserted = DCAST(EggVertexUV, egg_stack.back())->_duvs. + insert(EggMorphTexCoord((yyvsp[-4]._string), LVector3d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number)))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + (yyvsp[-4]._string)); + } } +#line 3066 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 70: - -/* Line 1464 of yacc.c */ -#line 1132 "parser.yxx" +#line 1166 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggVertex, egg_stack.back())->set_normal(LNormald((yyvsp[(1) - (3)]._number), (yyvsp[(2) - (3)]._number), (yyvsp[(3) - (3)]._number))); + DCAST(EggVertexAux, egg_stack.back())->set_aux(LVecBase4d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number), (yyvsp[0]._number))); } +#line 3074 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 71: - -/* Line 1464 of yacc.c */ -#line 1136 "parser.yxx" +#line 1180 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - bool inserted = DCAST(EggVertex, egg_stack.back())->_dnormals. - insert(EggMorphNormal((yyvsp[(3) - (8)]._string), LVector3d((yyvsp[(5) - (8)]._number), (yyvsp[(6) - (8)]._number), (yyvsp[(7) - (8)]._number)))).second; - if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(3) - (8)]._string)); - } + DCAST(EggVertex, egg_stack.back())->set_normal(LNormald((yyvsp[-2]._number), (yyvsp[-1]._number), (yyvsp[0]._number))); } +#line 3082 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 72: - -/* Line 1464 of yacc.c */ -#line 1144 "parser.yxx" +#line 1184 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { bool inserted = DCAST(EggVertex, egg_stack.back())->_dnormals. - insert(EggMorphNormal((yyvsp[(4) - (8)]._string), LVector3d((yyvsp[(5) - (8)]._number), (yyvsp[(6) - (8)]._number), (yyvsp[(7) - (8)]._number)))).second; + insert(EggMorphNormal((yyvsp[-5]._string), LVector3d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number)))).second; if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(4) - (8)]._string)); + eggyywarning("Ignoring repeated morph name " + (yyvsp[-5]._string)); } } +#line 3094 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 73: - -/* Line 1464 of yacc.c */ -#line 1162 "parser.yxx" +#line 1192 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggVertex, egg_stack.back())->set_color(LColor((yyvsp[(1) - (4)]._number), (yyvsp[(2) - (4)]._number), (yyvsp[(3) - (4)]._number), (yyvsp[(4) - (4)]._number))); + bool inserted = DCAST(EggVertex, egg_stack.back())->_dnormals. + insert(EggMorphNormal((yyvsp[-4]._string), LVector3d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number)))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + (yyvsp[-4]._string)); + } } +#line 3106 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 74: - -/* Line 1464 of yacc.c */ -#line 1166 "parser.yxx" +#line 1210 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - bool inserted = DCAST(EggVertex, egg_stack.back())->_drgbas. - insert(EggMorphColor((yyvsp[(3) - (9)]._string), LVector4((yyvsp[(5) - (9)]._number), (yyvsp[(6) - (9)]._number), (yyvsp[(7) - (9)]._number), (yyvsp[(8) - (9)]._number)))).second; - if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(3) - (9)]._string)); - } + DCAST(EggVertex, egg_stack.back())->set_color(LColor((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number), (yyvsp[0]._number))); } +#line 3114 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 75: - -/* Line 1464 of yacc.c */ -#line 1174 "parser.yxx" +#line 1214 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { bool inserted = DCAST(EggVertex, egg_stack.back())->_drgbas. - insert(EggMorphColor((yyvsp[(4) - (9)]._string), LVector4((yyvsp[(5) - (9)]._number), (yyvsp[(6) - (9)]._number), (yyvsp[(7) - (9)]._number), (yyvsp[(8) - (9)]._number)))).second; + insert(EggMorphColor((yyvsp[-6]._string), LVector4((yyvsp[-4]._number), (yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number)))).second; if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(4) - (9)]._string)); + eggyywarning("Ignoring repeated morph name " + (yyvsp[-6]._string)); } } +#line 3126 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 76: - -/* Line 1464 of yacc.c */ -#line 1192 "parser.yxx" +#line 1222 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - EggGroup *group = new EggGroup((yyvsp[(2) - (2)]._string)); - egg_stack.push_back(group); + bool inserted = DCAST(EggVertex, egg_stack.back())->_drgbas. + insert(EggMorphColor((yyvsp[-5]._string), LVector4((yyvsp[-4]._number), (yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number)))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + (yyvsp[-5]._string)); + } } +#line 3138 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 77: +#line 1240 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggGroup *group = new EggGroup((yyvsp[0]._string)); + egg_stack.push_back(group); +} +#line 3147 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 1197 "parser.yxx" + case 78: +#line 1245 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); (yyval._egg) = group; @@ -3485,44 +3153,40 @@ yyreduce: } Thread::consider_yield(); } - break; - - case 78: - -/* Line 1464 of yacc.c */ -#line 1217 "parser.yxx" - { - EggGroup *group = new EggGroup((yyvsp[(2) - (2)]._string)); - group->set_group_type(EggGroup::GT_joint); - egg_stack.push_back(group); -} +#line 3161 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 79: +#line 1265 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggGroup *group = new EggGroup((yyvsp[0]._string)); + group->set_group_type(EggGroup::GT_joint); + egg_stack.push_back(group); +} +#line 3171 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 1223 "parser.yxx" + case 80: +#line 1271 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { (yyval._egg) = egg_stack.back(); egg_stack.pop_back(); } - break; - - case 80: - -/* Line 1464 of yacc.c */ -#line 1238 "parser.yxx" - { - EggGroup *group = new EggGroup((yyvsp[(2) - (2)]._string)); - group->set_group_type(EggGroup::GT_instance); - egg_stack.push_back(group); -} +#line 3180 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 81: +#line 1286 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggGroup *group = new EggGroup((yyvsp[0]._string)); + group->set_group_type(EggGroup::GT_instance); + egg_stack.push_back(group); +} +#line 3190 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 1244 "parser.yxx" + case 82: +#line 1292 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); (yyval._egg) = group; @@ -3531,18 +3195,17 @@ yyreduce: groups[group->get_name()] = group; } } +#line 3203 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 83: - -/* Line 1464 of yacc.c */ -#line 1264 "parser.yxx" + case 84: +#line 1312 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); - string name = (yyvsp[(3) - (6)]._string); - double value = (yyvsp[(5) - (6)]._number); - unsigned long ulong_value = (yyvsp[(5) - (6)]._ulong); - string strval = (yyvsp[(5) - (6)]._string); + string name = (yyvsp[-3]._string); + double value = (yyvsp[-1]._number); + unsigned long ulong_value = (yyvsp[-1]._ulong); + string strval = (yyvsp[-1]._string); if (cmp_nocase_uh(name, "fps") == 0) { group->set_switch_fps(value); @@ -3565,7 +3228,7 @@ yyreduce: } } else if (cmp_nocase_uh(name, "depth_write") == 0) { - EggRenderMode::DepthWriteMode m = + EggRenderMode::DepthWriteMode m = EggRenderMode::string_depth_write_mode(strval); if (m == EggRenderMode::DWM_unspecified) { eggyywarning("Unknown depth-write mode " + strval); @@ -3574,7 +3237,7 @@ yyreduce: } } else if (cmp_nocase_uh(name, "depth_test") == 0) { - EggRenderMode::DepthTestMode m = + EggRenderMode::DepthTestMode m = EggRenderMode::string_depth_test_mode(strval); if (m == EggRenderMode::DTM_unspecified) { eggyywarning("Unknown depth-test mode " + strval); @@ -3583,7 +3246,7 @@ yyreduce: } } else if (cmp_nocase_uh(name, "visibility") == 0) { - EggRenderMode::VisibilityMode m = + EggRenderMode::VisibilityMode m = EggRenderMode::string_visibility_mode(strval); if (m == EggRenderMode::VM_unspecified) { eggyywarning("Unknown visibility mode " + strval); @@ -3684,15 +3347,14 @@ yyreduce: eggyywarning("Unknown group scalar " + name); } } +#line 3355 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 84: - -/* Line 1464 of yacc.c */ -#line 1409 "parser.yxx" + case 85: +#line 1460 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); - string strval = (yyvsp[(4) - (5)]._string); + string strval = (yyvsp[-1]._string); EggGroup::BillboardType f = EggGroup::string_billboard_type(strval); if (f == EggGroup::BT_none) { @@ -3701,49 +3363,45 @@ yyreduce: group->set_billboard_type(f); } } - break; - - case 85: - -/* Line 1464 of yacc.c */ -#line 1421 "parser.yxx" - { - EggGroup *group = DCAST(EggGroup, egg_stack.back()); - group->set_billboard_center(LPoint3d((yyvsp[(4) - (7)]._number), (yyvsp[(5) - (7)]._number), (yyvsp[(6) - (7)]._number))); -} +#line 3371 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 86: - -/* Line 1464 of yacc.c */ -#line 1426 "parser.yxx" +#line 1472 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); - string name = (yyvsp[(3) - (7)]._string); - - group->set_collision_name(name); + group->set_billboard_center(LPoint3d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number))); } +#line 3380 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 87: - -/* Line 1464 of yacc.c */ -#line 1433 "parser.yxx" +#line 1477 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); - int value = (int)(yyvsp[(4) - (5)]._number); - group->set_dcs_type(value!=0 ? EggGroup::DC_default : EggGroup::DC_none); + string name = (yyvsp[-4]._string); + + group->set_collision_name(name); } +#line 3391 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 88: +#line 1484 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + int value = (int)(yyvsp[-1]._number); + group->set_dcs_type(value!=0 ? EggGroup::DC_default : EggGroup::DC_none); +} +#line 3401 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 1439 "parser.yxx" + case 89: +#line 1490 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { // The special flavor of DCS, with { sync } or { nosync }. EggGroup *group = DCAST(EggGroup, egg_stack.back()); - string strval = (yyvsp[(4) - (5)]._string); + string strval = (yyvsp[-1]._string); EggGroup::DCSType f = EggGroup::string_dcs_type(strval); if (f == EggGroup::DC_unspecified) { @@ -3752,28 +3410,26 @@ yyreduce: group->set_dcs_type(f); } } - break; - - case 89: - -/* Line 1464 of yacc.c */ -#line 1452 "parser.yxx" - { - // The traditional flavor of DART, with { 0 } or { 1 }. - EggGroup *group = DCAST(EggGroup, egg_stack.back()); - int value = (int)(yyvsp[(4) - (5)]._number); - group->set_dart_type(value!=0 ? EggGroup::DT_default : EggGroup::DT_none); -} +#line 3418 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 90: +#line 1503 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + // The traditional flavor of DART, with { 0 } or { 1 }. + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + int value = (int)(yyvsp[-1]._number); + group->set_dart_type(value!=0 ? EggGroup::DT_default : EggGroup::DT_none); +} +#line 3429 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 1459 "parser.yxx" + case 91: +#line 1510 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { // The special flavor of DART, with { sync } or { nosync }. EggGroup *group = DCAST(EggGroup, egg_stack.back()); - string strval = (yyvsp[(4) - (5)]._string); + string strval = (yyvsp[-1]._string); EggGroup::DartType f = EggGroup::string_dart_type(strval); if (f == EggGroup::DT_none) { @@ -3782,92 +3438,84 @@ yyreduce: group->set_dart_type(f); } } - break; - - case 91: - -/* Line 1464 of yacc.c */ -#line 1472 "parser.yxx" - { - EggGroup *group = DCAST(EggGroup, egg_stack.back()); - int value = (int)(yyvsp[(4) - (5)]._number); - group->set_switch_flag(value!=0); -} +#line 3446 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 92: - -/* Line 1464 of yacc.c */ -#line 1478 "parser.yxx" +#line 1523 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); - string type = (yyvsp[(4) - (5)]._string); - group->add_object_type(type); + int value = (int)(yyvsp[-1]._number); + group->set_switch_flag(value!=0); } +#line 3456 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 93: - -/* Line 1464 of yacc.c */ -#line 1484 "parser.yxx" +#line 1529 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); - int value = (int)(yyvsp[(4) - (5)]._number); - group->set_model_flag(value!=0); + string type = (yyvsp[-1]._string); + group->add_object_type(type); } +#line 3466 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 94: - -/* Line 1464 of yacc.c */ -#line 1490 "parser.yxx" +#line 1535 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); - group->set_tag((yyvsp[(3) - (6)]._string), (yyvsp[(5) - (6)]._string)); + int value = (int)(yyvsp[-1]._number); + group->set_model_flag(value!=0); } +#line 3476 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 95: - -/* Line 1464 of yacc.c */ -#line 1495 "parser.yxx" +#line 1541 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); - int value = (int)(yyvsp[(4) - (5)]._number); - group->set_texlist_flag(value!=0); + group->set_tag((yyvsp[-3]._string), (yyvsp[-1]._string)); } +#line 3485 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 100: + case 96: +#line 1546 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + int value = (int)(yyvsp[-1]._number); + group->set_texlist_flag(value!=0); +} +#line 3495 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 1505 "parser.yxx" + case 101: +#line 1556 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); if (group->get_group_type() != EggGroup::GT_instance) { eggyyerror(" valid only within "); - } else if ((yyvsp[(4) - (5)]._egg) != (EggObject *)NULL) { - group->add_group_ref(DCAST(EggGroup, (yyvsp[(4) - (5)]._egg))); + } else if ((yyvsp[-1]._egg) != nullptr) { + group->add_group_ref(DCAST(EggGroup, (yyvsp[-1]._egg))); } } - break; - - case 101: - -/* Line 1464 of yacc.c */ -#line 1514 "parser.yxx" - { - DCAST(EggGroup, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[(2) - (2)]._egg))); -} +#line 3508 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 102: +#line 1565 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + DCAST(EggGroup, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[0]._egg))); +} +#line 3516 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 1528 "parser.yxx" + case 103: +#line 1579 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); - string strval = (yyvsp[(1) - (1)]._string); + string strval = (yyvsp[0]._string); EggGroup::CollisionSolidType f = EggGroup::string_cs_type(strval); if (f == EggGroup::CST_none) { @@ -3884,15 +3532,14 @@ yyreduce: } } } +#line 3540 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 104: - -/* Line 1464 of yacc.c */ -#line 1559 "parser.yxx" + case 105: +#line 1610 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); - string strval = (yyvsp[(2) - (2)]._string); + string strval = (yyvsp[0]._string); EggGroup::CollideFlags f = EggGroup::string_collide_flags(strval); if (f == EggGroup::CF_none) { @@ -3901,22 +3548,20 @@ yyreduce: group->set_collide_flags(group->get_collide_flags() | f); } } +#line 3556 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 105: - -/* Line 1464 of yacc.c */ -#line 1581 "parser.yxx" + case 106: +#line 1632 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { egg_top_transform = egg_stack.back()->as_transform(); egg_top_transform->clear_transform(); } +#line 3565 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 107: - -/* Line 1464 of yacc.c */ -#line 1597 "parser.yxx" + case 108: +#line 1648 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); if (group->get_group_type() != EggGroup::GT_joint) { @@ -3925,140 +3570,127 @@ yyreduce: egg_top_transform = &group->modify_default_pose(); egg_top_transform->clear_transform(); } - break; - - case 122: - -/* Line 1464 of yacc.c */ -#line 1634 "parser.yxx" - { - egg_top_transform->add_translate2d(LVector2d((yyvsp[(3) - (5)]._number), (yyvsp[(4) - (5)]._number))); -} +#line 3578 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 123: - -/* Line 1464 of yacc.c */ -#line 1641 "parser.yxx" +#line 1685 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_top_transform->add_translate3d(LVector3d((yyvsp[(3) - (6)]._number), (yyvsp[(4) - (6)]._number), (yyvsp[(5) - (6)]._number))); + egg_top_transform->add_translate2d(LVector2d((yyvsp[-2]._number), (yyvsp[-1]._number))); } +#line 3586 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 124: - -/* Line 1464 of yacc.c */ -#line 1648 "parser.yxx" +#line 1692 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_top_transform->add_rotate2d((yyvsp[(3) - (4)]._number)); + egg_top_transform->add_translate3d(LVector3d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number))); } +#line 3594 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 125: - -/* Line 1464 of yacc.c */ -#line 1655 "parser.yxx" +#line 1699 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_top_transform->add_rotx((yyvsp[(3) - (4)]._number)); + egg_top_transform->add_rotate2d((yyvsp[-1]._number)); } +#line 3602 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 126: - -/* Line 1464 of yacc.c */ -#line 1662 "parser.yxx" +#line 1706 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_top_transform->add_roty((yyvsp[(3) - (4)]._number)); + egg_top_transform->add_rotx((yyvsp[-1]._number)); } +#line 3610 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 127: - -/* Line 1464 of yacc.c */ -#line 1669 "parser.yxx" +#line 1713 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_top_transform->add_rotz((yyvsp[(3) - (4)]._number)); + egg_top_transform->add_roty((yyvsp[-1]._number)); } +#line 3618 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 128: - -/* Line 1464 of yacc.c */ -#line 1676 "parser.yxx" +#line 1720 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_top_transform->add_rotate3d((yyvsp[(3) - (7)]._number), LVector3d((yyvsp[(4) - (7)]._number), (yyvsp[(5) - (7)]._number), (yyvsp[(6) - (7)]._number))); + egg_top_transform->add_rotz((yyvsp[-1]._number)); } +#line 3626 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 129: - -/* Line 1464 of yacc.c */ -#line 1683 "parser.yxx" +#line 1727 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_top_transform->add_scale2d(LVecBase2d((yyvsp[(3) - (5)]._number), (yyvsp[(4) - (5)]._number))); + egg_top_transform->add_rotate3d((yyvsp[-4]._number), LVector3d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number))); } +#line 3634 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 130: - -/* Line 1464 of yacc.c */ -#line 1690 "parser.yxx" +#line 1734 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_top_transform->add_scale3d(LVecBase3d((yyvsp[(3) - (6)]._number), (yyvsp[(4) - (6)]._number), (yyvsp[(5) - (6)]._number))); + egg_top_transform->add_scale2d(LVecBase2d((yyvsp[-2]._number), (yyvsp[-1]._number))); } +#line 3642 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 131: - -/* Line 1464 of yacc.c */ -#line 1697 "parser.yxx" +#line 1741 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_top_transform->add_uniform_scale((yyvsp[(3) - (4)]._number)); + egg_top_transform->add_scale3d(LVecBase3d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number))); } +#line 3650 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 134: + case 132: +#line 1748 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + egg_top_transform->add_uniform_scale((yyvsp[-1]._number)); +} +#line 3658 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 1711 "parser.yxx" + case 135: +#line 1762 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { egg_top_transform->add_matrix3 - (LMatrix3d((yyvsp[(1) - (9)]._number), (yyvsp[(2) - (9)]._number), (yyvsp[(3) - (9)]._number), - (yyvsp[(4) - (9)]._number), (yyvsp[(5) - (9)]._number), (yyvsp[(6) - (9)]._number), - (yyvsp[(7) - (9)]._number), (yyvsp[(8) - (9)]._number), (yyvsp[(9) - (9)]._number))); -} - break; - - case 137: - -/* Line 1464 of yacc.c */ -#line 1729 "parser.yxx" - { - egg_top_transform->add_matrix4 - (LMatrix4d((yyvsp[(1) - (16)]._number), (yyvsp[(2) - (16)]._number), (yyvsp[(3) - (16)]._number), (yyvsp[(4) - (16)]._number), - (yyvsp[(5) - (16)]._number), (yyvsp[(6) - (16)]._number), (yyvsp[(7) - (16)]._number), (yyvsp[(8) - (16)]._number), - (yyvsp[(9) - (16)]._number), (yyvsp[(10) - (16)]._number), (yyvsp[(11) - (16)]._number), (yyvsp[(12) - (16)]._number), - (yyvsp[(13) - (16)]._number), (yyvsp[(14) - (16)]._number), (yyvsp[(15) - (16)]._number), (yyvsp[(16) - (16)]._number))); + (LMatrix3d((yyvsp[-8]._number), (yyvsp[-7]._number), (yyvsp[-6]._number), + (yyvsp[-5]._number), (yyvsp[-4]._number), (yyvsp[-3]._number), + (yyvsp[-2]._number), (yyvsp[-1]._number), (yyvsp[0]._number))); } +#line 3669 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 138: - -/* Line 1464 of yacc.c */ -#line 1748 "parser.yxx" +#line 1780 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - if ((yyvsp[(7) - (9)]._egg) != (EggVertexPool *)NULL) { - EggVertexPool *pool = DCAST(EggVertexPool, (yyvsp[(7) - (9)]._egg)); + egg_top_transform->add_matrix4 + (LMatrix4d((yyvsp[-15]._number), (yyvsp[-14]._number), (yyvsp[-13]._number), (yyvsp[-12]._number), + (yyvsp[-11]._number), (yyvsp[-10]._number), (yyvsp[-9]._number), (yyvsp[-8]._number), + (yyvsp[-7]._number), (yyvsp[-6]._number), (yyvsp[-5]._number), (yyvsp[-4]._number), + (yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number), (yyvsp[0]._number))); +} +#line 3681 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; + + case 139: +#line 1799 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + if ((yyvsp[-2]._egg) != nullptr) { + EggVertexPool *pool = DCAST(EggVertexPool, (yyvsp[-2]._egg)); EggGroup *group = DCAST(EggGroup, egg_stack.back()); - PTA_double nums = (yyvsp[(3) - (9)]._number_list); - double membership = (yyvsp[(4) - (9)]._number); + PTA_double nums = (yyvsp[-6]._number_list); + double membership = (yyvsp[-5]._number); for (int i = 0; i < (int)nums.size(); i++) { int index = (int)nums[i]; EggVertex *vertex = pool->get_forward_vertex(index); - if (vertex == NULL) { + if (vertex == nullptr) { ostringstream errmsg; - errmsg << "No vertex " << index << " in pool " << pool->get_name() + errmsg << "No vertex " << index << " in pool " << pool->get_name() << std::ends; eggyyerror(errmsg); } else { @@ -4067,26 +3699,24 @@ yyreduce: } } } - break; - - case 139: - -/* Line 1464 of yacc.c */ -#line 1781 "parser.yxx" - { - (yyval._number) = 1.0; -} +#line 3707 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 140: - -/* Line 1464 of yacc.c */ -#line 1785 "parser.yxx" +#line 1832 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - string name = (yyvsp[(3) - (6)]._string); - double value = (yyvsp[(5) - (6)]._number); - double result = (yyvsp[(1) - (6)]._number); - + (yyval._number) = 1.0; +} +#line 3715 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; + + case 141: +#line 1836 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + string name = (yyvsp[-3]._string); + double value = (yyvsp[-1]._number); + double result = (yyvsp[-5]._number); + if (cmp_nocase_uh(name, "membership") == 0) { result = value; } else { @@ -4095,190 +3725,171 @@ yyreduce: (yyval._number) = result; } - break; - - case 142: - -/* Line 1464 of yacc.c */ -#line 1822 "parser.yxx" - { - EggGroup *group = DCAST(EggGroup, egg_stack.back()); - group->set_lod(EggSwitchConditionDistance((yyvsp[(3) - (11)]._number), (yyvsp[(4) - (11)]._number), LPoint3d((yyvsp[(7) - (11)]._number), (yyvsp[(8) - (11)]._number), (yyvsp[(9) - (11)]._number)))); -} +#line 3733 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 143: - -/* Line 1464 of yacc.c */ -#line 1827 "parser.yxx" +#line 1873 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggGroup *group = DCAST(EggGroup, egg_stack.back()); - group->set_lod(EggSwitchConditionDistance((yyvsp[(3) - (12)]._number), (yyvsp[(4) - (12)]._number), LPoint3d((yyvsp[(8) - (12)]._number), (yyvsp[(9) - (12)]._number), (yyvsp[(10) - (12)]._number)), (yyvsp[(5) - (12)]._number))); + group->set_lod(EggSwitchConditionDistance((yyvsp[-8]._number), (yyvsp[-7]._number), LPoint3d((yyvsp[-4]._number), (yyvsp[-3]._number), (yyvsp[-2]._number)))); } +#line 3742 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 144: - -/* Line 1464 of yacc.c */ -#line 1844 "parser.yxx" +#line 1878 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_stack.push_back(new EggPolygon((yyvsp[(2) - (2)]._string))); + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + group->set_lod(EggSwitchConditionDistance((yyvsp[-9]._number), (yyvsp[-8]._number), LPoint3d((yyvsp[-4]._number), (yyvsp[-3]._number), (yyvsp[-2]._number)), (yyvsp[-7]._number))); } +#line 3751 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 145: - -/* Line 1464 of yacc.c */ -#line 1848 "parser.yxx" +#line 1895 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._egg) = egg_stack.back(); - egg_stack.pop_back(); + egg_stack.push_back(new EggPolygon((yyvsp[0]._string))); } +#line 3759 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 146: - -/* Line 1464 of yacc.c */ -#line 1863 "parser.yxx" +#line 1899 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_stack.push_back(new EggTriangleFan((yyvsp[(2) - (2)]._string))); + (yyval._egg) = egg_stack.back(); + egg_stack.pop_back(); } +#line 3768 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 147: - -/* Line 1464 of yacc.c */ -#line 1867 "parser.yxx" +#line 1914 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._egg) = egg_stack.back(); - egg_stack.pop_back(); + egg_stack.push_back(new EggTriangleFan((yyvsp[0]._string))); } +#line 3776 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 148: - -/* Line 1464 of yacc.c */ -#line 1882 "parser.yxx" +#line 1918 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_stack.push_back(new EggTriangleStrip((yyvsp[(2) - (2)]._string))); + (yyval._egg) = egg_stack.back(); + egg_stack.pop_back(); } +#line 3785 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 149: - -/* Line 1464 of yacc.c */ -#line 1886 "parser.yxx" +#line 1933 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._egg) = egg_stack.back(); - egg_stack.pop_back(); + egg_stack.push_back(new EggTriangleStrip((yyvsp[0]._string))); } +#line 3793 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 150: - -/* Line 1464 of yacc.c */ -#line 1901 "parser.yxx" +#line 1937 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_stack.push_back(new EggPatch((yyvsp[(2) - (2)]._string))); + (yyval._egg) = egg_stack.back(); + egg_stack.pop_back(); } +#line 3802 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 151: - -/* Line 1464 of yacc.c */ -#line 1905 "parser.yxx" +#line 1952 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._egg) = egg_stack.back(); - egg_stack.pop_back(); + egg_stack.push_back(new EggPatch((yyvsp[0]._string))); } +#line 3810 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 152: - -/* Line 1464 of yacc.c */ -#line 1920 "parser.yxx" +#line 1956 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_stack.push_back(new EggPoint((yyvsp[(2) - (2)]._string))); + (yyval._egg) = egg_stack.back(); + egg_stack.pop_back(); } +#line 3819 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 153: - -/* Line 1464 of yacc.c */ -#line 1924 "parser.yxx" +#line 1971 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._egg) = egg_stack.back(); - egg_stack.pop_back(); + egg_stack.push_back(new EggPoint((yyvsp[0]._string))); } +#line 3827 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 154: - -/* Line 1464 of yacc.c */ -#line 1939 "parser.yxx" +#line 1975 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_stack.push_back(new EggLine((yyvsp[(2) - (2)]._string))); + (yyval._egg) = egg_stack.back(); + egg_stack.pop_back(); } +#line 3836 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 155: - -/* Line 1464 of yacc.c */ -#line 1943 "parser.yxx" +#line 1990 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._egg) = egg_stack.back(); - egg_stack.pop_back(); + egg_stack.push_back(new EggLine((yyvsp[0]._string))); } +#line 3844 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 156: - -/* Line 1464 of yacc.c */ -#line 1958 "parser.yxx" +#line 1994 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - egg_stack.push_back(new EggNurbsSurface((yyvsp[(2) - (2)]._string))); + (yyval._egg) = egg_stack.back(); + egg_stack.pop_back(); } +#line 3853 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 157: - -/* Line 1464 of yacc.c */ -#line 1962 "parser.yxx" +#line 2009 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._egg) = egg_stack.back(); - egg_stack.pop_back(); + egg_stack.push_back(new EggNurbsSurface((yyvsp[0]._string))); } +#line 3861 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 158: - -/* Line 1464 of yacc.c */ -#line 1977 "parser.yxx" - { - egg_stack.push_back(new EggNurbsCurve((yyvsp[(2) - (2)]._string))); -} - break; - - case 159: - -/* Line 1464 of yacc.c */ -#line 1981 "parser.yxx" +#line 2013 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { (yyval._egg) = egg_stack.back(); egg_stack.pop_back(); } +#line 3870 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 164: + case 159: +#line 2028 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + egg_stack.push_back(new EggNurbsCurve((yyvsp[0]._string))); +} +#line 3878 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 2011 "parser.yxx" + case 160: +#line 2032 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + (yyval._egg) = egg_stack.back(); + egg_stack.pop_back(); +} +#line 3887 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; + + case 165: +#line 2062 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { if (!egg_stack.back()->is_of_type(EggCompositePrimitive::get_class_type())) { eggyyerror("Not a composite primitive; components are not allowed here."); } else { PT(EggCompositePrimitive) comp = DCAST(EggCompositePrimitive, egg_stack.back()); - if ((yyvsp[(3) - (4)]._number) < 0 || (yyvsp[(3) - (4)]._number) >= comp->get_num_components()) { + if ((yyvsp[-1]._number) < 0 || (yyvsp[-1]._number) >= comp->get_num_components()) { eggyyerror("Invalid component number"); } } @@ -4286,30 +3897,28 @@ yyreduce: // the component attributes. egg_stack.push_back(new EggPolygon); } +#line 3905 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 165: - -/* Line 1464 of yacc.c */ -#line 2025 "parser.yxx" + case 166: +#line 2076 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { PT(EggPrimitive) prim = DCAST(EggPrimitive, egg_stack.back()); egg_stack.pop_back(); PT(EggCompositePrimitive) comp = DCAST(EggCompositePrimitive, egg_stack.back()); - comp->set_component((int)(yyvsp[(3) - (7)]._number), prim); + comp->set_component((int)(yyvsp[-4]._number), prim); } +#line 3916 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 173: - -/* Line 1464 of yacc.c */ -#line 2039 "parser.yxx" + case 174: +#line 2090 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggPrimitive *primitive = DCAST(EggPrimitive, egg_stack.back()); - string name = (yyvsp[(3) - (6)]._string); - double value = (yyvsp[(5) - (6)]._number); - string strval = (yyvsp[(5) - (6)]._string); - + string name = (yyvsp[-3]._string); + double value = (yyvsp[-1]._number); + string strval = (yyvsp[-1]._string); + if (cmp_nocase_uh(name, "alpha") == 0) { EggRenderMode::AlphaMode a = EggRenderMode::string_alpha_mode(strval); if (a == EggRenderMode::AM_unspecified) { @@ -4318,7 +3927,7 @@ yyreduce: primitive->set_alpha_mode(a); } } else if (cmp_nocase_uh(name, "depth_write") == 0) { - EggRenderMode::DepthWriteMode m = + EggRenderMode::DepthWriteMode m = EggRenderMode::string_depth_write_mode(strval); if (m == EggRenderMode::DWM_unspecified) { eggyywarning("Unknown depth-write mode " + strval); @@ -4327,7 +3936,7 @@ yyreduce: } } else if (cmp_nocase_uh(name, "depth_test") == 0) { - EggRenderMode::DepthTestMode m = + EggRenderMode::DepthTestMode m = EggRenderMode::string_depth_test_mode(strval); if (m == EggRenderMode::DTM_unspecified) { eggyywarning("Unknown depth-test mode " + strval); @@ -4336,7 +3945,7 @@ yyreduce: } } else if (cmp_nocase_uh(name, "visibility") == 0) { - EggRenderMode::VisibilityMode m = + EggRenderMode::VisibilityMode m = EggRenderMode::string_visibility_mode(strval); if (m == EggRenderMode::VM_unspecified) { eggyywarning("Unknown visibility mode " + strval); @@ -4368,29 +3977,27 @@ yyreduce: eggyywarning("Unknown scalar " + name); } } +#line 3985 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 185: - -/* Line 1464 of yacc.c */ -#line 2125 "parser.yxx" + case 186: +#line 2176 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - EggNurbsCurve *curve = DCAST(EggNurbsCurve, (yyvsp[(2) - (2)]._egg)); + EggNurbsCurve *curve = DCAST(EggNurbsCurve, (yyvsp[0]._egg)); EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); nurbs->_curves_on_surface.push_back(curve); } +#line 3995 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 187: - -/* Line 1464 of yacc.c */ -#line 2132 "parser.yxx" + case 188: +#line 2183 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggNurbsSurface *primitive = DCAST(EggNurbsSurface, egg_stack.back()); - string name = (yyvsp[(3) - (6)]._string); - double value = (yyvsp[(5) - (6)]._number); - string strval = (yyvsp[(5) - (6)]._string); - + string name = (yyvsp[-3]._string); + double value = (yyvsp[-1]._number); + string strval = (yyvsp[-1]._string); + if (cmp_nocase_uh(name, "alpha") == 0) { EggRenderMode::AlphaMode a = EggRenderMode::string_alpha_mode(strval); if (a == EggRenderMode::AM_unspecified) { @@ -4399,7 +4006,7 @@ yyreduce: primitive->set_alpha_mode(a); } } else if (cmp_nocase_uh(name, "depth_write") == 0) { - EggRenderMode::DepthWriteMode m = + EggRenderMode::DepthWriteMode m = EggRenderMode::string_depth_write_mode(strval); if (m == EggRenderMode::DWM_unspecified) { eggyywarning("Unknown depth-write mode " + strval); @@ -4408,7 +4015,7 @@ yyreduce: } } else if (cmp_nocase_uh(name, "depth_test") == 0) { - EggRenderMode::DepthTestMode m = + EggRenderMode::DepthTestMode m = EggRenderMode::string_depth_test_mode(strval); if (m == EggRenderMode::DTM_unspecified) { eggyywarning("Unknown depth-test mode " + strval); @@ -4417,7 +4024,7 @@ yyreduce: } } else if (cmp_nocase_uh(name, "visibility") == 0) { - EggRenderMode::VisibilityMode m = + EggRenderMode::VisibilityMode m = EggRenderMode::string_visibility_mode(strval); if (m == EggRenderMode::VM_unspecified) { eggyywarning("Unknown visibility mode " + strval); @@ -4439,18 +4046,17 @@ yyreduce: eggyywarning("Unknown scalar " + name); } } +#line 4054 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 198: - -/* Line 1464 of yacc.c */ -#line 2208 "parser.yxx" + case 199: +#line 2259 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggNurbsCurve *primitive = DCAST(EggNurbsCurve, egg_stack.back()); - string name = (yyvsp[(3) - (6)]._string); - double value = (yyvsp[(5) - (6)]._number); - string strval = (yyvsp[(5) - (6)]._string); - + string name = (yyvsp[-3]._string); + double value = (yyvsp[-1]._number); + string strval = (yyvsp[-1]._string); + if (cmp_nocase_uh(name, "alpha") == 0) { EggRenderMode::AlphaMode a = EggRenderMode::string_alpha_mode(strval); if (a == EggRenderMode::AM_unspecified) { @@ -4459,7 +4065,7 @@ yyreduce: primitive->set_alpha_mode(a); } } else if (cmp_nocase_uh(name, "depth_write") == 0) { - EggRenderMode::DepthWriteMode m = + EggRenderMode::DepthWriteMode m = EggRenderMode::string_depth_write_mode(strval); if (m == EggRenderMode::DWM_unspecified) { eggyywarning("Unknown depth-write mode " + strval); @@ -4468,7 +4074,7 @@ yyreduce: } } else if (cmp_nocase_uh(name, "depth_test") == 0) { - EggRenderMode::DepthTestMode m = + EggRenderMode::DepthTestMode m = EggRenderMode::string_depth_test_mode(strval); if (m == EggRenderMode::DTM_unspecified) { eggyywarning("Unknown depth-test mode " + strval); @@ -4477,7 +4083,7 @@ yyreduce: } } else if (cmp_nocase_uh(name, "visibility") == 0) { - EggRenderMode::VisibilityMode m = + EggRenderMode::VisibilityMode m = EggRenderMode::string_visibility_mode(strval); if (m == EggRenderMode::VM_unspecified) { eggyywarning("Unknown visibility mode " + strval); @@ -4500,34 +4106,32 @@ yyreduce: } else { primitive->set_curve_type(a); } - + } else { eggyywarning("Unknown scalar " + name); } } - break; - - case 199: - -/* Line 1464 of yacc.c */ -#line 2279 "parser.yxx" - { - if ((yyvsp[(1) - (1)]._egg) != (EggTexture *)NULL) { - EggTexture *texture = DCAST(EggTexture, (yyvsp[(1) - (1)]._egg)); - DCAST(EggPrimitive, egg_stack.back())->add_texture(texture); - } -} +#line 4119 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 200: - -/* Line 1464 of yacc.c */ -#line 2296 "parser.yxx" +#line 2330 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - EggTexture *texture = NULL; + if ((yyvsp[0]._egg) != nullptr) { + EggTexture *texture = DCAST(EggTexture, (yyvsp[0]._egg)); + DCAST(EggPrimitive, egg_stack.back())->add_texture(texture); + } +} +#line 4130 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; + + case 201: +#line 2347 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggTexture *texture = nullptr; // Defining a texture on-the-fly. - Filename filename = (yyvsp[(1) - (1)]._string); + Filename filename = (yyvsp[0]._string); string tref_name = filename.get_basename(); Textures::iterator vpi = textures.find(tref_name); @@ -4536,7 +4140,7 @@ yyreduce: texture = new EggTexture(tref_name, filename); textures[tref_name] = texture; - if (egg_top_node != NULL) { + if (egg_top_node != nullptr) { egg_top_node->add_child(texture); } @@ -4544,125 +4148,116 @@ yyreduce: // The texture already existed. Use it. texture = (*vpi).second; if (filename != texture->get_filename()) { - eggyywarning(string("Using previous path: ") + + eggyywarning(string("Using previous path: ") + texture->get_filename().get_fullpath()); } } - nassertr(texture != NULL, 0); + nassertr(texture != nullptr, 0); DCAST(EggPrimitive, egg_stack.back())->add_texture(texture); } - break; - - case 201: - -/* Line 1464 of yacc.c */ -#line 2336 "parser.yxx" - { - if ((yyvsp[(1) - (1)]._egg) != (EggMaterial *)NULL) { - EggMaterial *material = DCAST(EggMaterial, (yyvsp[(1) - (1)]._egg)); - DCAST(EggPrimitive, egg_stack.back())->set_material(material); - } -} +#line 4164 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 202: - -/* Line 1464 of yacc.c */ -#line 2353 "parser.yxx" +#line 2387 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggPrimitive, egg_stack.back())->set_normal(LNormald((yyvsp[(1) - (3)]._number), (yyvsp[(2) - (3)]._number), (yyvsp[(3) - (3)]._number))); + if ((yyvsp[0]._egg) != nullptr) { + EggMaterial *material = DCAST(EggMaterial, (yyvsp[0]._egg)); + DCAST(EggPrimitive, egg_stack.back())->set_material(material); + } } +#line 4175 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 203: - -/* Line 1464 of yacc.c */ -#line 2357 "parser.yxx" +#line 2404 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - bool inserted = DCAST(EggPrimitive, egg_stack.back())->_dnormals. - insert(EggMorphNormal((yyvsp[(3) - (8)]._string), LVector3d((yyvsp[(5) - (8)]._number), (yyvsp[(6) - (8)]._number), (yyvsp[(7) - (8)]._number)))).second; - if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(3) - (8)]._string)); - } + DCAST(EggPrimitive, egg_stack.back())->set_normal(LNormald((yyvsp[-2]._number), (yyvsp[-1]._number), (yyvsp[0]._number))); } +#line 4183 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 204: - -/* Line 1464 of yacc.c */ -#line 2365 "parser.yxx" +#line 2408 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { bool inserted = DCAST(EggPrimitive, egg_stack.back())->_dnormals. - insert(EggMorphNormal((yyvsp[(4) - (8)]._string), LVector3d((yyvsp[(5) - (8)]._number), (yyvsp[(6) - (8)]._number), (yyvsp[(7) - (8)]._number)))).second; + insert(EggMorphNormal((yyvsp[-5]._string), LVector3d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number)))).second; if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(4) - (8)]._string)); + eggyywarning("Ignoring repeated morph name " + (yyvsp[-5]._string)); } } +#line 4195 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 205: - -/* Line 1464 of yacc.c */ -#line 2383 "parser.yxx" +#line 2416 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggPrimitive, egg_stack.back())->set_color(LColor((yyvsp[(1) - (4)]._number), (yyvsp[(2) - (4)]._number), (yyvsp[(3) - (4)]._number), (yyvsp[(4) - (4)]._number))); + bool inserted = DCAST(EggPrimitive, egg_stack.back())->_dnormals. + insert(EggMorphNormal((yyvsp[-4]._string), LVector3d((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number)))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + (yyvsp[-4]._string)); + } } +#line 4207 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 206: - -/* Line 1464 of yacc.c */ -#line 2387 "parser.yxx" +#line 2434 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - bool inserted = DCAST(EggPrimitive, egg_stack.back())->_drgbas. - insert(EggMorphColor((yyvsp[(3) - (9)]._string), LVector4((yyvsp[(5) - (9)]._number), (yyvsp[(6) - (9)]._number), (yyvsp[(7) - (9)]._number), (yyvsp[(8) - (9)]._number)))).second; - if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(3) - (9)]._string)); - } + DCAST(EggPrimitive, egg_stack.back())->set_color(LColor((yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number), (yyvsp[0]._number))); } +#line 4215 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 207: - -/* Line 1464 of yacc.c */ -#line 2395 "parser.yxx" +#line 2438 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { bool inserted = DCAST(EggPrimitive, egg_stack.back())->_drgbas. - insert(EggMorphColor((yyvsp[(4) - (9)]._string), LVector4((yyvsp[(5) - (9)]._number), (yyvsp[(6) - (9)]._number), (yyvsp[(7) - (9)]._number), (yyvsp[(8) - (9)]._number)))).second; + insert(EggMorphColor((yyvsp[-6]._string), LVector4((yyvsp[-4]._number), (yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number)))).second; if (!inserted) { - eggyywarning("Ignoring repeated morph name " + (yyvsp[(4) - (9)]._string)); + eggyywarning("Ignoring repeated morph name " + (yyvsp[-6]._string)); } } +#line 4227 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 208: - -/* Line 1464 of yacc.c */ -#line 2413 "parser.yxx" +#line 2446 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - EggPrimitive *primitive = DCAST(EggPrimitive, egg_stack.back()); - int value = (int)(yyvsp[(1) - (1)]._number); - primitive->set_bface_flag(value!=0); + bool inserted = DCAST(EggPrimitive, egg_stack.back())->_drgbas. + insert(EggMorphColor((yyvsp[-5]._string), LVector4((yyvsp[-4]._number), (yyvsp[-3]._number), (yyvsp[-2]._number), (yyvsp[-1]._number)))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + (yyvsp[-5]._string)); + } } +#line 4239 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 209: - -/* Line 1464 of yacc.c */ -#line 2429 "parser.yxx" +#line 2464 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - if ((yyvsp[(6) - (8)]._egg) != (EggVertexPool *)NULL) { - EggVertexPool *pool = DCAST(EggVertexPool, (yyvsp[(6) - (8)]._egg)); + EggPrimitive *primitive = DCAST(EggPrimitive, egg_stack.back()); + int value = (int)(yyvsp[0]._number); + primitive->set_bface_flag(value!=0); +} +#line 4249 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; + + case 210: +#line 2480 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + if ((yyvsp[-2]._egg) != nullptr) { + EggVertexPool *pool = DCAST(EggVertexPool, (yyvsp[-2]._egg)); EggPrimitive *prim = DCAST(EggPrimitive, egg_stack.back()); - PTA_double nums = (yyvsp[(3) - (8)]._number_list); + PTA_double nums = (yyvsp[-5]._number_list); for (int i = 0; i < (int)nums.size(); i++) { int index = (int)nums[i]; EggVertex *vertex = pool->get_forward_vertex(index); - if (vertex == NULL) { + if (vertex == nullptr) { ostringstream errmsg; - errmsg << "No vertex " << index << " in pool " << pool->get_name() + errmsg << "No vertex " << index << " in pool " << pool->get_name() << std::ends; eggyyerror(errmsg); } else { @@ -4671,227 +4266,207 @@ yyreduce: } } } - break; - - case 210: - -/* Line 1464 of yacc.c */ -#line 2460 "parser.yxx" - { - EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); - int u_order = (int)(yyvsp[(1) - (2)]._number); - int v_order = (int)(yyvsp[(2) - (2)]._number); - nurbs->set_u_order(u_order); - nurbs->set_v_order(v_order); -} +#line 4274 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 211: - -/* Line 1464 of yacc.c */ -#line 2478 "parser.yxx" +#line 2511 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); - PTA_double nums = (yyvsp[(1) - (1)]._number_list); + int u_order = (int)(yyvsp[-1]._number); + int v_order = (int)(yyvsp[0]._number); + nurbs->set_u_order(u_order); + nurbs->set_v_order(v_order); +} +#line 4286 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; + + case 212: +#line 2529 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); + PTA_double nums = (yyvsp[0]._number_list); nurbs->set_num_u_knots(nums.size()); for (int i = 0; i < (int)nums.size(); i++) { nurbs->set_u_knot(i, nums[i]); } } +#line 4300 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 212: - -/* Line 1464 of yacc.c */ -#line 2498 "parser.yxx" + case 213: +#line 2549 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); - PTA_double nums = (yyvsp[(1) - (1)]._number_list); + PTA_double nums = (yyvsp[0]._number_list); nurbs->set_num_v_knots(nums.size()); for (int i = 0; i < (int)nums.size(); i++) { nurbs->set_v_knot(i, nums[i]); } } +#line 4314 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 213: - -/* Line 1464 of yacc.c */ -#line 2518 "parser.yxx" + case 214: +#line 2569 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); nurbs->_trims.push_back(EggNurbsSurface::Trim()); } +#line 4323 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 215: - -/* Line 1464 of yacc.c */ -#line 2534 "parser.yxx" + case 216: +#line 2585 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); nassertr(!nurbs->_trims.empty(), 0); nurbs->_trims.back().push_back(EggNurbsSurface::Loop()); } +#line 4333 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 216: - -/* Line 1464 of yacc.c */ -#line 2540 "parser.yxx" + case 217: +#line 2591 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); nassertr(!nurbs->_trims.empty(), 0); nassertr(!nurbs->_trims.back().empty(), 0); - EggNurbsCurve *curve = DCAST(EggNurbsCurve, (yyvsp[(2) - (2)]._egg)); + EggNurbsCurve *curve = DCAST(EggNurbsCurve, (yyvsp[0]._egg)); nurbs->_trims.back().back().push_back(curve); } - break; - - case 217: - -/* Line 1464 of yacc.c */ -#line 2559 "parser.yxx" - { - EggNurbsCurve *nurbs = DCAST(EggNurbsCurve, egg_stack.back()); - int order = (int)(yyvsp[(1) - (1)]._number); - nurbs->set_order(order); -} +#line 4345 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 218: - -/* Line 1464 of yacc.c */ -#line 2575 "parser.yxx" +#line 2610 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggNurbsCurve *nurbs = DCAST(EggNurbsCurve, egg_stack.back()); - PTA_double nums = (yyvsp[(1) - (1)]._number_list); + int order = (int)(yyvsp[0]._number); + nurbs->set_order(order); +} +#line 4355 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; + + case 219: +#line 2626 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggNurbsCurve *nurbs = DCAST(EggNurbsCurve, egg_stack.back()); + PTA_double nums = (yyvsp[0]._number_list); nurbs->set_num_knots(nums.size()); for (int i = 0; i < (int)nums.size(); i++) { nurbs->set_knot(i, nums[i]); } } - break; - - case 219: - -/* Line 1464 of yacc.c */ -#line 2596 "parser.yxx" - { - EggTable *table = new EggTable((yyvsp[(2) - (2)]._string)); - table->set_table_type(EggTable::TT_table); - egg_stack.push_back(table); -} +#line 4369 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 220: +#line 2647 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggTable *table = new EggTable((yyvsp[0]._string)); + table->set_table_type(EggTable::TT_table); + egg_stack.push_back(table); +} +#line 4379 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 2602 "parser.yxx" + case 221: +#line 2653 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { (yyval._egg) = egg_stack.back(); egg_stack.pop_back(); Thread::consider_yield(); } - break; - - case 221: - -/* Line 1464 of yacc.c */ -#line 2619 "parser.yxx" - { - EggTable *table = new EggTable((yyvsp[(2) - (2)]._string)); - table->set_table_type(EggTable::TT_bundle); - egg_stack.push_back(table); -} +#line 4389 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 222: +#line 2670 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggTable *table = new EggTable((yyvsp[0]._string)); + table->set_table_type(EggTable::TT_bundle); + egg_stack.push_back(table); +} +#line 4399 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 2625 "parser.yxx" + case 223: +#line 2676 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { (yyval._egg) = egg_stack.back(); egg_stack.pop_back(); } - break; - - case 224: - -/* Line 1464 of yacc.c */ -#line 2641 "parser.yxx" - { - DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[(2) - (2)]._egg))); -} +#line 4408 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 225: - -/* Line 1464 of yacc.c */ -#line 2645 "parser.yxx" +#line 2692 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[(2) - (2)]._egg))); + DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[0]._egg))); } +#line 4416 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 226: - -/* Line 1464 of yacc.c */ -#line 2649 "parser.yxx" +#line 2696 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[(2) - (2)]._egg))); + DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[0]._egg))); } +#line 4424 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 227: - -/* Line 1464 of yacc.c */ -#line 2653 "parser.yxx" +#line 2700 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[(2) - (2)]._egg))); + DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[0]._egg))); } +#line 4432 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 228: - -/* Line 1464 of yacc.c */ -#line 2657 "parser.yxx" +#line 2704 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[(2) - (2)]._egg))); + DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[0]._egg))); } +#line 4440 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 229: - -/* Line 1464 of yacc.c */ -#line 2672 "parser.yxx" +#line 2708 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - EggSAnimData *anim_data = new EggSAnimData((yyvsp[(2) - (2)]._string)); - egg_stack.push_back(anim_data); + DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[0]._egg))); } +#line 4448 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 230: +#line 2723 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggSAnimData *anim_data = new EggSAnimData((yyvsp[0]._string)); + egg_stack.push_back(anim_data); +} +#line 4457 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 2677 "parser.yxx" + case 231: +#line 2728 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { (yyval._egg) = egg_stack.back(); egg_stack.pop_back(); } +#line 4466 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 232: - -/* Line 1464 of yacc.c */ -#line 2694 "parser.yxx" + case 233: +#line 2745 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggSAnimData *anim_data = DCAST(EggSAnimData, egg_stack.back()); - string name = (yyvsp[(3) - (6)]._string); - double value = (yyvsp[(5) - (6)]._number); + string name = (yyvsp[-3]._string); + double value = (yyvsp[-1]._number); if (cmp_nocase_uh(name, "fps") == 0) { anim_data->set_fps(value); @@ -4899,46 +4474,42 @@ yyreduce: eggyywarning("Unsupported S$Anim scalar: " + name); } } - break; - - case 233: - -/* Line 1464 of yacc.c */ -#line 2706 "parser.yxx" - { - DCAST(EggSAnimData, egg_stack.back())->set_data((yyvsp[(4) - (5)]._number_list)); -} +#line 4482 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 234: - -/* Line 1464 of yacc.c */ -#line 2720 "parser.yxx" +#line 2757 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - EggXfmAnimData *anim_data = new EggXfmAnimData((yyvsp[(2) - (2)]._string)); - egg_stack.push_back(anim_data); + DCAST(EggSAnimData, egg_stack.back())->set_data((yyvsp[-1]._number_list)); } +#line 4490 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 235: +#line 2771 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggXfmAnimData *anim_data = new EggXfmAnimData((yyvsp[0]._string)); + egg_stack.push_back(anim_data); +} +#line 4499 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 2725 "parser.yxx" + case 236: +#line 2776 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { (yyval._egg) = egg_stack.back(); egg_stack.pop_back(); } +#line 4508 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 237: - -/* Line 1464 of yacc.c */ -#line 2742 "parser.yxx" + case 238: +#line 2793 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggXfmAnimData *anim_data = DCAST(EggXfmAnimData, egg_stack.back()); - string name = (yyvsp[(3) - (6)]._string); - double value = (yyvsp[(5) - (6)]._number); - string strval = (yyvsp[(5) - (6)]._string); + string name = (yyvsp[-3]._string); + double value = (yyvsp[-1]._number); + string strval = (yyvsp[-1]._string); if (cmp_nocase_uh(name, "fps") == 0) { anim_data->set_fps(value); @@ -4950,46 +4521,42 @@ yyreduce: eggyywarning("Unsupported Xfm$Anim scalar: " + name); } } - break; - - case 238: - -/* Line 1464 of yacc.c */ -#line 2759 "parser.yxx" - { - DCAST(EggXfmAnimData, egg_stack.back())->set_data((yyvsp[(4) - (5)]._number_list)); -} +#line 4529 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 239: - -/* Line 1464 of yacc.c */ -#line 2773 "parser.yxx" +#line 2810 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - EggXfmSAnim *anim_group = new EggXfmSAnim((yyvsp[(2) - (2)]._string)); - egg_stack.push_back(anim_group); + DCAST(EggXfmAnimData, egg_stack.back())->set_data((yyvsp[-1]._number_list)); } +#line 4537 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 240: +#line 2824 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggXfmSAnim *anim_group = new EggXfmSAnim((yyvsp[0]._string)); + egg_stack.push_back(anim_group); +} +#line 4546 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 2778 "parser.yxx" + case 241: +#line 2829 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { (yyval._egg) = egg_stack.back(); egg_stack.pop_back(); } +#line 4555 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 242: - -/* Line 1464 of yacc.c */ -#line 2795 "parser.yxx" + case 243: +#line 2846 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggXfmSAnim *anim_group = DCAST(EggXfmSAnim, egg_stack.back()); - string name = (yyvsp[(3) - (6)]._string); - double value = (yyvsp[(5) - (6)]._number); - string strval = (yyvsp[(5) - (6)]._string); + string name = (yyvsp[-3]._string); + double value = (yyvsp[-1]._number); + string strval = (yyvsp[-1]._string); if (cmp_nocase_uh(name, "fps") == 0) { anim_group->set_fps(value); @@ -4999,45 +4566,41 @@ yyreduce: eggyywarning("Unsupported Xfm$Anim_S$ scalar: " + name); } } - break; - - case 243: - -/* Line 1464 of yacc.c */ -#line 2810 "parser.yxx" - { - DCAST(EggXfmSAnim, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[(2) - (2)]._egg))); -} +#line 4574 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 244: - -/* Line 1464 of yacc.c */ -#line 2825 "parser.yxx" +#line 2861 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - EggAnimPreload *anim_preload = new EggAnimPreload((yyvsp[(2) - (2)]._string)); - egg_stack.push_back(anim_preload); + DCAST(EggXfmSAnim, egg_stack.back())->add_child(DCAST(EggNode, (yyvsp[0]._egg))); } +#line 4582 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 245: +#line 2876 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + EggAnimPreload *anim_preload = new EggAnimPreload((yyvsp[0]._string)); + egg_stack.push_back(anim_preload); +} +#line 4591 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; -/* Line 1464 of yacc.c */ -#line 2830 "parser.yxx" + case 246: +#line 2881 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { (yyval._egg) = egg_stack.back(); egg_stack.pop_back(); } +#line 4600 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 247: - -/* Line 1464 of yacc.c */ -#line 2847 "parser.yxx" + case 248: +#line 2898 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { EggAnimPreload *anim_preload = DCAST(EggAnimPreload, egg_stack.back()); - string name = (yyvsp[(3) - (6)]._string); - double value = (yyvsp[(5) - (6)]._number); + string name = (yyvsp[-3]._string); + double value = (yyvsp[-1]._number); if (cmp_nocase_uh(name, "fps") == 0) { anim_preload->set_fps(value); @@ -5047,50 +4610,45 @@ yyreduce: eggyywarning("Unsupported AnimPreload scalar: " + name); } } - break; - - case 248: - -/* Line 1464 of yacc.c */ -#line 2871 "parser.yxx" - { - (yyval._number_list) = PTA_double::empty_array(0); -} +#line 4618 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 249: - -/* Line 1464 of yacc.c */ -#line 2875 "parser.yxx" - { - (yyval._number_list).push_back((double)(yyvsp[(2) - (2)]._number)); -} - break; - - case 250: - -/* Line 1464 of yacc.c */ -#line 2889 "parser.yxx" +#line 2922 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { (yyval._number_list) = PTA_double::empty_array(0); } +#line 4626 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; + + case 250: +#line 2926 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + (yyval._number_list).push_back((double)(yyvsp[0]._number)); +} +#line 4634 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 251: - -/* Line 1464 of yacc.c */ -#line 2893 "parser.yxx" +#line 2940 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._number_list).push_back((yyvsp[(2) - (2)]._number)); + (yyval._number_list) = PTA_double::empty_array(0); } +#line 4642 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 252: - -/* Line 1464 of yacc.c */ -#line 2907 "parser.yxx" +#line 2944 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - string name = (yyvsp[(1) - (1)]._string); + (yyval._number_list).push_back((yyvsp[0]._number)); +} +#line 4650 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; + + case 253: +#line 2958 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + string name = (yyvsp[0]._string); Textures::iterator vpi = textures.find(name); if (vpi == textures.end()) { eggyyerror("Unknown texture " + name); @@ -5099,14 +4657,13 @@ yyreduce: (yyval._egg) = (*vpi).second; } } +#line 4665 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 253: - -/* Line 1464 of yacc.c */ -#line 2928 "parser.yxx" + case 254: +#line 2979 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - string name = (yyvsp[(1) - (1)]._string); + string name = (yyvsp[0]._string); Materials::iterator vpi = materials.find(name); if (vpi == materials.end()) { eggyyerror("Unknown material " + name); @@ -5115,14 +4672,13 @@ yyreduce: (yyval._egg) = (*vpi).second; } } +#line 4680 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 254: - -/* Line 1464 of yacc.c */ -#line 2949 "parser.yxx" + case 255: +#line 3000 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - string name = (yyvsp[(1) - (1)]._string); + string name = (yyvsp[0]._string); VertexPools::iterator vpi = vertex_pools.find(name); if (vpi == vertex_pools.end()) { // This will become a forward reference. @@ -5135,14 +4691,13 @@ yyreduce: (yyval._egg) = (*vpi).second; } } +#line 4699 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 255: - -/* Line 1464 of yacc.c */ -#line 2974 "parser.yxx" + case 256: +#line 3025 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - string name = (yyvsp[(1) - (1)]._string); + string name = (yyvsp[0]._string); Groups::iterator vpi = groups.find(name); if (vpi == groups.end()) { eggyyerror("Unknown group " + name); @@ -5151,161 +4706,156 @@ yyreduce: (yyval._egg) = (*vpi).second; } } +#line 4714 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 256: - -/* Line 1464 of yacc.c */ -#line 2995 "parser.yxx" + case 257: +#line 3046 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { eggyyerror("Name required."); (yyval._string) = ""; } +#line 4723 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 259: - -/* Line 1464 of yacc.c */ -#line 3024 "parser.yxx" + case 260: +#line 3075 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { eggyyerror("String required."); (yyval._string) = ""; } +#line 4732 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 261: - -/* Line 1464 of yacc.c */ -#line 3040 "parser.yxx" + case 262: +#line 3091 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { (yyval._string) = ""; } - break; - - case 263: - -/* Line 1464 of yacc.c */ -#line 3058 "parser.yxx" - { - (yyval._string) = (yyvsp[(1) - (1)]._string); -} +#line 4740 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 264: - -/* Line 1464 of yacc.c */ -#line 3062 "parser.yxx" +#line 3109 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._string) = (yyvsp[(1) - (1)]._string); + (yyval._string) = (yyvsp[0]._string); } +#line 4748 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 266: - -/* Line 1464 of yacc.c */ -#line 3079 "parser.yxx" + case 265: +#line 3113 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._string) = ""; + (yyval._string) = (yyvsp[0]._string); } +#line 4756 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 267: - -/* Line 1464 of yacc.c */ -#line 3083 "parser.yxx" +#line 3130 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._string) = (yyvsp[(1) - (1)]._string); + (yyval._string) = ""; } +#line 4764 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 268: - -/* Line 1464 of yacc.c */ -#line 3099 "parser.yxx" +#line 3134 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._string) = (yyvsp[(1) - (1)]._string); + (yyval._string) = (yyvsp[0]._string); } +#line 4772 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 269: - -/* Line 1464 of yacc.c */ -#line 3103 "parser.yxx" +#line 3150 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._string) = (yyvsp[(1) - (2)]._string) + "\n" + (yyvsp[(2) - (2)]._string); + (yyval._string) = (yyvsp[0]._string); } +#line 4780 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 271: - -/* Line 1464 of yacc.c */ -#line 3118 "parser.yxx" + case 270: +#line 3154 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._number) = (yyvsp[(1) - (1)]._ulong); + (yyval._string) = (yyvsp[-1]._string) + "\n" + (yyvsp[0]._string); } +#line 4788 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 272: - -/* Line 1464 of yacc.c */ -#line 3133 "parser.yxx" +#line 3169 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._number) = (yyvsp[(1) - (1)]._number); - (yyval._ulong) = (unsigned long)(yyvsp[(1) - (1)]._number); - (yyval._string) = (yyvsp[(1) - (1)]._string); + (yyval._number) = (yyvsp[0]._ulong); } +#line 4796 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 273: - -/* Line 1464 of yacc.c */ -#line 3139 "parser.yxx" +#line 3184 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._number) = (yyvsp[(1) - (1)]._ulong); - (yyval._ulong) = (yyvsp[(1) - (1)]._ulong); - (yyval._string) = (yyvsp[(1) - (1)]._string); + (yyval._number) = (yyvsp[0]._number); + (yyval._ulong) = (unsigned long)(yyvsp[0]._number); + (yyval._string) = (yyvsp[0]._string); } +#line 4806 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 274: - -/* Line 1464 of yacc.c */ -#line 3145 "parser.yxx" +#line 3190 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._number) = 0.0; - (yyval._ulong) = 0; - (yyval._string) = (yyvsp[(1) - (1)]._string); + (yyval._number) = (yyvsp[0]._ulong); + (yyval._ulong) = (yyvsp[0]._ulong); + (yyval._string) = (yyvsp[0]._string); } +#line 4816 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; case 275: - -/* Line 1464 of yacc.c */ -#line 3162 "parser.yxx" +#line 3196 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - int i = (int)(yyvsp[(1) - (1)]._number); - if ((double)i != (yyvsp[(1) - (1)]._number)) { + (yyval._number) = 0.0; + (yyval._ulong) = 0; + (yyval._string) = (yyvsp[0]._string); +} +#line 4826 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ + break; + + case 276: +#line 3213 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ + { + int i = (int)(yyvsp[0]._number); + if ((double)i != (yyvsp[0]._number)) { eggyywarning("Integer expected."); (yyval._number) = (double)i; } } +#line 4838 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - case 276: - -/* Line 1464 of yacc.c */ -#line 3170 "parser.yxx" + case 277: +#line 3221 "panda/src/egg/parser.yxx" /* yacc.c:1645 */ { - (yyval._number) = (yyvsp[(1) - (1)]._ulong); + (yyval._number) = (yyvsp[0]._ulong); } +#line 4846 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ break; - -/* Line 1464 of yacc.c */ -#line 5274 "y.tab.c" +#line 4850 "built/tmp/parser.yxx.c" /* yacc.c:1645 */ default: break; } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); @@ -5314,25 +4864,28 @@ yyreduce: *++yyvsp = yyval; - /* Now `shift' the result of the reduction. Determine what state + /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ - - yyn = yyr1[yyn]; - - yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; - if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) - yystate = yytable[yystate]; - else - yystate = yydefgoto[yyn - YYNTOKENS]; + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } goto yynewstate; -/*------------------------------------. -| yyerrlab -- here on detecting error | -`------------------------------------*/ +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { @@ -5340,37 +4893,36 @@ yyerrlab: #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) { - YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); - if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) - { - YYSIZE_T yyalloc = 2 * yysize; - if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) - yyalloc = YYSTACK_ALLOC_MAXIMUM; - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yyalloc); - if (yymsg) - yymsg_alloc = yyalloc; - else - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - } - } - - if (0 < yysize && yysize <= yymsg_alloc) - { - (void) yysyntax_error (yymsg, yystate, yychar); - yyerror (yymsg); - } - else - { - yyerror (YY_("syntax error")); - if (yysize != 0) - goto yyexhaustedlab; - } + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; } +# undef YYSYNTAX_ERROR #endif } @@ -5379,20 +4931,20 @@ yyerrlab: if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an - error, discard it. */ + error, discard it. */ if (yychar <= YYEOF) - { - /* Return failure if at end of input. */ - if (yychar == YYEOF) - YYABORT; - } + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } else - { - yydestruct ("Error: discarding", - yytoken, &yylval); - yychar = YYEMPTY; - } + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } } /* Else will try to reuse lookahead token after shifting the error @@ -5411,7 +4963,7 @@ yyerrorlab: if (/*CONSTCOND*/ 0) goto yyerrorlab; - /* Do not reclaim the symbols of the rule which action triggered + /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; @@ -5424,35 +4976,37 @@ yyerrorlab: | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: - yyerrstatus = 3; /* Each real token shifted decrements this. */ + yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; - if (yyn != YYPACT_NINF) - { - yyn += YYTERROR; - if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) - { - yyn = yytable[yyn]; - if (0 < yyn) - break; - } - } + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) - YYABORT; + YYABORT; yydestruct ("Error: popping", - yystos[yystate], yyvsp); + yystos[yystate], yyvsp); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END /* Shift the error token. */ @@ -5476,7 +5030,7 @@ yyabortlab: yyresult = 1; goto yyreturn; -#if !defined(yyoverflow) || YYERROR_VERBOSE +#if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ @@ -5488,16 +5042,21 @@ yyexhaustedlab: yyreturn: if (yychar != YYEMPTY) - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); - /* Do not reclaim the symbols of the rule which action triggered + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", - yystos[*yyssp], yyvsp); + yystos[*yyssp], yyvsp); YYPOPSTACK (1); } #ifndef yyoverflow @@ -5508,9 +5067,5 @@ yyreturn: if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif - /* Make sure YYID is used. */ - return YYID (yyresult); + return yyresult; } - - - diff --git a/panda/src/egg/parser.h.prebuilt b/panda/src/egg/parser.h.prebuilt index ea5a85cea9..24f4dde7da 100644 --- a/panda/src/egg/parser.h.prebuilt +++ b/panda/src/egg/parser.h.prebuilt @@ -1,20 +1,19 @@ -/* A Bison parser, made by GNU Bison 2.4.2. */ +/* A Bison parser, made by GNU Bison 3.2.2. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc. -/* Skeleton interface for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software - Foundation, Inc. - This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program. If not, see . */ @@ -27,107 +26,118 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ -/* Tokens. */ +#ifndef YY_EGGYY_BUILT_TMP_PARSER_YXX_H_INCLUDED +# define YY_EGGYY_BUILT_TMP_PARSER_YXX_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int eggyydebug; +#endif + +/* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE - /* Put the tokens into the symbol table, so that GDB and other debuggers - know about them. */ - enum yytokentype { - EGG_NUMBER = 258, - EGG_ULONG = 259, - EGG_STRING = 260, - ANIMPRELOAD = 261, - BEZIERCURVE = 262, - BFACE = 263, - BILLBOARD = 264, - BILLBOARDCENTER = 265, - BINORMAL = 266, - BUNDLE = 267, - CLOSED = 268, - COLLIDE = 269, - COMMENT = 270, - COMPONENT = 271, - COORDSYSTEM = 272, - CV = 273, - DART = 274, - DNORMAL = 275, - DRGBA = 276, - DUV = 277, - DXYZ = 278, - DCS = 279, - DISTANCE = 280, - DTREF = 281, - DYNAMICVERTEXPOOL = 282, - EXTERNAL_FILE = 283, - GROUP = 284, - DEFAULTPOSE = 285, - JOINT = 286, - KNOTS = 287, - INCLUDE = 288, - INSTANCE = 289, - LINE = 290, - LOOP = 291, - MATERIAL = 292, - MATRIX3 = 293, - MATRIX4 = 294, - MODEL = 295, - MREF = 296, - NORMAL = 297, - NURBSCURVE = 298, - NURBSSURFACE = 299, - OBJECTTYPE = 300, - ORDER = 301, - OUTTANGENT = 302, - PATCH = 303, - POINTLIGHT = 304, - POLYGON = 305, - REF = 306, - RGBA = 307, - ROTATE = 308, - ROTX = 309, - ROTY = 310, - ROTZ = 311, - SANIM = 312, - SCALAR = 313, - SCALE = 314, - SEQUENCE = 315, - SHADING = 316, - SWITCH = 317, - SWITCHCONDITION = 318, - TABLE = 319, - TABLE_V = 320, - TAG = 321, - TANGENT = 322, - TEXLIST = 323, - TEXTURE = 324, - TLENGTHS = 325, - TRANSFORM = 326, - TRANSLATE = 327, - TREF = 328, - TRIANGLEFAN = 329, - TRIANGLESTRIP = 330, - TRIM = 331, - TXT = 332, - UKNOTS = 333, - UV = 334, - AUX = 335, - VKNOTS = 336, - VERTEX = 337, - VERTEXANIM = 338, - VERTEXPOOL = 339, - VERTEXREF = 340, - XFMANIM = 341, - XFMSANIM = 342, - START_EGG = 343, - START_GROUP_BODY = 344, - START_TEXTURE_BODY = 345, - START_PRIMITIVE_BODY = 346 - }; + enum yytokentype + { + EGG_NUMBER = 258, + EGG_ULONG = 259, + EGG_STRING = 260, + ANIMPRELOAD = 261, + BEZIERCURVE = 262, + BFACE = 263, + BILLBOARD = 264, + BILLBOARDCENTER = 265, + BINORMAL = 266, + BUNDLE = 267, + CLOSED = 268, + COLLIDE = 269, + COMMENT = 270, + COMPONENT = 271, + COORDSYSTEM = 272, + CV = 273, + DART = 274, + DNORMAL = 275, + DRGBA = 276, + DUV = 277, + DXYZ = 278, + DCS = 279, + DISTANCE = 280, + DTREF = 281, + DYNAMICVERTEXPOOL = 282, + EXTERNAL_FILE = 283, + GROUP = 284, + DEFAULTPOSE = 285, + JOINT = 286, + KNOTS = 287, + INCLUDE = 288, + INSTANCE = 289, + LINE = 290, + LOOP = 291, + MATERIAL = 292, + MATRIX3 = 293, + MATRIX4 = 294, + MODEL = 295, + MREF = 296, + NORMAL = 297, + NURBSCURVE = 298, + NURBSSURFACE = 299, + OBJECTTYPE = 300, + ORDER = 301, + OUTTANGENT = 302, + PATCH = 303, + POINTLIGHT = 304, + POLYGON = 305, + REF = 306, + RGBA = 307, + ROTATE = 308, + ROTX = 309, + ROTY = 310, + ROTZ = 311, + SANIM = 312, + SCALAR = 313, + SCALE = 314, + SEQUENCE = 315, + SHADING = 316, + SWITCH = 317, + SWITCHCONDITION = 318, + TABLE = 319, + TABLE_V = 320, + TAG = 321, + TANGENT = 322, + TEXLIST = 323, + TEXTURE = 324, + TLENGTHS = 325, + TRANSFORM = 326, + TRANSLATE = 327, + TREF = 328, + TRIANGLEFAN = 329, + TRIANGLESTRIP = 330, + TRIM = 331, + TXT = 332, + UKNOTS = 333, + UV = 334, + AUX = 335, + VKNOTS = 336, + VERTEX = 337, + VERTEXANIM = 338, + VERTEXPOOL = 339, + VERTEXREF = 340, + XFMANIM = 341, + XFMSANIM = 342, + START_EGG = 343, + START_GROUP_BODY = 344, + START_TEXTURE_BODY = 345, + START_PRIMITIVE_BODY = 346 + }; #endif /* Tokens. */ #define EGG_NUMBER 258 @@ -220,15 +230,11 @@ #define START_TEXTURE_BODY 345 #define START_PRIMITIVE_BODY 346 +/* Value type. */ - -#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED - -# define yystype YYSTYPE /* obsolescent; will be withdrawn */ -# define YYSTYPE_IS_DECLARED 1 -#endif - extern YYSTYPE eggyylval; +int eggyyparse (void); +#endif /* !YY_EGGYY_BUILT_TMP_PARSER_YXX_H_INCLUDED */ diff --git a/panda/src/egg/parser.yxx b/panda/src/egg/parser.yxx index 1ffaa3a078..a41b08ba57 100644 --- a/panda/src/egg/parser.yxx +++ b/panda/src/egg/parser.yxx @@ -98,10 +98,6 @@ static Groups groups; // temporarily. static int vertex_index; -// We need to hold a matrix for a little bit while parsing the -// entries. -static LMatrix3d matrix_2d; - //////////////////////////////////////////////////////////////////// // Defining the interface to the parser. @@ -1103,6 +1099,14 @@ vertex_uv_body: } else { DCAST(EggVertexUV, egg_stack.back())->set_tangent(LNormald($4, $5, $6)); } +} + | vertex_uv_body TANGENT '{' real real real real '}' +{ + if (DCAST(EggVertexUV, egg_stack.back())->has_tangent()) { + eggyywarning("Ignoring repeated tangent"); + } else { + DCAST(EggVertexUV, egg_stack.back())->set_tangent4(LVecBase4d($4, $5, $6, $7)); + } } | vertex_uv_body BINORMAL '{' real real real '}' { diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index 6aeb84f54f..66a9ea44c6 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -726,6 +726,8 @@ make_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent, * This deprecated interface creates a NurbsCurve object for the EggNurbsCurve * entry. It will eventually be removed in favor of the above, which creates * a RopeNode. + * + * @deprecated See make_nurbs_curve. */ void EggLoader:: make_old_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent, @@ -1814,10 +1816,11 @@ make_node(EggGroup *egg_group, PandaNode *parent) { // A collision group: create collision geometry. node = new CollisionNode(egg_group->get_name()); + // Piggy-back the desired transform to apply onto the node, since we can't + // break the ABI in 1.10. + node->set_transform(TransformState::make_mat(LCAST(PN_stdfloat, egg_group->get_vertex_to_node()))); make_collision_solids(egg_group, egg_group, (CollisionNode *)node.p()); - - // Transform all of the collision solids into local space. - node->xform(LCAST(PN_stdfloat, egg_group->get_vertex_to_node())); + node->clear_transform(); if ((egg_group->get_collide_flags() & EggGroup::CF_keep) != 0) { // If we also specified to keep the geometry, continue the traversal. @@ -2773,7 +2776,7 @@ make_sphere(EggGroup *egg_group, EggGroup::CollideFlags flags, */ bool EggLoader:: make_box(EggGroup *egg_group, EggGroup::CollideFlags flags, - LPoint3 &min_p, LPoint3 &max_p, LColor &color) { + const LMatrix4 &xform, LPoint3 &min_p, LPoint3 &max_p) { EggGroup *geom_group = find_collision_geometry(egg_group, flags); if (geom_group != nullptr) { // Collect all of the vertices. @@ -2802,13 +2805,12 @@ make_box(EggGroup *egg_group, EggGroup::CollideFlags flags, } EggVertex *vertex = (*vi); - LVertexd min_pd = vertex->get_pos3(); - LVertexd max_pd = min_pd; - color = vertex->get_color(); + LPoint3 min_pd = LCAST(PN_stdfloat, vertex->get_pos3()) * xform; + LPoint3 max_pd = min_pd; for (++vi; vi != vertices.end(); ++vi) { vertex = (*vi); - const LVertexd &pos = vertex->get_pos3(); + LPoint3 pos = LCAST(PN_stdfloat, vertex->get_pos3()) * xform; min_pd.set(min(min_pd[0], pos[0]), min(min_pd[1], pos[1]), min(min_pd[2], pos[2])); @@ -2817,13 +2819,25 @@ make_box(EggGroup *egg_group, EggGroup::CollideFlags flags, max(max_pd[2], pos[2])); } - min_p = LCAST(PN_stdfloat, min_pd); - max_p = LCAST(PN_stdfloat, max_pd); + min_p = min_pd; + max_p = max_pd; return (min_pd != max_pd); } return false; } +/** + * Creates a single generic Box corresponding to the polygons associated with + * this group. This box is used by make_collision_box. + */ +bool EggLoader:: +make_box(EggGroup *egg_group, EggGroup::CollideFlags flags, + LPoint3 &min_p, LPoint3 &max_p, LColor &color) { + + color.set(1.0, 1.0, 1.0, 1.0); + return make_box(egg_group, flags, LMatrix4::ident_mat(), min_p, max_p); +} + /** * Creates CollisionSolids corresponding to the collision geometry indicated * at the given node and below. @@ -2904,6 +2918,7 @@ make_collision_plane(EggGroup *egg_group, CollisionNode *cnode, create_collision_plane(DCAST(EggPolygon, *ci), egg_group); if (csplane != nullptr) { apply_collision_flags(csplane, flags); + csplane->xform(cnode->get_transform()->get_mat()); cnode->add_solid(csplane); return; } @@ -3004,6 +3019,7 @@ make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode, CollisionSphere *cssphere = new CollisionSphere(center, radius); apply_collision_flags(cssphere, flags); + cssphere->xform(cnode->get_transform()->get_mat()); cnode->add_solid(cssphere); } } @@ -3017,8 +3033,8 @@ make_collision_box(EggGroup *egg_group, CollisionNode *cnode, EggGroup::CollideFlags flags) { LPoint3 min_p; LPoint3 max_p; - LColor dummycolor; - if (make_box(egg_group, flags, min_p, max_p, dummycolor)) { + CPT(TransformState) transform = cnode->get_transform(); + if (make_box(egg_group, flags, transform->get_mat(), min_p, max_p)) { CollisionBox *csbox = new CollisionBox(min_p, max_p); apply_collision_flags(csbox, flags); @@ -3040,6 +3056,7 @@ make_collision_inv_sphere(EggGroup *egg_group, CollisionNode *cnode, CollisionInvSphere *cssphere = new CollisionInvSphere(center, radius); apply_collision_flags(cssphere, flags); + cssphere->xform(cnode->get_transform()->get_mat()); cnode->add_solid(cssphere); } } @@ -3234,6 +3251,7 @@ make_collision_capsule(EggGroup *egg_group, CollisionNode *cnode, new CollisionCapsule(LCAST(PN_stdfloat, point_a), LCAST(PN_stdfloat, point_b), radius); apply_collision_flags(cscapsule, flags); + cscapsule->xform(cnode->get_transform()->get_mat()); cnode->add_solid(cscapsule); } } @@ -3395,6 +3413,7 @@ create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly, new CollisionPolygon(vertices_begin, vertices_end); if (cspoly->is_valid()) { apply_collision_flags(cspoly, flags); + cspoly->xform(cnode->get_transform()->get_mat()); cnode->add_solid(cspoly); } } @@ -3485,6 +3504,7 @@ create_collision_floor_mesh(CollisionNode *cnode, CollisionFloorMesh::TriangleIndices triangle = *ti; csfloor->add_triangle(triangle.p1, triangle.p2, triangle.p3); } + csfloor->xform(cnode->get_transform()->get_mat()); cnode->add_solid(csfloor); } diff --git a/panda/src/egg2pg/eggLoader.h b/panda/src/egg2pg/eggLoader.h index 0f974f41c2..ab11214e19 100644 --- a/panda/src/egg2pg/eggLoader.h +++ b/panda/src/egg2pg/eggLoader.h @@ -163,6 +163,8 @@ private: bool make_sphere(EggGroup *start_group, EggGroup::CollideFlags flags, LPoint3 ¢er, PN_stdfloat &radius, LColor &color); + bool make_box(EggGroup *start_group, EggGroup::CollideFlags flags, + const LMatrix4 &xform, LPoint3 &min_p, LPoint3 &max_p); bool make_box(EggGroup *start_group, EggGroup::CollideFlags flags, LPoint3 &min_p, LPoint3 &max_p, LColor &color); diff --git a/panda/src/event/asyncFuture.cxx b/panda/src/event/asyncFuture.cxx index 4805650260..ec07b634c9 100644 --- a/panda/src/event/asyncFuture.cxx +++ b/panda/src/event/asyncFuture.cxx @@ -14,7 +14,6 @@ #include "asyncFuture.h" #include "asyncTask.h" #include "asyncTaskManager.h" -#include "conditionVarFull.h" #include "config_event.h" #include "pStatTimer.h" #include "throw_event.h" @@ -30,6 +29,20 @@ AsyncFuture:: // If this triggers, the future destroyed before it was cancelled, which is // not valid. Unless we should simply call cancel() here? nassertv(_waiting.empty()); + + // This is an attempt to work around what appears to be a compiler bug in + // MSVC when compiling with optimizations and having an EventStoreInt stored + // in this field. It crashes when we delete via the ReferenceCount base + // instead of via the TypedObject. I haven't been able to find out why; + // just that it doesn't happen with ParamString. ~rdb + ReferenceCount *result_ref = _result_ref.p(); + if (result_ref != nullptr) { + _result_ref.cheat() = nullptr; + if (!result_ref->unref()) { + delete _result; + } + _result = nullptr; + } } /** diff --git a/panda/src/event/asyncFuture.h b/panda/src/event/asyncFuture.h index 27cab7b585..27fa1db29c 100644 --- a/panda/src/event/asyncFuture.h +++ b/panda/src/event/asyncFuture.h @@ -22,7 +22,6 @@ class AsyncTaskManager; class AsyncTask; -class ConditionVarFull; /** * This class represents a thread-safe handle to a promised future result of @@ -55,6 +54,8 @@ class ConditionVarFull; * coroutine, which only suspends the current task and not the entire thread. * * This API aims to mirror and be compatible with Python's Future class. + * + * @since 1.10.0 */ class EXPCL_PANDA_EVENT AsyncFuture : public TypedReferenceCount { PUBLISHED: diff --git a/panda/src/event/asyncTaskChain.cxx b/panda/src/event/asyncTaskChain.cxx index 34e458691e..1d511bce45 100644 --- a/panda/src/event/asyncTaskChain.cxx +++ b/panda/src/event/asyncTaskChain.cxx @@ -788,18 +788,17 @@ cleanup_task(AsyncTask *task, bool upon_death, bool clean_exit) { _manager->remove_task_by_name(task); - if (upon_death && task->set_future_state(clean_exit ? AsyncFuture::FS_finished - : AsyncFuture::FS_cancelled)) { - task->notify_done(clean_exit); - } - - task->_manager = nullptr; - if (upon_death) { _manager->_lock.unlock(); + if (task->set_future_state(clean_exit ? AsyncFuture::FS_finished + : AsyncFuture::FS_cancelled)) { + task->notify_done(clean_exit); + } task->upon_death(_manager, clean_exit); _manager->_lock.lock(); } + + task->_manager = nullptr; } /** diff --git a/panda/src/event/asyncTaskChain.h b/panda/src/event/asyncTaskChain.h index c211b30c85..6de098b6ed 100644 --- a/panda/src/event/asyncTaskChain.h +++ b/panda/src/event/asyncTaskChain.h @@ -20,7 +20,7 @@ #include "asyncTaskCollection.h" #include "typedReferenceCount.h" #include "thread.h" -#include "conditionVarFull.h" +#include "conditionVar.h" #include "pvector.h" #include "pdeque.h" #include "pStatCollector.h" @@ -159,7 +159,7 @@ protected: AsyncTaskManager *_manager; - ConditionVarFull _cvar; // signaled when one of the task heaps, _state, or _current_sort changes, or a task finishes. + ConditionVar _cvar; // signaled when one of the task heaps, _state, or _current_sort changes, or a task finishes. enum State { S_initial, // no threads yet diff --git a/panda/src/event/asyncTaskManager.cxx b/panda/src/event/asyncTaskManager.cxx index a570609f8a..b10c7ea935 100644 --- a/panda/src/event/asyncTaskManager.cxx +++ b/panda/src/event/asyncTaskManager.cxx @@ -480,6 +480,12 @@ poll() { for (unsigned int i = 0; i < _task_chains.size(); ++i) { AsyncTaskChain *chain = _task_chains[i]; chain->do_poll(); + + if (chain->_state == AsyncTaskChain::S_interrupted) { + // If a task returned DS_interrupt, we need to interrupt the entire + // manager, since an exception state may have been set. + break; + } } // Just in case the clock was ticked explicitly by one of our polling diff --git a/panda/src/event/asyncTaskManager.h b/panda/src/event/asyncTaskManager.h index 69711ab6f6..0bb137deb4 100644 --- a/panda/src/event/asyncTaskManager.h +++ b/panda/src/event/asyncTaskManager.h @@ -23,7 +23,7 @@ #include "thread.h" #include "pmutex.h" #include "mutexHolder.h" -#include "conditionVarFull.h" +#include "conditionVar.h" #include "pvector.h" #include "pdeque.h" #include "pStatCollector.h" @@ -130,7 +130,7 @@ protected: TasksByName _tasks_by_name; PT(ClockObject) _clock; - ConditionVarFull _frame_cvar; // Signalled when the clock ticks. + ConditionVar _frame_cvar; // Signalled when the clock ticks. static AsyncTaskManager* _global_ptr; diff --git a/panda/src/event/eventQueue.cxx b/panda/src/event/eventQueue.cxx index 69c025734c..cb060944c3 100644 --- a/panda/src/event/eventQueue.cxx +++ b/panda/src/event/eventQueue.cxx @@ -46,11 +46,13 @@ queue_event(CPT_Event event) { LightMutexHolder holder(_lock); _queue.push_back(event); - if (event_cat.is_spam() || event_cat.is_debug()) { + if (event_cat.is_debug()) { if (event->get_name() == "NewFrame") { // Don't bother us with this particularly spammy event. - event_cat.spam() - << "Throwing event " << *event << "\n"; + if (event_cat.is_spam()) { + event_cat.spam() + << "Throwing event " << *event << "\n"; + } } else { event_cat.debug() << "Throwing event " << *event << "\n"; @@ -79,7 +81,7 @@ is_queue_empty() const { } /** - * This function is deprecated--the queue is never full these days. + * @deprecated Always returns false; the queue can never be full. */ bool EventQueue:: is_queue_full() const { diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index 554e1368e7..fdf6be440c 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -283,15 +283,25 @@ exception() const { */ int PythonTask:: __setattr__(PyObject *self, PyObject *attr, PyObject *v) { - if (PyObject_GenericSetAttr(self, attr, v) == 0) { - return 0; - } - - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { +#if PY_MAJOR_VERSION >= 3 + if (!PyUnicode_Check(attr)) { +#else + if (!PyString_Check(attr)) { +#endif + PyErr_Format(PyExc_TypeError, + "attribute name must be string, not '%.200s'", + attr->ob_type->tp_name); return -1; } - PyErr_Clear(); + PyObject *descr = _PyType_Lookup(Py_TYPE(self), attr); + if (descr != nullptr) { + Py_INCREF(descr); + descrsetfunc f = descr->ob_type->tp_descr_set; + if (f != nullptr) { + return f(descr, self, v); + } + } if (task_cat.is_debug()) { PyObject *str = PyObject_Repr(v); @@ -352,31 +362,18 @@ __delattr__(PyObject *self, PyObject *attr) { * object. */ PyObject *PythonTask:: -__getattr__(PyObject *attr) const { - // Note that with the new Interrogate behavior, this method behaves more - // like the Python __getattr__ rather than being directly assigned to the - // tp_getattro slot (a la __getattribute__). So, we won't get here when the - // attribute has already been found via other methods. - +__getattribute__(PyObject *self, PyObject *attr) const { + // We consult the instance dict first, since the user may have overridden a + // method or something. PyObject *item = PyDict_GetItem(__dict__, attr); - if (item == nullptr) { - // PyDict_GetItem does not raise an exception. -#if PY_MAJOR_VERSION < 3 - PyErr_Format(PyExc_AttributeError, - "'PythonTask' object has no attribute '%.400s'", - PyString_AS_STRING(attr)); -#else - PyErr_Format(PyExc_AttributeError, - "'PythonTask' object has no attribute '%U'", - attr); -#endif - return nullptr; + if (item != nullptr) { + // PyDict_GetItem returns a borrowed reference. + Py_INCREF(item); + return item; } - // PyDict_GetItem returns a borrowed reference. - Py_INCREF(item); - return item; + return PyObject_GenericGetAttr(self, attr); } /** @@ -598,7 +595,9 @@ do_python_task() { // directly instead of having to do: // await taskMgr.add(Task.pause(1.0)) AsyncTask *task = (AsyncTask *)fut; - _manager->add(task); + if (!task->is_alive()) { + _manager->add(task); + } } if (fut->add_waiting_task(this)) { if (task_cat.is_debug()) { diff --git a/panda/src/event/pythonTask.h b/panda/src/event/pythonTask.h index 06ff8b6fe4..0861827f94 100644 --- a/panda/src/event/pythonTask.h +++ b/panda/src/event/pythonTask.h @@ -55,7 +55,7 @@ public: PUBLISHED: int __setattr__(PyObject *self, PyObject *attr, PyObject *v); int __delattr__(PyObject *self, PyObject *attr); - PyObject *__getattr__(PyObject *attr) const; + PyObject *__getattribute__(PyObject *self, PyObject *attr) const; int __traverse__(visitproc visit, void *arg); int __clear__(); diff --git a/panda/src/express/dcast.cxx b/panda/src/express/dcast.cxx index 63265fec6f..890468588b 100644 --- a/panda/src/express/dcast.cxx +++ b/panda/src/express/dcast.cxx @@ -21,7 +21,6 @@ #include // for IsBadWritePtr() #endif -#ifdef DO_DCAST /** * This function performs the actual check that the indicated TypedObject * pointer is of the intended type. @@ -29,6 +28,7 @@ bool _dcast_verify(TypeHandle want_handle, size_t want_size, const TypedObject *ptr) { +#ifdef DO_DCAST if (get_verify_dcast()) { if (ptr == nullptr) { // This is allowed these days. It used to be an error, but what the @@ -54,7 +54,7 @@ _dcast_verify(TypeHandle want_handle, size_t want_size, return false; } } +#endif // DO_DCAST return true; } -#endif // DO_DCAST diff --git a/panda/src/express/dcast.h b/panda/src/express/dcast.h index a234f2bbc7..db27852671 100644 --- a/panda/src/express/dcast.h +++ b/panda/src/express/dcast.h @@ -62,12 +62,12 @@ INLINE WantType *_dcast_ref(WantType *&, TypedObject *ptr); template INLINE const WantType *_dcast_ref(WantType *&, const TypedObject *ptr); -#ifdef DO_DCAST -// _dcast_verify performs the actual verification. +// _dcast_verify performs the actual verification. This is an empty function +// when DO_DCAST is not set, but we still need to define it for ABI +// compatibility reasons. EXPCL_PANDA_EXPRESS bool _dcast_verify(TypeHandle want_handle, size_t want_size, const TypedObject *ptr); -#endif // DO_DCAST #define DCAST_INTO_V(to_pointer, from_pointer) \ { \ diff --git a/panda/src/express/encrypt_string.cxx b/panda/src/express/encrypt_string.cxx index 6c400d77f7..8c21d986fa 100644 --- a/panda/src/express/encrypt_string.cxx +++ b/panda/src/express/encrypt_string.cxx @@ -17,6 +17,7 @@ #include "encryptStream.h" #include "virtualFileSystem.h" #include "config_express.h" +#include "stringStream.h" using std::istream; using std::istringstream; @@ -26,12 +27,12 @@ using std::string; /** * Encrypts the indicated source string using the given password, and the - * algorithm specified by encryption-algorithm. Returns the encrypted string. + * algorithm specified by encryption-algorithm. Returns the encrypted data. */ -string +vector_uchar encrypt_string(const string &source, const string &password, const string &algorithm, int key_length, int iteration_count) { - ostringstream dest; + StringStream dest; { OEncryptStream encrypt; @@ -48,11 +49,13 @@ encrypt_string(const string &source, const string &password, encrypt.write(source.data(), source.length()); if (encrypt.fail()) { - return string(); + return vector_uchar(); } } - return dest.str(); + vector_uchar result; + dest.swap_data(result); + return result; } /** @@ -64,8 +67,8 @@ encrypt_string(const string &source, const string &password, * easily be detected, and the return value may simply be a garbage string. */ string -decrypt_string(const string &source, const string &password) { - istringstream source_stream(source); +decrypt_string(const vector_uchar &source, const string &password) { + StringStream source_stream(source); ostringstream dest_stream; if (!decrypt_stream(source_stream, dest_stream, password)) { diff --git a/panda/src/express/encrypt_string.h b/panda/src/express/encrypt_string.h index 7b247baefe..caf7f14466 100644 --- a/panda/src/express/encrypt_string.h +++ b/panda/src/express/encrypt_string.h @@ -19,15 +19,16 @@ #ifdef HAVE_OPENSSL #include "filename.h" +#include "vector_uchar.h" BEGIN_PUBLISH -EXPCL_PANDA_EXPRESS std::string +EXPCL_PANDA_EXPRESS vector_uchar encrypt_string(const std::string &source, const std::string &password, const std::string &algorithm = std::string(), int key_length = -1, int iteration_count = -1); EXPCL_PANDA_EXPRESS std::string -decrypt_string(const std::string &source, const std::string &password); +decrypt_string(const vector_uchar &source, const std::string &password); EXPCL_PANDA_EXPRESS bool encrypt_file(const Filename &source, const Filename &dest, const std::string &password, diff --git a/panda/src/express/hashVal.cxx b/panda/src/express/hashVal.cxx index 5d74f1dac4..479f91f752 100644 --- a/panda/src/express/hashVal.cxx +++ b/panda/src/express/hashVal.cxx @@ -50,7 +50,7 @@ input_hex(istream &in) { size_t i = 0; int ch = in.get(); - while (!in.eof() && !in.fail() && isxdigit(ch)) { + while (ch != EOF && !in.fail() && isxdigit(ch)) { if (i < 32) { buffer[i] = (char)ch; } @@ -63,7 +63,7 @@ input_hex(istream &in) { return; } - if (!in.eof()) { + if (ch != EOF) { in.putback((char)ch); } else { in.clear(); diff --git a/panda/src/express/make_ca_bundle.cxx b/panda/src/express/make_ca_bundle.cxx index 0773bb88ec..2a9712b3ce 100644 --- a/panda/src/express/make_ca_bundle.cxx +++ b/panda/src/express/make_ca_bundle.cxx @@ -108,7 +108,7 @@ main(int argc, char *argv[]) { int col = 0; unsigned int ch; ch = in.get(); - while (!in.fail() && !in.eof()) { + while (!in.fail() && ch != EOF) { if (col == 0) { out << "\n "; } else if (col == col_width) { diff --git a/panda/src/express/multifile.cxx b/panda/src/express/multifile.cxx index cdb1c56ec8..f857940774 100644 --- a/panda/src/express/multifile.cxx +++ b/panda/src/express/multifile.cxx @@ -1785,8 +1785,7 @@ compare_subfile(int index, const Filename &filename) { in2.seekg(0); int byte1 = in1->get(); int byte2 = in2.get(); - while (!in1->fail() && !in1->eof() && - !in2.fail() && !in2.eof()) { + while (!in1->fail() && !in2.fail()) { if (byte1 != byte2) { close_read_subfile(in1); return false; @@ -2069,10 +2068,7 @@ open_read_subfile(Subfile *subfile) { stream = wrapper; // Validate the password by confirming that the encryption header matches. - char this_header[_encrypt_header_size]; - stream->read(this_header, _encrypt_header_size); - if (stream->fail() || stream->gcount() != (unsigned)_encrypt_header_size || - memcmp(this_header, _encrypt_header, _encrypt_header_size) != 0) { + if (!wrapper->read_magic(_encrypt_header, _encrypt_header_size)) { express_cat.error() << "Unable to decrypt subfile " << subfile->_name << ".\n"; delete stream; @@ -2497,7 +2493,7 @@ read_index(istream &read, streampos fpos, Multifile *multifile) { StreamReader reader(read); streampos next_index = multifile->word_to_streampos(reader.get_uint32()); - if (read.eof() || read.fail()) { + if (read.fail()) { _flags |= SF_index_invalid; return 0; } @@ -2529,7 +2525,7 @@ read_index(istream &read, streampos fpos, Multifile *multifile) { } size_t name_length = reader.get_uint16(); - if (read.eof() || read.fail()) { + if (read.fail()) { _flags |= SF_index_invalid; return 0; } @@ -2543,7 +2539,7 @@ read_index(istream &read, streampos fpos, Multifile *multifile) { _name = string(name_buffer, name_length); PANDA_FREE_ARRAY(name_buffer); - if (read.eof() || read.fail()) { + if (read.fail()) { _flags |= SF_index_invalid; return 0; } diff --git a/panda/src/express/p3express_composite1.cxx b/panda/src/express/p3express_composite1.cxx index 6bfb55de8e..7f071921e7 100644 --- a/panda/src/express/p3express_composite1.cxx +++ b/panda/src/express/p3express_composite1.cxx @@ -30,5 +30,4 @@ #include "pointerToArray.cxx" #include "pointerToBase.cxx" #include "pointerToVoid.cxx" -#include "profileTimer.cxx" #include "pStatCollectorForwardBase.cxx" diff --git a/panda/src/express/p3express_composite2.cxx b/panda/src/express/p3express_composite2.cxx index 5caa03986d..1d7a081996 100644 --- a/panda/src/express/p3express_composite2.cxx +++ b/panda/src/express/p3express_composite2.cxx @@ -4,12 +4,12 @@ #include "pta_float.cxx" #include "ramfile.cxx" #include "referenceCount.cxx" +#include "stringStreamBuf.cxx" +#include "stringStream.cxx" #include "subfileInfo.cxx" #include "subStream.cxx" #include "subStreamBuf.cxx" #include "temporaryFile.cxx" -#include "threadSafePointerTo.cxx" -#include "threadSafePointerToBase.cxx" #include "trueClock.cxx" #include "typedReferenceCount.cxx" #include "virtualFile.cxx" diff --git a/panda/src/express/p3express_ext_composite.cxx b/panda/src/express/p3express_ext_composite.cxx index ee104077cb..a8f3311753 100644 --- a/panda/src/express/p3express_ext_composite.cxx +++ b/panda/src/express/p3express_ext_composite.cxx @@ -1,4 +1,5 @@ #include "memoryUsagePointers_ext.cxx" #include "ramfile_ext.cxx" +#include "stringStream_ext.cxx" #include "virtualFileSystem_ext.cxx" #include "virtualFile_ext.cxx" diff --git a/panda/src/express/patchfile.cxx b/panda/src/express/patchfile.cxx index c7e212c260..bd5c4d9b8f 100644 --- a/panda/src/express/patchfile.cxx +++ b/panda/src/express/patchfile.cxx @@ -26,15 +26,6 @@ #include // for strstr -#ifdef HAVE_TAR -#include -#include // for O_RDONLY -#endif // HAVE_TAR - -#ifdef HAVE_TAR -std::istream *Patchfile::_tar_istream = nullptr; -#endif // HAVE_TAR - using std::endl; using std::ios; using std::istream; @@ -575,8 +566,10 @@ internal_read_header(const Filename &patch_file) { // get the MD5 of the resultant patched file _MD5_ofResult.read_stream(patch_reader); - express_cat.debug() - << "Patchfile::initiate() - valid patchfile" << endl; + if (express_cat.is_debug()) { + express_cat.debug() + << "Patchfile::initiate() - valid patchfile" << endl; + } return EU_success; } @@ -1158,202 +1151,6 @@ compute_mf_patches(ostream &write_stream, return true; } -#ifdef HAVE_TAR -/** - * Uses libtar to extract the location within the tar file of each of the - * subfiles. Returns true if the tar file is read successfully, false if - * there is an error (e.g. it is not a tar file). - */ -bool Patchfile:: -read_tar(TarDef &tar, istream &stream) { - TAR *tfile; - tartype_t tt; - tt.openfunc = tar_openfunc; - tt.closefunc = tar_closefunc; - tt.readfunc = tar_readfunc; - tt.writefunc = tar_writefunc; - - stream.seekg(0, ios::beg); - nassertr(_tar_istream == nullptr, false); - _tar_istream = &stream; - if (tar_open(&tfile, (char *)"dummy", &tt, O_RDONLY, 0, 0) != 0) { - _tar_istream = nullptr; - return false; - } - - // Walk through the tar file, noting the current file position as we reach - // each subfile. Use this information to infer the start and end of each - // subfile within the stream. - - streampos last_pos = 0; - int flag = th_read(tfile); - while (flag == 0) { - TarSubfile subfile; - subfile._name = th_get_pathname(tfile); - subfile._header_start = last_pos; - subfile._data_start = stream.tellg(); - subfile._data_end = subfile._data_start + (streampos)th_get_size(tfile); - tar_skip_regfile(tfile); - subfile._end = stream.tellg(); - tar.push_back(subfile); - - last_pos = subfile._end; - flag = th_read(tfile); - } - - // Create one more "subfile" for the bytes at the tail of the file. This - // subfile has no name. - TarSubfile subfile; - subfile._header_start = last_pos; - stream.clear(); - stream.seekg(0, ios::end); - subfile._data_start = stream.tellg(); - subfile._data_end = subfile._data_start; - subfile._end = subfile._data_start; - tar.push_back(subfile); - - tar_close(tfile); - _tar_istream = nullptr; - return (flag == 1); -} -#endif // HAVE_TAR - -#ifdef HAVE_TAR -/** - * Computes patches for the files, knowing that they are both tar files. This - * is similar to compute_mf_patches(). - * - * The tar indexes should have been built up by a previous call to read_tar(). - */ -bool Patchfile:: -compute_tar_patches(ostream &write_stream, - uint32_t offset_orig, uint32_t offset_new, - istream &stream_orig, istream &stream_new, - TarDef &tar_orig, TarDef &tar_new) { - - // Sort the orig list by filename, so we can quickly look up files from the - // new list. - tar_orig.sort(); - - // However, it is important to keep the new list in its original, on-disk - // order. - - // Walk through each subfile in the new tar file. If a particular subfile - // exists in both source files, we compute the patches for the subfile; for - // a new subfile, we trivially add it. If a subfile has been removed, we - // simply don't add it (we'll never even notice this case). - - IStreamWrapper stream_origw(stream_orig); - IStreamWrapper stream_neww(stream_new); - - TarDef::const_iterator ni; - streampos last_pos = 0; - for (ni = tar_new.begin(); ni != tar_new.end(); ++ni) { - const TarSubfile &sf_new =(*ni); - nassertr(sf_new._header_start == last_pos, false); - - TarDef::const_iterator oi = tar_orig.find(sf_new); - - if (oi == tar_orig.end()) { - // This is a newly-added subfile. Add it the hard way. - express_cat.info() - << "Adding subfile " << sf_new._name << "\n"; - - streampos new_start = sf_new._header_start; - size_t new_size = sf_new._end - sf_new._header_start; - char *buffer_new = (char *)PANDA_MALLOC_ARRAY(new_size); - stream_new.seekg(new_start, ios::beg); - stream_new.read(buffer_new, new_size); - cache_add_and_copy(write_stream, new_size, buffer_new, 0, 0); - PANDA_FREE_ARRAY(buffer_new); - - } else { - // This subfile exists in both the original and the new files. Patch - // it. - const TarSubfile &sf_orig =(*oi); - - // We patch the header and data of the file separately, so we can - // accurately detect nested multifiles. The extra data at the end of - // the file (possibly introduced by a tar file's blocking) is the - // footer, which is also patched separately. - if (!patch_subfile(write_stream, offset_orig, offset_new, "", - stream_origw, sf_orig._header_start, sf_orig._data_start, - stream_neww, sf_new._header_start, sf_new._data_start)) { - return false; - } - - if (!patch_subfile(write_stream, offset_orig, offset_new, sf_new._name, - stream_origw, sf_orig._data_start, sf_orig._data_end, - stream_neww, sf_new._data_start, sf_new._data_end)) { - return false; - } - - if (!patch_subfile(write_stream, offset_orig, offset_new, "", - stream_origw, sf_orig._data_end, sf_orig._end, - stream_neww, sf_new._data_end, sf_new._end)) { - return false; - } - } - - last_pos = sf_new._end; - } - - return true; -} -#endif // HAVE_TAR - -#ifdef HAVE_TAR -/** - * A callback function to redirect libtar to read from our istream instead of - * using low-level Unix I/O. - */ -int Patchfile:: -tar_openfunc(const char *, int, ...) { - // Since we don't actually open a file--the stream is already open--we do - // nothing here. - return 0; -} -#endif // HAVE_TAR - -#ifdef HAVE_TAR -/** - * A callback function to redirect libtar to read from our istream instead of - * using low-level Unix I/O. - */ -int Patchfile:: -tar_closefunc(int) { - // Since we don't actually open a file, no need to close it either. - return 0; -} -#endif // HAVE_TAR - -#ifdef HAVE_TAR -/** - * A callback function to redirect libtar to read from our istream instead of - * using low-level Unix I/O. - */ -ssize_t Patchfile:: -tar_readfunc(int, void *buffer, size_t nbytes) { - nassertr(_tar_istream != nullptr, 0); - _tar_istream->read((char *)buffer, nbytes); - return (ssize_t)_tar_istream->gcount(); -} -#endif // HAVE_TAR - -#ifdef HAVE_TAR -/** - * A callback function to redirect libtar to read from our istream instead of - * using low-level Unix I/O. - */ -ssize_t Patchfile:: -tar_writefunc(int, const void *, size_t) { - // Since we use libtar only for reading, it is an error if this method gets - // called. - nassertr(false, -1); - return -1; -} -#endif // HAVE_TAR - /** * * This implementation uses the "greedy differencing algorithm" described in @@ -1438,10 +1235,6 @@ do_compute_patches(const Filename &file_orig, const Filename &file_new, // Check whether our input files are Panda multifiles or tar files. bool is_multifile = false; -#ifdef HAVE_TAR - bool is_tarfile = false; - TarDef tar_orig, tar_new; -#endif // HAVE_TAR if (_allow_multifile) { if (strstr(file_orig.get_basename().c_str(), ".mf") != nullptr || @@ -1463,15 +1256,6 @@ do_compute_patches(const Filename &file_orig, const Filename &file_new, } PANDA_FREE_ARRAY(buffer); } -#ifdef HAVE_TAR - if (strstr(file_orig.get_basename().c_str(), ".tar") != nullptr || - strstr(file_new.get_basename().c_str(), ".tar") != nullptr) { - if (read_tar(tar_orig, stream_orig) && - read_tar(tar_new, stream_new)) { - is_tarfile = true; - } - } -#endif // HAVE_TAR } if (is_multifile) { @@ -1483,17 +1267,6 @@ do_compute_patches(const Filename &file_orig, const Filename &file_new, stream_orig, stream_new)) { return false; } -#ifdef HAVE_TAR - } else if (is_tarfile) { - if (express_cat.is_debug()) { - express_cat.debug() - << file_orig.get_basename() << " appears to be a tar file.\n"; - } - if (!compute_tar_patches(write_stream, offset_orig, offset_new, - stream_orig, stream_new, tar_orig, tar_new)) { - return false; - } -#endif // HAVE_TAR } else { if (express_cat.is_debug()) { express_cat.debug() diff --git a/panda/src/express/patchfile.h b/panda/src/express/patchfile.h index d052ef8386..852bd5b857 100644 --- a/panda/src/express/patchfile.h +++ b/panda/src/express/patchfile.h @@ -108,35 +108,6 @@ private: bool compute_mf_patches(std::ostream &write_stream, uint32_t offset_orig, uint32_t offset_new, std::istream &stream_orig, std::istream &stream_new); -#ifdef HAVE_TAR - class TarSubfile { - public: - inline bool operator < (const TarSubfile &other) const { - return _name < other._name; - } - std::string _name; - std::streampos _header_start; - std::streampos _data_start; - std::streampos _data_end; - std::streampos _end; - }; - typedef ov_set TarDef; - - bool read_tar(TarDef &tar, std::istream &stream); - bool compute_tar_patches(std::ostream &write_stream, - uint32_t offset_orig, uint32_t offset_new, - std::istream &stream_orig, std::istream &stream_new, - TarDef &tar_orig, TarDef &tar_new); - - // Because this is static, we can only call read_tar() one at a time--no - // threads, please. - static std::istream *_tar_istream; - - static int tar_openfunc(const char *filename, int oflags, ...); - static int tar_closefunc(int fd); - static ssize_t tar_readfunc(int fd, void *buffer, size_t nbytes); - static ssize_t tar_writefunc(int fd, const void *buffer, size_t nbytes); -#endif // HAVE_TAR bool do_compute_patches(const Filename &file_orig, const Filename &file_new, std::ostream &write_stream, diff --git a/panda/src/express/profileTimer.I b/panda/src/express/profileTimer.I deleted file mode 100644 index bd5102e6de..0000000000 --- a/panda/src/express/profileTimer.I +++ /dev/null @@ -1,67 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file profileTimer.I - */ - -INLINE void ProfileTimer:: -on() { - _on = TrueClock::get_global_ptr()->get_short_time(); -} - - -INLINE double ProfileTimer:: -getTime() { - double time = TrueClock::get_global_ptr()->get_short_time(); - double et=_elapsedTime+=time-_on; - _on=time; - _elapsedTime=0.0; - return et; -} - - -INLINE void ProfileTimer:: -mark(const char* tag) { - if (!_entries) { - std::cerr << "ProfileTimer::mark !_entries" << std::endl; - exit(1); - } - if (_entryCount < _maxEntries-1) { - TimerEntry& p=_entries[_entryCount]; - p._tag=tag; - p._time=getTime(); - ++_entryCount; - } else { - _entries[_entryCount]._tag="*** Overflow ***"; - } -} - - -INLINE void ProfileTimer:: -off() { - double time = TrueClock::get_global_ptr()->get_short_time(); - _elapsedTime+=time-_on; -} - - -INLINE void ProfileTimer:: -off(const char* tag) { - double time = TrueClock::get_global_ptr()->get_short_time(); - _elapsedTime+=time-_on; - mark(tag); -} - - -INLINE ProfileTimer::AutoTimer:: -~AutoTimer() { - // If the AutoTimer is the first auto ctor, then it will be the last auto - // dtor, for that block. Therefore, now is the time to mark the time for - // the blockfunction: - _profile.mark(_tag); - --_profile._autoTimerCount; -} diff --git a/panda/src/express/profileTimer.cxx b/panda/src/express/profileTimer.cxx deleted file mode 100644 index dfad4b03b4..0000000000 --- a/panda/src/express/profileTimer.cxx +++ /dev/null @@ -1,179 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file profileTimer.cxx - */ - -#include "profileTimer.h" - -#include "pmap.h" - -using std::ostream; -using std::string; - -// See ProfileTimer.h for documentation. - - -EXPCL_PANDA_EXPRESS ProfileTimer Skyler_timer_global=ProfileTimer("startup"); - -ProfileTimer* ProfileTimer::_head; - -ProfileTimer:: -ProfileTimer(const char* name, int maxEntries) : - _entries(nullptr), - _autoTimerCount(0) { - // Keep a list of the ProfileTimers, so we can print them: - _next=_head; - _head=this; - if (name) { - init(name, maxEntries); - } -} - -ProfileTimer:: -ProfileTimer(const ProfileTimer& other) { - // Add to list: - _next=_head; - _head=this; - // init it: - _name=other._name; - _maxEntries=other._maxEntries; - if (_name) { - init(_name, _maxEntries); - } - // Copy other entries: - _on=other._on; - _elapsedTime=other._elapsedTime; - _autoTimerCount=other._autoTimerCount; - _entryCount=other._entryCount; - if (other._entries) { - memcpy(_entries, other._entries, _entryCount * sizeof(TimerEntry)); - } -} - -ProfileTimer:: -~ProfileTimer() { - PANDA_FREE_ARRAY(_entries); - // Remove this from the list: - if (_head==this) { - _head=_next; - } else { - ProfileTimer* p=_head; - ProfileTimer* prior=p; - while (p) { - if (p==this) { - prior->_next=_next; - break; - } - prior=p; - p=p->_next; - } - } -} - -void ProfileTimer:: -init(const char* name, int maxEntries) { - _name=name; - _maxEntries=maxEntries; - _entries = (TimerEntry *)PANDA_MALLOC_ARRAY(_maxEntries * sizeof(TimerEntry)); - _entryCount=0; - _elapsedTime=0.0; - _on=0.0; -} - -double ProfileTimer:: -getTotalTime() const { - double total=0; - int i; - for (i=0; i<_entryCount; ++i) { - TimerEntry& te=_entries[i]; - total+=te._time; - } - return total; -} - -void ProfileTimer:: -consolidateAllTo(ostream &out) { - ProfileTimer* p=_head; - while (p) { - p->consolidateTo(out); - p=p->_next; - } -} - -void ProfileTimer:: -consolidateTo(ostream &out) const { - pmap entries; - int i; - for (i=0; i<_entryCount; ++i) { - TimerEntry& te=_entries[i]; - entries[te._tag]+=te._time; - } - out << "-------------------------------------------------------------------\n" - << "Profile Timing of " << _name - << "\n\n"; // ...should print data and time too. - double total=0; - { - pmap::const_iterator i=entries.begin(); - for (;i!=entries.end(); ++i) { - out << " " << std::setw(50) << i->first << ": " - << std::setiosflags(std::ios::fixed) << std::setprecision(6) << std::setw(10) << i->second << "\n"; - total+=i->second; - } - } - out << "\n [Total Time: " - << std::setiosflags(std::ios::fixed) << std::setprecision(6) << total - << " seconds]\n" - << "-------------------------------------------------------------------\n"; - out << std::endl; -} - -void ProfileTimer:: -printAllTo(ostream &out) { - ProfileTimer* p=_head; - while (p) { - p->printTo(out); - p=p->_next; - } -} - -void ProfileTimer:: -printTo(ostream &out) const { - out << "-------------------------------------------------------------------\n" - << "Profile Timing of " << _name - << "\n\n"; // ...should print data and time too. - double total=0; - int i; - for (i=0; i<_entryCount; ++i) { - TimerEntry& te=_entries[i]; - out << " " << std::setw(50) << te._tag << ": " - << std::setiosflags(std::ios::fixed) << std::setprecision(6) << std::setw(10) << te._time << "\n"; - total+=te._time; - } - out << "\n [Total Time: " - << std::setiosflags(std::ios::fixed) << std::setprecision(6) << total - << " seconds]\n" - << "-------------------------------------------------------------------\n"; - out << std::endl; -} - -ProfileTimer::AutoTimer::AutoTimer(ProfileTimer& profile, const char* tag) : - _profile(profile) { - _tag=tag; - if (_profile._autoTimerCount) { - // ...this is a nested call to another AutoTimer. Assign the time to the - // prior AutoTimer: - _profile.mark(_profile._entries[_profile._entryCount-1]._tag); - } else { - // ...this is not a nested call. - _profile.mark("other"); - } - // Tell the profile that it's in an AutoTimer: - ++_profile._autoTimerCount; - _profile.mark(_tag); -} diff --git a/panda/src/express/profileTimer.h b/panda/src/express/profileTimer.h deleted file mode 100644 index 39ab65c588..0000000000 --- a/panda/src/express/profileTimer.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file profileTimer.h - * @author skyler - */ - -#ifndef PROFILETIMER_H -#define PROFILETIMER_H - -#include "pandabase.h" -#include "trueClock.h" - -/* - ProfileTimer - - HowTo: - Create a ProfileTimer and hold onto it. - Call init() whenever you like (the timer doesn't - start yet). - Call on() to start the timer. - While the timer is on, call mark() at each point of interest, - in the code you are timing. - You can turn the timer off() and on() to skip things you - don't want to time. - When your timing is finished, call printTo() to see the - results (e.g. myTimer.printTo(cerr)). - - Notes: - You should be able to time things down to the millisecond - well enough, but if you call on() and off() within micro- - seconds of each other, I don't think you'll get very good - results. -*/ -class EXPCL_PANDA_EXPRESS ProfileTimer { - enum { MaxEntriesDefault=4096 }; -PUBLISHED: - explicit ProfileTimer(const char* name=0, int maxEntries=MaxEntriesDefault); - ProfileTimer(const ProfileTimer& other); - ~ProfileTimer(); - - void init(const char* name, int maxEntries=MaxEntriesDefault); - - void on(); - void mark(const char* tag); - void off(); - void off(const char* tag); - - // Don't call any of the following during timing: (Because they are slow, - // not because anything will break). - double getTotalTime() const; - static void consolidateAllTo(std::ostream &out=std::cout); - void consolidateTo(std::ostream &out=std::cout) const; - static void printAllTo(std::ostream &out=std::cout); - void printTo(std::ostream &out=std::cout) const; - -public: - /* - e.g. - void Foo() { - ProfileTimer::AutoTimer(myProfiler, "Foo()"); - ... - } - */ - class EXPCL_PANDA_EXPRESS AutoTimer { - public: - AutoTimer(ProfileTimer& profile, const char* tag); - ~AutoTimer(); - - protected: - ProfileTimer& _profile; - const char* _tag; - }; - -protected: - static ProfileTimer* _head; - ProfileTimer* _next; - class TimerEntry { - public: - const char* _tag; // not owned by this. - double _time; - }; - double _on; - double _elapsedTime; - const char* _name; // not owned by this. - int _maxEntries; - int _entryCount; - TimerEntry* _entries; - int _autoTimerCount; // see class AutoTimer - - double getTime(); - - friend class ProfileTimer::AutoTimer; -}; - -#include "profileTimer.I" - -#endif //] diff --git a/panda/src/downloader/stringStream.I b/panda/src/express/stringStream.I similarity index 88% rename from panda/src/downloader/stringStream.I rename to panda/src/express/stringStream.I index 7d1e59fc8f..f5720dd2bb 100644 --- a/panda/src/downloader/stringStream.I +++ b/panda/src/express/stringStream.I @@ -27,6 +27,15 @@ StringStream(const std::string &source) : std::iostream(&_buf) { set_data(source); } +/** + * This version of the constructor preloads the buffer with the indicated + * data. + */ +INLINE StringStream:: +StringStream(vector_uchar source) : std::iostream(&_buf) { + _buf.swap_data(source); +} + /** * Empties the buffer. */ diff --git a/panda/src/downloader/stringStream.cxx b/panda/src/express/stringStream.cxx similarity index 100% rename from panda/src/downloader/stringStream.cxx rename to panda/src/express/stringStream.cxx diff --git a/panda/src/downloader/stringStream.h b/panda/src/express/stringStream.h similarity index 93% rename from panda/src/downloader/stringStream.h rename to panda/src/express/stringStream.h index a91dd0eea5..79c10f38ed 100644 --- a/panda/src/downloader/stringStream.h +++ b/panda/src/express/stringStream.h @@ -24,9 +24,10 @@ * buffer, which can be retrieved and/or set as a string in Python 2 or a * bytes object in Python 3. */ -class EXPCL_PANDA_DOWNLOADER StringStream : public std::iostream { +class EXPCL_PANDA_EXPRESS StringStream : public std::iostream { public: INLINE StringStream(const std::string &source); + INLINE StringStream(vector_uchar source); PUBLISHED: EXTENSION(StringStream(PyObject *source)); diff --git a/panda/src/downloader/stringStreamBuf.I b/panda/src/express/stringStreamBuf.I similarity index 100% rename from panda/src/downloader/stringStreamBuf.I rename to panda/src/express/stringStreamBuf.I diff --git a/panda/src/downloader/stringStreamBuf.cxx b/panda/src/express/stringStreamBuf.cxx similarity index 100% rename from panda/src/downloader/stringStreamBuf.cxx rename to panda/src/express/stringStreamBuf.cxx diff --git a/panda/src/downloader/stringStreamBuf.h b/panda/src/express/stringStreamBuf.h similarity index 94% rename from panda/src/downloader/stringStreamBuf.h rename to panda/src/express/stringStreamBuf.h index 333e945197..f404f1e2ec 100644 --- a/panda/src/downloader/stringStreamBuf.h +++ b/panda/src/express/stringStreamBuf.h @@ -22,7 +22,7 @@ * to a memory buffer, whose contents can be appended to or extracted at any * time by application code. */ -class EXPCL_PANDA_DOWNLOADER StringStreamBuf : public std::streambuf { +class EXPCL_PANDA_EXPRESS StringStreamBuf : public std::streambuf { public: StringStreamBuf(); virtual ~StringStreamBuf(); diff --git a/panda/src/downloader/stringStream_ext.cxx b/panda/src/express/stringStream_ext.cxx similarity index 100% rename from panda/src/downloader/stringStream_ext.cxx rename to panda/src/express/stringStream_ext.cxx diff --git a/panda/src/downloader/stringStream_ext.h b/panda/src/express/stringStream_ext.h similarity index 100% rename from panda/src/downloader/stringStream_ext.h rename to panda/src/express/stringStream_ext.h diff --git a/panda/src/express/threadSafePointerTo.I b/panda/src/express/threadSafePointerTo.I deleted file mode 100644 index ef2b8db410..0000000000 --- a/panda/src/express/threadSafePointerTo.I +++ /dev/null @@ -1,207 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file threadSafePointerTo.I - * @author drose - * @date 2006-04-28 - */ - -/** - * - */ -template -INLINE ThreadSafePointerTo:: -ThreadSafePointerTo(To *ptr) : ThreadSafePointerToBase(ptr) { -} - -/** - * - */ -template -INLINE ThreadSafePointerTo:: -ThreadSafePointerTo(const ThreadSafePointerTo ©) : - ThreadSafePointerToBase((const ThreadSafePointerToBase &)copy) -{ -} - -/** - * - */ -template -INLINE ThreadSafePointerTo:: -~ThreadSafePointerTo() { -} - -/** - * - */ -template -INLINE typename ThreadSafePointerTo::To &ThreadSafePointerTo:: -operator *() const { - return *((To *)AtomicAdjust::get_ptr(this->_void_ptr)); -} - -/** - * - */ -template -INLINE typename ThreadSafePointerTo::To *ThreadSafePointerTo:: -operator -> () const { - return (To *)AtomicAdjust::get_ptr(this->_void_ptr); -} - -/** - * We also have the typecast operator to automatically convert - * ThreadSafePointerTo's to the required kind of actual pointer. This - * introduces ambiguities which the compiler will resolve one way or the - * other, but we don't care which way it goes because either will be correct. - */ -template -INLINE ThreadSafePointerTo:: -operator T * () const { - return (To *)AtomicAdjust::get_ptr(this->_void_ptr); -} - -/** - * Returns an ordinary pointer instead of a ThreadSafePointerTo. Useful to - * work around compiler problems, particularly for implicit upcasts. - */ -template -INLINE typename ThreadSafePointerTo::To *ThreadSafePointerTo:: -p() const { - return (To *)AtomicAdjust::get_ptr(this->_void_ptr); -} - -/** - * - */ -template -INLINE ThreadSafePointerTo &ThreadSafePointerTo:: -operator = (To *ptr) { - this->reassign(ptr); - return *this; -} - -/** - * - */ -template -INLINE ThreadSafePointerTo &ThreadSafePointerTo:: -operator = (const ThreadSafePointerTo ©) { - this->reassign((const ThreadSafePointerToBase &)copy); - return *this; -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo:: -ThreadSafeConstPointerTo(const typename ThreadSafeConstPointerTo::To *ptr) : - ThreadSafePointerToBase((typename ThreadSafeConstPointerTo::To *)ptr) -{ -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo:: -ThreadSafeConstPointerTo(const ThreadSafePointerTo ©) : - ThreadSafePointerToBase((const ThreadSafePointerToBase &)copy) -{ -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo:: -~ThreadSafeConstPointerTo() { -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo:: -ThreadSafeConstPointerTo(const ThreadSafeConstPointerTo ©) : - ThreadSafePointerToBase((const ThreadSafePointerToBase &)copy) -{ -} - -/** - * - */ -template -INLINE const typename ThreadSafeConstPointerTo::To &ThreadSafeConstPointerTo:: -operator *() const { - return *((To *)AtomicAdjust::get_ptr(this->_void_ptr)); -} - -/** - * - */ -template -INLINE const typename ThreadSafeConstPointerTo::To *ThreadSafeConstPointerTo:: -operator -> () const { - return (To *)AtomicAdjust::get_ptr(this->_void_ptr); -} - -/** - * We also have the typecast operator to automatically convert - * ThreadSafeConstPointerTo's to the required kind of actual pointer. This - * introduces ambiguities which the compiler will resolve one way or the - * other, but we don't care which way it goes because either will be correct. - */ - -template -INLINE ThreadSafeConstPointerTo:: -operator const T * () const { - return (To *)AtomicAdjust::get_ptr(this->_void_ptr); -} - -/** - * Returns an ordinary pointer instead of a ThreadSafeConstPointerTo. Useful - * to work around compiler problems, particularly for implicit upcasts. - */ -template -INLINE const typename ThreadSafeConstPointerTo::To *ThreadSafeConstPointerTo:: -p() const { - return (To *)AtomicAdjust::get_ptr(this->_void_ptr); -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo &ThreadSafeConstPointerTo:: -operator = (const To *ptr) { - this->reassign((To *)ptr); - return *this; -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo &ThreadSafeConstPointerTo:: -operator = (const ThreadSafePointerTo ©) { - this->reassign((const ThreadSafePointerToBase &)copy); - return *this; -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo &ThreadSafeConstPointerTo:: -operator = (const ThreadSafeConstPointerTo ©) { - this->reassign((const ThreadSafePointerToBase &)copy); - return *this; -} diff --git a/panda/src/express/threadSafePointerTo.cxx b/panda/src/express/threadSafePointerTo.cxx deleted file mode 100644 index 4c3692d32e..0000000000 --- a/panda/src/express/threadSafePointerTo.cxx +++ /dev/null @@ -1,14 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file threadSafePointerTo.cxx - * @author drose - * @date 2006-04-28 - */ - -#include "threadSafePointerTo.h" diff --git a/panda/src/express/threadSafePointerTo.h b/panda/src/express/threadSafePointerTo.h deleted file mode 100644 index 88699fa23a..0000000000 --- a/panda/src/express/threadSafePointerTo.h +++ /dev/null @@ -1,105 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file threadSafePointerTo.h - * @author drose - * @date 2006-04-28 - */ - -#ifndef THREADSAFEPOINTERTO_H -#define THREADSAFEPOINTERTO_H - -#include "pandabase.h" -#include "threadSafePointerToBase.h" - -/** - * This works exactly like PointerTo, except that the object is designed to be - * thread-safe: it is generally safe to make unprotected assignments to this - * pointer, in the sense that the last assignment will win and the reference - * counts will be properly maintained. - */ -template -class ThreadSafePointerTo : public ThreadSafePointerToBase { -public: - typedef typename ThreadSafePointerToBase::To To; -PUBLISHED: - INLINE ThreadSafePointerTo(To *ptr = nullptr); - INLINE ThreadSafePointerTo(const ThreadSafePointerTo ©); - INLINE ~ThreadSafePointerTo(); - -public: - INLINE To &operator *() const; - INLINE To *operator -> () const; - // MSVC.NET 2005 insists that we use T *, and not To *, here. - INLINE operator T *() const; - -PUBLISHED: - // When downcasting to a derived class from a - // ThreadSafePointerTo, C++ would normally require you to cast - // twice: once to an actual BaseClass pointer, and then again to your - // desired pointer. You can use the handy function p() to avoid this first - // cast and make your code look a bit cleaner. - - // e.g. instead of (MyType *)(BaseClass *)ptr, use (MyType *)ptr.p() - - // If your base class is a derivative of TypedObject, you might want to use - // the DCAST macro defined in typedObject.h instead, e.g. DCAST(MyType, - // ptr). This provides a clean downcast that doesn't require .p() or any - // double-casting, and it can be run-time checked for correctness. - INLINE To *p() const; - - INLINE ThreadSafePointerTo &operator = (To *ptr); - INLINE ThreadSafePointerTo &operator = (const ThreadSafePointerTo ©); - - // These functions normally wouldn't need to be redefined here, but we do so - // anyway just to help out interrogate (which doesn't seem to want to - // automatically export the ThreadSafePointerToBase class). When this works - // again in interrogate, we can remove these. - INLINE bool is_null() const { return ThreadSafePointerToBase::is_null(); } - INLINE void clear() { ThreadSafePointerToBase::clear(); } -}; - - -/** - * - */ -template -class ThreadSafeConstPointerTo : public ThreadSafePointerToBase { -public: - typedef typename ThreadSafePointerToBase::To To; -PUBLISHED: - INLINE ThreadSafeConstPointerTo(const To *ptr = nullptr); - INLINE ThreadSafeConstPointerTo(const ThreadSafePointerTo ©); - INLINE ThreadSafeConstPointerTo(const ThreadSafeConstPointerTo ©); - INLINE ~ThreadSafeConstPointerTo(); - -public: - INLINE const To &operator *() const; - INLINE const To *operator -> () const; - INLINE operator const T *() const; - -PUBLISHED: - INLINE const To *p() const; - - INLINE ThreadSafeConstPointerTo &operator = (const To *ptr); - INLINE ThreadSafeConstPointerTo &operator = (const ThreadSafePointerTo ©); - INLINE ThreadSafeConstPointerTo &operator = (const ThreadSafeConstPointerTo ©); - - // This functions normally wouldn't need to be redefined here, but we do so - // anyway just to help out interrogate (which doesn't seem to want to - // automatically export the ThreadSafePointerToBase class). When this works - // again in interrogate, we can remove this. - INLINE void clear() { ThreadSafePointerToBase::clear(); } -}; - -#define TSPT(type) ThreadSafePointerTo< type > -#define TSCPT(type) ThreadSafeConstPointerTo< type > - -#include "threadSafePointerTo.I" - -#endif diff --git a/panda/src/express/threadSafePointerToBase.I b/panda/src/express/threadSafePointerToBase.I deleted file mode 100644 index bee16180b1..0000000000 --- a/panda/src/express/threadSafePointerToBase.I +++ /dev/null @@ -1,132 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file threadSafePointerToBase.I - * @author drose - * @date 2006-04-28 - */ - -/** - * - */ -template -INLINE ThreadSafePointerToBase:: -ThreadSafePointerToBase(To *ptr) { - reassign(ptr); -} - -/** - * - */ -template -INLINE ThreadSafePointerToBase:: -ThreadSafePointerToBase(const ThreadSafePointerToBase ©) { - reassign(copy); -} - -/** - * - */ -template -INLINE ThreadSafePointerToBase:: -~ThreadSafePointerToBase() { - reassign(nullptr); -} - -/** - * This is the main work of the ThreadSafePointerTo family. When the pointer - * is reassigned, decrement the old reference count and increment the new one. - */ -template -INLINE void ThreadSafePointerToBase:: -reassign(To *ptr) { - To *old_ptr = (To *)AtomicAdjust::get_ptr(_void_ptr); - if (ptr == old_ptr) { - return; - } - -#ifdef HAVE_THREADS - void *orig_ptr = AtomicAdjust::compare_and_exchange_ptr(_void_ptr, old_ptr, ptr); - while (orig_ptr != old_ptr) { - // Some other thread assigned it first. Try again. - old_ptr = (To *)AtomicAdjust::get_ptr(_void_ptr); - if (ptr == old_ptr) { - return; - } - - orig_ptr = AtomicAdjust::compare_and_exchange_ptr(_void_ptr, old_ptr, ptr); - } -#else // HAVE_THREADS - _void_ptr = ptr; -#endif // HAVE_THREADS - - if (ptr != nullptr) { - ptr->ref(); -#ifdef DO_MEMORY_USAGE - if (MemoryUsage::get_track_memory_usage()) { - update_type(ptr); - } -#endif - } - - // Now delete the old pointer. - if (old_ptr != nullptr) { - unref_delete(old_ptr); - } -} - -/** - * - */ -template -INLINE void ThreadSafePointerToBase:: -reassign(const ThreadSafePointerToBase ©) { - reassign((To *)copy._void_ptr); -} - -/** - * Ensures that the MemoryUsage record for the pointer has the right type of - * object, if we know the type ourselves. - */ -template -void ThreadSafePointerToBase:: -update_type(To *ptr) { -#ifdef DO_MEMORY_USAGE - TypeHandle type = get_type_handle(To); - if (type == TypeHandle::none()) { - do_init_type(To); - type = get_type_handle(To); - } - if (type != TypeHandle::none()) { - MemoryUsage::update_type(ptr, type); - } -#endif // DO_MEMORY_USAGE -} - -/** - * A convenient way to set the ThreadSafePointerTo object to NULL. (Assignment - * to a NULL pointer also works, of course.) - */ -template -INLINE void ThreadSafePointerToBase:: -clear() { - reassign(nullptr); -} - -/** - * A handy function to output ThreadSafePointerTo's as a hex pointer followed - * by a reference count. - */ -template -INLINE void ThreadSafePointerToBase:: -output(std::ostream &out) const { - out << _void_ptr; - if (_void_ptr != nullptr) { - out << ":" << ((To *)_void_ptr)->get_ref_count(); - } -} diff --git a/panda/src/express/threadSafePointerToBase.cxx b/panda/src/express/threadSafePointerToBase.cxx deleted file mode 100644 index 20d0230c86..0000000000 --- a/panda/src/express/threadSafePointerToBase.cxx +++ /dev/null @@ -1,14 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file threadSafePointerToBase.cxx - * @author drose - * @date 2006-04-28 - */ - -#include "threadSafePointerToBase.h" diff --git a/panda/src/express/threadSafePointerToBase.h b/panda/src/express/threadSafePointerToBase.h deleted file mode 100644 index 764e538d2b..0000000000 --- a/panda/src/express/threadSafePointerToBase.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file threadSafePointerToBase.h - * @author drose - * @date 2006-04-28 - */ - -#ifndef THREADSAFEPOINTERTOBASE_H -#define THREADSAFEPOINTERTOBASE_H - -#include "pandabase.h" -#include "pointerToVoid.h" -#include "referenceCount.h" -#include "typedef.h" -#include "memoryUsage.h" -#include "config_express.h" -#include "atomicAdjust.h" - -/** - * This is the base class for ThreadSafePointerTo and - * ThreadSafeConstPointerTo. Don't try to use it directly; use either derived - * class instead. - */ -template -class ThreadSafePointerToBase : public PointerToVoid { -public: - typedef T To; - -protected: - INLINE ThreadSafePointerToBase(To *ptr); - INLINE ThreadSafePointerToBase(const ThreadSafePointerToBase ©); - INLINE ~ThreadSafePointerToBase(); - - INLINE void reassign(To *ptr); - INLINE void reassign(const ThreadSafePointerToBase ©); - - void update_type(To *ptr); - - // No assignment or retrieval functions are declared in - // ThreadSafePointerToBase, because we will have to specialize on const vs. - // non-const later. - -PUBLISHED: - INLINE void clear(); - - void output(std::ostream &out) const; -}; - -template -INLINE std::ostream &operator <<(std::ostream &out, const ThreadSafePointerToBase &pointer) { - pointer.output(out); - return out; -} - -#include "threadSafePointerToBase.I" - -#endif diff --git a/panda/src/express/weakPointerTo.I b/panda/src/express/weakPointerTo.I index 8d60057c31..316fe8ca71 100644 --- a/panda/src/express/weakPointerTo.I +++ b/panda/src/express/weakPointerTo.I @@ -118,9 +118,11 @@ operator T * () const { /** * A thread-safe way to access the underlying pointer; will silently return * null if the underlying pointer was deleted or null. - * Note that this may return null even if was_deleted() still returns true, + * Note that this may return null even if was_deleted() still returns false, * which can occur if the object has reached reference count 0 and is about to * be destroyed. + * + * @since 1.10.0 */ template INLINE PointerTo WeakPointerTo:: @@ -397,6 +399,11 @@ operator const T * () const { /** * A thread-safe way to access the underlying pointer; will silently return * null if the underlying pointer was deleted or null. + * Note that this may return null even if was_deleted() still returns false, + * which can occur if the object has reached reference count 0 and is about to + * be destroyed. + * + * @since 1.10.0 */ template INLINE ConstPointerTo WeakConstPointerTo:: diff --git a/panda/src/express/weakPointerToBase.I b/panda/src/express/weakPointerToBase.I index 5673114764..3b27047da1 100644 --- a/panda/src/express/weakPointerToBase.I +++ b/panda/src/express/weakPointerToBase.I @@ -290,7 +290,7 @@ update_type(To *ptr) { * A thread-safe way to access the underlying pointer; will only write to the * given pointer if the underlying pointer has not yet been deleted and is not * null. Note that it may leave the pointer unassigned even if was_deleted() - * still returns true, which can occur if the object has reached reference + * still returns false, which can occur if the object has reached reference * count 0 and is about to be destroyed. */ template diff --git a/panda/src/express/weakReferenceList.I b/panda/src/express/weakReferenceList.I index 0e4efc8c52..eb6e7b9976 100644 --- a/panda/src/express/weakReferenceList.I +++ b/panda/src/express/weakReferenceList.I @@ -13,6 +13,8 @@ /** * Increases the number of weak references. + * + * @since 1.10.0 */ INLINE void WeakReferenceList:: ref() const { @@ -23,6 +25,8 @@ ref() const { * Decreases the number of weak references. Returns true if, after this, * there are still any weak or strong references remaining, or false if this * structure should be deleted right away. + * + * @since 1.10.0 */ INLINE bool WeakReferenceList:: unref() const { @@ -32,6 +36,8 @@ unref() const { /** * Returns true if the object represented has been deleted, ie. there are only * weak references left pointing to the object. + * + * @since 1.10.0 */ INLINE bool WeakReferenceList:: was_deleted() const { diff --git a/panda/src/express/weakReferenceList.cxx b/panda/src/express/weakReferenceList.cxx index 0534556d7a..ac1363c85a 100644 --- a/panda/src/express/weakReferenceList.cxx +++ b/panda/src/express/weakReferenceList.cxx @@ -37,6 +37,8 @@ WeakReferenceList:: * * The data pointer can be an arbitrary pointer and is passed as only argument * to the callback. + * + * @since 1.10.0 */ void WeakReferenceList:: add_callback(WeakPointerCallback *callback, void *data) { @@ -61,6 +63,8 @@ add_callback(WeakPointerCallback *callback, void *data) { * weak reference-counting pointer), this removes the indicated PointerToVoid * structure from the list of such structures that are maintaining a weak * pointer to this object. + * + * @since 1.10.0 */ void WeakReferenceList:: remove_callback(WeakPointerCallback *callback) { @@ -73,6 +77,8 @@ remove_callback(WeakPointerCallback *callback) { /** * Called only by the ReferenceCount pointer to indicate that it has been * deleted. + * + * @since 1.10.0 */ void WeakReferenceList:: mark_deleted() { diff --git a/panda/src/express/zStreamBuf.cxx b/panda/src/express/zStreamBuf.cxx index 877254918d..57a64bed7a 100644 --- a/panda/src/express/zStreamBuf.cxx +++ b/panda/src/express/zStreamBuf.cxx @@ -202,8 +202,8 @@ seekoff(streamoff off, ios_seekdir dir, ios_openmode which) { gbump(n); - _source->seekg(0, ios::beg); - if (_source->tellg() == (streampos)0) { + if (_source->rdbuf()->pubseekpos(0, ios::in) == (streampos)0) { + _source->clear(); _z_source.next_in = Z_NULL; _z_source.avail_in = 0; _z_source.next_out = Z_NULL; diff --git a/panda/src/ffmpeg/config_ffmpeg.cxx b/panda/src/ffmpeg/config_ffmpeg.cxx index 0dbe147d27..fddd9bb81d 100644 --- a/panda/src/ffmpeg/config_ffmpeg.cxx +++ b/panda/src/ffmpeg/config_ffmpeg.cxx @@ -17,8 +17,8 @@ #include "ffmpegVideoCursor.h" #include "ffmpegAudio.h" #include "ffmpegAudioCursor.h" - #include "movieTypeRegistry.h" +#include "pandaSystem.h" extern "C" { #include @@ -129,6 +129,9 @@ init_libffmpeg() { FfmpegVideo::register_with_read_factory(); FfmpegVideoCursor::register_with_read_factory(); + PandaSystem *ps = PandaSystem::get_global_ptr(); + ps->add_system("FFmpeg"); + // Register ffmpeg as catch-all audiovideo type. MovieTypeRegistry *reg = MovieTypeRegistry::get_global_ptr(); reg->register_audio_type(&FfmpegAudio::make, "*"); diff --git a/panda/src/ffmpeg/ffmpegAudioCursor.cxx b/panda/src/ffmpeg/ffmpegAudioCursor.cxx index 4b17800c2b..af66ef28d4 100644 --- a/panda/src/ffmpeg/ffmpegAudioCursor.cxx +++ b/panda/src/ffmpeg/ffmpegAudioCursor.cxx @@ -132,8 +132,10 @@ FfmpegAudioCursor(FfmpegAudio *src) : // Set up the resample context if necessary. if (_audio_ctx->sample_fmt != AV_SAMPLE_FMT_S16) { #ifdef HAVE_SWRESAMPLE - ffmpeg_cat.debug() - << "Codec does not use signed 16-bit sample format. Setting up swresample context.\n"; + if (ffmpeg_cat.is_debug()) { + ffmpeg_cat.debug() + << "Codec does not use signed 16-bit sample format. Setting up swresample context.\n"; + } _resample_ctx = swr_alloc(); av_opt_set_int(_resample_ctx, "in_channel_count", _audio_channels, 0); @@ -462,7 +464,7 @@ seek(double t) { * read. Your buffer must be equal in size to N * channels. Multiple-channel * audio will be interleaved. */ -void FfmpegAudioCursor:: +int FfmpegAudioCursor:: read_samples(int n, int16_t *data) { int desired = n * _audio_channels; @@ -486,4 +488,5 @@ read_samples(int n, int16_t *data) { } _samples_read += n; + return n; } diff --git a/panda/src/ffmpeg/ffmpegAudioCursor.h b/panda/src/ffmpeg/ffmpegAudioCursor.h index f3963ff527..77e8890d6c 100644 --- a/panda/src/ffmpeg/ffmpegAudioCursor.h +++ b/panda/src/ffmpeg/ffmpegAudioCursor.h @@ -45,7 +45,7 @@ PUBLISHED: virtual void seek(double offset); public: - virtual void read_samples(int n, int16_t *data); + virtual int read_samples(int n, int16_t *data); protected: void fetch_packet(); diff --git a/panda/src/ffmpeg/ffmpegVideoCursor.cxx b/panda/src/ffmpeg/ffmpegVideoCursor.cxx index fa5789b6c4..8b700d2342 100644 --- a/panda/src/ffmpeg/ffmpegVideoCursor.cxx +++ b/panda/src/ffmpeg/ffmpegVideoCursor.cxx @@ -138,6 +138,15 @@ init_from(FfmpegVideo *source) { _convert_ctx = sws_getContext(_size_x, _size_y, _video_ctx->pix_fmt, _size_x, _size_y, (AVPixelFormat)_pixel_format, SWS_BILINEAR | SWS_PRINT_INFO, nullptr, nullptr, nullptr); +#else + if (_video_ctx->pix_fmt != _pixel_format) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(_video_ctx->pix_fmt); + ffmpeg_cat.error() + << "Video with pixel format " << (desc ? desc->name : "?") + << " needs conversion, but libswscale support is not enabled.\n"; + cleanup(); + return; + } #endif // HAVE_SWSCALE #ifdef HAVE_THREADS @@ -755,7 +764,9 @@ do_poll() { PT(FfmpegBuffer) frame = do_alloc_frame(); nassertr(frame != nullptr, false); _lock.release(); - advance_to_frame(seek_frame); + if (seek_frame != _begin_frame) { + advance_to_frame(seek_frame); + } if (_frame_ready) { export_frame(frame); _lock.acquire(); @@ -1162,24 +1173,29 @@ export_frame(FfmpegBuffer *buffer) { buffer->_begin_frame = _begin_frame; buffer->_end_frame = _end_frame; +#ifdef HAVE_SWSCALE + nassertv(_convert_ctx != nullptr && _frame != nullptr); if (ffmpeg_global_lock) { ReMutexHolder av_holder(_av_lock); -#ifdef HAVE_SWSCALE - nassertv(_convert_ctx != nullptr && _frame != nullptr && _frame_out != nullptr); sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize); -#else - img_convert((AVPicture *)_frame_out, (AVPixelFormat)_pixel_format, - (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y); -#endif } else { -#ifdef HAVE_SWSCALE - nassertv(_convert_ctx != nullptr && _frame != nullptr && _frame_out != nullptr); sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize); -#else - img_convert((AVPicture *)_frame_out, (AVPixelFormat)_pixel_format, - (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y); -#endif } +#else + nassertv(_frame != nullptr); + uint8_t const *src = _frame->data[0]; + uint8_t *dst = _frame_out->data[0]; + int src_stride = _frame->linesize[0]; + int dst_stride = _frame_out->linesize[0]; + size_t copy_size = _size_x * _num_components; + nassertv(copy_size <= (size_t)std::abs(src_stride)); + + for (int y = 0; y < _size_y; ++y) { + memcpy(dst, src, copy_size); + src += src_stride; + dst += dst_stride; + } +#endif } /** diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index ebb038de7a..d5252944f5 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -659,6 +659,10 @@ rebuild_bitplanes() { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } + + // Mark the GSG as supporting multisampling, so that it will respect an + // AntialiasAttrib with mode M_multisample. + glgsg->_supports_multisample = true; } else { glDisable(GL_MULTISAMPLE); } @@ -774,7 +778,9 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, } if (attachpoint == GL_DEPTH_ATTACHMENT_EXT) { - GLCAT.debug() << "Binding texture " << *tex << " to depth attachment.\n"; + if (GLCAT.is_debug()) { + GLCAT.debug() << "Binding texture " << *tex << " to depth attachment.\n"; + } attach_tex(layer, 0, tex, GL_DEPTH_ATTACHMENT_EXT); @@ -785,7 +791,9 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, #endif if (slot == RTP_depth_stencil) { - GLCAT.debug() << "Binding texture " << *tex << " to stencil attachment.\n"; + if (GLCAT.is_debug()) { + GLCAT.debug() << "Binding texture " << *tex << " to stencil attachment.\n"; + } attach_tex(layer, 0, tex, GL_STENCIL_ATTACHMENT_EXT); @@ -797,7 +805,9 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, } } else { - GLCAT.debug() << "Binding texture " << *tex << " to color attachment.\n"; + if (GLCAT.is_debug()) { + GLCAT.debug() << "Binding texture " << *tex << " to color attachment.\n"; + } attach_tex(layer, 0, tex, attachpoint); @@ -984,7 +994,9 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rb[slot]); if (slot == RTP_depth_stencil) { - GLCAT.debug() << "Creating depth stencil renderbuffer.\n"; + if (GLCAT.is_debug()) { + GLCAT.debug() << "Creating depth stencil renderbuffer.\n"; + } // Allocate renderbuffer storage for depth stencil. GLint depth_size = 0, stencil_size = 0; glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, gl_format, _rb_size_x, _rb_size_y); @@ -1010,7 +1022,9 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, report_my_gl_errors(); } else if (slot == RTP_depth) { - GLCAT.debug() << "Creating depth renderbuffer.\n"; + if (GLCAT.is_debug()) { + GLCAT.debug() << "Creating depth renderbuffer.\n"; + } // Allocate renderbuffer storage for regular depth. GLint depth_size = 0; glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, gl_format, _rb_size_x, _rb_size_y); @@ -1048,7 +1062,9 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, report_my_gl_errors(); } else { - GLCAT.debug() << "Creating color renderbuffer.\n"; + if (GLCAT.is_debug()) { + GLCAT.debug() << "Creating color renderbuffer.\n"; + } glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, gl_format, _rb_size_x, _rb_size_y); GLint red_size = 0, green_size = 0, blue_size = 0, alpha_size = 0; @@ -1162,12 +1178,6 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot, GLuint gl_format = GL_RGBA; #ifndef OPENGLES switch (slot) { - case RTP_aux_rgba_0: - case RTP_aux_rgba_1: - case RTP_aux_rgba_2: - case RTP_aux_rgba_3: - gl_format = GL_RGBA; - break; case RTP_aux_hrgba_0: case RTP_aux_hrgba_1: case RTP_aux_hrgba_2: @@ -1180,8 +1190,18 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot, case RTP_aux_float_3: gl_format = GL_RGBA32F_ARB; break; + case RTP_aux_rgba_0: + case RTP_aux_rgba_1: + case RTP_aux_rgba_2: + case RTP_aux_rgba_3: default: - gl_format = GL_RGBA; + if (_fb_properties.get_srgb_color()) { + gl_format = GL_SRGB8_ALPHA8; + } else if (_fb_properties.get_float_color()) { + gl_format = GL_RGBA32F_ARB; + } else { + gl_format = GL_RGBA; + } break; } #endif diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 6151a2c00d..42b29f5944 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -275,6 +275,22 @@ uchar_bgr_to_rgb(unsigned char *dest, const unsigned char *source, } } +/** + * Recopies the given array of pixels, converting from luminance to RGB + * arrangement. + */ +static void +uchar_l_to_rgb(unsigned char *dest, const unsigned char *source, + int num_pixels) { + for (int i = 0; i < num_pixels; i++) { + dest[0] = source[0]; + dest[1] = source[0]; + dest[2] = source[0]; + dest += 3; + source += 1; + } +} + /** * Recopies the given array of pixels, converting from BGRA to RGBA * arrangement. @@ -292,6 +308,23 @@ uchar_bgra_to_rgba(unsigned char *dest, const unsigned char *source, } } +/** + * Recopies the given array of pixels, converting from luminance-alpha to RGBA + * arrangement. + */ +static void +uchar_la_to_rgba(unsigned char *dest, const unsigned char *source, + int num_pixels) { + for (int i = 0; i < num_pixels; i++) { + dest[0] = source[0]; + dest[1] = source[0]; + dest[2] = source[0]; + dest[3] = source[1]; + dest += 4; + source += 2; + } +} + /** * Recopies the given array of pixels, converting from BGR to RGB arrangement. */ @@ -307,6 +340,22 @@ ushort_bgr_to_rgb(unsigned short *dest, const unsigned short *source, } } +/** + * Recopies the given array of pixels, converting from luminance to RGB + * arrangement. + */ +static void +ushort_l_to_rgb(unsigned short *dest, const unsigned short *source, + int num_pixels) { + for (int i = 0; i < num_pixels; i++) { + dest[0] = source[0]; + dest[1] = source[0]; + dest[2] = source[0]; + dest += 3; + source += 1; + } +} + /** * Recopies the given array of pixels, converting from BGRA to RGBA * arrangement. @@ -324,6 +373,23 @@ ushort_bgra_to_rgba(unsigned short *dest, const unsigned short *source, } } +/** + * Recopies the given array of pixels, converting from luminance-alpha to RGBA + * arrangement. + */ +static void +ushort_la_to_rgba(unsigned short *dest, const unsigned short *source, + int num_pixels) { + for (int i = 0; i < num_pixels; i++) { + dest[0] = source[0]; + dest[1] = source[0]; + dest[2] = source[0]; + dest[3] = source[1]; + dest += 4; + source += 2; + } +} + /** * Reverses the order of the components within the image, to convert (for * instance) GL_BGR to GL_RGB. Returns the byte pointer representing the @@ -343,6 +409,12 @@ fix_component_ordering(PTA_uchar &new_image, switch (external_format) { case GL_RGB: + if (tex->get_num_components() == 1) { + new_image = PTA_uchar::empty_array(orig_image_size * 3); + uchar_l_to_rgb(new_image, orig_image, orig_image_size); + result = new_image; + break; + } switch (tex->get_component_type()) { case Texture::T_unsigned_byte: case Texture::T_byte: @@ -366,6 +438,12 @@ fix_component_ordering(PTA_uchar &new_image, break; case GL_RGBA: + if (tex->get_num_components() == 2) { + new_image = PTA_uchar::empty_array(orig_image_size * 2); + uchar_la_to_rgba(new_image, orig_image, orig_image_size / 2); + result = new_image; + break; + } switch (tex->get_component_type()) { case Texture::T_unsigned_byte: case Texture::T_byte: @@ -597,8 +675,31 @@ reset() { query_glsl_version(); #ifndef OPENGLES - bool core_profile = is_at_least_gl_version(3, 2) && - !has_extension("GL_ARB_compatibility"); + // Determine whether this OpenGL context has compatibility features. + bool core_profile = false; + + if (_gl_version_major >= 3) { + if (_gl_version_major > 3 || _gl_version_minor >= 2) { + // OpenGL 3.2 has a built-in way to check this. + GLint profile_mask = 0; + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profile_mask); + + if (profile_mask & GL_CONTEXT_CORE_PROFILE_BIT) { + core_profile = true; + } else if (profile_mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) { + core_profile = false; + } else { + core_profile = !has_extension("GL_ARB_compatibility"); + } + } else { + // OpenGL 3.0/3.1. + GLint flags = 0; + glGetIntegerv(GL_CONTEXT_FLAGS, &flags); + if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) { + core_profile = true; + } + } + } if (GLCAT.is_debug()) { if (core_profile) { @@ -699,10 +800,12 @@ reset() { || has_extension("GL_KHR_debug") || has_extension("GL_ARB_debug_output"); - if (_supports_debug) { - GLCAT.debug() << "gl-debug supported, but NOT enabled.\n"; - } else { - GLCAT.debug() << "gl-debug disabled and unsupported.\n"; + if (GLCAT.is_debug()) { + if (_supports_debug) { + GLCAT.debug() << "gl-debug supported, but NOT enabled.\n"; + } else { + GLCAT.debug() << "gl-debug disabled and unsupported.\n"; + } } } @@ -9425,7 +9528,6 @@ get_external_image_format(Texture *tex) const { return _supports_bgr ? GL_BGRA : GL_RGBA; case Texture::F_luminance: - case Texture::F_sluminance: #ifdef OPENGLES return GL_LUMINANCE; #else @@ -9433,13 +9535,25 @@ get_external_image_format(Texture *tex) const { #endif case Texture::F_luminance_alphamask: case Texture::F_luminance_alpha: - case Texture::F_sluminance_alpha: #ifdef OPENGLES return GL_LUMINANCE_ALPHA; #else return _supports_luminance_texture ? GL_LUMINANCE_ALPHA : GL_RG; #endif + case Texture::F_sluminance: +#ifdef OPENGLES + return GL_LUMINANCE; +#else + return _supports_luminance_texture ? GL_LUMINANCE : GL_RGB; +#endif + case Texture::F_sluminance_alpha: +#ifdef OPENGLES + return GL_LUMINANCE_ALPHA; +#else + return _supports_luminance_texture ? GL_LUMINANCE_ALPHA : GL_RGBA; +#endif + #ifndef OPENGLES_1 case Texture::F_r8i: case Texture::F_r16i: @@ -10175,9 +10289,9 @@ get_internal_image_format(Texture *tex, bool force_sized) const { #endif #ifndef OPENGLES case Texture::F_sluminance: - return GL_SLUMINANCE8; + return _core_profile ? GL_SRGB8 : GL_SLUMINANCE8; case Texture::F_sluminance_alpha: - return GL_SLUMINANCE8_ALPHA8; + return _core_profile ? GL_SRGB8_ALPHA8 : GL_SLUMINANCE8_ALPHA8; #endif #ifndef OPENGLES diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index ae7a845efc..7c621f69ba 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -890,6 +890,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) { // Add it once for each index. for (bind._index = 0; bind._index < param_size; ++bind._index) { + bind._id._seqno = p + bind._index; _shader->_mat_spec.push_back(bind); } _shader->_mat_deps |= bind._dep[0]; @@ -2042,10 +2043,6 @@ set_state_and_transform(const RenderState *target_rs, target_rs->get_attrib(MaterialAttrib::get_class_slot())) { altered |= Shader::SSD_material; } - if (state_rs->get_attrib(ShaderAttrib::get_class_slot()) != - target_rs->get_attrib(ShaderAttrib::get_class_slot())) { - altered |= Shader::SSD_shaderinputs; - } if (state_rs->get_attrib(FogAttrib::get_class_slot()) != target_rs->get_attrib(FogAttrib::get_class_slot())) { altered |= Shader::SSD_fog; @@ -2069,6 +2066,11 @@ set_state_and_transform(const RenderState *target_rs, _state_rs = target_rs; } + if (_shader_attrib.get_orig() != _glgsg->_target_shader || _shader_attrib.was_deleted()) { + altered |= Shader::SSD_shaderinputs; + _shader_attrib = _glgsg->_target_shader; + } + // Is this the first time this shader is used this frame? int frame_number = ClockObject::get_global_clock()->get_frame_count(); if (frame_number != _frame_number) { @@ -2109,8 +2111,8 @@ issue_parameters(int altered) { for (int i = 0; i < (int)_shader->_ptr_spec.size(); ++i) { Shader::ShaderPtrSpec &spec = _shader->_ptr_spec[i]; - const Shader::ShaderPtrData* ptr_data = _glgsg->fetch_ptr_parameter(spec); - if (ptr_data == nullptr) { //the input is not contained in ShaderPtrData + Shader::ShaderPtrData ptr_data; + if (!_glgsg->fetch_ptr_parameter(spec, ptr_data)) { //the input is not contained in ShaderPtrData release_resources(); return; } @@ -2118,18 +2120,18 @@ issue_parameters(int altered) { nassertd(spec._dim[1] > 0) continue; GLint p = spec._id._seqno; - int array_size = min(spec._dim[0], (int)ptr_data->_size / spec._dim[1]); + int array_size = min(spec._dim[0], (int)ptr_data._size / spec._dim[1]); switch (spec._type) { case Shader::SPT_float: { float *data = nullptr; - switch (ptr_data->_type) { + switch (ptr_data._type) { case Shader::SPT_int: // Convert int data to float data. data = (float*) alloca(sizeof(float) * array_size * spec._dim[1]); for (int i = 0; i < (array_size * spec._dim[1]); ++i) { - data[i] = (float)(((int*)ptr_data->_ptr)[i]); + data[i] = (float)(((int*)ptr_data._ptr)[i]); } break; @@ -2137,7 +2139,7 @@ issue_parameters(int altered) { // Convert unsigned int data to float data. data = (float*) alloca(sizeof(float) * array_size * spec._dim[1]); for (int i = 0; i < (array_size * spec._dim[1]); ++i) { - data[i] = (float)(((unsigned int*)ptr_data->_ptr)[i]); + data[i] = (float)(((unsigned int*)ptr_data._ptr)[i]); } break; @@ -2145,12 +2147,12 @@ issue_parameters(int altered) { // Downgrade double data to float data. data = (float*) alloca(sizeof(float) * array_size * spec._dim[1]); for (int i = 0; i < (array_size * spec._dim[1]); ++i) { - data[i] = (float)(((double*)ptr_data->_ptr)[i]); + data[i] = (float)(((double*)ptr_data._ptr)[i]); } break; case Shader::SPT_float: - data = (float*)ptr_data->_ptr; + data = (float*)ptr_data._ptr; break; default: @@ -2170,8 +2172,8 @@ issue_parameters(int altered) { break; case Shader::SPT_int: - if (ptr_data->_type != Shader::SPT_int && - ptr_data->_type != Shader::SPT_uint) { + if (ptr_data._type != Shader::SPT_int && + ptr_data._type != Shader::SPT_uint) { GLCAT.error() << "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n"; @@ -2182,18 +2184,18 @@ issue_parameters(int altered) { } else { switch (spec._dim[1]) { - case 1: _glgsg->_glUniform1iv(p, array_size, (int*)ptr_data->_ptr); continue; - case 2: _glgsg->_glUniform2iv(p, array_size, (int*)ptr_data->_ptr); continue; - case 3: _glgsg->_glUniform3iv(p, array_size, (int*)ptr_data->_ptr); continue; - case 4: _glgsg->_glUniform4iv(p, array_size, (int*)ptr_data->_ptr); continue; + case 1: _glgsg->_glUniform1iv(p, array_size, (int*)ptr_data._ptr); continue; + case 2: _glgsg->_glUniform2iv(p, array_size, (int*)ptr_data._ptr); continue; + case 3: _glgsg->_glUniform3iv(p, array_size, (int*)ptr_data._ptr); continue; + case 4: _glgsg->_glUniform4iv(p, array_size, (int*)ptr_data._ptr); continue; } nassertd(false) continue; } break; case Shader::SPT_uint: - if (ptr_data->_type != Shader::SPT_uint && - ptr_data->_type != Shader::SPT_int) { + if (ptr_data._type != Shader::SPT_uint && + ptr_data._type != Shader::SPT_int) { GLCAT.error() << "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n"; @@ -2204,10 +2206,10 @@ issue_parameters(int altered) { } else { switch (spec._dim[1]) { - case 1: _glgsg->_glUniform1uiv(p, array_size, (GLuint *)ptr_data->_ptr); continue; - case 2: _glgsg->_glUniform2uiv(p, array_size, (GLuint *)ptr_data->_ptr); continue; - case 3: _glgsg->_glUniform3uiv(p, array_size, (GLuint *)ptr_data->_ptr); continue; - case 4: _glgsg->_glUniform4uiv(p, array_size, (GLuint *)ptr_data->_ptr); continue; + case 1: _glgsg->_glUniform1uiv(p, array_size, (GLuint *)ptr_data._ptr); continue; + case 2: _glgsg->_glUniform2uiv(p, array_size, (GLuint *)ptr_data._ptr); continue; + case 3: _glgsg->_glUniform3uiv(p, array_size, (GLuint *)ptr_data._ptr); continue; + case 4: _glgsg->_glUniform4uiv(p, array_size, (GLuint *)ptr_data._ptr); continue; } nassertd(false) continue; } @@ -2461,7 +2463,7 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) { } GLint p = bind._id._seqno; - max_p = max(max_p, p + 1); + max_p = max(max_p, p + bind._elements); // Don't apply vertex colors if they are disabled with a ColorAttrib. int num_elements, element_stride, divisor; @@ -3205,6 +3207,10 @@ glsl_compile_and_link() { valid &= glsl_compile_shader(Shader::ST_compute); } + if (!valid) { + return false; + } + // There might be warnings, so report those. GLSLShaders::const_iterator // it; for (it = _glsl_shaders.begin(); it != _glsl_shaders.end(); ++it) { // glsl_report_shader_errors(*it); } @@ -3300,7 +3306,7 @@ glsl_compile_and_link() { } _glgsg->report_my_gl_errors(); - return true; + return valid; } #endif // OPENGLES_1 diff --git a/panda/src/glstuff/glShaderContext_src.h b/panda/src/glstuff/glShaderContext_src.h index 91130e9224..0ccb99fe90 100644 --- a/panda/src/glstuff/glShaderContext_src.h +++ b/panda/src/glstuff/glShaderContext_src.h @@ -76,6 +76,7 @@ private: CPT(TransformState) _camera_transform; CPT(TransformState) _projection_transform; CPT(ColorAttrib) _color_attrib; + WCPT(ShaderAttrib) _shader_attrib; /* * struct ParamContext { CPT(InternalName) _name; GLint _location; GLsizei diff --git a/panda/src/glstuff/glmisc_src.cxx b/panda/src/glstuff/glmisc_src.cxx index 08e200a4a0..49eaf78a69 100644 --- a/panda/src/glstuff/glmisc_src.cxx +++ b/panda/src/glstuff/glmisc_src.cxx @@ -17,6 +17,11 @@ ConfigVariableInt gl_version ("gl-version", "", PRC_DESC("Set this to get an OpenGL context with a specific version.")); +ConfigVariableBool gl_forward_compatible + ("gl-forward-compatible", false, + PRC_DESC("Setting this to true will request a forward-compatible OpenGL " + "context, which will not support the fixed-function pipeline.")); + ConfigVariableBool gl_support_fbo ("gl-support-fbo", true, PRC_DESC("Configure this false if your GL's implementation of " diff --git a/panda/src/glstuff/glmisc_src.h b/panda/src/glstuff/glmisc_src.h index 3c6dd81e7b..415286c674 100644 --- a/panda/src/glstuff/glmisc_src.h +++ b/panda/src/glstuff/glmisc_src.h @@ -41,6 +41,7 @@ // #define GSG_VERBOSE 1 extern EXPCL_GL ConfigVariableInt gl_version; +extern EXPCL_GL ConfigVariableBool gl_forward_compatible; extern EXPCL_GL ConfigVariableBool gl_support_fbo; extern ConfigVariableBool gl_cheap_textures; extern ConfigVariableBool gl_ignore_clamp; diff --git a/panda/src/glxdisplay/glxGraphicsStateGuardian.cxx b/panda/src/glxdisplay/glxGraphicsStateGuardian.cxx index d620427afa..83ea5f2b2f 100644 --- a/panda/src/glxdisplay/glxGraphicsStateGuardian.cxx +++ b/panda/src/glxdisplay/glxGraphicsStateGuardian.cxx @@ -352,9 +352,20 @@ choose_pixel_format(const FrameBufferProperties &properties, attrib_list[n++] = gl_version[1]; } } + int flags = 0; if (gl_debug) { + flags |= GLX_CONTEXT_DEBUG_BIT_ARB; + } + if (gl_forward_compatible) { + flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + if (gl_version.get_num_words() == 0 || gl_version[0] < 2) { + glxdisplay_cat.error() + << "gl-forward-compatible requires gl-version >= 3 0\n"; + } + } + if (flags != 0) { attrib_list[n++] = GLX_CONTEXT_FLAGS_ARB; - attrib_list[n++] = GLX_CONTEXT_DEBUG_BIT_ARB; + attrib_list[n++] = flags; } attrib_list[n] = None; _context = _glXCreateContextAttribs(_display, _fbconfig, _share_context, diff --git a/panda/src/gobj/geom.I b/panda/src/gobj/geom.I index 94469ec77c..3fa32ade36 100644 --- a/panda/src/gobj/geom.I +++ b/panda/src/gobj/geom.I @@ -215,6 +215,8 @@ make_patches() const { /** * Returns a new Geom with each primitive converted into a corresponding * version with adjacency information. + * + * @since 1.10.0 */ INLINE PT(Geom) Geom:: make_adjacency() const { diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index 3b6dee026a..4fc2b8ceb2 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -892,6 +892,8 @@ make_patches_in_place() { * * Don't call this in a downstream thread unless you don't mind it blowing * away other changes you might have recently made in an upstream thread. + * + * @since 1.10.0 */ void Geom:: make_adjacency_in_place() { diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h index 82aac75cfa..a1e0b960e4 100644 --- a/panda/src/gobj/geom.h +++ b/panda/src/gobj/geom.h @@ -85,7 +85,7 @@ PUBLISHED: void offset_vertices(const GeomVertexData *data, int offset); int make_nonindexed(bool composite_only); - CPT(GeomVertexData) get_animated_vertex_data(bool force, Thread *current_thread) const; + CPT(GeomVertexData) get_animated_vertex_data(bool force, Thread *current_thread = Thread::get_current_thread()) const; INLINE bool is_empty() const; diff --git a/panda/src/gobj/geomLinesAdjacency.h b/panda/src/gobj/geomLinesAdjacency.h index 884875cb39..7202d324e6 100644 --- a/panda/src/gobj/geomLinesAdjacency.h +++ b/panda/src/gobj/geomLinesAdjacency.h @@ -20,6 +20,8 @@ /** * Defines a series of disconnected line segments with adjacency information, * for use with geometry shaders. + * + * @since 1.10.0 */ class EXPCL_PANDA_GOBJ GeomLinesAdjacency : public GeomPrimitive { PUBLISHED: diff --git a/panda/src/gobj/geomLinestripsAdjacency.h b/panda/src/gobj/geomLinestripsAdjacency.h index baf3d19255..16753c302a 100644 --- a/panda/src/gobj/geomLinestripsAdjacency.h +++ b/panda/src/gobj/geomLinestripsAdjacency.h @@ -18,7 +18,9 @@ #include "geomPrimitive.h" /** - * Defines a series of line strips. + * Defines a series of line strips with adjacency information. + * + * @since 1.10.0 */ class EXPCL_PANDA_GOBJ GeomLinestripsAdjacency : public GeomPrimitive { PUBLISHED: diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index 6fa66329ba..73aaf77c40 100644 --- a/panda/src/gobj/geomPrimitive.cxx +++ b/panda/src/gobj/geomPrimitive.cxx @@ -1038,6 +1038,8 @@ make_patches() const { /** * Adds adjacency information to this primitive. May return null if this type * of geometry does not support adjacency information. + * + * @since 1.10.0 */ CPT(GeomPrimitive) GeomPrimitive:: make_adjacency() const { @@ -1610,7 +1612,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, // Find the first non-NaN vertex. while (!found_any && i < cdata->_num_vertices) { reader.set_row(cdata->_first_vertex + i); - LPoint3 first_vertex = mat.xform_point(reader.get_data3()); + LPoint3 first_vertex = mat.xform_point_general(reader.get_data3()); if (!first_vertex.is_nan()) { min_point = first_vertex; max_point = first_vertex; @@ -1622,7 +1624,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, for (; i < cdata->_num_vertices; ++i) { reader.set_row_unsafe(cdata->_first_vertex + i); - LPoint3 vertex = mat.xform_point(reader.get_data3()); + LPoint3 vertex = mat.xform_point_general(reader.get_data3()); min_point.set(min(min_point[0], vertex[0]), min(min_point[1], vertex[1]), @@ -1675,7 +1677,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, int ii = index.get_data1i(); if (ii != strip_cut_index) { reader.set_row(ii); - LPoint3 first_vertex = mat.xform_point(reader.get_data3()); + LPoint3 first_vertex = mat.xform_point_general(reader.get_data3()); if (!first_vertex.is_nan()) { min_point = first_vertex; max_point = first_vertex; @@ -1691,7 +1693,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, continue; } reader.set_row_unsafe(ii); - LPoint3 vertex = mat.xform_point(reader.get_data3()); + LPoint3 vertex = mat.xform_point_general(reader.get_data3()); min_point.set(min(min_point[0], vertex[0]), min(min_point[1], vertex[1]), diff --git a/panda/src/gobj/geomTrianglesAdjacency.h b/panda/src/gobj/geomTrianglesAdjacency.h index 6ebdbdc6eb..90f3811b06 100644 --- a/panda/src/gobj/geomTrianglesAdjacency.h +++ b/panda/src/gobj/geomTrianglesAdjacency.h @@ -19,6 +19,8 @@ /** * Defines a series of disconnected triangles, with adjacency information. + * + * @since 1.10.0 */ class EXPCL_PANDA_GOBJ GeomTrianglesAdjacency : public GeomPrimitive { PUBLISHED: diff --git a/panda/src/gobj/geomTristripsAdjacency.h b/panda/src/gobj/geomTristripsAdjacency.h index 7811079401..896233c6ee 100644 --- a/panda/src/gobj/geomTristripsAdjacency.h +++ b/panda/src/gobj/geomTristripsAdjacency.h @@ -18,7 +18,9 @@ #include "geomPrimitive.h" /** - * Defines a series of triangle strips. + * Defines a series of triangle strips with adjacency information. + * + * @since 1.10.0 */ class EXPCL_PANDA_GOBJ GeomTristripsAdjacency : public GeomPrimitive { PUBLISHED: diff --git a/panda/src/gobj/geomVertexColumn.cxx b/panda/src/gobj/geomVertexColumn.cxx index d04b69a307..9e6c7bd188 100644 --- a/panda/src/gobj/geomVertexColumn.cxx +++ b/panda/src/gobj/geomVertexColumn.cxx @@ -258,7 +258,12 @@ make_packer() const { case 3: return new Packer_point_nativefloat_3; case 4: - return new Packer_point_nativefloat_4; + // Reader may assume 16-byte alignment. + if ((get_start() & 0xf) == 0) { + return new Packer_point_nativefloat_4; + } else { + return new Packer_point_float32_4; + } } } else { switch (get_num_components()) { @@ -309,7 +314,8 @@ make_packer() const { return new Packer_argb_packed; case NT_float32: - if (sizeof(float) == sizeof(PN_float32)) { + if (sizeof(float) == sizeof(PN_float32) && + (get_start() & 0xf) == 0) { // Use the native float type implementation for a tiny bit more // optimization. return new Packer_rgba_nativefloat_4; diff --git a/panda/src/gobj/geomVertexReader.I b/panda/src/gobj/geomVertexReader.I index 893d6458cf..2fc27b911a 100644 --- a/panda/src/gobj/geomVertexReader.I +++ b/panda/src/gobj/geomVertexReader.I @@ -328,6 +328,7 @@ set_row_unsafe(int row) { */ INLINE void GeomVertexReader:: set_row(int row) { + nassertv(row >= 0); _start_row = row; if (has_column()) { bool result = set_pointer(_start_row); diff --git a/panda/src/gobj/geomVertexWriter.I b/panda/src/gobj/geomVertexWriter.I index b55fdd5f4a..00b9652e8d 100644 --- a/panda/src/gobj/geomVertexWriter.I +++ b/panda/src/gobj/geomVertexWriter.I @@ -297,6 +297,7 @@ set_row_unsafe(int row) { */ INLINE void GeomVertexWriter:: set_row(int row) { + nassertv(row >= 0); _start_row = row; if (has_column()) { set_pointer(_start_row); diff --git a/panda/src/gobj/lens.cxx b/panda/src/gobj/lens.cxx index f66f2ba6b9..6fabc0cf8f 100644 --- a/panda/src/gobj/lens.cxx +++ b/panda/src/gobj/lens.cxx @@ -1221,8 +1221,8 @@ do_project(const CData *cdata, const LPoint3 &point3d, LPoint3 &point2d) const { point2d.set(full[0] * recip_full3, full[1] * recip_full3, full[2] * recip_full3); return (full[3] > 0.0f) && - (point2d[0] >= -1.0f) && (point2d[0] <= 1.0f) && - (point2d[1] >= -1.0f) && (point2d[1] <= 1.0f); + (point2d[0] >= -1.0f - NEARLY_ZERO(PN_stdfloat)) && (point2d[0] <= 1.0f + NEARLY_ZERO(PN_stdfloat)) && + (point2d[1] >= -1.0f - NEARLY_ZERO(PN_stdfloat)) && (point2d[1] <= 1.0f + NEARLY_ZERO(PN_stdfloat)); } /** diff --git a/panda/src/gobj/shader.cxx b/panda/src/gobj/shader.cxx index d42e6a4734..a60a7e169b 100644 --- a/panda/src/gobj/shader.cxx +++ b/panda/src/gobj/shader.cxx @@ -3274,8 +3274,10 @@ load(const Filename &file, ShaderLanguage lang) { shader_cat.info() << "Shader " << file << " was modified on disk, reloading.\n"; } else { - shader_cat.debug() - << "Shader " << file << " was found in shader cache.\n"; + if (shader_cat.is_debug()) { + shader_cat.debug() + << "Shader " << file << " was found in shader cache.\n"; + } return i->second; } } @@ -3312,8 +3314,10 @@ load(ShaderLanguage lang, const Filename &vertex, shader_cat.info() << "Shader was modified on disk, reloading.\n"; } else { - shader_cat.debug() - << "Shader was found in shader cache.\n"; + if (shader_cat.is_debug()) { + shader_cat.debug() + << "Shader was found in shader cache.\n"; + } return i->second; } } @@ -3365,8 +3369,10 @@ load_compute(ShaderLanguage lang, const Filename &fn) { shader_cat.info() << "Compute shader " << fn << " was modified on disk, reloading.\n"; } else { - shader_cat.debug() - << "Compute shader " << fn << " was found in shader cache.\n"; + if (shader_cat.is_debug()) { + shader_cat.debug() + << "Compute shader " << fn << " was found in shader cache.\n"; + } return i->second; } } diff --git a/panda/src/gobj/shaderBuffer.h b/panda/src/gobj/shaderBuffer.h index 0cc0660433..d523e13256 100644 --- a/panda/src/gobj/shaderBuffer.h +++ b/panda/src/gobj/shaderBuffer.h @@ -27,6 +27,8 @@ class PreparedGraphicsObjects; /** * This is a generic buffer object that lives in graphics memory. + * + * @since 1.10.0 */ class EXPCL_PANDA_GOBJ ShaderBuffer : public TypedWritableReferenceCount, public Namable, public GeomEnums { private: diff --git a/panda/src/gobj/texture.I b/panda/src/gobj/texture.I index 7d859dbda8..ce3aa6cffe 100644 --- a/panda/src/gobj/texture.I +++ b/panda/src/gobj/texture.I @@ -175,6 +175,8 @@ setup_cube_map(int size, ComponentType component_type, Format format) { * is not the same as the z_size. Follow up with read() or load() to fill the * texture properties and image data, or use set_clear_color to let the * texture be cleared to a solid color. + * + * @since 1.10.0 */ INLINE void Texture:: setup_cube_map_array(int num_cube_maps) { @@ -189,6 +191,8 @@ setup_cube_map_array(int num_cube_maps) { * * The num_cube_maps given here is multiplied by six to become the z_size of * the image. + * + * @since 1.10.0 */ INLINE void Texture:: setup_cube_map_array(int size, int num_cube_maps, ComponentType component_type, Format format) { diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index 1ba5b891e3..d28e2d208b 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -4158,7 +4158,7 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) cdata->_num_mipmap_levels_read = cdata->_ram_images.size(); } - if (in.fail() || in.eof()) { + if (in.fail()) { gobj_cat.error() << filename << ": truncated DDS file.\n"; return false; @@ -4924,7 +4924,7 @@ do_read_ktx(CData *cdata, istream &in, const string &filename, bool header_only) } } - if (in.fail() || in.eof()) { + if (in.fail()) { gobj_cat.error() << filename << ": truncated KTX file.\n"; return false; diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index 7cb5f305fb..4bde90c973 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -29,7 +29,7 @@ #include "pStatCollector.h" #include "pmutex.h" #include "mutexHolder.h" -#include "conditionVarFull.h" +#include "conditionVar.h" #include "loaderOptions.h" #include "string_utils.h" #include "cycleData.h" @@ -1034,7 +1034,7 @@ protected: Mutex _lock; // Used to implement unlocked_reload_ram_image(). - ConditionVarFull _cvar; // condition: _reloading is true. + ConditionVar _cvar; // condition: _reloading is true. bool _reloading; // A Texture keeps a list (actually, a map) of all the diff --git a/panda/src/gobj/vertexDataPage.h b/panda/src/gobj/vertexDataPage.h index bceca6adea..4aafeebac1 100644 --- a/panda/src/gobj/vertexDataPage.h +++ b/panda/src/gobj/vertexDataPage.h @@ -21,7 +21,6 @@ #include "vertexDataSaveFile.h" #include "pmutex.h" #include "conditionVar.h" -#include "conditionVarFull.h" #include "thread.h" #include "mutexHolder.h" #include "pdeque.h" @@ -147,7 +146,7 @@ private: // Signaled when anything new is added to either of the above queues, or // when _shutdown is set true. This wakes up any pending thread. - ConditionVarFull _pending_cvar; + ConditionVar _pending_cvar; PageThreads _threads; friend class PageThread; diff --git a/panda/src/grutil/geoMipTerrain.I b/panda/src/grutil/geoMipTerrain.I index 7eeeddab7a..20f0f1241a 100644 --- a/panda/src/grutil/geoMipTerrain.I +++ b/panda/src/grutil/geoMipTerrain.I @@ -237,10 +237,14 @@ is_dirty() { * the terrain will be, but more expensive to render. A value of 0 makes the * terrain the lowest quality possible, depending on blocksize. The default * value is 100. + * + * @deprecated use set_near/set_far instead */ INLINE void GeoMipTerrain:: set_factor(PN_stdfloat factor) { - grutil_cat.debug() << "Using deprecated method set_factor, use set_near and set_far instead!\n"; + if (grutil_cat.is_debug()) { + grutil_cat.debug() << "Using deprecated method set_factor, use set_near and set_far instead!\n"; + } _use_near_far = false; _factor = factor; } diff --git a/panda/src/grutil/shaderTerrainMesh.cxx b/panda/src/grutil/shaderTerrainMesh.cxx index e8078f7b68..3aaaccc8b0 100644 --- a/panda/src/grutil/shaderTerrainMesh.cxx +++ b/panda/src/grutil/shaderTerrainMesh.cxx @@ -30,6 +30,7 @@ #include "renderAttrib.h" #include "shaderInput.h" #include "boundingBox.h" +#include "boundingSphere.h" #include "samplerState.h" #include "config_grutil.h" #include "typeHandle.h" @@ -106,8 +107,6 @@ ShaderTerrainMesh::ShaderTerrainMesh() : _update_enabled(true), _heightfield_tex(nullptr) { - set_final(true); - set_bounds(new OmniBoundingVolume()); } /** @@ -563,6 +562,64 @@ void ShaderTerrainMesh::add_for_draw(CullTraverser *trav, CullTraverserData &dat _basic_collector.stop(); } +/** + * This is used to support NodePath::calc_tight_bounds(). It is not intended + * to be called directly, and it has nothing to do with the normal Panda + * bounding-volume computation. + * + * If the node contains any geometry, this updates min_point and max_point to + * enclose its bounding box. found_any is to be set true if the node has any + * geometry at all, or left alone if it has none. This method may be called + * over several nodes, so it may enter with min_point, max_point, and + * found_any already set. + */ +CPT(TransformState) ShaderTerrainMesh:: +calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any, + const TransformState *transform, Thread *current_thread) const { + CPT(TransformState) next_transform = + PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform, + current_thread); + + const LMatrix4 &mat = next_transform->get_mat(); + LPoint3 terrain_min_point = LPoint3(0, 0, 0) * mat; + LPoint3 terrain_max_point = LPoint3(1, 1, 1) * mat; + if (!found_any) { + min_point = terrain_min_point; + max_point = terrain_max_point; + found_any = true; + } else { + min_point = min_point.fmin(terrain_min_point); + max_point = max_point.fmax(terrain_max_point); + } + + return next_transform; +} + +/** + * Returns a newly-allocated BoundingVolume that represents the internal + * contents of the node. Should be overridden by PandaNode classes that + * contain something internally. + */ +void ShaderTerrainMesh:: +compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, + int &internal_vertices, + int pipeline_stage, + Thread *current_thread) const { + + BoundingVolume::BoundsType btype = get_bounds_type(); + if (btype == BoundingVolume::BT_default) { + btype = bounds_type; + } + + if (btype == BoundingVolume::BT_sphere) { + internal_bounds = new BoundingSphere(LPoint3(0.5, 0.5, 0.5), csqrt(0.75)); + } else { + internal_bounds = new BoundingBox(LPoint3(0, 0, 0), LPoint3(1, 1, 1)); + } + + internal_vertices = 0; +} + /** * @brief Traverses the quadtree * @details This method traverses the given chunk, deciding whether it should diff --git a/panda/src/grutil/shaderTerrainMesh.h b/panda/src/grutil/shaderTerrainMesh.h index 9c4f8d9a3a..9b08e12c0d 100644 --- a/panda/src/grutil/shaderTerrainMesh.h +++ b/panda/src/grutil/shaderTerrainMesh.h @@ -91,6 +91,16 @@ public: virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); private: + virtual CPT(TransformState) + calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, + bool &found_any, + const TransformState *transform, + Thread *current_thread = Thread::get_current_thread()) const; + + virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, + int &internal_vertices, + int pipeline_stage, + Thread *current_thread) const; // Chunk data struct Chunk { diff --git a/panda/src/linmath/lmatrix3_src.I b/panda/src/linmath/lmatrix3_src.I index 782c8599ee..9aaccd380c 100644 --- a/panda/src/linmath/lmatrix3_src.I +++ b/panda/src/linmath/lmatrix3_src.I @@ -95,31 +95,6 @@ ident_mat() { return _ident_mat; } -/** - * - */ -INLINE_LINMATH FLOATNAME(LMatrix3):: -FLOATNAME(LMatrix3)() { -} - -/** - * - */ -INLINE_LINMATH FLOATNAME(LMatrix3):: -FLOATNAME(LMatrix3)(const FLOATNAME(LMatrix3) ©) : _m(copy._m) { -} - -/** - * - */ -INLINE_LINMATH FLOATNAME(LMatrix3) &FLOATNAME(LMatrix3):: -operator = (const FLOATNAME(LMatrix3) ©) { - TAU_PROFILE("void LMatrix3::operator = (const LMatrix3 &)", " ", TAU_USER); - - _m = copy._m; - return *this; -} - /** * */ @@ -157,11 +132,6 @@ FLOATNAME(LMatrix3)(const FLOATNAME(LVecBase3) &row0, const FLOATNAME(LVecBase3) &row2) { TAU_PROFILE("LMatrix3::LMatrix3(const LVecBase3 &, ...)", " ", TAU_USER); -#ifdef HAVE_EIGEN - _m.row(0) = row0._v; - _m.row(1) = row1._v; - _m.row(2) = row2._v; -#else _m(0, 0) = row0._v(0); _m(0, 1) = row0._v(1); _m(0, 2) = row0._v(2); @@ -171,7 +141,6 @@ FLOATNAME(LMatrix3)(const FLOATNAME(LVecBase3) &row0, _m(2, 0) = row2._v(0); _m(2, 1) = row2._v(1); _m(2, 2) = row2._v(2); -#endif // HAVE_EIGEN } /** diff --git a/panda/src/linmath/lmatrix3_src.h b/panda/src/linmath/lmatrix3_src.h index 2138f0fc39..2509471e9e 100644 --- a/panda/src/linmath/lmatrix3_src.h +++ b/panda/src/linmath/lmatrix3_src.h @@ -55,10 +55,10 @@ PUBLISHED: friend class FLOATNAME(LMatrix3); }; - INLINE_LINMATH FLOATNAME(LMatrix3)(); - INLINE_LINMATH FLOATNAME(LMatrix3)(const FLOATNAME(LMatrix3) &other); + INLINE_LINMATH FLOATNAME(LMatrix3)() = default; + INLINE_LINMATH FLOATNAME(LMatrix3)(const FLOATNAME(LMatrix3) &other) = default; INLINE_LINMATH FLOATNAME(LMatrix3) &operator = ( - const FLOATNAME(LMatrix3) &other); + const FLOATNAME(LMatrix3) &other) = default; INLINE_LINMATH FLOATNAME(LMatrix3) &operator = (FLOATTYPE fill_value); INLINE_LINMATH FLOATNAME(LMatrix3)(FLOATTYPE, FLOATTYPE, FLOATTYPE, FLOATTYPE, FLOATTYPE, FLOATTYPE, diff --git a/panda/src/linmath/lmatrix4_src.I b/panda/src/linmath/lmatrix4_src.I index a887d393f1..ca3ff8eed6 100644 --- a/panda/src/linmath/lmatrix4_src.I +++ b/panda/src/linmath/lmatrix4_src.I @@ -111,20 +111,6 @@ zeros_mat() { return _zeros_mat; } -/** - * - */ -INLINE_LINMATH FLOATNAME(LMatrix4):: -FLOATNAME(LMatrix4)() { -} - -/** - * - */ -INLINE_LINMATH FLOATNAME(LMatrix4):: -FLOATNAME(LMatrix4)(const FLOATNAME(LMatrix4) ©) : _m(copy._m) { -} - /** * */ @@ -133,16 +119,6 @@ FLOATNAME(LMatrix4)(const FLOATNAME(UnalignedLMatrix4) ©) { operator = (copy); } -/** - * - */ -INLINE_LINMATH FLOATNAME(LMatrix4) &FLOATNAME(LMatrix4):: -operator = (const FLOATNAME(LMatrix4) ©) { - TAU_PROFILE("void LMatrix4::operator = (const LMatrix4 &)", " ", TAU_USER); - _m = copy._m; - return *this; -} - /** * */ @@ -484,13 +460,9 @@ get_col(int col) const { */ INLINE_LINMATH FLOATNAME(LVecBase3) FLOATNAME(LMatrix4):: get_row3(int row) const { -#ifdef HAVE_EIGEN - return FLOATNAME(LVecBase3)(_m.block<1, 3>(row, 0)); -#else return FLOATNAME(LVecBase3)((*this)(row, 0), (*this)(row, 1), (*this)(row, 2)); -#endif // HAVE_EIGEN } /** @@ -514,13 +486,9 @@ get_row3(FLOATNAME(LVecBase3) &result_vec,int row) const { */ INLINE_LINMATH FLOATNAME(LVecBase3) FLOATNAME(LMatrix4):: get_col3(int col) const { -#ifdef HAVE_EIGEN - return FLOATNAME(LVecBase3)(_m.block<1, 3>(0, col)); -#else return FLOATNAME(LVecBase3)((*this)(0, col), (*this)(1, col), (*this)(2, col)); -#endif // HAVE_EIGEN } /** @@ -1635,14 +1603,6 @@ invert(const FLOATNAME(LMatrix4) &a) { return result; } - -/** - * - */ -INLINE_LINMATH FLOATNAME(UnalignedLMatrix4):: -FLOATNAME(UnalignedLMatrix4)() { -} - /** * */ @@ -1651,13 +1611,6 @@ FLOATNAME(UnalignedLMatrix4)(const FLOATNAME(LMatrix4) ©) { operator = (copy); } -/** - * - */ -INLINE_LINMATH FLOATNAME(UnalignedLMatrix4):: -FLOATNAME(UnalignedLMatrix4)(const FLOATNAME(UnalignedLMatrix4) ©) : _m(copy._m) { -} - /** * */ @@ -1667,16 +1620,6 @@ operator = (const FLOATNAME(LMatrix4) ©) { return *this; } -/** - * - */ -INLINE_LINMATH FLOATNAME(UnalignedLMatrix4) &FLOATNAME(UnalignedLMatrix4):: -operator = (const FLOATNAME(UnalignedLMatrix4) ©) { - TAU_PROFILE("void UnalignedLMatrix4::operator =(UnalignedLMatrix4 &)", " ", TAU_USER); - _m = copy._m; - return *this; -} - /** * */ diff --git a/panda/src/linmath/lmatrix4_src.h b/panda/src/linmath/lmatrix4_src.h index a66a5ab45f..9fd695dbf6 100644 --- a/panda/src/linmath/lmatrix4_src.h +++ b/panda/src/linmath/lmatrix4_src.h @@ -53,11 +53,11 @@ PUBLISHED: friend class FLOATNAME(LMatrix4); }; - INLINE_LINMATH FLOATNAME(LMatrix4)(); - INLINE_LINMATH FLOATNAME(LMatrix4)(const FLOATNAME(LMatrix4) &other); + INLINE_LINMATH FLOATNAME(LMatrix4)() = default; + INLINE_LINMATH FLOATNAME(LMatrix4)(const FLOATNAME(LMatrix4) &other) = default; INLINE_LINMATH FLOATNAME(LMatrix4)(const FLOATNAME(UnalignedLMatrix4) &other); INLINE_LINMATH FLOATNAME(LMatrix4) &operator = ( - const FLOATNAME(LMatrix4) &other); + const FLOATNAME(LMatrix4) &other) = default; INLINE_LINMATH FLOATNAME(LMatrix4) &operator = ( const FLOATNAME(UnalignedLMatrix4) &other); INLINE_LINMATH FLOATNAME(LMatrix4) &operator = (FLOATTYPE fill_value); @@ -330,11 +330,11 @@ PUBLISHED: num_components = 16 }; - INLINE_LINMATH FLOATNAME(UnalignedLMatrix4)(); + INLINE_LINMATH FLOATNAME(UnalignedLMatrix4)() = default; INLINE_LINMATH FLOATNAME(UnalignedLMatrix4)(const FLOATNAME(LMatrix4) ©); - INLINE_LINMATH FLOATNAME(UnalignedLMatrix4)(const FLOATNAME(UnalignedLMatrix4) ©); + INLINE_LINMATH FLOATNAME(UnalignedLMatrix4)(const FLOATNAME(UnalignedLMatrix4) ©) = default; INLINE_LINMATH FLOATNAME(UnalignedLMatrix4) &operator = (const FLOATNAME(LMatrix4) ©); - INLINE_LINMATH FLOATNAME(UnalignedLMatrix4) &operator = (const FLOATNAME(UnalignedLMatrix4) ©); + INLINE_LINMATH FLOATNAME(UnalignedLMatrix4) &operator = (const FLOATNAME(UnalignedLMatrix4) ©) = default; INLINE_LINMATH FLOATNAME(UnalignedLMatrix4)(FLOATTYPE e00, FLOATTYPE e01, FLOATTYPE e02, FLOATTYPE e03, FLOATTYPE e10, FLOATTYPE e11, FLOATTYPE e12, FLOATTYPE e13, FLOATTYPE e20, FLOATTYPE e21, FLOATTYPE e22, FLOATTYPE e23, diff --git a/panda/src/linmath/lvector3_src.I b/panda/src/linmath/lvector3_src.I index 3313e6e45f..3aa198c513 100644 --- a/panda/src/linmath/lvector3_src.I +++ b/panda/src/linmath/lvector3_src.I @@ -230,7 +230,7 @@ signed_angle_deg(const FLOATNAME(LVector3) &other, } /** - * This method is deprecated. Do not use. + * @deprecated Do not use. */ INLINE_LINMATH FLOATTYPE FLOATNAME(LVector3):: relative_angle_rad(const FLOATNAME(LVector3) &other) const { @@ -238,7 +238,7 @@ relative_angle_rad(const FLOATNAME(LVector3) &other) const { } /** - * This method is deprecated. Do not use. + * @deprecated Do not use. */ INLINE_LINMATH FLOATTYPE FLOATNAME(LVector3):: relative_angle_deg(const FLOATNAME(LVector3) &other) const { diff --git a/panda/src/mathutil/boundingBox.I b/panda/src/mathutil/boundingBox.I index 2bb67a500e..f0b12f8dfd 100644 --- a/panda/src/mathutil/boundingBox.I +++ b/panda/src/mathutil/boundingBox.I @@ -101,7 +101,7 @@ get_plane(int n) const { INLINE_MATHUTIL void BoundingBox:: set_min_max(const LPoint3 &min, const LPoint3 &max) { nassertv(!min.is_nan() && !max.is_nan()); - nassertv(_min[0] <= _max[0] && _min[1] <= _max[1] && _min[2] <= _max[2]); + nassertv(min[0] <= max[0] && min[1] <= max[1] && min[2] <= max[2]); _min = min; _max = max; _flags = 0; diff --git a/panda/src/mathutil/perlinNoise2.cxx b/panda/src/mathutil/perlinNoise2.cxx index ef187beb9c..1dc576e08a 100644 --- a/panda/src/mathutil/perlinNoise2.cxx +++ b/panda/src/mathutil/perlinNoise2.cxx @@ -19,6 +19,9 @@ */ double PerlinNoise2:: noise(const LVecBase2d &value) const { + // If this triggers, you passed in 0 for table_size. + nassertr(!_index.empty(), make_nan(0.0)); + // Convert the vector to our local coordinate space. LVecBase2d vec = _input_xform.xform_point(value); @@ -41,9 +44,13 @@ noise(const LVecBase2d &value) const { double v = fade(y); // Hash coordinates of the 4 square corners (A, B, A + 1, and B + 1) + nassertr(X >= 0 && X + 1 < _index.size(), make_nan(0.0)); int A = _index[X] + Y; int B = _index[X + 1] + Y; + nassertr(A >= 0 && A + 1 < _index.size(), make_nan(0.0)); + nassertr(B >= 0 && B + 1 < _index.size(), make_nan(0.0)); + // and add blended results from 4 corners of square. double result = lerp(v, lerp(u, grad(_index[A], x, y), diff --git a/panda/src/movies/flacAudio.h b/panda/src/movies/flacAudio.h index 4fb9818930..5309e95eae 100644 --- a/panda/src/movies/flacAudio.h +++ b/panda/src/movies/flacAudio.h @@ -21,6 +21,8 @@ class FlacAudioCursor; /** * Reads FLAC audio files. Ogg-encapsulated FLAC files are not supported. + * + * @since 1.10.0 */ class EXPCL_PANDA_MOVIES FlacAudio : public MovieAudio { PUBLISHED: diff --git a/panda/src/movies/flacAudioCursor.cxx b/panda/src/movies/flacAudioCursor.cxx index 0e3bbb163a..1c9d06984b 100644 --- a/panda/src/movies/flacAudioCursor.cxx +++ b/panda/src/movies/flacAudioCursor.cxx @@ -118,8 +118,10 @@ seek(double t) { * read. Your buffer must be equal in size to N * channels. Multiple-channel * audio will be interleaved. */ -void FlacAudioCursor:: +int FlacAudioCursor:: read_samples(int n, int16_t *data) { int desired = n * _audio_channels; - _samples_read += drflac_read_s16(_drflac, desired, data) / _audio_channels; + n = drflac_read_s16(_drflac, desired, data) / _audio_channels; + _samples_read += n; + return n; } diff --git a/panda/src/movies/flacAudioCursor.h b/panda/src/movies/flacAudioCursor.h index 1de46bad01..93a48e5f32 100644 --- a/panda/src/movies/flacAudioCursor.h +++ b/panda/src/movies/flacAudioCursor.h @@ -25,8 +25,10 @@ extern "C" { class FlacAudio; /** - * Interfaces with the libvorbisfile library to implement decoding of Ogg - * Vorbis audio files. + * Implements decoding of FLAC audio files. + * + * @see FlacAudio + * @since 1.10.0 */ class EXPCL_PANDA_MOVIES FlacAudioCursor : public MovieAudioCursor { PUBLISHED: @@ -35,7 +37,7 @@ PUBLISHED: virtual void seek(double offset); public: - virtual void read_samples(int n, int16_t *data); + virtual int read_samples(int n, int16_t *data); bool _is_valid; diff --git a/panda/src/movies/microphoneAudioDS.cxx b/panda/src/movies/microphoneAudioDS.cxx index 1ce78b0209..c04da23c3e 100644 --- a/panda/src/movies/microphoneAudioDS.cxx +++ b/panda/src/movies/microphoneAudioDS.cxx @@ -91,7 +91,7 @@ public: int _samples_per_buffer; public: - virtual void read_samples(int n, int16_t *data); + virtual int read_samples(int n, int16_t *data); virtual int ready() const; public: @@ -323,7 +323,7 @@ MicrophoneAudioCursorDS:: /** * */ -void MicrophoneAudioCursorDS:: +int MicrophoneAudioCursorDS:: read_samples(int n, int16_t *data) { int orign = n; if (_handle) { @@ -373,6 +373,7 @@ read_samples(int n, int16_t *data) { if (n > 0) { memcpy(data, 0, n*2*_audio_channels); } + return orign - n; } /** diff --git a/panda/src/movies/movieAudioCursor.cxx b/panda/src/movies/movieAudioCursor.cxx index 33acc0e10c..a836937455 100644 --- a/panda/src/movies/movieAudioCursor.cxx +++ b/panda/src/movies/movieAudioCursor.cxx @@ -45,14 +45,14 @@ MovieAudioCursor:: * read. Your buffer must be equal in size to N * channels. Multiple-channel * audio will be interleaved. */ -void MovieAudioCursor:: +int MovieAudioCursor:: read_samples(int n, int16_t *data) { // This is the null implementation, which generates pure silence. Normally, // this method will be overridden by a subclass. if (n <= 0) { - return; + return 0; } int desired = n * _audio_channels; @@ -60,6 +60,7 @@ read_samples(int n, int16_t *data) { data[i] = 0; } _samples_read += n; + return n; } /** @@ -92,23 +93,28 @@ read_samples(int n, Datagram *dg) { * This is not particularly efficient, but it may be a convenient way to * manipulate samples in python. */ -std::string MovieAudioCursor:: +vector_uchar MovieAudioCursor:: read_samples(int n) { - std::ostringstream result; + vector_uchar result; int16_t tmp[4096]; while (n > 0) { int blocksize = (4096 / _audio_channels); - if (blocksize > n) blocksize = n; - int words = blocksize * _audio_channels; - read_samples(blocksize, tmp); - for (int i=0; i>8) & 255)); + if (blocksize > n) { + blocksize = n; } - n -= blocksize; + int nread = read_samples(blocksize, tmp); + if (nread == 0) { + return result; + } + int words = nread * _audio_channels; + for (int i = 0; i < words; ++i) { + int16_t word = tmp[i]; + result.push_back((uint8_t)(word & 255u)); + result.push_back((uint8_t)((word >> 8) & 255u)); + } + n -= nread; } - return result.str(); + return result; } diff --git a/panda/src/movies/movieAudioCursor.h b/panda/src/movies/movieAudioCursor.h index b2d5d9bebe..e7c9668713 100644 --- a/panda/src/movies/movieAudioCursor.h +++ b/panda/src/movies/movieAudioCursor.h @@ -48,10 +48,10 @@ PUBLISHED: virtual int ready() const; virtual void seek(double offset); void read_samples(int n, Datagram *dg); - std::string read_samples(int n); + vector_uchar read_samples(int n); public: - virtual void read_samples(int n, int16_t *data); + virtual int read_samples(int n, int16_t *data); protected: PT(MovieAudio) _source; diff --git a/panda/src/movies/movieTypeRegistry.cxx b/panda/src/movies/movieTypeRegistry.cxx index 5bc8228bb4..64acc8e42a 100644 --- a/panda/src/movies/movieTypeRegistry.cxx +++ b/panda/src/movies/movieTypeRegistry.cxx @@ -31,6 +31,12 @@ PT(MovieAudio) MovieTypeRegistry:: make_audio(const Filename &name) { string ext = downcase(name.get_extension()); +#ifdef HAVE_ZLIB + if (ext == "pz" || ext == "gz") { + ext = Filename(name.get_basename_wo_extension()).get_extension(); + } +#endif + _audio_lock.lock(); // Make sure that the list of audio types has been read in. @@ -84,7 +90,7 @@ register_audio_type(MakeAudioFunc func, const string &extensions) { if (_audio_type_registry.count(*wi)) { movies_cat->warning() << "Attempt to register multiple audio types with extension " << (*wi) << "\n"; - } else { + } else if (movies_cat->is_debug()) { movies_cat->debug() << "Registered audio type with extension " << (*wi) << "\n"; } @@ -154,6 +160,12 @@ PT(MovieVideo) MovieTypeRegistry:: make_video(const Filename &name) { string ext = downcase(name.get_extension()); +#ifdef HAVE_ZLIB + if (ext == "pz" || ext == "gz") { + ext = Filename(name.get_basename_wo_extension()).get_extension(); + } +#endif + _video_lock.lock(); // Make sure that the list of video types has been read in. @@ -207,7 +219,7 @@ register_video_type(MakeVideoFunc func, const string &extensions) { if (_video_type_registry.count(*wi)) { movies_cat->warning() << "Attempt to register multiple video types with extension " << (*wi) << "\n"; - } else { + } else if (movies_cat->is_debug()) { movies_cat->debug() << "Registered video type with extension " << (*wi) << "\n"; } diff --git a/panda/src/movies/opusAudio.h b/panda/src/movies/opusAudio.h index 813a4dbd58..3de8fb8535 100644 --- a/panda/src/movies/opusAudio.h +++ b/panda/src/movies/opusAudio.h @@ -24,6 +24,8 @@ class OpusAudioCursor; /** * Interfaces with the libopusfile library to implement decoding of Opus * audio files. + * + * @since 1.10.0 */ class EXPCL_PANDA_MOVIES OpusAudio : public MovieAudio { PUBLISHED: diff --git a/panda/src/movies/opusAudioCursor.cxx b/panda/src/movies/opusAudioCursor.cxx index 2b1591e971..ab81068742 100644 --- a/panda/src/movies/opusAudioCursor.cxx +++ b/panda/src/movies/opusAudioCursor.cxx @@ -12,6 +12,10 @@ */ #include "opusAudioCursor.h" + +#include "config_movies.h" + +#include "opusAudio.h" #include "virtualFileSystem.h" #ifdef HAVE_OPUS @@ -52,6 +56,22 @@ int cb_seek(void *stream, opus_int64 offset, int whence) { break; case SEEK_CUR: + // opusfile uses a seek with offset 0 to determine whether seeking is + // supported, but this is not good enough. We seek to the end and back. + if (offset == 0) { + std::streambuf *buf = in->rdbuf(); + std::streampos pos = buf->pubseekoff(0, std::ios::cur, std::ios::in); + if (pos < 0) { + return -1; + } + if (buf->pubseekoff(0, std::ios::end, std::ios::in) >= 0) { + // It worked; seek back to the previous location. + buf->pubseekpos(pos, std::ios::in); + return 0; + } else { + return -1; + } + } in->seekg(offset, std::ios::cur); break; @@ -97,16 +117,7 @@ opus_int64 cb_tell(void *stream) { return in->tellg(); } -int cb_close(void *stream) { - istream *in = (istream *)stream; - nassertr(in != nullptr, EOF); - - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - vfs->close_read_file(in); - return 0; -} - -static const OpusFileCallbacks callbacks = {cb_read, cb_seek, cb_tell, cb_close}; +static const OpusFileCallbacks callbacks = {cb_read, cb_seek, cb_tell, nullptr}; TypeHandle OpusAudioCursor::_type_handle; @@ -118,6 +129,7 @@ OpusAudioCursor:: OpusAudioCursor(OpusAudio *src, istream *stream) : MovieAudioCursor(src), _is_valid(false), + _stream(stream), _link(0) { nassertv(stream != nullptr); @@ -155,6 +167,11 @@ OpusAudioCursor:: op_free(_op); _op = nullptr; } + + if (_stream != nullptr) { + VirtualFileSystem::close_read_file(_stream); + _stream = nullptr; + } } /** @@ -170,7 +187,32 @@ seek(double t) { t = std::max(t, 0.0); // Use op_time_seek_lap if cross-lapping is enabled. - int error = op_pcm_seek(_op, (ogg_int64_t)(t * 48000.0)); + ogg_int64_t sample = (ogg_int64_t)(t * 48000.0); + int error = op_pcm_seek(_op, sample); + + // Special case for seeking to the beginning; if normal seek fails, we may + // be able to explicitly seek to the beginning of the file and call op_open + // again. This allows looping compressed .opus files. + if (error == OP_ENOSEEK && sample == 0) { + if (_stream->rdbuf()->pubseekpos(0, std::ios::in) == (std::streampos)0) { + OggOpusFile *op = op_open_callbacks((void *)_stream, &callbacks, nullptr, 0, nullptr); + if (op != nullptr) { + op_free(_op); + _op = op; + } else { + movies_cat.error() + << "Failed to reopen Opus file to seek to beginning.\n"; + return; + } + + // Reset this field for good measure, just in case this changed. + _audio_channels = op_channel_count(_op, -1); + + _last_seek = 0.0; + _samples_read = 0; + return; + } + } if (error != 0) { movies_cat.error() << "Seek failed (error " << error << "). Opus stream may not be seekable.\n"; @@ -186,7 +228,7 @@ seek(double t) { * read. Your buffer must be equal in size to N * channels. Multiple-channel * audio will be interleaved. */ -void OpusAudioCursor:: +int OpusAudioCursor:: read_samples(int n, int16_t *data) { int16_t *end = data + (n * _audio_channels); @@ -198,6 +240,9 @@ read_samples(int n, int16_t *data) { data += read_samples * _audio_channels; _samples_read += read_samples; } else { + if (read_samples == 0 && _length == 1.0E10) { + _length = op_pcm_tell(_op) / 48000.0; + } break; } @@ -220,7 +265,9 @@ read_samples(int n, int16_t *data) { // Fill the rest of the buffer with silence. if (data < end) { memset(data, 0, (unsigned char *)end - (unsigned char *)data); + n -= (end - data) / _audio_channels; } + return n; } #endif // HAVE_OPUS diff --git a/panda/src/movies/opusAudioCursor.h b/panda/src/movies/opusAudioCursor.h index 1a3dfa035b..3e2137de5e 100644 --- a/panda/src/movies/opusAudioCursor.h +++ b/panda/src/movies/opusAudioCursor.h @@ -28,6 +28,9 @@ class OpusAudio; /** * Interfaces with the libopusfile library to implement decoding of Opus * audio files. + * + * @see OpusAudio + * @since 1.10.0 */ class EXPCL_PANDA_MOVIES OpusAudioCursor : public MovieAudioCursor { PUBLISHED: @@ -36,22 +39,14 @@ PUBLISHED: virtual void seek(double offset); public: - virtual void read_samples(int n, int16_t *data); + virtual int read_samples(int n, int16_t *data); bool _is_valid; protected: OggOpusFile *_op; - + std::istream *_stream; int _link; - double _byte_rate; - int _block_align; - int _bytes_per_sample; - bool _is_float; - - std::streampos _data_start; - std::streampos _data_pos; - size_t _data_size; public: static TypeHandle get_class_type() { diff --git a/panda/src/movies/userDataAudio.cxx b/panda/src/movies/userDataAudio.cxx index f02726d10b..9490432dc0 100644 --- a/panda/src/movies/userDataAudio.cxx +++ b/panda/src/movies/userDataAudio.cxx @@ -57,12 +57,14 @@ open() { * read. Your buffer must be equal in size to N * channels. Multiple-channel * audio will be interleaved. */ -void UserDataAudio:: +int UserDataAudio:: read_samples(int n, int16_t *data) { int ready = (_data.size() / _desired_channels); int desired = n * _desired_channels; - int avail = ready * _desired_channels; - if (avail > desired) avail = desired; + if (n > ready) { + n = ready; + } + int avail = n * _desired_channels; for (int i=0; i_remove_after_read) { - source->read_samples(n, data); + if (source->_remove_after_read) { + n = source->read_samples(n, data); } else { int offset = _samples_read * _audio_channels; @@ -66,9 +66,12 @@ read_samples(int n, int16_t *data) { for (int i=avail; irdbuf()->pubseekpos(0, std::ios::in) == (std::streampos)0) { + // Back up the callbacks, then destroy the stream, making sure to first + // unset the datasource so that it won't close the file. + ov_callbacks callbacks = _ov.callbacks; + _ov.datasource = nullptr; + ov_clear(&_ov); + + if (ov_open_callbacks((void *)stream, &_ov, nullptr, 0, callbacks) != 0) { + movies_cat.error() + << "Failed to reopen Ogg Vorbis file to seek to beginning.\n"; + return; + } + + // Reset these fields for good measure, just in case the file changed. + vorbis_info *vi = ov_info(&_ov, -1); + _audio_channels = vi->channels; + _audio_rate = vi->rate; + + _last_seek = 0.0; + _samples_read = 0; return; } } + if (result != 0) { + movies_cat.error() + << "Seek failed. Ogg Vorbis stream may not be seekable.\n"; + } _last_seek = ov_time_tell(&_ov); _samples_read = 0; @@ -110,7 +141,7 @@ seek(double t) { * read. Your buffer must be equal in size to N * channels. Multiple-channel * audio will be interleaved. */ -void VorbisAudioCursor:: +int VorbisAudioCursor:: read_samples(int n, int16_t *data) { int desired = n * _audio_channels; @@ -127,6 +158,9 @@ read_samples(int n, int16_t *data) { buffer += read_bytes; length -= read_bytes; } else { + if (read_bytes == 0 && _length == 1.0E10) { + _length = ov_time_tell(&_ov); + } break; } @@ -155,6 +189,7 @@ read_samples(int n, int16_t *data) { } _samples_read += n; + return n; } /** @@ -195,6 +230,22 @@ cb_seek_func(void *datasource, ogg_int64_t offset, int whence) { break; case SEEK_CUR: + // Vorbis uses a seek with offset 0 to determine whether seeking is + // supported, but this is not good enough. We seek to the end and back. + if (offset == 0) { + std::streambuf *buf = stream->rdbuf(); + std::streampos pos = buf->pubseekoff(0, std::ios::cur, std::ios::in); + if (pos < 0) { + return -1; + } + if (buf->pubseekoff(0, std::ios::end, std::ios::in) >= 0) { + // It worked; seek back to the previous location. + buf->pubseekpos(pos, std::ios::in); + return 0; + } else { + return -1; + } + } stream->seekg(offset, std::ios::cur); break; diff --git a/panda/src/movies/vorbisAudioCursor.h b/panda/src/movies/vorbisAudioCursor.h index d194cb0c23..cd74acc750 100644 --- a/panda/src/movies/vorbisAudioCursor.h +++ b/panda/src/movies/vorbisAudioCursor.h @@ -35,7 +35,7 @@ PUBLISHED: virtual void seek(double offset); public: - virtual void read_samples(int n, int16_t *data); + virtual int read_samples(int n, int16_t *data); bool _is_valid; @@ -50,16 +50,7 @@ protected: #ifndef CPPPARSER OggVorbis_File _ov; #endif - int _bitstream; - double _byte_rate; - int _block_align; - int _bytes_per_sample; - bool _is_float; - - std::streampos _data_start; - std::streampos _data_pos; - size_t _data_size; public: static TypeHandle get_class_type() { diff --git a/panda/src/movies/wavAudioCursor.cxx b/panda/src/movies/wavAudioCursor.cxx index 381ce2d81b..036588c189 100644 --- a/panda/src/movies/wavAudioCursor.cxx +++ b/panda/src/movies/wavAudioCursor.cxx @@ -294,27 +294,61 @@ seek(double t) { t = std::max(t, 0.0); std::streampos pos = _data_start + (std::streampos) std::min((size_t) (t * _byte_rate), _data_size); + std::streambuf *buf = _stream->rdbuf(); + if (_can_seek_fast) { - _stream->seekg(pos); - if (_stream->tellg() != pos) { + if (buf->pubseekpos(pos, std::ios::in) != pos) { // Clearly, we can't seek fast. Fall back to the case below. _can_seek_fast = false; } } - if (!_can_seek_fast) { - std::streampos current = _stream->tellg(); + // Get the current position of the cursor in the file. + std::streampos current = buf->pubseekoff(0, std::ios::cur, std::ios::in); + if (!_can_seek_fast) { if (pos > current) { // It is ahead of our current position. Skip ahead. - _reader.skip_bytes(pos - current); + _stream->ignore(pos - current); + current = pos; } else if (pos < current) { - // We'll have to reopen the file. TODO + // Can we seek to the beginning? Some streams, such as ZStream, let us + // rewind the stream. + if (buf->pubseekpos(0, std::ios::in) == (std::streampos)0) { + if (pos > _data_start && movies_cat.is_info()) { + Filename fn = get_source()->get_filename(); + movies_cat.info() + << "Unable to seek backwards in " << fn.get_basename() + << "; seeking to beginning and skipping " << pos << " bytes.\n"; + } + _stream->ignore(pos); + current = pos; + } else { + // No; close and reopen the file. + Filename fn = get_source()->get_filename(); + movies_cat.warning() + << "Unable to seek backwards in " << fn.get_basename() + << "; reopening and skipping " << pos << " bytes.\n"; + + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + std::istream *stream = vfs->open_read_file(get_source()->get_filename(), true); + if (stream != nullptr) { + vfs->close_read_file(_stream); + stream->ignore(pos); + _stream = stream; + _reader = StreamReader(stream, false); + current = pos; + } else { + movies_cat.error() + << "Unable to reopen " << fn << ".\n"; + _can_seek = false; + } + } } } - _data_pos = _stream->tellg() - _data_start; + _data_pos = (size_t)current - _data_start; _last_seek = _data_pos / _byte_rate; _samples_read = 0; } @@ -324,13 +358,13 @@ seek(double t) { * read. Your buffer must be equal in size to N * channels. Multiple-channel * audio will be interleaved. */ -void WavAudioCursor:: +int WavAudioCursor:: read_samples(int n, int16_t *data) { int desired = n * _audio_channels; int read_samples = std::min(desired, ((int) (_data_size - _data_pos)) / _bytes_per_sample); if (read_samples <= 0) { - return; + return 0; } switch (_format) { @@ -421,8 +455,10 @@ read_samples(int n, int16_t *data) { // Fill the rest of the buffer with silence. if (read_samples < desired) { memset(data + read_samples, 0, (desired - read_samples) * 2); + n = read_samples / _audio_channels; } _data_pos = _stream->tellg() - _data_start; - _samples_read += read_samples / _audio_channels; + _samples_read += n; + return n; } diff --git a/panda/src/movies/wavAudioCursor.h b/panda/src/movies/wavAudioCursor.h index 21133b1c62..9ae03a5d7d 100644 --- a/panda/src/movies/wavAudioCursor.h +++ b/panda/src/movies/wavAudioCursor.h @@ -31,7 +31,7 @@ PUBLISHED: virtual void seek(double offset); public: - virtual void read_samples(int n, int16_t *data); + virtual int read_samples(int n, int16_t *data); bool _is_valid; diff --git a/panda/src/net/datagramQueue.h b/panda/src/net/datagramQueue.h index 131de070cc..9dac974f5d 100644 --- a/panda/src/net/datagramQueue.h +++ b/panda/src/net/datagramQueue.h @@ -18,7 +18,7 @@ #include "netDatagram.h" #include "pmutex.h" -#include "conditionVarFull.h" +#include "conditionVar.h" #include "pdeque.h" /** @@ -41,7 +41,7 @@ public: private: Mutex _cvlock; - ConditionVarFull _cv; // signaled when queue contents change. + ConditionVar _cv; // signaled when queue contents change. typedef pdeque QueueType; QueueType _queue; diff --git a/panda/src/ode/odeBody.h b/panda/src/ode/odeBody.h index f1cd9dae68..5cdfd4c8d1 100644 --- a/panda/src/ode/odeBody.h +++ b/panda/src/ode/odeBody.h @@ -133,6 +133,7 @@ PUBLISHED: OdeJoint get_joint(int index) const; MAKE_SEQ(get_joints, get_num_joints, get_joint); EXTENSION(INLINE PyObject *get_converted_joint(int i) const); + MAKE_SEQ_PROPERTY(joints, get_num_joints, get_converted_joint); INLINE void enable(); INLINE void disable(); diff --git a/panda/src/ode/odeGeom.cxx b/panda/src/ode/odeGeom.cxx index e25c6c45b6..093d075372 100644 --- a/panda/src/ode/odeGeom.cxx +++ b/panda/src/ode/odeGeom.cxx @@ -33,12 +33,16 @@ TypeHandle OdeGeom::_type_handle; OdeGeom:: OdeGeom(dGeomID id) : _id(id) { - odegeom_cat.debug() << get_type() << "(" << _id << ")\n"; + if (odegeom_cat.is_debug()) { + odegeom_cat.debug() << get_type() << "(" << _id << ")\n"; + } } OdeGeom:: ~OdeGeom() { - odegeom_cat.debug() << "~" << get_type() << "(" << _id << ")\n"; + if (odegeom_cat.is_debug()) { + odegeom_cat.debug() << "~" << get_type() << "(" << _id << ")\n"; + } /* GeomSurfaceMap::iterator iter = _geom_surface_map.find(this->get_id()); if (iter != _geom_surface_map.end()) { diff --git a/panda/src/ode/odeJoint.cxx b/panda/src/ode/odeJoint.cxx index dd10c57f0a..4ada7fefa5 100644 --- a/panda/src/ode/odeJoint.cxx +++ b/panda/src/ode/odeJoint.cxx @@ -31,15 +31,19 @@ TypeHandle OdeJoint::_type_handle; OdeJoint:: OdeJoint() : _id(nullptr) { - std::ostream &out = odejoint_cat.debug(); - out << get_type() << "(" << _id << ")\n"; + if (odejoint_cat.is_debug()) { + std::ostream &out = odejoint_cat.debug(); + out << get_type() << "(" << _id << ")\n"; + } } OdeJoint:: OdeJoint(dJointID id) : _id(id) { - std::ostream &out = odejoint_cat.debug(); - out << get_type() << "(" << _id << ")\n"; + if (odejoint_cat.is_debug()) { + std::ostream &out = odejoint_cat.debug(); + out << get_type() << "(" << _id << ")\n"; + } } OdeJoint:: diff --git a/panda/src/ode/odeJoint.h b/panda/src/ode/odeJoint.h index eda2a98b96..47233a9361 100644 --- a/panda/src/ode/odeJoint.h +++ b/panda/src/ode/odeJoint.h @@ -83,7 +83,7 @@ PUBLISHED: INLINE void set_feedback(bool flag = true); INLINE OdeJointFeedback *get_feedback(); - EXTENSION(void attach(const OdeBody *body1, const OdeBody *body2)); + EXTENSION(void attach(PyObject *body1, PyObject *body2)); void attach_bodies(const OdeBody &body1, const OdeBody &body2); void attach_body(const OdeBody &body, int index); void detach(); diff --git a/panda/src/ode/odeJoint_ext.cxx b/panda/src/ode/odeJoint_ext.cxx index 6f284e1213..57669b7644 100644 --- a/panda/src/ode/odeJoint_ext.cxx +++ b/panda/src/ode/odeJoint_ext.cxx @@ -29,6 +29,7 @@ #include "odePlane2dJoint.h" #ifndef CPPPARSER +extern Dtool_PyTypedObject Dtool_OdeBody; extern Dtool_PyTypedObject Dtool_OdeJoint; extern Dtool_PyTypedObject Dtool_OdeBallJoint; extern Dtool_PyTypedObject Dtool_OdeHingeJoint; @@ -48,7 +49,23 @@ extern Dtool_PyTypedObject Dtool_OdePlane2dJoint; * attached to the environment. */ void Extension:: -attach(const OdeBody *body1, const OdeBody *body2) { +attach(PyObject *param1, PyObject *param2) { + const OdeBody *body1 = nullptr; + if (param1 != Py_None) { + body1 = (const OdeBody *)DTOOL_Call_GetPointerThisClass(param1, &Dtool_OdeBody, 1, "OdeJoint.attach", true, true); + if (body1 == nullptr) { + return; + } + } + + const OdeBody *body2 = nullptr; + if (param2 != Py_None) { + body2 = (const OdeBody *)DTOOL_Call_GetPointerThisClass(param2, &Dtool_OdeBody, 2, "OdeJoint.attach", true, true); + if (body2 == nullptr) { + return; + } + } + if (body1 && body2) { _this->attach_bodies(*body1, *body2); diff --git a/panda/src/ode/odeJoint_ext.h b/panda/src/ode/odeJoint_ext.h index b61938506f..84dbf4ac93 100644 --- a/panda/src/ode/odeJoint_ext.h +++ b/panda/src/ode/odeJoint_ext.h @@ -30,7 +30,7 @@ template<> class Extension : public ExtensionBase { public: - void attach(const OdeBody *body1, const OdeBody *body2); + void attach(PyObject *body1, PyObject *body2); PyObject *convert() const; }; diff --git a/panda/src/ode/odeSpace.cxx b/panda/src/ode/odeSpace.cxx index f2beca8efb..4368a0b437 100644 --- a/panda/src/ode/odeSpace.cxx +++ b/panda/src/ode/odeSpace.cxx @@ -157,9 +157,11 @@ auto_callback(void *data, dGeomID o1, dGeomID o2) { numc = dCollide(o1, o2, OdeSpace::MAX_CONTACTS, &contact[0].geom, sizeof(dContact)); if (numc) { - odespace_cat.debug() << "collision between geoms " << o1 << " and " << o2 << "\n"; - odespace_cat.debug() << "collision between body " << b1 << " and " << b2 << "\n"; - odespace_cat.debug() << "surface1= "<< surface1 << " surface2=" << surface2 << "\n"; + if (odespace_cat.is_debug()) { + odespace_cat.debug() << "collision between geoms " << o1 << " and " << o2 << "\n"; + odespace_cat.debug() << "collision between body " << b1 << " and " << b2 << "\n"; + odespace_cat.debug() << "surface1= "<< surface1 << " surface2=" << surface2 << "\n"; + } PT(OdeCollisionEntry) entry; if (!_static_auto_collide_space->_collision_event.empty()) { diff --git a/panda/src/ode/odeTriMeshData.I b/panda/src/ode/odeTriMeshData.I index 2aad3a2404..be5afa0218 100644 --- a/panda/src/ode/odeTriMeshData.I +++ b/panda/src/ode/odeTriMeshData.I @@ -38,7 +38,9 @@ get(int data_id) { INLINE void OdeTriMeshData:: build_single(const void* vertices, int vertex_stride, int vertex_count, const void* indices, int index_count, int tri_stride) { - odetrimeshdata_cat.debug() << "build_single(" << vertices << ", " << vertex_stride << ", " << vertex_count << ", " << indices << ", " << index_count << ", " << tri_stride << ")\n"; + if (odetrimeshdata_cat.is_debug()) { + odetrimeshdata_cat.debug() << "build_single(" << vertices << ", " << vertex_stride << ", " << vertex_count << ", " << indices << ", " << index_count << ", " << tri_stride << ")\n"; + } dGeomTriMeshDataBuildSingle(_id, vertices, vertex_stride, vertex_count, indices, index_count, tri_stride); } diff --git a/panda/src/ode/odeTriMeshData.cxx b/panda/src/ode/odeTriMeshData.cxx index c92602ac34..8e741529f6 100644 --- a/panda/src/ode/odeTriMeshData.cxx +++ b/panda/src/ode/odeTriMeshData.cxx @@ -20,7 +20,9 @@ OdeTriMeshData::TriMeshDataMap *OdeTriMeshData::_tri_mesh_data_map = nullptr; void OdeTriMeshData:: link_data(dGeomID id, PT(OdeTriMeshData) data) { - odetrimeshdata_cat.debug() << get_class_type() << "::link_data(" << id << "->" << data << ")" << "\n"; + if (odetrimeshdata_cat.is_debug()) { + odetrimeshdata_cat.debug() << get_class_type() << "::link_data(" << id << "->" << data << ")" << "\n"; + } get_tri_mesh_data_map()[id] = data; } @@ -36,7 +38,9 @@ get_data(dGeomID id) { void OdeTriMeshData:: unlink_data(dGeomID id) { - odetrimeshdata_cat.debug() << get_class_type() << "::unlink_data(" << id << ")" << "\n"; + if (odetrimeshdata_cat.is_debug()) { + odetrimeshdata_cat.debug() << get_class_type() << "::unlink_data(" << id << ")" << "\n"; + } nassertv(_tri_mesh_data_map != nullptr); TriMeshDataMap::iterator iter = _tri_mesh_data_map->find(id); if (iter != _tri_mesh_data_map->end()) { @@ -46,11 +50,13 @@ unlink_data(dGeomID id) { void OdeTriMeshData:: print_data(const std::string &marker) { - odetrimeshdata_cat.debug() << get_class_type() << "::print_data(" << marker << ")\n"; - const TriMeshDataMap &data_map = get_tri_mesh_data_map(); - TriMeshDataMap::const_iterator iter = data_map.begin(); - for (;iter != data_map.end(); ++iter) { - odetrimeshdata_cat.debug() << "\t" << iter->first << " : " << iter->second << "\n"; + if (odetrimeshdata_cat.is_debug()) { + odetrimeshdata_cat.debug() << get_class_type() << "::print_data(" << marker << ")\n"; + const TriMeshDataMap &data_map = get_tri_mesh_data_map(); + TriMeshDataMap::const_iterator iter = data_map.begin(); + for (;iter != data_map.end(); ++iter) { + odetrimeshdata_cat.debug() << "\t" << iter->first << " : " << iter->second << "\n"; + } } } @@ -95,11 +101,15 @@ OdeTriMeshData(const NodePath& model, bool use_normals) : _normals(nullptr), _num_vertices(0), _num_faces(0) { - odetrimeshdata_cat.debug() << get_type() << "(" << _id << ")" << "\n"; + if (odetrimeshdata_cat.is_debug()) { + odetrimeshdata_cat.debug() << get_type() << "(" << _id << ")" << "\n"; + } process_model(model, use_normals); - write_faces(odetrimeshdata_cat.debug()); + if (odetrimeshdata_cat.is_debug()) { + write_faces(odetrimeshdata_cat.debug()); + } #ifdef dSINGLE if (!use_normals) { @@ -131,7 +141,9 @@ OdeTriMeshData(const OdeTriMeshData &other) { OdeTriMeshData:: ~OdeTriMeshData() { - odetrimeshdata_cat.debug() << "~" << get_type() << "(" << _id << ")" << "\n"; + if (odetrimeshdata_cat.is_debug()) { + odetrimeshdata_cat.debug() << "~" << get_type() << "(" << _id << ")" << "\n"; + } destroy(); if (_vertices != nullptr) { PANDA_FREE_ARRAY(_vertices); @@ -152,7 +164,9 @@ OdeTriMeshData:: void OdeTriMeshData:: destroy() { - odetrimeshdata_cat.debug() << get_type() << "::destroy(" << _id << ")" << "\n"; + if (odetrimeshdata_cat.is_debug()) { + odetrimeshdata_cat.debug() << get_type() << "::destroy(" << _id << ")" << "\n"; + } if (_id != nullptr) { dGeomTriMeshDataDestroy(_id); remove_data(this); @@ -185,8 +199,10 @@ process_model(const NodePath& model, bool &use_normals) { analyze((GeomNode*)geomNodePaths[i].node()); } - odetrimeshdata_cat.debug() << "Found " << _num_vertices << " vertices.\n"; - odetrimeshdata_cat.debug() << "Found " << _num_faces << " faces.\n"; + if (odetrimeshdata_cat.is_debug()) { + odetrimeshdata_cat.debug() << "Found " << _num_vertices << " vertices.\n"; + odetrimeshdata_cat.debug() << "Found " << _num_faces << " faces.\n"; + } _vertices = (StridedVertex *)PANDA_MALLOC_ARRAY(_num_vertices * sizeof(StridedVertex)); _faces = (StridedTri *)PANDA_MALLOC_ARRAY(_num_faces * sizeof(StridedTri)); @@ -195,17 +211,23 @@ process_model(const NodePath& model, bool &use_normals) { for (int i = 0; i < geomNodePaths.get_num_paths(); ++i) { process_geom_node((GeomNode*)geomNodePaths[i].node()); - odetrimeshdata_cat.debug() << "_num_vertices now at " << _num_vertices << "\n"; + if (odetrimeshdata_cat.is_debug()) { + odetrimeshdata_cat.debug() << "_num_vertices now at " << _num_vertices << "\n"; + } } - odetrimeshdata_cat.debug() << "Filled " << _num_faces << " triangles(" \ - << _num_vertices << " vertices)\n"; + if (odetrimeshdata_cat.is_debug()) { + odetrimeshdata_cat.debug() + << "Filled " << _num_faces << " triangles(" << _num_vertices << " vertices)\n"; + } } void OdeTriMeshData:: process_geom_node(const GeomNode *geomNode) { - ostream &out = odetrimeshdata_cat.debug(); - out.width(2); out << "" << "process_geom_node(" << *geomNode << ")" << "\n"; + if (odetrimeshdata_cat.is_debug()) { + ostream &out = odetrimeshdata_cat.debug(); + out.width(2); out << "" << "process_geom_node(" << *geomNode << ")" << "\n"; + } for (int i = 0; i < geomNode->get_num_geoms(); ++i) { process_geom(geomNode->get_geom(i)); } @@ -213,8 +235,10 @@ process_geom_node(const GeomNode *geomNode) { void OdeTriMeshData:: process_geom(const Geom *geom) { - ostream &out = odetrimeshdata_cat.debug(); - out.width(4); out << "" << "process_geom(" << *geom << ")" << "\n"; + if (odetrimeshdata_cat.is_debug()) { + ostream &out = odetrimeshdata_cat.debug(); + out.width(4); out << "" << "process_geom(" << *geom << ")" << "\n"; + } if (geom->get_primitive_type() != Geom::PT_polygons) { return; } diff --git a/panda/src/ode/odeWorld.cxx b/panda/src/ode/odeWorld.cxx index f087c46374..66a7233dca 100644 --- a/panda/src/ode/odeWorld.cxx +++ b/panda/src/ode/odeWorld.cxx @@ -20,7 +20,9 @@ TypeHandle OdeWorld::_type_handle; OdeWorld:: OdeWorld() : _id(dWorldCreate()) { - odeworld_cat.debug() << get_type() << "(" << _id << ")" << "\n"; + if (odeworld_cat.is_debug()) { + odeworld_cat.debug() << get_type() << "(" << _id << ")" << "\n"; + } _num_surfaces = 0; } @@ -34,7 +36,9 @@ OdeWorld(const OdeWorld ©) : OdeWorld:: ~OdeWorld() { - odeworld_cat.debug() << "~" << get_type() << "(" << _id << ")" << "\n"; + if (odeworld_cat.is_debug()) { + odeworld_cat.debug() << "~" << get_type() << "(" << _id << ")" << "\n"; + } } void OdeWorld:: @@ -71,7 +75,9 @@ init_surface_table(uint8_t num_surfaces) { void OdeWorld:: set_surface(int pos1, int pos2, sSurfaceParams& entry) { - odeworld_cat.debug() << " pos1 " << pos1 << " pos2 " << pos2 << " num surfaces " << (int)_num_surfaces << " endline\n"; + if (odeworld_cat.is_debug()) { + odeworld_cat.debug() << " pos1 " << pos1 << " pos2 " << pos2 << " num surfaces " << (int)_num_surfaces << " endline\n"; + } if((_num_surfaces <= pos1) || (_num_surfaces <= pos2)) { odeworld_cat.error() << "surface position exceeds size of surface table, set num_surface in initSurfaceTable higher." << "\n"; return; diff --git a/panda/src/osxdisplay/config_osxdisplay.cxx b/panda/src/osxdisplay/config_osxdisplay.cxx deleted file mode 100644 index 0ebc9454bd..0000000000 --- a/panda/src/osxdisplay/config_osxdisplay.cxx +++ /dev/null @@ -1,87 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file config_osxdisplay.cxx - */ - -#include "config_osxdisplay.h" -#include "osxGraphicsBuffer.h" -#include "osxGraphicsPipe.h" -#include "osxGraphicsStateGuardian.h" -#include "osxGraphicsWindow.h" - -#include "graphicsPipeSelection.h" -#include "dconfig.h" -#include "pandaSystem.h" - - -#if !defined(CPPPARSER) && !defined(LINK_ALL_STATIC) && !defined(BUILDING_PANDA_OSXDISPLAY) - #error Buildsystem error: BUILDING_PANDA_OSXDISPLAY not defined -#endif - -Configure(config_osxdisplay); - -NotifyCategoryDef(osxdisplay, "display"); - -ConfigureFn(config_osxdisplay) { - init_libosxdisplay(); -} - -ConfigVariableBool show_resize_box -("show-resize-box", true, - PRC_DESC("When this variable is true, then resizable OSX Panda windows will " - "be rendered with a resize control in the lower-right corner. " - "This is specially handled by Panda, since otherwise the 3-d " - "window would completely hide any resize control drawn by the " - "OS. Set this variable false to allow this control to be hidden.")); - -ConfigVariableBool osx_support_gl_buffer -("osx-support-gl-buffer", true, - PRC_DESC("Set this true to support use of GLBuffers on OSX. When true, there is a risk of " - "a program crash due to buggy driver support for GLBuffers. " - "If this is false, offscreen buffers will be created as " - "AGLPbuffers, which are less powerful, and appear to have their " - "own set of problems.")); - -ConfigVariableBool osx_disable_event_loop -("osx-disable-event-loop", false, - PRC_DESC("Set this true to disable the window event loop for the Panda " - "windows. This makes sense only in a publish environment where " - "the window event loop is already handled by another part of the " - "app.")); - -ConfigVariableInt osx_mouse_wheel_scale -("osx-mouse-wheel-scale", 1, - PRC_DESC("Specify the number of units to spin the Mac mouse wheel to " - "represent a single wheel_up or wheel_down message.")); - -/** - * Initializes the library. This must be called at least once before any of - * the functions or classes in this library can be used. Normally it will be - * called by the static initializers and need not be called explicitly, but - * special cases exist. - */ -void -init_libosxdisplay() { - static bool initialized = false; - if (initialized) { - return; - } - initialized = true; - - osxGraphicsBuffer::init_type(); - osxGraphicsPipe::init_type(); - osxGraphicsWindow::init_type(); - osxGraphicsStateGuardian::init_type(); - - GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr(); - selection->add_pipe_type(osxGraphicsPipe::get_class_type(), osxGraphicsPipe::pipe_constructor); - - PandaSystem *ps = PandaSystem::get_global_ptr(); - ps->set_system_tag("OpenGL", "window_system", "OSX"); -} diff --git a/panda/src/osxdisplay/config_osxdisplay.h b/panda/src/osxdisplay/config_osxdisplay.h deleted file mode 100644 index 5eadc80a7e..0000000000 --- a/panda/src/osxdisplay/config_osxdisplay.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file config_osxdisplay.h - */ - -#ifndef __CONFIG_OSXDISPLAY_H__ -#define __CONFIG_OSXDISPLAY_H__ - -#include "pandabase.h" -#include "notifyCategoryProxy.h" -#include "configVariableBool.h" -#include "configVariableInt.h" - -NotifyCategoryDecl( osxdisplay , EXPCL_PANDA_OSXDISPLAY, EXPTP_PANDA_OSXDISPLAY); - -extern EXPCL_PANDA_OSXDISPLAY void init_libosxdisplay(); - -extern ConfigVariableBool show_resize_box; -extern ConfigVariableBool osx_support_gl_buffer; -extern ConfigVariableBool osx_disable_event_loop; -extern ConfigVariableInt osx_mouse_wheel_scale; - -#endif /* __CONFIG_OSXDISPLAY_H__ */ diff --git a/panda/src/osxdisplay/osxGraphicsBuffer.cxx b/panda/src/osxdisplay/osxGraphicsBuffer.cxx deleted file mode 100644 index 03bfa46d79..0000000000 --- a/panda/src/osxdisplay/osxGraphicsBuffer.cxx +++ /dev/null @@ -1,200 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file osxGraphicsBuffer.cxx - */ - -#include "osxGraphicsBuffer.h" -#include "osxGraphicsStateGuardian.h" -#include "config_osxdisplay.h" -#include "osxGraphicsPipe.h" - -#include "graphicsPipe.h" -#include "glgsg.h" -#include "pStatTimer.h" - -TypeHandle osxGraphicsBuffer::_type_handle; - -/** - * - */ -osxGraphicsBuffer:: -osxGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe, - const std::string &name, - const FrameBufferProperties &fb_prop, - const WindowProperties &win_prop, - int flags, - GraphicsStateGuardian *gsg, - GraphicsOutput *host) : - GraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) -{ - osxGraphicsPipe *osx_pipe; - DCAST_INTO_V(osx_pipe, _pipe); - - _pbuffer = nullptr; - - // Since the pbuffer never gets flipped, we get screenshots from the same - // buffer we draw into. - _screenshot_buffer_type = _draw_buffer_type; -} - -/** - * - */ -osxGraphicsBuffer:: -~osxGraphicsBuffer() { - nassertv(_pbuffer == nullptr); -} - -/** - * This function will be called within the draw thread before beginning - * rendering for a given frame. It should do whatever setup is required, and - * return true if the frame should be rendered, or false if it should be - * skipped. - */ -bool osxGraphicsBuffer:: -begin_frame(FrameMode mode, Thread *current_thread) { - PStatTimer timer(_make_current_pcollector); - - begin_frame_spam(mode); - if (_gsg == nullptr) { - return false; - } - nassertr(_pbuffer != nullptr, false); - - osxGraphicsStateGuardian *osxgsg; - DCAST_INTO_R(osxgsg, _gsg, false); - if (!aglSetPBuffer(osxgsg->get_context(), _pbuffer, 0, 0, 0)) { - report_agl_error("aglSetPBuffer"); - return false; - } - - if (!aglSetCurrentContext(osxgsg->get_context())) { - report_agl_error("aglSetCurrentContext"); - return false; - } - - osxgsg->reset_if_new(); - - if (mode == FM_render) { - CDLockedReader cdata(_cycler); - for (size_t i = 0; i != cdata->_textures.size(); ++i) { - const RenderTexture &rt = cdata->_textures[i]; - RenderTextureMode rtm_mode = rt._rtm_mode; - if (rtm_mode == RTM_bind_or_copy) { - CDWriter cdataw(_cycler, cdata, false); - nassertr(cdata->_textures.size() == cdataw->_textures.size(), false); - cdataw->_textures[i]._rtm_mode = RTM_copy_texture; - } - } - clear_cube_map_selection(); - } - _gsg->set_current_properties(&get_fb_properties()); - return _gsg->begin_frame(current_thread); -} - -/** - * This function will be called within the draw thread after rendering is - * completed for a given frame. It should do whatever finalization is - * required. - */ -void osxGraphicsBuffer:: -end_frame(FrameMode mode, Thread *current_thread) { - end_frame_spam(mode); - nassertv(_gsg != nullptr); - - if (mode == FM_render) { - copy_to_textures(); - } - - _gsg->end_frame(current_thread); - - if (mode == FM_render) { - trigger_flip(); - clear_cube_map_selection(); - } -} - -/** - * Closes the buffer right now. Called from the window thread. - */ -void osxGraphicsBuffer:: -close_buffer() { - if (_gsg != nullptr) { - // aglSetPBuffer(osxgsg->get_context(), _pbuffer, 0, 0, 0); - _gsg.clear(); - } - if (_pbuffer != nullptr) { - aglDestroyPBuffer(_pbuffer); - _pbuffer = nullptr; - } - _is_valid = false; -} - -/** - * Opens the buffer right now. Called from the window thread. Returns true - * if the buffer is successfully opened, or false if there was a problem. - */ -bool osxGraphicsBuffer:: -open_buffer() { - if (_gsg == 0) { - _gsg = new osxGraphicsStateGuardian(_engine, _pipe, nullptr); - } - - if (_pbuffer == nullptr) { - GLenum target = GL_TEXTURE_RECTANGLE_ARB; - if (_size[0] == Texture::up_to_power_2(_size[0]) && - _size[1] == Texture::up_to_power_2(_size[1])) { - // It's a power-of-two size, so we can use GL_TEXTURE_2D as the target. - // Dunno, but maybe this will be more likely to work on some hardware. - target = GL_TEXTURE_2D; - } - if (!aglCreatePBuffer(_size.get_x(), _size.get_y(), target, GL_RGBA, 0, &_pbuffer)) { - report_agl_error("aglCreatePBuffer"); - close_buffer(); - return false; - } - } - - osxGraphicsStateGuardian *osxgsg; - DCAST_INTO_R(osxgsg, _gsg, false); - - OSStatus stat = osxgsg->build_gl(false, true, _fb_properties); - if (stat != noErr) { - return false; - } - - if (!aglSetPBuffer(osxgsg->get_context(), _pbuffer, 0, 0, 0)) { - report_agl_error("aglSetPBuffer"); - close_buffer(); - return false; - } - - if (!aglSetCurrentContext(osxgsg->get_context())) { - report_agl_error("aglSetCurrentContext"); - return false; - } - - osxgsg->reset_if_new(); - if (!osxgsg->is_valid()) { - close_buffer(); - return false; - } - - /* - if (!osxgsg->get_fb_properties().verify_hardware_software - (_fb_properties, osxgsg->get_gl_renderer())) { - close_buffer(); - return false; - } - _fb_properties = osxgsg->get_fb_properties(); - */ - - _is_valid = true; - return true; -} diff --git a/panda/src/osxdisplay/osxGraphicsBuffer.h b/panda/src/osxdisplay/osxGraphicsBuffer.h deleted file mode 100644 index 7ea83d1c4c..0000000000 --- a/panda/src/osxdisplay/osxGraphicsBuffer.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file osxGraphicsBuffer.h - */ - -#ifndef OSXGRAPHICSBUFFER_H -#define OSXGRAPHICSBUFFER_H -#include - -#define __glext_h_ -#include -#include - -#include "pandabase.h" -#include "graphicsBuffer.h" -#include "glgsg.h" - -/** - * An offscreen buffer in the OSX environment. This creates an AGLPbuffer. - */ -class osxGraphicsBuffer : public GraphicsBuffer { -public: - osxGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe, - const std::string &name, - const FrameBufferProperties &fb_prop, - const WindowProperties &win_prop, - int flags, - GraphicsStateGuardian *gsg, - GraphicsOutput *host); - virtual ~osxGraphicsBuffer(); - - virtual bool begin_frame(FrameMode mode, Thread *current_thread); - virtual void end_frame(FrameMode mode, Thread *current_thread); - -protected: - virtual void close_buffer(); - virtual bool open_buffer(); - -private: - AGLPbuffer _pbuffer; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - GraphicsBuffer::init_type(); - register_type(_type_handle, "osxGraphicsBuffer", - GraphicsBuffer::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; - - friend class osxGraphicsStateGuardian; -}; - - -#endif diff --git a/panda/src/osxdisplay/osxGraphicsPipe.cxx b/panda/src/osxdisplay/osxGraphicsPipe.cxx deleted file mode 100644 index 5a56a6259d..0000000000 --- a/panda/src/osxdisplay/osxGraphicsPipe.cxx +++ /dev/null @@ -1,451 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file osxGraphicsPipe.cxx - */ - -#include "osxGraphicsPipe.h" -#include "config_osxdisplay.h" -#include "osxGraphicsWindow.h" -#include "osxGraphicsBuffer.h" -#include "osxGraphicsStateGuardian.h" -#include "pnmImage.h" -#include "subprocessWindow.h" -#include "nativeWindowHandle.h" -#include "displayInformation.h" -#import - -// some macros to make code more readable. -#define GetModeWidth(mode) GetDictionaryLong((mode), kCGDisplayWidth) -#define GetModeHeight(mode) GetDictionaryLong((mode), kCGDisplayHeight) -#define GetModeRefreshRate(mode) GetDictionaryLong((mode), kCGDisplayRefreshRate) -#define GetModeBitsPerPixel(mode) GetDictionaryLong((mode), kCGDisplayBitsPerPixel) -#define GetModeSafeForHardware(mode) GetDictionaryBoolean((mode), kCGDisplayModeIsSafeForHardware) -#define GetModeStretched(mode) GetDictionaryBoolean((mode), kCGDisplayModeIsStretched) -#define MAX_DISPLAYS 32 - -Boolean GetDictionaryBoolean(CFDictionaryRef theDict, const void* key) { - // get a boolean from the dictionary - Boolean value = false; - CFBooleanRef boolRef; - boolRef = (CFBooleanRef)CFDictionaryGetValue(theDict, key); - if (boolRef != nullptr) - value = CFBooleanGetValue(boolRef); - return value; -} - -long GetDictionaryLong(CFDictionaryRef theDict, const void* key) { - // get a long from the dictionary - long value = 0; - CFNumberRef numRef; - numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key); - if (numRef != nullptr) - CFNumberGetValue(numRef, kCFNumberLongType, &value); - return value; -} - -static CFComparisonResult CompareModes (const void *val1,const void *val2,void *context) { - // CFArray comparison callback for sorting display modes. -#pragma unused(context) - CFDictionaryRef thisMode = (CFDictionaryRef)val1; - CFDictionaryRef otherMode = (CFDictionaryRef)val2; - - long width = GetModeWidth(thisMode); - long otherWidth = GetModeWidth(otherMode); - long height = GetModeHeight(thisMode); - long otherHeight = GetModeHeight(otherMode); - - // sort modes in screen size order - if (width * height < otherWidth * otherHeight) { - return kCFCompareLessThan; - } else if (width * height > otherWidth * otherHeight) { - return kCFCompareGreaterThan; - } - - // sort modes by bits per pixel - long bitsPerPixel = GetModeBitsPerPixel(thisMode); - long otherBitsPerPixel = GetModeBitsPerPixel(otherMode); - if (bitsPerPixel < otherBitsPerPixel) { - return kCFCompareLessThan; - } else if (bitsPerPixel > otherBitsPerPixel) { - return kCFCompareGreaterThan; - } - - // sort modes by refresh rate. - long refreshRate = GetModeRefreshRate(thisMode); - long otherRefreshRate = GetModeRefreshRate(otherMode); - if (refreshRate < otherRefreshRate) { - return kCFCompareLessThan; - } else if (refreshRate > otherRefreshRate) { - return kCFCompareGreaterThan; - } - - return kCFCompareEqualTo; -} - -CFArrayRef GSCGDisplayAvailableModesUsefulForOpenGL(CGDirectDisplayID display) { - // get a list of all possible display modes for this system. - CFArrayRef availableModes = CGDisplayAvailableModes(display); - unsigned int numberOfAvailableModes = CFArrayGetCount(availableModes); - - // creat mutable array to hold the display modes we are interested int. - CFMutableArrayRef usefulModes = CFArrayCreateMutable(kCFAllocatorDefault, numberOfAvailableModes, nullptr); - - // get the current bits per pixel. - long currentModeBitsPerPixel = GetModeBitsPerPixel(CGDisplayCurrentMode(display)); - - unsigned int i; - for (i= 0; i refreshRate) { - skipped = true; - break; - } - } - } - // this is a useful mode so add it to the array. - if (!replaced && !skipped) { - CFArrayAppendValue(usefulModes, mode); - } - } - // now sort the useful mode array, using the comparison callback. - CFArraySortValues( usefulModes, - CFRangeMake(0, CFArrayGetCount(usefulModes)), - (CFComparatorFunction) CompareModes, nullptr); - // return the CFArray of the useful display modes. - return usefulModes; -} - -TypeHandle osxGraphicsPipe::_type_handle; - -/** - * - */ -osxGraphicsPipe:: -osxGraphicsPipe() { - CGRect display_bounds = CGDisplayBounds(kCGDirectMainDisplay); - _display_width = CGRectGetWidth(display_bounds); - _display_height = CGRectGetHeight(display_bounds); - - CGDirectDisplayID display, displayArray[MAX_DISPLAYS] ; - CGDisplayCount numDisplays; - CFDictionaryRef displayMode; - CFArrayRef displayModeArray; - int number, i; - CGGetActiveDisplayList (MAX_DISPLAYS, displayArray, &numDisplays); - display = displayArray [numDisplays - 1]; - displayModeArray = GSCGDisplayAvailableModesUsefulForOpenGL( display ); - number = CFArrayGetCount( displayModeArray ); - DisplayMode *displays = new DisplayMode[ number ]; - for(i = 0; i < number; i++) { - displayMode = (CFDictionaryRef) CFArrayGetValueAtIndex (displayModeArray, i); - _display_information -> _total_display_modes++; - displays[i].width = (signed int)GetModeWidth (displayMode); - displays[i].height = (signed int)GetModeHeight (displayMode); - displays[i].bits_per_pixel = (signed int)GetModeBitsPerPixel (displayMode); - displays[i].refresh_rate = (signed int)GetModeRefreshRate (displayMode); - } - _display_information -> _display_mode_array = displays; -} - -/** - * - */ -osxGraphicsPipe:: -~osxGraphicsPipe() { -} - -/** - * Returns the name of the rendering interface associated with this - * GraphicsPipe. This is used to present to the user to allow him/her to - * choose between several possible GraphicsPipes available on a particular - * platform, so the name should be meaningful and unique for a given platform. - */ -std::string osxGraphicsPipe:: -get_interface_name() const { - return "OpenGL"; -} - -/** - * This function is passed to the GraphicsPipeSelection object to allow the - * user to make a default osxGraphicsPipe. - */ -PT(GraphicsPipe) osxGraphicsPipe:: -pipe_constructor() { - return new osxGraphicsPipe; -} - -/** - * Returns an indication of the thread in which this GraphicsPipe requires its - * window processing to be performed: typically either the app thread (e.g. - * X) or the draw thread (Windows). - */ -GraphicsPipe::PreferredWindowThread -osxGraphicsPipe::get_preferred_window_thread() const { - return PWT_app; -} - -/** - * Creates a new Quartz bitmap image with the data in the indicated PNMImage. - * The caller should eventually free this image via CGImageRelease. - */ -CGImageRef osxGraphicsPipe:: -create_cg_image(const PNMImage &pnm_image) { - size_t width = pnm_image.get_x_size(); - size_t height = pnm_image.get_y_size(); - -#ifdef PGM_BIGGRAYS - size_t bytes_per_component = 2; -#else - size_t bytes_per_component = 1; -#endif - size_t bits_per_component = bytes_per_component * 8; - size_t num_components = pnm_image.get_num_channels(); - - size_t bits_per_pixel = num_components * bits_per_component; - size_t bytes_per_row = num_components * bytes_per_component * width; - - size_t num_bytes = bytes_per_row * height; - bool has_alpha; - bool is_grayscale; - - CFStringRef color_space_name = nullptr; - switch (pnm_image.get_color_type()) { - case PNMImage::CT_grayscale: - color_space_name = kCGColorSpaceGenericGray; - has_alpha = false; - is_grayscale = true; - break; - - case PNMImage::CT_two_channel: - color_space_name = kCGColorSpaceGenericGray; - has_alpha = true; - is_grayscale = true; - break; - - case PNMImage::CT_color: - color_space_name = kCGColorSpaceGenericRGB; - has_alpha = false; - is_grayscale = false; - break; - - case PNMImage::CT_four_channel: - color_space_name = kCGColorSpaceGenericRGB; - has_alpha = true; - is_grayscale = false; - break; - - case PNMImage::CT_invalid: - // Shouldn't get here. - nassertr(false, nullptr); - break; - } - nassertr(color_space_name != nullptr, nullptr); - - CGColorSpaceRef color_space = CGColorSpaceCreateWithName(color_space_name); - nassertr(color_space != nullptr, nullptr); - - CGBitmapInfo bitmap_info = 0; -#ifdef PGM_BIGGRAYS - bitmap_info |= kCGBitmapByteOrder16Host; -#endif - if (has_alpha) { - bitmap_info |= kCGImageAlphaLast; - } - - // Now convert the pixel data to a format friendly to CGImageCreate(). - char *char_array = (char *)PANDA_MALLOC_ARRAY(num_bytes); - - xelval *dp = (xelval *)char_array; - for (size_t yi = 0; yi < height; ++yi) { - for (size_t xi = 0; xi < width; ++xi) { - if (is_grayscale) { - *dp++ = (xelval)(pnm_image.get_gray(xi, yi) * PGM_MAXMAXVAL); - } else { - *dp++ = (xelval)(pnm_image.get_red(xi, yi) * PGM_MAXMAXVAL); - *dp++ = (xelval)(pnm_image.get_green(xi, yi) * PGM_MAXMAXVAL); - *dp++ = (xelval)(pnm_image.get_blue(xi, yi) * PGM_MAXMAXVAL); - } - if (has_alpha) { - *dp++ = (xelval)(pnm_image.get_alpha(xi, yi) * PGM_MAXMAXVAL); - } - } - } - nassertr((void *)dp == (void *)(char_array + num_bytes), nullptr); - - CGDataProviderRef provider = - CGDataProviderCreateWithData(nullptr, char_array, num_bytes, release_data); - nassertr(provider != nullptr, nullptr); - - CGImageRef image = CGImageCreate - (width, height, bits_per_component, bits_per_pixel, bytes_per_row, - color_space, bitmap_info, provider, - nullptr, false, kCGRenderingIntentDefault); - nassertr(image != nullptr, nullptr); - - CGColorSpaceRelease(color_space); - CGDataProviderRelease(provider); - - return image; -} - -/** - * This callback is assigned to delete the data array allocated within - * create_cg_image(). - */ -void osxGraphicsPipe:: -release_data(void *info, const void *data, size_t size) { - char *char_array = (char *)data; - PANDA_FREE_ARRAY(char_array); -} - -/** - * Creates a new window on the pipe, if possible. - */ -PT(GraphicsOutput) osxGraphicsPipe:: -make_output(const std::string &name, - const FrameBufferProperties &fb_prop, - const WindowProperties &win_prop, - int flags, - GraphicsEngine *engine, - GraphicsStateGuardian *gsg, - GraphicsOutput *host, - int retry, - bool &precertify) { - if (!_is_valid) { - return nullptr; - } - - osxGraphicsStateGuardian *osxgsg = 0; - if (gsg != 0) { - DCAST_INTO_R(osxgsg, gsg, nullptr); - } - - // First thing to try: an osxGraphicsWindow - - if (retry == 0) { - if (((flags&BF_require_parasite)!=0)|| - ((flags&BF_refuse_window)!=0)|| - ((flags&BF_resizeable)!=0)|| - ((flags&BF_size_track_host)!=0)|| - ((flags&BF_can_bind_color)!=0)|| - ((flags&BF_can_bind_every)!=0)|| - ((flags&BF_can_bind_layered)!=0)) { - return nullptr; - } - WindowHandle *window_handle = win_prop.get_parent_window(); - if (window_handle != nullptr) { - osxdisplay_cat.info() - << "Got parent_window " << *window_handle << "\n"; -#ifdef SUPPORT_SUBPROCESS_WINDOW - WindowHandle::OSHandle *os_handle = window_handle->get_os_handle(); - if (os_handle != nullptr && - os_handle->is_of_type(NativeWindowHandle::SubprocessHandle::get_class_type())) { - return new SubprocessWindow(engine, this, name, fb_prop, win_prop, - flags, gsg, host); - } -#endif // SUPPORT_SUBPROCESS_WINDOW - } - return new osxGraphicsWindow(engine, this, name, fb_prop, win_prop, - flags, gsg, host); - } - - // Second thing to try: a GLGraphicsBuffer - - if (retry == 1) { - if (!osx_support_gl_buffer || !gl_support_fbo || host == nullptr || - (flags & (BF_require_parasite | BF_require_window)) != 0) { - return nullptr; - } - // Early failure - if we are sure that this buffer WONT meet specs, we can - // bail out early. - if ((flags & BF_fb_props_optional) == 0) { - if (fb_prop.get_indexed_color() || - fb_prop.get_back_buffers() > 0 || - fb_prop.get_accum_bits() > 0) { - return nullptr; - } - } - if (osxgsg != nullptr && osxgsg->is_valid() && !osxgsg->needs_reset()) { - if (!osxgsg->_supports_framebuffer_object || - osxgsg->_glDrawBuffers == nullptr) { - return nullptr; - } else if (fb_prop.is_basic()) { - // Early success - if we are sure that this buffer WILL meet specs, we - // can precertify it. - precertify = true; - } - } - return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop, flags, gsg, host); - } - - // Third thing to try: an osxGraphicsBuffer - if (retry == 2) { - if ((!support_render_texture)|| - ((flags&BF_require_parasite)!=0)|| - ((flags&BF_require_window)!=0)|| - ((flags&BF_resizeable)!=0)|| - ((flags&BF_size_track_host)!=0)|| - ((flags&BF_can_bind_every)!=0)|| - ((flags&BF_can_bind_layered)!=0)) { - return nullptr; - } - return new osxGraphicsBuffer(engine, this, name, fb_prop, win_prop, - flags, gsg, host); - } - - // Nothing else left to try. - return nullptr; -} - -/** - * This is called when make_output() is used to create a - * CallbackGraphicsWindow. If the GraphicsPipe can construct a GSG that's not - * associated with any particular window object, do so now, assuming the - * correct graphics context has been set up externally. - */ -PT(GraphicsStateGuardian) osxGraphicsPipe:: -make_callback_gsg(GraphicsEngine *engine) { - return new osxGraphicsStateGuardian(engine, this, nullptr); -} diff --git a/panda/src/osxdisplay/osxGraphicsPipe.h b/panda/src/osxdisplay/osxGraphicsPipe.h deleted file mode 100644 index 75094778d6..0000000000 --- a/panda/src/osxdisplay/osxGraphicsPipe.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file osxGraphicsPipe.h - */ - -#ifndef OSXGRAPHICSPIPE_H -#define OSXGRAPHICSPIPE_H - -#include "pandabase.h" -#include "graphicsPipe.h" - -#include - -class osxGraphicsStateGuardian; -class PNMImage; - -/** - * This graphics pipe represents the interface for creating OpenGL graphics - * windows on the various OSX's. - */ -class EXPCL_PANDA_OSXDISPLAY osxGraphicsPipe : public GraphicsPipe { -public: - osxGraphicsPipe(); - virtual ~osxGraphicsPipe(); - - virtual std::string get_interface_name() const; - static PT(GraphicsPipe) pipe_constructor(); - virtual PreferredWindowThread get_preferred_window_thread() const; - - static CGImageRef create_cg_image(const PNMImage &pnm_image); - -private: - static void release_data(void *info, const void *data, size_t size); - -protected: - virtual PT(GraphicsOutput) make_output(const std::string &name, - const FrameBufferProperties &fb_prop, - const WindowProperties &win_prop, - int flags, - GraphicsEngine *engine, - GraphicsStateGuardian *gsg, - GraphicsOutput *host, - int retry, - bool &precertify); - virtual PT(GraphicsStateGuardian) make_callback_gsg(GraphicsEngine *engine); - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - GraphicsPipe::init_type(); - register_type(_type_handle, "osxGraphicsPipe", - GraphicsPipe::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; - - friend class osxGraphicsBuffer; -}; - -#endif diff --git a/panda/src/osxdisplay/osxGraphicsStateGuardian.cxx b/panda/src/osxdisplay/osxGraphicsStateGuardian.cxx deleted file mode 100644 index 2fb60d2d74..0000000000 --- a/panda/src/osxdisplay/osxGraphicsStateGuardian.cxx +++ /dev/null @@ -1,459 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file osxGraphicsStateGuardian.cxx - */ - -#include "osxGraphicsStateGuardian.h" -#include "osxGraphicsBuffer.h" -#include "string_utils.h" -#include "config_osxdisplay.h" -#include "depthWriteAttrib.h" -#include "depthTestAttrib.h" -#include "textureAttrib.h" -#include "pnmImage.h" - -#include -#import - -// This is generated data for the standard texture we use for drawing the -// resize box in the window corner. -#include "resize_box.rgb.c" - -TypeHandle osxGraphicsStateGuardian::_type_handle; - -/** - * Returns the pointer to the GL extension function with the indicated name. - * It is the responsibility of the caller to ensure that the required - * extension is defined in the OpenGL runtime prior to calling this; it is an - * error to call this for a function that is not defined. - */ -void *osxGraphicsStateGuardian:: -do_get_extension_func(const char *name) { - std::string fullname = "_" + std::string(name); - NSSymbol symbol = nullptr; - - if (NSIsSymbolNameDefined(fullname.c_str())) { - symbol = NSLookupAndBindSymbol(fullname.c_str()); - } - - return symbol ? NSAddressOfSymbol(symbol) : nullptr; -} - -/** - * - */ -osxGraphicsStateGuardian:: -osxGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe, - osxGraphicsStateGuardian *share_with) : - GLGraphicsStateGuardian(engine, pipe), - _share_with(share_with), - _aglPixFmt(nullptr), - _aglcontext(nullptr) -{ - _shared_buffer = 1011; - get_gamma_table(); -} - -/** - * - */ -osxGraphicsStateGuardian:: -~osxGraphicsStateGuardian() { - if (_aglcontext != (AGLContext)nullptr) { - aglSetCurrentContext(nullptr); - aglDestroyContext(_aglcontext); - report_agl_error("aglDestroyContext"); - _aglcontext = (AGLContext)nullptr; - } -} - -/** - * Resets all internal state as if the gsg were newly created. - */ -void osxGraphicsStateGuardian::reset() -{ -/* - if(_aglcontext != (AGLContext)NULL) - { - aglDestroyContext(_aglcontext); - report_agl_error(); - _aglcontext = (AGLContext)NULL; - } - */ - - GLGraphicsStateGuardian::reset(); - - if (_aglcontext != (AGLContext)nullptr) { - // Apply the video-sync setting. - GLint value = sync_video ? 1 : 0; - aglSetInteger(_aglcontext, AGL_SWAP_INTERVAL, &value); - } -} - -/** - * Draws an OSX-style resize icon in the bottom right corner of the current - * display region. This is normally done automatically at the end of each - * frame when the window is indicated as resizable, since the 3-D graphics - * overlay the normal, OS-drawn resize icon and the user won't be able see it. - */ -void osxGraphicsStateGuardian:: -draw_resize_box() { - // This state is created, once, and never freed. - static CPT(RenderState) state; - if (state == nullptr) { - state = RenderState::make(TransparencyAttrib::make(TransparencyAttrib::M_alpha), - DepthWriteAttrib::make(DepthWriteAttrib::M_off), - DepthTestAttrib::make(DepthTestAttrib::M_none)); - - // Get the default texture to apply to the resize box; it's compiled into - // the code. - std::string resize_box_string((const char *)resize_box, resize_box_len); - std::istringstream resize_box_strm(resize_box_string); - PNMImage resize_box_pnm; - if (resize_box_pnm.read(resize_box_strm, "resize_box.rgb")) { - PT(Texture) tex = new Texture; - tex->set_name("resize_box.rgb"); - tex->load(resize_box_pnm); - tex->set_minfilter(SamplerState::FT_linear); - tex->set_magfilter(SamplerState::FT_linear); - state = state->add_attrib(TextureAttrib::make(tex)); - } - } - - // Clear out the lens. - _projection_mat_inv = _projection_mat = TransformState::make_identity(); - prepare_lens(); - - // Set the state to our specific, known state for drawing the icon. - set_state_and_transform(state, TransformState::make_identity()); - - // Now determine the inner corner of the quad, choosing a 15x15 pixel square - // in the lower-right corner, computed from the viewport size. - PN_stdfloat inner_x = 1.0f - (15.0f * 2.0f / _viewport_width); - PN_stdfloat inner_y = (15.0f * 2.0f / _viewport_height) - 1.0f; - - // Draw the quad. We just use the slow, simple immediate mode calls here. - // It's just one quad, after all. - glBegin(GL_QUADS); - - glColor4f(1.0, 1.0, 1.0, 1.0); - glTexCoord2f(0.0, 0.0); - glVertex2f(inner_x, -1.0); - - glTexCoord2f(0.9375, 0.0); - glVertex2f(1.0, -1.0); - - glTexCoord2f(0.9375, 0.9375); - glVertex2f(1.0, inner_y); - - glTexCoord2f(0.0, 0.9375); - glVertex2f(inner_x, inner_y); - - glEnd(); -} - -/** - * This function will build up a context for a gsg.. - */ -OSStatus osxGraphicsStateGuardian:: -build_gl(bool full_screen, bool pbuffer, FrameBufferProperties &fb_props) { - if (_aglcontext) { - describe_pixel_format(fb_props); - return noErr; // already built - } - - OSStatus err = noErr; - - GDHandle display = GetMainDevice(); - - pvector attrib; - if (!fb_props.get_indexed_color()) { - attrib.push_back(AGL_RGBA); - int color_bits = fb_props.get_color_bits(); - int alpha_bits = fb_props.get_alpha_bits(); - attrib.push_back(AGL_BUFFER_SIZE); - attrib.push_back(color_bits + alpha_bits); - attrib.push_back(AGL_PIXEL_SIZE); - attrib.push_back(color_bits); - attrib.push_back(AGL_RED_SIZE); - attrib.push_back(fb_props.get_red_bits()); - attrib.push_back(AGL_GREEN_SIZE); - attrib.push_back(fb_props.get_green_bits()); - attrib.push_back(AGL_BLUE_SIZE); - attrib.push_back(fb_props.get_blue_bits()); - attrib.push_back(AGL_ALPHA_SIZE); - attrib.push_back(alpha_bits); - } - attrib.push_back(AGL_DEPTH_SIZE); - attrib.push_back(fb_props.get_depth_bits()); - attrib.push_back(AGL_STENCIL_SIZE); - attrib.push_back(fb_props.get_stencil_bits()); - if (fb_props.get_multisamples() != 0) { - attrib.push_back(AGL_MULTISAMPLE); - attrib.push_back(AGL_SAMPLE_BUFFERS_ARB); - attrib.push_back(1); - attrib.push_back(AGL_SAMPLES_ARB); - attrib.push_back(fb_props.get_multisamples()); - } - - if (fb_props.is_stereo()) { - attrib.push_back(AGL_STEREO); - } - - if (!fb_props.is_single_buffered()) { - attrib.push_back(AGL_DOUBLEBUFFER); - } - if (full_screen) { - attrib.push_back(AGL_FULLSCREEN); - } - if (pbuffer) { - attrib.push_back(AGL_PBUFFER); - } - - if (fb_props.get_force_hardware()) { - attrib.push_back(AGL_ACCELERATED); - attrib.push_back(AGL_NO_RECOVERY); - } - - // Allow the system to choose the largest buffers requested that meets all - // our selections. - attrib.push_back(AGL_MAXIMUM_POLICY); - - // Terminate the list. - attrib.push_back(AGL_NONE); - - // build context - _aglcontext = nullptr; - _aglPixFmt = aglChoosePixelFormat(&display, 1, &attrib[0]); - err = report_agl_error("aglChoosePixelFormat"); - if (_aglPixFmt) { - if(_share_with == nullptr) { - _aglcontext = aglCreateContext(_aglPixFmt, nullptr); - } else { - _aglcontext = aglCreateContext(_aglPixFmt, ((osxGraphicsStateGuardian *)_share_with)->_aglcontext); - } - err = report_agl_error("aglCreateContext"); - - if (_aglcontext == nullptr) { - osxdisplay_cat.error() - << "osxGraphicsStateGuardian::build_gl Error Getting GL Context \n" ; - if(err == noErr) { - err = -1; - } - } else { - aglSetInteger(_aglcontext, AGL_BUFFER_NAME, &_shared_buffer); - err = report_agl_error("aglSetInteger AGL_BUFFER_NAME"); - } - - } else { - osxdisplay_cat.error() - << "osxGraphicsStateGuardian::build_gl Error Getting Pixel Format\n" ; - osxdisplay_cat.error() - << fb_props << "\n"; - if(err == noErr) { - err = -1; - } - } - - if (err == noErr) { - describe_pixel_format(fb_props); - } - - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "osxGraphicsStateGuardian::build_gl Returning :" << err << "\n"; - osxdisplay_cat.debug() - << fb_props << "\n"; - } - - return err; -} - - -/** - * Fills in the fb_props member with the appropriate values according to the - * chosen pixel format. - */ -void osxGraphicsStateGuardian:: -describe_pixel_format(FrameBufferProperties &fb_props) { - fb_props.clear(); - GLint value; - - if (aglDescribePixelFormat(_aglPixFmt, AGL_RGBA, &value)) { - fb_props.set_indexed_color(!value); - fb_props.set_rgb_color(value); - } - if (aglDescribePixelFormat(_aglPixFmt, AGL_DEPTH_SIZE, &value)) { - fb_props.set_depth_bits(value); - } - int color_bits = 0; - if (aglDescribePixelFormat(_aglPixFmt, AGL_RED_SIZE, &value)) { - fb_props.set_red_bits(value); - color_bits += value; - } - if (aglDescribePixelFormat(_aglPixFmt, AGL_GREEN_SIZE, &value)) { - fb_props.set_green_bits(value); - color_bits += value; - } - if (aglDescribePixelFormat(_aglPixFmt, AGL_BLUE_SIZE, &value)) { - fb_props.set_blue_bits(value); - color_bits += value; - } - fb_props.set_color_bits(color_bits); - if (aglDescribePixelFormat(_aglPixFmt, AGL_ALPHA_SIZE, &value)) { - fb_props.set_alpha_bits(value); - } - - if (aglDescribePixelFormat(_aglPixFmt, AGL_STENCIL_SIZE, &value)) { - fb_props.set_stencil_bits(value); - } - - int accum_bits = 0; - if (aglDescribePixelFormat(_aglPixFmt, AGL_ACCUM_RED_SIZE, &value)) { - accum_bits += value; - } - if (aglDescribePixelFormat(_aglPixFmt, AGL_ACCUM_GREEN_SIZE, &value)) { - accum_bits += value; - } - if (aglDescribePixelFormat(_aglPixFmt, AGL_ACCUM_BLUE_SIZE, &value)) { - accum_bits += value; - } - - if (aglDescribePixelFormat(_aglPixFmt, AGL_SAMPLES_ARB, &value)) { - fb_props.set_multisamples(value); - } - - if (aglDescribePixelFormat(_aglPixFmt, AGL_DOUBLEBUFFER, &value)) { - if (value) { - fb_props.set_back_buffers(1); - } else { - fb_props.set_back_buffers(0); - } - } - - if (aglDescribePixelFormat(_aglPixFmt, AGL_STEREO, &value)) { - fb_props.set_stereo(value); - } - - // Until we query the renderer, we don't know whether it's hardware or - // software based, so set both flags to indicate we don't know. - fb_props.set_force_hardware(true); - fb_props.set_force_software(true); - - GLint ndevs; - AGLDevice *gdevs = aglDevicesOfPixelFormat(_aglPixFmt, &ndevs); - if (gdevs != nullptr) { - AGLRendererInfo rinfo = aglQueryRendererInfo(gdevs, ndevs); - if (rinfo != nullptr) { - if (aglDescribeRenderer(rinfo, AGL_ACCELERATED, &value)) { - // Now we know whether it's hardware or software. - fb_props.set_force_hardware(value); - fb_props.set_force_software(!value); - } - if (aglDescribeRenderer(rinfo, AGL_VIDEO_MEMORY, &value)) { - osxdisplay_cat.debug() - << "Reported video memory is " << value << "\n"; - } - if (aglDescribeRenderer(rinfo, AGL_TEXTURE_MEMORY, &value)) { - osxdisplay_cat.debug() - << "Reported texture memory is " << value << "\n"; - } - } - } -} - -/** - * Static function for getting the orig gamma tables - */ -bool osxGraphicsStateGuardian:: -get_gamma_table() { - CGDisplayRestoreColorSyncSettings(); - _cgErr = CGGetDisplayTransferByTable( 0, 256, _gOriginalRedTable, _gOriginalGreenTable, _gOriginalBlueTable, &_sampleCount); -} - -/** - * Static function for setting gamma which is needed for atexit. - */ -bool osxGraphicsStateGuardian:: -static_set_gamma(bool restore, PN_stdfloat gamma) { - bool set; - - set = false; - - if (restore) { - CGDisplayRestoreColorSyncSettings(); - set = true; - return set; - } - // CGDisplayRestoreColorSyncSettings(); - - // CGGammaValue gOriginalRedTable[ 256 ]; CGGammaValue gOriginalGreenTable[ - // 256 ]; CGGammaValue gOriginalBlueTable[ 256 ]; - - // CGTableCount sampleCount; CGDisplayErr cgErr; - - // cgErr = CGGetDisplayTransferByTable( 0, 256, _gOriginalRedTable, - // _gOriginalGreenTable, _gOriginalBlueTable, &_sampleCount); - - CGGammaValue redTable[ 256 ]; - CGGammaValue greenTable[ 256 ]; - CGGammaValue blueTable[ 256 ]; - - short j, i; - short y[3]; - - for (j = 0; j < 3; j++) { - y[j] = 255; - } - - y[0] = 256 * gamma; - y[1] = 256 * gamma; - y[2] = 256 * gamma; - - for (i = 0; i < 256; i++) { - redTable[i] = _gOriginalRedTable[ i ] * (y[ 0 ] ) / 256; - greenTable[ i ] = _gOriginalGreenTable[ i ] * (y[ 1 ] ) / 256; - blueTable[ i ] = _gOriginalBlueTable[ i ] * (y[ 2 ] ) / 256; - } - _cgErr = CGSetDisplayTransferByTable( 0, 256, redTable, greenTable, blueTable); - - if (_cgErr == 0) { - set = true; - } - - return set; -} - -/** - * Non static version of setting gamma. Returns true on success. - */ -bool osxGraphicsStateGuardian:: -set_gamma(PN_stdfloat gamma) { - bool set; - - set = static_set_gamma(false, gamma); - - return set; -} - -/** - * Restore original gamma. - */ -void osxGraphicsStateGuardian:: -restore_gamma() { - static_set_gamma(true, 1.0f); -} - -/** - * This function is passed to the atexit function. - */ -void osxGraphicsStateGuardian:: -atexit_function() { - static_set_gamma(true, 1.0); -} diff --git a/panda/src/osxdisplay/osxGraphicsStateGuardian.h b/panda/src/osxdisplay/osxGraphicsStateGuardian.h deleted file mode 100644 index 96c86f28dd..0000000000 --- a/panda/src/osxdisplay/osxGraphicsStateGuardian.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file osxGraphicsStateGuardian.h - */ - -#ifndef OSXGRAPHICSSTATEGUARDIAN_H -#define OSXGRAPHICSSTATEGUARDIAN_H -#include -#include - -#define __glext_h_ -#include -#include - -#include "pandabase.h" -#include "glgsg.h" - -#include "osxGraphicsWindow.h" - -class osxGraphicsWindow; - -/** - * A tiny specialization on GLGraphicsStateGuardian to add some wgl-specific - * information. - */ -class osxGraphicsStateGuardian : public GLGraphicsStateGuardian { -public: - osxGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe, - osxGraphicsStateGuardian *share_with); - virtual ~osxGraphicsStateGuardian(); - virtual void reset(); - - void draw_resize_box(); - - bool get_gamma_table(); - bool static_set_gamma(bool restore, PN_stdfloat gamma); - bool set_gamma(PN_stdfloat gamma); - void atexit_function(); - void restore_gamma(); - -protected: - virtual void *do_get_extension_func(const char *name); - -public: - OSStatus build_gl(bool full_screen, bool pbuffer, FrameBufferProperties &fb_props); - AGLContext get_context() { return _aglcontext; }; - - const AGLPixelFormat get_agl_pixel_format() const { return _aglPixFmt; }; - -private: - void describe_pixel_format(FrameBufferProperties &fb_props); - - // We have to save a pointer to the GSG we intend to share texture context - // with, since we don't create our own context in the constructor. - PT(osxGraphicsStateGuardian) _share_with; - AGLPixelFormat _aglPixFmt; - AGLContext _aglcontext; - CGGammaValue _gOriginalRedTable[ 256 ]; - CGGammaValue _gOriginalGreenTable[ 256 ]; - CGGammaValue _gOriginalBlueTable[ 256 ]; - uint32_t _sampleCount; - CGDisplayErr _cgErr; - -public: - GLint _shared_buffer; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - GLGraphicsStateGuardian::init_type(); - register_type(_type_handle, "osxGraphicsStateGuardian", - GLGraphicsStateGuardian::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; - - friend class osxGraphicsBuffer; -}; - - -#endif diff --git a/panda/src/osxdisplay/osxGraphicsWindow.I b/panda/src/osxdisplay/osxGraphicsWindow.I deleted file mode 100644 index a5a7661366..0000000000 --- a/panda/src/osxdisplay/osxGraphicsWindow.I +++ /dev/null @@ -1,24 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file osxGraphicsWindow.I - * @author drose - * @date 2009-02-13 - */ - -/** - * - */ -inline void osxGraphicsWindow:: -send_key_event(ButtonHandle key, bool down) { - if (down) { - _input_devices[0].button_down(key); - } else { - _input_devices[0].button_up(key); - } -} diff --git a/panda/src/osxdisplay/osxGraphicsWindow.h b/panda/src/osxdisplay/osxGraphicsWindow.h deleted file mode 100644 index b797cc10f4..0000000000 --- a/panda/src/osxdisplay/osxGraphicsWindow.h +++ /dev/null @@ -1,145 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file osxGraphicsWindow.h - */ - -#ifndef OSXGRAPHICSWINDOW_H -#define OSXGRAPHICSWINDOW_H - -#include "pandabase.h" -#include "graphicsWindow.h" -#include "buttonHandle.h" - -#include - -#define __glext_h_ -#include -#include - -#define HACK_SCREEN_HASH_CONTEXT true -OSStatus report_agl_error(const std::string &comment); - -/** - * An interface to the osx/ system for managing GL windows under X. - */ -class osxGraphicsWindow : public GraphicsWindow { -public: - osxGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, - const std::string &name, - const FrameBufferProperties &fb_prop, - const WindowProperties &win_prop, - int flags, - GraphicsStateGuardian *gsg, - GraphicsOutput *host); - virtual ~osxGraphicsWindow(); - - virtual bool move_pointer(int device, int x, int y); - - virtual bool begin_frame(FrameMode mode, Thread *current_thread); - virtual void end_frame(FrameMode mode, Thread *current_thread); - virtual void begin_flip(); - virtual void end_flip(); - virtual void process_events(); - - virtual bool do_reshape_request(int x_origin, int y_origin, bool has_origin, - int x_size, int y_size); - - virtual void mouse_mode_absolute(); - virtual void mouse_mode_relative(); - - virtual void set_properties_now(WindowProperties &properties); - -private: - void release_system_resources(bool destructing); - inline void send_key_event(ButtonHandle key, bool down); - -protected: - virtual void close_window(); - virtual bool open_window(); - -private: - bool os_open_window(WindowProperties &properties); - - // a singleton .. for the events to find the right pipe to push the event - // into - -public: // do not call direct .. - OSStatus handle_key_input(EventHandlerCallRef myHandler, EventRef event, - Boolean keyDown); - OSStatus handle_text_input(EventHandlerCallRef myHandler, EventRef event); - OSStatus handle_window_mouse_events(EventHandlerCallRef myHandler, EventRef event); - ButtonHandle osx_translate_key(UInt32 key, EventRef event); - static osxGraphicsWindow *get_current_osx_window(WindowRef hint); - - void handle_modifier_delta(UInt32 new_modifiers); - void handle_button_delta(UInt32 new_buttons); - void do_resize(); - - OSStatus event_handler(EventHandlerCallRef myHandler, EventRef event); - - virtual void user_close_request(); - void system_close_window(); - void system_set_window_foreground(bool foreground); - void system_point_to_local_point(Point &global_point); - void local_point_to_system_point(Point &local_point); - AGLContext get_gsg_context(); - AGLContext get_context(); - OSStatus build_gl(bool full_screen); - bool set_icon_filename(const Filename &icon_filename); - - void set_pointer_in_window(int x, int y); - void set_pointer_out_of_window(); - -private: - UInt32 _last_key_modifiers; - UInt32 _last_buttons; - WindowRef _osx_window; - bool _is_fullscreen; - - CGImageRef _pending_icon; - CGImageRef _current_icon; - - int _ID; - static osxGraphicsWindow *full_screen_window; - -#ifdef HACK_SCREEN_HASH_CONTEXT - AGLContext _holder_aglcontext; -#endif - CFDictionaryRef _originalMode; - - // True if _properties.get_cursor_hidden() is true. - bool _cursor_hidden; - - // True if the cursor is actually hidden right now via system calls. - bool _display_hide_cursor; - - SInt32 _wheel_hdelta; - SInt32 _wheel_vdelta; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - GraphicsWindow::init_type(); - register_type(_type_handle, "osxGraphicsWindow", - GraphicsWindow::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; -}; - -#include "osxGraphicsWindow.I" - -#endif diff --git a/panda/src/osxdisplay/osxGraphicsWindow.mm b/panda/src/osxdisplay/osxGraphicsWindow.mm deleted file mode 100644 index 293f2fd2a3..0000000000 --- a/panda/src/osxdisplay/osxGraphicsWindow.mm +++ /dev/null @@ -1,2019 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file osxGraphicsWindow.mm - */ - -// We include these system header files first, because there is a namescope -// conflict between them and some other header file that gets included later -// (in particular, TCP_NODELAY must not be a #define symbol for these headers -// to be included properly). - -#include -#include -#include -#include -#include - -// We have to include this before we include the system OpenGLgl.h file, but -// after we include all of the above header files. Deal with this -// contradiction later. -#include "glgsg.h" - -#include "osxGraphicsWindow.h" -#include "config_osxdisplay.h" -#include "osxGraphicsPipe.h" -#include "pStatTimer.h" -#include "keyboardButton.h" -#include "mouseButton.h" -#include "osxGraphicsStateGuardian.h" -#include "osxGraphicsPipe.h" -#include "throw_event.h" -#include "pnmImage.h" -#include "virtualFileSystem.h" -#include "config_putil.h" -#include "pset.h" -#include "pmutex.h" - - -static Mutex & -osx_global_mutex() { - static Mutex m("osx_global_mutex"); - return m; -} - - -TypeHandle osxGraphicsWindow::_type_handle; -osxGraphicsWindow *osxGraphicsWindow::full_screen_window = NULL; - -#define USER_CONTAINER - -#ifdef USER_CONTAINER - -pset my_windows; -static void -add_a_window(WindowRef window) { - my_windows.insert(window); -} - -static bool -check_my_window(WindowRef window) { - return my_windows.find(window) != my_windows.end(); -} - -#else // USER_CONTAINER - -static void -add_a_window(WindowRef window) { -} - -static bool -check_my_window(WindowRef window) { - return true; -} - -#endif // USER_CONTAINER - - - -/** - * Returns the active window for the purpose of recording events. - */ -osxGraphicsWindow *osxGraphicsWindow:: -get_current_osx_window(WindowRef window) { - if (full_screen_window != NULL) { - return full_screen_window; - } - - if (window == NULL) { - // HID use this path - - // Assume first we are a child window. If we cant find a window of that - // class, then we are standalone and can jsut grab the front window. - window = GetFrontWindowOfClass(kSimpleWindowClass, TRUE); - if (window == NULL) { - window = FrontNonFloatingWindow(); - } - } - - if (window && check_my_window(window)) { - return (osxGraphicsWindow *)GetWRefCon (window); - } else { - return NULL; - } -} - -/** - * Convenience function to report the current AGL error code as a formatted - * error message. - */ -OSStatus -report_agl_error(const string &comment) { - GLenum err = aglGetError(); - if (err != AGL_NO_ERROR) { - osxdisplay_cat.error() - << "AGL Error " << aglErrorString(err) << " [" <> 1; - i += rowBytes, j -= rowBytes) { - memcpy(buffer, &imageData[i], rowBytes); - memcpy(&imageData[i], &imageData[j], rowBytes); - memcpy(&imageData[j], buffer, rowBytes); - } -} - -/** - * Drop a GL overlay onto a carbon window.. - */ -static void -composite_gl_buffer_into_window(AGLContext ctx, Rect *bufferRect, - GrafPtr out_port) { - GWorldPtr world; - QDErr err; - - // blit OpenGL content into window backing store allocate buffer to hold - // pane image - long width = (bufferRect->right - bufferRect->left); - long height = (bufferRect->bottom - bufferRect->top); - - Rect src_rect = {0, 0, height, width}; - Rect ddrc_rect = {0, 0, height, width}; - long row_bytes = width * 4; - long image_size = row_bytes * height; - char *image = (char *)NewPtr(image_size); - - if (!image) { - osxdisplay_cat.error() - << "Out of memory in composite_gl_buffer_into_window()!\n"; - return; // no harm in continuing - } - - // pull GL content down to our image buffer - aglSetCurrentContext(ctx); - glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, - image); - - // GL buffers are upside-down relative to QD buffers, so we need to flip it - invert_gl_image(image, image_size, row_bytes); - - // create a GWorld containing our image - err = NewGWorldFromPtr(&world, k32ARGBPixelFormat, &src_rect, 0, 0, 0, - image, row_bytes); - if (err != noErr) { - osxdisplay_cat.error() - << " error in NewGWorldFromPtr, called from composite_gl_buffer_into_window()\n"; - DisposePtr(image); - return; - } - - GrafPtr port_save = NULL; - Boolean port_changed = QDSwapPort(out_port, &port_save); - - CopyBits(GetPortBitMapForCopyBits(world), - GetPortBitMapForCopyBits(out_port), - &src_rect, &ddrc_rect, srcCopy, 0); - - if (port_changed) { - QDSwapPort(port_save, NULL); - } - - DisposeGWorld(world); - DisposePtr(image); -} - -/** - * The standard window event handler for non-fullscreen windows. - */ -OSStatus osxGraphicsWindow:: -event_handler(EventHandlerCallRef myHandler, EventRef event) { - OSStatus result = eventNotHandledErr; - UInt32 the_class = GetEventClass(event); - UInt32 kind = GetEventKind(event); - - WindowRef window = NULL; - GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, - sizeof(window), NULL, &window); - - UInt32 attributes = 0; - GetEventParameter(event, kEventParamAttributes, typeUInt32, NULL, - sizeof(attributes), NULL, &attributes); - - if (osxdisplay_cat.is_spam()) { - osxdisplay_cat.spam() - << ClockObject::get_global_clock()->get_real_time() - << " event_handler: " << (void *)this << ", " << window - << ", " << the_class << ", " << kind << "\n"; - } - - switch (the_class) { - case kEventClassMouse: - result = handle_window_mouse_events (myHandler, event); - break; - - case kEventClassWindow: - switch (kind) { - case kEventWindowCollapsing: - /* - Rect r; - GetWindowPortBounds (window, &r); - composite_gl_buffer_into_window(get_context(), &r, GetWindowPort (window)); - UpdateCollapsedWindowDockTile (window); - */ - system_set_window_foreground(false); - break; - - case kEventWindowActivated: // called on click activation and initially - system_set_window_foreground(true); - do_resize(); - break; - - case kEventWindowDeactivated: - system_set_window_foreground(false); - break; - - case kEventWindowClose: // called when window is being closed (close box) - // This is a message from the window manager indicating that the user - // has requested to close the window. - user_close_request(); - result = noErr; - break; - - case kEventWindowShown: // called on initial show (not on un-minimize) - if (window == FrontNonFloatingWindow ()) - SetUserFocusWindow (window); - break; - - case kEventWindowBoundsChanging: - // Gives us a chance to intercept resize attempts - if (attributes & kWindowBoundsChangeSizeChanged) { - // If the window is supposed to be fixed-size, enforce this. - if (_properties.get_fixed_size()) { - Rect bounds; - GetEventParameter(event, kEventParamCurrentBounds, - typeQDRectangle, NULL, sizeof(bounds), NULL, &bounds); - bounds.right = bounds.left + _properties.get_x_size(); - bounds.bottom = bounds.top + _properties.get_y_size(); - SetEventParameter(event, kEventParamCurrentBounds, - typeQDRectangle, sizeof(bounds), &bounds); - result = noErr; - } - } - break; - - case kEventWindowBoundsChanged: // called for resize and moves (drag) - do_resize(); - break; - - case kEventWindowZoomed: - break; - - case kEventWindowCollapsed: - { - WindowProperties properties; - properties.set_minimized(true); - system_changed_properties(properties); - } - break; - - case kEventWindowExpanded: - { - WindowProperties properties; - properties.set_minimized(false); - system_changed_properties(properties); - } - break; - } - break; - } - - return result; -} - -/** - * The user has requested to close the window, for instance with Cmd-W, or by - * clicking on the close button. - */ -void osxGraphicsWindow:: -user_close_request() { - string close_request_event = get_close_request_event(); - if (!close_request_event.empty()) { - // In this case, the app has indicated a desire to intercept the request - // and process it directly. - throw_event(close_request_event); - } else { - // In this case, the default case, the app does not intend to service the - // request, so we do by closing the window. - close_window(); - } -} - -/** - * The window has been closed by an OS resource, not by an internal request - */ -void osxGraphicsWindow:: -system_close_window() { - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "System Closing Window \n"; - } - release_system_resources(false); -} - -/** - * The C callback for Window Events - * - * We only hook this up for non-fullscreen windows, so we only handle system - * window events. - */ -static pascal OSStatus -window_event_handler(EventHandlerCallRef my_handler, EventRef event, void *) { - // volatile().lock(); - - WindowRef window = NULL; - GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, - sizeof(WindowRef), NULL, &window); - - if (window != NULL) { - osxGraphicsWindow *osx_win = osxGraphicsWindow::get_current_osx_window(window); - if (osx_win != (osxGraphicsWindow *)NULL) { - // osx_global_mutex().release(); - return osx_win->event_handler(my_handler, event); - } - } - - // osx_global_mutex().release(); - return eventNotHandledErr; -} - -/** - * - */ -void osxGraphicsWindow:: -do_resize() { - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "In Resize....." << _properties << "\n"; - } - - // only in window mode .. not full screen - if (_osx_window != NULL && !_is_fullscreen && _properties.has_size()) { - Rect rectPort = { - 0, 0, 0, 0 - }; - CGRect viewRect = { - { 0.0f, 0.0f }, { 0.0f, 0.0f } - }; - - GetWindowPortBounds(_osx_window, &rectPort); - viewRect.size.width = (PN_stdfloat)(rectPort.right - rectPort.left); - viewRect.size.height = (PN_stdfloat)(rectPort.bottom - rectPort.top); - - // tell panda - WindowProperties properties; - properties.set_size((int)viewRect.size.width,(int)viewRect.size.height); - properties.set_origin((int) rectPort.left,(int)rectPort.top); - system_changed_properties(properties); - - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << " Resizing Window " << viewRect.size.width - << " " << viewRect.size.height << "\n"; - } - - AGLContext context = get_gsg_context(); - if (context != (AGLContext)NULL) { - // ping gl - if (!aglSetCurrentContext(context)) { - report_agl_error("aglSetCurrentContext"); - } - aglUpdateContext(context); - report_agl_error("aglUpdateContext .. This is a Resize.."); - } - - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "Resize Complete.....\n"; - } - } -} - -/** - * The C callback for Application events. - * - * Hooked once per application. - */ -static pascal OSStatus -app_event_handler(EventHandlerCallRef my_handler, EventRef event, - void *user_data) { - OSStatus result = eventNotHandledErr; - { - // osx_global_mutex().lock(); - - osxGraphicsWindow *osx_win = NULL; - WindowRef window = NULL; - UInt32 the_class = GetEventClass (event); - UInt32 kind = GetEventKind (event); - - GetEventParameter(event, kEventParamWindowRef, typeWindowRef, NULL, - sizeof(WindowRef), NULL, (void*) &window); - osx_win = osxGraphicsWindow::get_current_osx_window(window); - if (osx_win == NULL) { - // osx_global_mutex().release(); - return eventNotHandledErr; - } - - switch (the_class) { - case kEventClassTextInput: - if (kind == kEventTextInputUnicodeForKeyEvent) { - osx_win->handle_text_input(my_handler, event); - } - // result = noErr; can not report handled .. the os will not sent the - // raw key strokes then if(osx_win->handle_text_input(my_handler, event) - // == noErr) result = noErr; - break; - - case kEventClassKeyboard: - { - switch (kind) { - case kEventRawKeyRepeat: - case kEventRawKeyDown: - result = osx_win->handle_key_input (my_handler, event, true); - break; - case kEventRawKeyUp: - result = osx_win->handle_key_input (my_handler, event, false); - break; - case kEventRawKeyModifiersChanged: - { - UInt32 newModifiers; - OSStatus error = GetEventParameter(event, kEventParamKeyModifiers,typeUInt32, NULL,sizeof(UInt32), NULL, &newModifiers); - if (error == noErr) { - osx_win->handle_modifier_delta(newModifiers); - result = noErr; - } - } - break; - } - } - break; - - case kEventClassMouse: - // osxdisplay_cat.info() << "Mouse movement handled by Application - // handler\n"; if(osxGraphicsWindow::full_screen_window != NULL) - result = osx_win->handle_window_mouse_events(my_handler, event); - // result = noErr; - break; - } - - // osx_global_mutex().release(); - } - - return result; -} - -/** - * Trap Unicode Input. - */ -OSStatus osxGraphicsWindow:: -handle_text_input(EventHandlerCallRef my_handler, EventRef text_event) { - UniChar *text = NULL; - UInt32 actual_size = 0; - - OSStatus ret = GetEventParameter(text_event, kEventParamTextInputSendText, - typeUnicodeText, NULL, 0, &actual_size, NULL); - if (ret != noErr) { - return ret; - } - - text = (UniChar*)NewPtr(actual_size); - if (text!= NULL) { - ret = GetEventParameter (text_event, kEventParamTextInputSendText,typeUnicodeText, NULL, actual_size, NULL, text); - if (ret != noErr) { - return ret; - } - - for (unsigned int x = 0; x < actual_size/sizeof(UniChar); ++x) { - _input_devices[0].keystroke(text[x]); - } - DisposePtr((char *)text); - } - - return ret; -} - -/** - * Clean up the OS level messes. - */ -void osxGraphicsWindow:: -release_system_resources(bool destructing) { - if (_is_fullscreen) { - _is_fullscreen = false; - full_screen_window = NULL; - - if (_originalMode != NULL) { - CGDisplaySwitchToMode(kCGDirectMainDisplay, _originalMode); - } - - CGDisplayRelease(kCGDirectMainDisplay); - aglSetDrawable(get_gsg_context(), NULL); - - _originalMode = NULL; - } - - // if the gsg context is assigned to this window clear it.. - if (_osx_window != NULL && GetWindowPort (_osx_window) == (GrafPtr)aglGetDrawable(get_gsg_context())) { - aglSetDrawable(get_gsg_context(),NULL); - } - - // if we are the active gl context clear it.. - if (aglGetCurrentContext() == get_gsg_context()) { - aglSetCurrentContext(NULL); - } - - if (_osx_window != NULL) { - SetWRefCon(_osx_window, (long int) NULL); - HideWindow(_osx_window); - DisposeWindow(_osx_window); - _osx_window = NULL; - } - - if (_holder_aglcontext) { - aglDestroyContext(_holder_aglcontext); - _holder_aglcontext = NULL; - } - - if (_pending_icon != NULL) { - CGImageRelease(_pending_icon); - _pending_icon = NULL; - } - - if (_current_icon != NULL) { - CGImageRelease(_current_icon); - _current_icon = NULL; - } - - if (!destructing) { - WindowProperties properties; - properties.set_foreground(false); - properties.set_open(false); - properties.set_cursor_filename(Filename()); - system_changed_properties(properties); - } - - _is_fullscreen = false; - _osx_window = NULL; -} - - -static int id_seed = 100; - -/** - * - */ -osxGraphicsWindow:: -osxGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, - const string &name, - const FrameBufferProperties &fb_prop, - const WindowProperties &win_prop, - int flags, - GraphicsStateGuardian *gsg, - GraphicsOutput *host) : - GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host), - _osx_window(NULL), - _is_fullscreen(false), - _pending_icon(NULL), - _current_icon(NULL), -#ifdef HACK_SCREEN_HASH_CONTEXT - _holder_aglcontext(NULL), -#endif - _originalMode(NULL), - _ID(id_seed++) -{ - GraphicsWindowInputDevice device = - GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse"); - _input_devices.push_back(device); - _input_devices[0].set_pointer_in_window(0, 0); - _last_key_modifiers = 0; - _last_buttons = 0; - - _cursor_hidden = false; - _display_hide_cursor = false; - _wheel_hdelta = 0; - _wheel_vdelta = 0; - - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "osxGraphicsWindow::osxGraphicsWindow() -" <<_ID << "\n"; - } -} - -/** - * - */ -osxGraphicsWindow:: -~osxGraphicsWindow() { - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "osxGraphicsWindow::~osxGraphicsWindow() -" <<_ID << "\n"; - } - - // Make sure the window callback won't come back to this (destructed) object - // any more. - if (_osx_window) { - SetWRefCon(_osx_window, (long) NULL); - } - - release_system_resources(true); -} - -/** - * Helper to decide whitch context to use if any - */ -AGLContext osxGraphicsWindow:: -get_context() { - if (_holder_aglcontext != NULL) { - return _holder_aglcontext; - } - - return get_gsg_context(); -} - -/** - * - */ -AGLContext osxGraphicsWindow:: -get_gsg_context() { - if (_gsg != NULL) { - osxGraphicsStateGuardian *osxgsg = NULL; - osxgsg = DCAST(osxGraphicsStateGuardian, _gsg); - return osxgsg->get_context(); - } - return NULL; -} - -/** - * Code of the class.. used to control the GL context Allocation. - */ -OSStatus osxGraphicsWindow:: -build_gl(bool full_screen) { - // make sure the gsg is up and runnig.. - osxGraphicsStateGuardian *osxgsg = NULL; - osxgsg = DCAST(osxGraphicsStateGuardian, _gsg); - OSStatus stat = osxgsg->build_gl(full_screen, false, _fb_properties); - - if (stat != noErr) { - return stat; - } - - OSStatus err = noErr; - - if (osxgsg->get_agl_pixel_format()) { - _holder_aglcontext = aglCreateContext(osxgsg->get_agl_pixel_format(), NULL); - - err = report_agl_error("aglCreateContext"); - if (_holder_aglcontext == NULL) { - osxdisplay_cat.error() - << "osxGraphicsWindow::build_gl Error aglCreateContext \n"; - if (err ==noErr) { - err = -1; - } - } else { - aglSetInteger(_holder_aglcontext, AGL_BUFFER_NAME, &osxgsg->_shared_buffer); - err = report_agl_error ("aglSetInteger AGL_BUFFER_NAME"); - } - } else { - osxdisplay_cat.error() - << "osxGraphicsWindow::build_gl Error Getting PixelFormat \n"; - if (err ==noErr) { - err = -1; - } - } - return err; -} - -/** - * Called internally to load up an icon file that should be applied to the - * window. Returns true on success, false on failure. - */ -bool osxGraphicsWindow:: -set_icon_filename(const Filename &icon_filename) { - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - - Filename icon_pathname = icon_filename; - if (!vfs->resolve_filename(icon_pathname, get_model_path())) { - // The filename doesn't exist along the search path. - if (icon_pathname.is_fully_qualified() && vfs->exists(icon_pathname)) { - // But it does exist locally, so accept it. - - } else { - osxdisplay_cat.warning() - << "Could not find icon filename " << icon_filename << "\n"; - return false; - } - } - - - PNMImage pnmimage; - if (!pnmimage.read(icon_pathname)) { - osxdisplay_cat.warning() - << "Could not read icon filename " << icon_pathname << "\n"; - return false; - } - - CGImageRef icon_image = osxGraphicsPipe::create_cg_image(pnmimage); - if (icon_image == NULL) { - return false; - } - - if (_pending_icon != NULL) { - CGImageRelease(_pending_icon); - _pending_icon = NULL; - } - _pending_icon = icon_image; - return true; -} - -/** - * Indicates the mouse pointer is seen within the window. - */ -void osxGraphicsWindow:: -set_pointer_in_window(int x, int y) { - _input_devices[0].set_pointer_in_window(x, y); - - if (_cursor_hidden != _display_hide_cursor) { - if (_cursor_hidden) { - CGDisplayHideCursor(kCGDirectMainDisplay); - _display_hide_cursor = true; - } else { - CGDisplayShowCursor(kCGDirectMainDisplay); - _display_hide_cursor = false; - } - } -} - -/** - * Indicates the mouse pointer is no longer within the window. - */ -void osxGraphicsWindow:: -set_pointer_out_of_window() { - _input_devices[0].set_pointer_out_of_window(); - - if (_display_hide_cursor) { - CGDisplayShowCursor(kCGDirectMainDisplay); - _display_hide_cursor = false; - } -} - - -/** - * This function will be called within the draw thread before beginning - * rendering for a given frame. It should do whatever setup is required, and - * return true if the frame should be rendered, or false if it should be - * skipped. - */ -bool osxGraphicsWindow:: -begin_frame(FrameMode mode, Thread *current_thread) { - PStatTimer timer(_make_current_pcollector); - - begin_frame_spam(mode); - if (_gsg == (GraphicsStateGuardian *)NULL || - (_osx_window == NULL && !_is_fullscreen)) { - // not powered up .. just abort.. - return false; - } - - // Now is a good time to apply the icon change that may have recently been - // requested. By this point, we should be able to get a handle to the dock - // context. - if (_pending_icon != NULL) { - CGContextRef context = BeginCGContextForApplicationDockTile(); - if (context != NULL) { - SetApplicationDockTileImage(_pending_icon); - EndCGContextForApplicationDockTile(context); - - if (_current_icon != NULL) { - CGImageRelease(_current_icon); - _current_icon = NULL; - } - _current_icon = _pending_icon; - _pending_icon = NULL; - } - } - - if (_is_fullscreen) { - aglSetFullScreen(get_gsg_context(),0,0,0,0); - report_agl_error ("aglSetFullScreen"); - - } else { - if (full_screen_window != NULL) { - return false; - } - - if (!aglSetDrawable(get_gsg_context(), GetWindowPort (_osx_window))) { - report_agl_error("aglSetDrawable"); - } - } - - if (!aglSetCurrentContext(get_gsg_context())) { - report_agl_error ("aglSetCurrentContext"); - } - - _gsg->reset_if_new(); - _gsg->set_current_properties(&get_fb_properties()); - - return _gsg->begin_frame(current_thread); -} - -/** - * This function will be called within the draw thread after rendering is - * completed for a given frame. It should do whatever finalization is - * required. - */ -void osxGraphicsWindow:: -end_frame(FrameMode mode, Thread *current_thread) { - end_frame_spam(mode); - - if (mode == FM_render) { - nassertv(_gsg != (GraphicsStateGuardian *)NULL); - - copy_to_textures(); - - if (!_properties.get_fixed_size() && - !_properties.get_undecorated() && - !_properties.get_fullscreen() && - show_resize_box) { - // Draw a kludgey little resize box in the corner of the window, so the - // user knows he's supposed to be able to drag the window if he wants. - DisplayRegionPipelineReader dr_reader(_overlay_display_region, current_thread); - _gsg->prepare_display_region(&dr_reader); - DCAST(osxGraphicsStateGuardian, _gsg)->draw_resize_box(); - } - - aglSwapBuffers (get_gsg_context()); - _gsg->end_frame(current_thread); - } -} - -/** - * This function will be called within the draw thread after end_frame() has - * been called on all windows, to initiate the exchange of the front and back - * buffers. - * - * This should instruct the window to prepare for the flip at the next video - * sync, but it should not wait. - * - * We have the two separate functions, begin_flip() and end_flip(), to make it - * easier to flip all of the windows at the same time. - */ -void osxGraphicsWindow:: -end_flip() { - // cerr << " end_flip [" << _ID << "]\n"; -} - -void osxGraphicsWindow:: -begin_flip() { - // this forces a rip to proper context cerr << " begin_flip [" << _ID << - // "]\n"; - return; - - if (_is_fullscreen) { - if (!aglSetFullScreen(get_gsg_context(),0,0,0,0)) { - report_agl_error("aglSetFullScreen"); - } - - if (!aglSetCurrentContext(get_gsg_context())) { - report_agl_error("aglSetCurrentContext"); - } - - aglSwapBuffers (get_gsg_context()); - } else { - if (!aglSetDrawable (get_gsg_context(),GetWindowPort (_osx_window))) { - report_agl_error("aglSetDrawable"); - } - - if (!aglSetCurrentContext(get_gsg_context())) { - report_agl_error("aglSetCurrentContext"); - } - - aglSwapBuffers (get_gsg_context()); - } -} - -/** - * Closes the window right now. Called from the window thread. - */ -void osxGraphicsWindow:: -close_window() { - system_close_window(); - - WindowProperties properties; - properties.set_open(false); - system_changed_properties(properties); - - release_system_resources(false); - _gsg.clear(); - GraphicsWindow::close_window(); -} - -/* - * HACK ALLERT ************ Undocumented OSX calls... I can not find any other - * way to get the mouse focus to a window in OSX.. extern "C" { struct - * CPSProcessSerNum { UInt32 lo; UInt32 hi; }; - */ - -// extern OSErr CPSGetCurrentProcess(CPSProcessSerNum *psn); extern OSErr -// CPSEnableForegroundOperation(struct CPSProcessSerNum *psn); extern OSErr -// CPSSetProcessName (struct CPSProcessSerNum *psn, char *processname); extern -// OSErr CPSSetFrontProcess(struct CPSProcessSerNum *psn); }; - -/** - * Opens the window right now. Called from the window thread. Returns true - * if the window is successfully opened, or false if there was a problem. - */ -bool osxGraphicsWindow:: -open_window() { - WindowProperties req_properties = _properties; - - if (_gsg == 0) { - _gsg = new osxGraphicsStateGuardian(_engine, _pipe, NULL); - } - - // osx_global_mutex().lock(); - bool answer = os_open_window(req_properties); - // osx_global_mutex().release(); - return answer; -} - -/** - * Actually makes the OS calls to open a window. - */ -bool osxGraphicsWindow:: -os_open_window(WindowProperties &req_properties) { - OSErr err = noErr; - - if (_current_icon != NULL && _pending_icon == NULL) { - // If we already have an icon specified, we'll need to reapply it when the - // window is successfully created. - _pending_icon = _current_icon; - _current_icon = NULL; - } - - static bool GlobalInits = false; - if (!GlobalInits) { - // one time aplication inits.. to get a window open from a standalone - // aplication.. - - EventHandlerRef application_event_ref_ref1; - EventTypeSpec list1[] = { - // { kEventClassCommand, kEventProcessCommand }, { kEventClassCommand, - // kEventCommandUpdateStatus }, - { kEventClassMouse, kEventMouseDown },// handle trackball functionality globaly because there is only a single user - { kEventClassMouse, kEventMouseUp }, - { kEventClassMouse, kEventMouseMoved }, - { kEventClassMouse, kEventMouseDragged }, - { kEventClassMouse, kEventMouseWheelMoved } , - { kEventClassKeyboard, kEventRawKeyDown }, - { kEventClassKeyboard, kEventRawKeyUp } , - { kEventClassKeyboard, kEventRawKeyRepeat }, - { kEventClassKeyboard, kEventRawKeyModifiersChanged } , - { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent}, - }; - - EventHandlerUPP gEvtHandler = NewEventHandlerUPP(app_event_handler); - err = InstallApplicationEventHandler (gEvtHandler, GetEventTypeCount (list1) , list1, this, &application_event_ref_ref1); - GlobalInits = true; - - ProcessSerialNumber psn = { 0, kCurrentProcess }; - - // Determine if we're running from a bundle. - CFDictionaryRef dref = - ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask); - // If the dictionary doesn't have "BundlePath" (or the BundlePath is the - // same as the executable path), then we're not running from a bundle, and - // we need to call TransformProcessType to make the process a "foreground" - // application, with its own icon in the dock and such. - - bool has_bundle = false; - - CFStringRef bundle_path = (CFStringRef)CFDictionaryGetValue(dref, CFSTR("BundlePath")); - if (bundle_path != NULL) { - // OK, we have a bundle path. We're probably running in a bundle . . . - has_bundle = true; - - // . . . unless it turns out it's the same as the executable path. - CFStringRef exe_path = (CFStringRef)CFDictionaryGetValue(dref, kCFBundleExecutableKey); - if (exe_path != NULL) { - if (CFStringCompare(bundle_path, exe_path, kCFCompareCaseInsensitive) == kCFCompareEqualTo) { - has_bundle = false; - } - CFRelease(exe_path); - } - - CFRelease(bundle_path); - } - - if (!has_bundle) { - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - } - SetFrontProcess(&psn); - } - - bool wants_fullscreen = req_properties.has_fullscreen() && req_properties.get_fullscreen(); - if (req_properties.get_minimized()) { - // A minimized window can't be fullscreen. - wants_fullscreen = false; - } - - if (wants_fullscreen) { - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "Creating full screen\n"; - } - - // capture the main display - CGDisplayCapture(kCGDirectMainDisplay); - // if sized try and switch it.. - if (req_properties.has_size()) { - _originalMode = CGDisplayCurrentMode(kCGDirectMainDisplay); - CFDictionaryRef newMode = CGDisplayBestModeForParameters(kCGDirectMainDisplay, 32, req_properties.get_x_size(), req_properties.get_y_size(), 0); - if (newMode == NULL) { - osxdisplay_cat.error() - << "Invalid fullscreen size: " << req_properties.get_x_size() - << ", " << req_properties.get_y_size() - << "\n"; - } else { - CGDisplaySwitchToMode(kCGDirectMainDisplay, newMode); - - // Set our new window size according to the size we actually got. - - SInt32 width, height; - CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(newMode, kCGDisplayWidth), kCFNumberSInt32Type, &width); - CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(newMode, kCGDisplayHeight), kCFNumberSInt32Type, &height); - - _properties.set_size(width, height); - } - } - - if (build_gl(true) != noErr) { - if (_originalMode != NULL) { - CGDisplaySwitchToMode(kCGDirectMainDisplay, _originalMode); - } - _originalMode = NULL; - - CGDisplayRelease(kCGDirectMainDisplay); - return false; - } - - _properties.set_fullscreen(true); - _properties.set_minimized(false); - _properties.set_foreground(true); - - _is_fullscreen = true; - full_screen_window = this; - req_properties.clear_fullscreen(); - - } else { - int x_origin = 10; - int y_origin = 50; - if (req_properties.has_origin()) { - y_origin = req_properties.get_y_origin(); - x_origin = req_properties.get_x_origin(); - } - - int x_size = 512; - int y_size = 512; - if (req_properties.has_size()) { - x_size = req_properties.get_x_size(); - y_size = req_properties.get_y_size(); - } - - // A coordinate of -2 means to center the window on screen. - if (y_origin == -2 || x_origin == -2) { - if (y_origin == -2) { - y_origin = (_pipe->get_display_height() - y_size) / 2; - } - if (x_origin == -2) { - x_origin = (_pipe->get_display_width() - x_size) / 2; - } - } - - // A coordinate of -1 means a default location. - if (y_origin == -1) { - y_origin = 50; - } - if (x_origin == -1) { - x_origin = 10; - } - - _properties.set_origin(x_origin, y_origin); - _properties.set_size(x_size, y_size); - - Rect r; - r.top = y_origin; - r.left = x_origin; - r.right = r.left + x_size; - r.bottom = r.top + y_size; - - /* - if (req_properties.has_parent_window()) { - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "Creating child window\n"; - } - - CreateNewWindow(kSimpleWindowClass, kWindowNoAttributes, &r, &_osx_window); - add_a_window(_osx_window); - - _properties.set_fixed_size(true); - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "Child window created\n"; - } - } else */ - { - int attributes = kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute; - if (req_properties.has_fixed_size() && req_properties.get_fixed_size()) { - attributes &= ~kWindowResizableAttribute; - } - - if (req_properties.has_undecorated() && req_properties.get_undecorated()) { - // create a unmovable .. no edge window.. - - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "Creating undecorated window\n"; - } - - // We don't want a resize box either. - attributes &= ~kWindowResizableAttribute; - attributes |= kWindowNoTitleBarAttribute; - CreateNewWindow(kDocumentWindowClass, attributes, &r, &_osx_window); - } else { - // create a window with crome and sizing and sucj In this case, we - // want to constrain the window to the available size. - - Rect bounds; - GetAvailableWindowPositioningBounds(GetMainDevice(), &bounds); - - r.left = max(r.left, bounds.left); - r.right = min(r.right, bounds.right); - r.top = max(r.top, bounds.top); - r.bottom = min(r.bottom, bounds.bottom); - - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "Creating standard window\n"; - } - CreateNewWindow(kDocumentWindowClass, attributes, &r, &_osx_window); - add_a_window(_osx_window); - } - } - - if (_osx_window) { - EventHandlerUPP gWinEvtHandler; // window event handler - EventTypeSpec list[] = { - { kEventClassWindow, kEventWindowCollapsing }, - { kEventClassWindow, kEventWindowShown }, - { kEventClassWindow, kEventWindowActivated }, - { kEventClassWindow, kEventWindowDeactivated }, - { kEventClassWindow, kEventWindowClose }, - { kEventClassWindow, kEventWindowBoundsChanging }, - { kEventClassWindow, kEventWindowBoundsChanged }, - - { kEventClassWindow, kEventWindowCollapsed }, - { kEventClassWindow, kEventWindowExpanded }, - { kEventClassWindow, kEventWindowZoomed }, - { kEventClassWindow, kEventWindowClosed }, - }; - - // point to the window record in the ref con of the window - SetWRefCon(_osx_window, (long) this); - gWinEvtHandler = NewEventHandlerUPP(window_event_handler); - InstallWindowEventHandler(_osx_window, gWinEvtHandler, GetEventTypeCount(list), list, (void*)this, NULL); // add event handler - - ShowWindow (_osx_window); - - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "Event handler installed, now build_gl\n"; - } - if (build_gl(false) != noErr) { - osxdisplay_cat.error() - << "Error in build_gl\n"; - - HideWindow(_osx_window); - SetWRefCon(_osx_window, (long int) NULL); - DisposeWindow(_osx_window); - _osx_window = NULL; - return false; - } - - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "build_gl complete, set properties\n"; - } - - // attach the holder context to the window.. - - if (!aglSetDrawable(_holder_aglcontext, GetWindowPort(_osx_window))) { - err = report_agl_error("aglSetDrawable"); - } - - if (req_properties.has_fullscreen()) { - _properties.set_fullscreen(false); - req_properties.clear_fullscreen(); - } - - if (req_properties.has_undecorated()) { - _properties.set_undecorated(req_properties.get_undecorated()); - req_properties.clear_undecorated(); - } - - _properties.set_minimized(false); - _properties.set_foreground(true); - - if (req_properties.has_minimized()) { - CollapseWindow(_osx_window, req_properties.get_minimized()); - _properties.set_minimized(req_properties.get_minimized()); - _properties.set_foreground(!req_properties.get_minimized()); - req_properties.clear_minimized(); - } - } - - // Now measure the size and placement of the window we actually ended up - // with. - Rect rectPort = {0,0,0,0}; - GetWindowPortBounds (_osx_window, &rectPort); - _properties.set_size((int)(rectPort.right - rectPort.left),(int) (rectPort.bottom - rectPort.top)); - req_properties.clear_size(); - req_properties.clear_origin(); - } - - if (req_properties.has_icon_filename()) { - set_icon_filename(req_properties.get_icon_filename()); - } - - if (req_properties.has_cursor_hidden()) { - _properties.set_cursor_hidden(req_properties.get_cursor_hidden()); - _cursor_hidden = req_properties.get_cursor_hidden(); - if (_cursor_hidden) { - if (!_display_hide_cursor) { - CGDisplayHideCursor(kCGDirectMainDisplay); - _display_hide_cursor = true; - } - } else { - if (_display_hide_cursor) { - CGDisplayShowCursor(kCGDirectMainDisplay); - _display_hide_cursor = false; - } - } - req_properties.clear_cursor_hidden(); - } - - _properties.set_open(true); - - if (_properties.has_size()) { - set_size_and_recalc(_properties.get_x_size(), _properties.get_y_size()); - } - - return (err == noErr); -} - -/** - * Required event upcall, used to dispatch window and application events back - * into panda. - */ -void osxGraphicsWindow:: -process_events() { - GraphicsWindow::process_events(); - - if (!osx_disable_event_loop) { - EventRef theEvent; - EventTargetRef theTarget = GetEventDispatcherTarget(); - - /*if (!_properties.has_parent_window()) */ { - while (ReceiveNextEvent(0, NULL, kEventDurationNoWait, true, &theEvent)== noErr) { - SendEventToEventTarget (theEvent, theTarget); - ReleaseEvent(theEvent); - } - } - } -} - -/** - * Required event upcall, used to dispatch window and application events back - * into panda. - */ -OSStatus osxGraphicsWindow:: -handle_key_input(EventHandlerCallRef my_handler, EventRef event, - Boolean key_down) { - if (osxdisplay_cat.is_debug()) { - UInt32 key_code; - GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, - sizeof(UInt32), NULL, &key_code); - - osxdisplay_cat.debug() - << ClockObject::get_global_clock()->get_real_time() - << " handle_key_input: " << (void *)this << ", " << key_code - << ", " << (int)key_down << "\n"; - } - - // CallNextEventHandler(my_handler, event); - - // We don't check the result of the above function. In principle, this - // should return eventNotHandledErr if the key event is not handled by the - // OS, but in practice, testing this just seems to eat the Escape keypress - // meaninglessly. Keypresses like F11 that are already mapped in the - // desktop seem to not even come into this function in the first place. - UInt32 new_modifiers = 0; - OSStatus error = GetEventParameter(event, kEventParamKeyModifiers, - typeUInt32, NULL, sizeof(UInt32), - NULL, &new_modifiers); - if (error == noErr) { - handle_modifier_delta(new_modifiers); - } - - UInt32 key_code; - GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, - sizeof(UInt32), NULL, &key_code); - ButtonHandle button = osx_translate_key(key_code, event); - - if (key_down) { - if ((new_modifiers & cmdKey) != 0) { - if (button == KeyboardButton::ascii_key("q") || - button == KeyboardButton::ascii_key("w")) { - // Command-Q or Command-W: quit the application or close the window, - // respectively. For now, we treat them both the same: close the - // window. - user_close_request(); - } - } - send_key_event(button, true); - } else { - send_key_event(button, false); - } - - return CallNextEventHandler(my_handler, event); - // return noErr; -} - -/** - * - */ -void osxGraphicsWindow:: -system_set_window_foreground(bool foreground) { - WindowProperties properties; - properties.set_foreground(foreground); - system_changed_properties(properties); -} - -/** - * - */ -void osxGraphicsWindow:: -system_point_to_local_point(Point &global_point) { - if (_osx_window != NULL) { - GrafPtr savePort; - Boolean port_changed = QDSwapPort(GetWindowPort(_osx_window), &savePort); - - GlobalToLocal(&global_point); - - if (port_changed) { - QDSwapPort(savePort, NULL); - } - } -} - -/** - * - */ -OSStatus osxGraphicsWindow:: -handle_window_mouse_events(EventHandlerCallRef my_handler, EventRef event) { - WindowRef window = NULL; - OSStatus result = eventNotHandledErr; - UInt32 kind = GetEventKind(event); - EventMouseButton button = 0; - Point global_point = {0, 0}; - UInt32 modifiers = 0; - Rect rect_port; - SInt32 this_wheel_delta; - EventMouseWheelAxis wheelAxis; - - // cerr <<" Start Mouse Event " << _ID << "\n"; - - // Mac OS X v10.1 and later should this be front window??? - GetEventParameter(event, kEventParamWindowRef, typeWindowRef, NULL, - sizeof(WindowRef), NULL, &window); - - if (!_is_fullscreen && (window == NULL || window != _osx_window)) { - if (kind == kEventMouseMoved) { - set_pointer_out_of_window(); - } - return eventNotHandledErr; - } - - GetWindowPortBounds(window, &rect_port); - - // result = CallNextEventHandler(my_handler, event); if (eventNotHandledErr - // == result) - { // only handle events not already handled (prevents weird resize interaction) - switch (kind) { - // Whenever mouse button state changes, generate the appropriate Panda - // downup events to represent the change. - - case kEventMouseDown: - case kEventMouseUp: - { - GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers); - if (_properties.get_mouse_mode() == WindowProperties::M_relative) { - HIPoint delta; - GetEventParameter(event, kEventParamMouseDelta, typeHIPoint, NULL, sizeof(HIPoint), NULL, (void*) &delta); - - MouseData currMouse = get_pointer(0); - delta.x += currMouse.get_x(); - delta.y += currMouse.get_y(); - set_pointer_in_window((int)delta.x, (int)delta.y); - } else { - GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL , (void*) &global_point); - system_point_to_local_point(global_point); - set_pointer_in_window((int)global_point.h, (int)global_point.v); - } - - UInt32 new_buttons = GetCurrentEventButtonState(); - handle_button_delta(new_buttons); - } - result = noErr; - break; - - case kEventMouseMoved: - case kEventMouseDragged: - if (_properties.get_mouse_mode() == WindowProperties::M_relative) { - HIPoint delta; - GetEventParameter(event, kEventParamMouseDelta, typeHIPoint, NULL, sizeof(HIPoint), NULL, (void*) &delta); - - MouseData currMouse = get_pointer(0); - delta.x += currMouse.get_x(); - delta.y += currMouse.get_y(); - set_pointer_in_window((int)delta.x, (int)delta.y); - } else { - GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, (void*) &global_point); - system_point_to_local_point(global_point); - - if (kind == kEventMouseMoved && - (global_point.h < 0 || global_point.v < 0)) { - // Moving into the titlebar region. - set_pointer_out_of_window(); - } else { - // Moving within the window itself (or dragging anywhere). - set_pointer_in_window((int)global_point.h, (int)global_point.v); - } - } - result = noErr; - break; - - case kEventMouseWheelMoved: - GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(this_wheel_delta), NULL, &this_wheel_delta); - GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(wheelAxis), NULL, &wheelAxis); - GetEventParameter(event, kEventParamMouseLocation,typeQDPoint, NULL, sizeof(Point),NULL , (void*) &global_point); - system_point_to_local_point(global_point); - - if (wheelAxis == kEventMouseWheelAxisX) { - set_pointer_in_window((int)global_point.h, (int)global_point.v); - _wheel_hdelta += this_wheel_delta; - SInt32 wheel_scale = osx_mouse_wheel_scale; - while (_wheel_hdelta > wheel_scale) { - _input_devices[0].button_down(MouseButton::wheel_left()); - _input_devices[0].button_up(MouseButton::wheel_left()); - _wheel_hdelta -= wheel_scale; - } - while (_wheel_hdelta < -wheel_scale) { - _input_devices[0].button_down(MouseButton::wheel_right()); - _input_devices[0].button_up(MouseButton::wheel_right()); - _wheel_hdelta += wheel_scale; - } - } - if (wheelAxis == kEventMouseWheelAxisY) { - set_pointer_in_window((int)global_point.h, (int)global_point.v); - _wheel_vdelta += this_wheel_delta; - SInt32 wheel_scale = osx_mouse_wheel_scale; - while (_wheel_vdelta > wheel_scale) { - _input_devices[0].button_down(MouseButton::wheel_up()); - _input_devices[0].button_up(MouseButton::wheel_up()); - _wheel_vdelta -= wheel_scale; - } - while (_wheel_vdelta < -wheel_scale) { - _input_devices[0].button_down(MouseButton::wheel_down()); - _input_devices[0].button_up(MouseButton::wheel_down()); - _wheel_vdelta += wheel_scale; - } - } - result = noErr; - break; - } - // result = noErr; - } - - return result; -} - -/** - * MAC Key Codes to Panda Key Codes - */ -ButtonHandle osxGraphicsWindow:: -osx_translate_key(UInt32 key, EventRef event) { - ButtonHandle nk = ButtonHandle::none(); - switch (key) { - case 0: nk = KeyboardButton::ascii_key('a'); break; - case 11: nk = KeyboardButton::ascii_key('b'); break; - case 8: nk = KeyboardButton::ascii_key('c'); break; - case 2: nk = KeyboardButton::ascii_key('d'); break; - case 14: nk = KeyboardButton::ascii_key('e'); break; - case 3: nk = KeyboardButton::ascii_key('f'); break; - case 5: nk = KeyboardButton::ascii_key('g'); break; - case 4: nk = KeyboardButton::ascii_key('h'); break; - case 34: nk = KeyboardButton::ascii_key('i'); break; - case 38: nk = KeyboardButton::ascii_key('j'); break; - case 40: nk = KeyboardButton::ascii_key('k'); break; - case 37: nk = KeyboardButton::ascii_key('l'); break; - case 46: nk = KeyboardButton::ascii_key('m'); break; - case 45: nk = KeyboardButton::ascii_key('n'); break; - case 31: nk = KeyboardButton::ascii_key('o'); break; - case 35: nk = KeyboardButton::ascii_key('p'); break; - case 12: nk = KeyboardButton::ascii_key('q'); break; - case 15: nk = KeyboardButton::ascii_key('r'); break; - case 1: nk = KeyboardButton::ascii_key('s'); break; - case 17: nk = KeyboardButton::ascii_key('t'); break; - case 32: nk = KeyboardButton::ascii_key('u'); break; - case 9: nk = KeyboardButton::ascii_key('v'); break; - case 13: nk = KeyboardButton::ascii_key('w'); break; - case 7: nk = KeyboardButton::ascii_key('x'); break; - case 16: nk = KeyboardButton::ascii_key('y'); break; - case 6: nk = KeyboardButton::ascii_key('z'); break; - - // top row numbers - case 29: nk = KeyboardButton::ascii_key('0'); break; - case 18: nk = KeyboardButton::ascii_key('1'); break; - case 19: nk = KeyboardButton::ascii_key('2'); break; - case 20: nk = KeyboardButton::ascii_key('3'); break; - case 21: nk = KeyboardButton::ascii_key('4'); break; - case 23: nk = KeyboardButton::ascii_key('5'); break; - case 22: nk = KeyboardButton::ascii_key('6'); break; - case 26: nk = KeyboardButton::ascii_key('7'); break; - case 28: nk = KeyboardButton::ascii_key('8'); break; - case 25: nk = KeyboardButton::ascii_key('9'); break; - - // key pad ... do they really map to the top number in panda ? - case 82: nk = KeyboardButton::ascii_key('0'); break; - case 83: nk = KeyboardButton::ascii_key('1'); break; - case 84: nk = KeyboardButton::ascii_key('2'); break; - case 85: nk = KeyboardButton::ascii_key('3'); break; - case 86: nk = KeyboardButton::ascii_key('4'); break; - case 87: nk = KeyboardButton::ascii_key('5'); break; - case 88: nk = KeyboardButton::ascii_key('6'); break; - case 89: nk = KeyboardButton::ascii_key('7'); break; - case 91: nk = KeyboardButton::ascii_key('8'); break; - case 92: nk = KeyboardButton::ascii_key('9'); break; - - // case 36: nk = KeyboardButton::ret(); break; no return in panda ??? - case 49: nk = KeyboardButton::space(); break; - case 51: nk = KeyboardButton::backspace(); break; - case 48: nk = KeyboardButton::tab(); break; - case 53: nk = KeyboardButton::escape(); break; - case 76: nk = KeyboardButton::enter(); break; - case 36: nk = KeyboardButton::enter(); break; - - case 123: nk = KeyboardButton::left(); break; - case 124: nk = KeyboardButton::right(); break; - case 125: nk = KeyboardButton::down(); break; - case 126: nk = KeyboardButton::up(); break; - case 116: nk = KeyboardButton::page_up(); break; - case 121: nk = KeyboardButton::page_down(); break; - case 115: nk = KeyboardButton::home(); break; - case 119: nk = KeyboardButton::end(); break; - case 114: nk = KeyboardButton::help(); break; - case 117: nk = KeyboardButton::del(); break; - - // case 71: nk = KeyboardButton::num_lock() break; - - case 122: nk = KeyboardButton::f1(); break; - case 120: nk = KeyboardButton::f2(); break; - case 99: nk = KeyboardButton::f3(); break; - case 118: nk = KeyboardButton::f4(); break; - case 96: nk = KeyboardButton::f5(); break; - case 97: nk = KeyboardButton::f6(); break; - case 98: nk = KeyboardButton::f7(); break; - case 100: nk = KeyboardButton::f8(); break; - case 101: nk = KeyboardButton::f9(); break; - case 109: nk = KeyboardButton::f10(); break; - case 103: nk = KeyboardButton::f11(); break; - case 111: nk = KeyboardButton::f12(); break; - - case 105: nk = KeyboardButton::f13(); break; - case 107: nk = KeyboardButton::f14(); break; - case 113: nk = KeyboardButton::f15(); break; - case 106: nk = KeyboardButton::f16(); break; - - // shiftable chartablet - case 50: nk = KeyboardButton::ascii_key('`'); break; - case 27: nk = KeyboardButton::ascii_key('-'); break; - case 24: nk = KeyboardButton::ascii_key('='); break; - case 33: nk = KeyboardButton::ascii_key('['); break; - case 30: nk = KeyboardButton::ascii_key(']'); break; - case 42: nk = KeyboardButton::ascii_key('\\'); break; - case 41: nk = KeyboardButton::ascii_key(';'); break; - case 39: nk = KeyboardButton::ascii_key('\''); break; - case 43: nk = KeyboardButton::ascii_key(','); break; - case 47: nk = KeyboardButton::ascii_key('.'); break; - case 44: nk = KeyboardButton::ascii_key('/'); break; - - default: - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << " Untranslated KeyCode: " << key - << " (0x" << hex << key << dec << ")\n"; - } - - // not sure this is right .. but no mapping for keypad and such this at - // least does a best gess.. - - char charCode = 0; - if (GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, nil, sizeof(charCode), nil, &charCode) == noErr) { - nk = KeyboardButton::ascii_key(charCode); - } - } - return nk; -} - -/** - * Used to emulate key events for the MAC key modifiers. - */ -void osxGraphicsWindow:: -handle_modifier_delta(UInt32 new_modifiers) { - UInt32 changed = _last_key_modifiers ^ new_modifiers; - - if ((changed & (shiftKey | rightShiftKey)) != 0) { - send_key_event(KeyboardButton::shift(),(new_modifiers & (shiftKey | rightShiftKey)) != 0) ; - } - - if ((changed & (optionKey | rightOptionKey)) != 0) { - send_key_event(KeyboardButton::alt(),(new_modifiers & (optionKey | rightOptionKey)) != 0); - } - - if ((changed & (controlKey | rightControlKey)) != 0) { - send_key_event(KeyboardButton::control(),(new_modifiers & (controlKey | rightControlKey)) != 0); - } - - if ((changed & cmdKey) != 0) { - send_key_event(KeyboardButton::meta(),(new_modifiers & cmdKey) != 0); - } - - if ((changed & alphaLock) != 0) { - send_key_event(KeyboardButton::caps_lock(),(new_modifiers & alphaLock) != 0); - } - - // save current state - _last_key_modifiers = new_modifiers; -} - -/** - * Used to emulate button events - */ -void osxGraphicsWindow:: -handle_button_delta(UInt32 new_buttons) { - UInt32 changed = _last_buttons ^ new_buttons; - - if (changed & 0x01) { - if (new_buttons & 0x01) { - _input_devices[0].button_down(MouseButton::one()); - } else { - _input_devices[0].button_up(MouseButton::one()); - } - } - - if (changed & 0x04) { - if (new_buttons & 0x04) { - _input_devices[0].button_down(MouseButton::two()); - } else { - _input_devices[0].button_up(MouseButton::two()); - } - } - - if (changed & 0x02) { - if (new_buttons & 0x02) { - _input_devices[0].button_down(MouseButton::three()); - } else { - _input_devices[0].button_up(MouseButton::three()); - } - } - - _last_buttons = new_buttons; -} - -/** - * Forces the pointer to the indicated position within the window, if - * possible. - * - * Returns true if successful, false on failure. This may fail if the mouse - * is not currently within the window, or if the API doesn't support this - * operation. - */ -bool osxGraphicsWindow:: -move_pointer(int device, int x, int y) { - if (_osx_window == NULL) { - return false; - } - - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "move_pointer " << device <<" "<< x <<" "<< y <<"\n"; - } - - Point pt = { 0, 0 }; - pt.h = x; - pt.v = y; - set_pointer_in_window(x, y); - - if (_properties.get_mouse_mode() == WindowProperties::M_absolute) { - local_point_to_system_point(pt); - CGPoint new_position = { 0, 0 }; - new_position.x = pt.h; - new_position.y = pt.v; - mouse_mode_relative(); - CGWarpMouseCursorPosition(new_position); - mouse_mode_absolute(); - } - - return true; -} - -/** - * - */ -bool osxGraphicsWindow:: -do_reshape_request(int x_origin, int y_origin, bool has_origin, - int x_size, int y_size) { - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "Do Reshape\n"; - } - - if (_properties.get_fullscreen()) { - return false; - } - - // A coordinate of -2 means to center the window on screen. - if (x_origin == -2 || y_origin == -2 || x_origin == -1 || y_origin == -1) { - if (y_origin == -2) { - y_origin = (_pipe->get_display_height() - y_size) / 2; - } - if (x_origin == -2) { - x_origin = (_pipe->get_display_width() - x_size) / 2; - } - if (y_origin == -1) { - y_origin = 50; - } - if (x_origin == -1) { - x_origin = 10; - } - _properties.set_origin(x_origin, y_origin); - system_changed_properties(_properties); - } - - /* - if (_properties.has_parent_window()) { - if (has_origin) { - NSWindow* parentWindow = (NSWindow *)_properties.get_parent_window(); - NSRect parentFrame = [parentWindow frame]; - - MoveWindow(_osx_window, x_origin+parentFrame.origin.x, y_origin+parentFrame.origin.y, false); - } - } else */ - { - // We sometimes get a bogus origin of (0, 0). As a special hack, treat - // this as a special case, and ignore it. - if (has_origin) { - if (x_origin != 0 || y_origin != 0) { - MoveWindow(_osx_window, x_origin, y_origin, false); - } - } - } - - if (!_properties.get_undecorated()) { - // Constrain the window to the available desktop size. - Rect bounds; - GetAvailableWindowPositioningBounds(GetMainDevice(), &bounds); - - x_size = min(x_size, bounds.right - bounds.left); - y_size = min(y_size, bounds.bottom - bounds.top); - } - - SizeWindow(_osx_window, x_size, y_size, false); - - system_changed_size(x_size, y_size); - return true; -} - -/** - * Applies the requested set of properties to the window, if possible, for - * instance to request a change in size or minimization status. - * - * The window properties are applied immediately, rather than waiting until - * the next frame. This implies that this method may *only* be called from - * within the window thread. - * - * The properties that have been applied are cleared from the structure by - * this function; so on return, whatever remains in the properties structure - * are those that were unchanged for some reason (probably because the - * underlying interface does not support changing that property on an open - * window). - */ -void osxGraphicsWindow:: -set_properties_now(WindowProperties &properties) { - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "------------------------------------------------------\n"; - osxdisplay_cat.debug() - << "set_properties_now " << properties << "\n"; - } - - GraphicsWindow::set_properties_now(properties); - - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "set_properties_now After Base Class" << properties << "\n"; - } - - // for some changes .. a full rebuild is required for the OS layer Window. - // I think it is the chrome attribute and full screen behaviour. - bool need_full_rebuild = false; - - // if we are not full and transitioning to full - if (properties.has_fullscreen() && - properties.get_fullscreen() != _properties.get_fullscreen()) { - need_full_rebuild = true; - } - - // If we are fullscreen and requesting a size change - if (_properties.get_fullscreen() && - (properties.has_size() && - (properties.get_x_size() != _properties.get_x_size() || - properties.get_y_size() != _properties.get_y_size()))) { - need_full_rebuild = true; - } - - // If we are fullscreen and requesting a minimize change - if (_properties.get_fullscreen() && - (properties.has_minimized() && - (properties.get_minimized() != _properties.get_minimized()))) { - need_full_rebuild = true; - } - - if (need_full_rebuild) { - // Logic here is .. take a union of the properties .. with the new allowed - // to overwrite the old states. and start a bootstrap of a new window .. - - // get a copy of my properties.. - WindowProperties req_properties(_properties); - release_system_resources(false); - req_properties.add_properties(properties); - - os_open_window(req_properties); - - // Now we've handled all of the requested properties. - properties.clear(); - } - - if (properties.has_title()) { - _properties.set_title(properties.get_title()); - - if (_osx_window) { - SetWindowTitleWithCFString(_osx_window, - CFStringCreateWithCString(NULL,properties.get_title().c_str(), - kCFStringEncodingMacRoman)); - } - properties.clear_title(); - } - - // An icon filename means to load up the icon and save it. We can't - // necessarily apply it immediately; it will get applied later, in the - // window event handler. - if (properties.has_icon_filename()) { - if (set_icon_filename(properties.get_icon_filename())) { - properties.clear_icon_filename(); - } - } - - // decorated .. if this changes it reqires a new window - if (properties.has_undecorated()) { - _properties.set_undecorated(properties.get_undecorated()); - properties.clear_undecorated(); - } - - if (properties.has_cursor_hidden()) { - _properties.set_cursor_hidden(properties.get_cursor_hidden()); - _cursor_hidden = properties.get_cursor_hidden(); - if (_cursor_hidden) { - if (!_display_hide_cursor) { - CGDisplayHideCursor(kCGDirectMainDisplay); - _display_hide_cursor = true; - } - } else { - if (_display_hide_cursor) { - CGDisplayShowCursor(kCGDirectMainDisplay); - _display_hide_cursor = false; - } - } - properties.clear_cursor_hidden(); - } - - if (properties.has_minimized()) { - if (_properties.get_minimized() != properties.get_minimized()) { - CollapseWindow(_osx_window, properties.get_minimized()); - _properties.set_minimized(properties.get_minimized()); - _properties.set_foreground(!properties.get_minimized()); - } - properties.clear_minimized(); - } - - if (properties.has_mouse_mode()) { - switch (properties.get_mouse_mode()) { - case WindowProperties::M_absolute: - CGAssociateMouseAndMouseCursorPosition(true); - _properties.set_mouse_mode(WindowProperties::M_absolute); - properties.clear_mouse_mode(); - break; - - case WindowProperties::M_relative: - CGAssociateMouseAndMouseCursorPosition(false); - _properties.set_mouse_mode(WindowProperties::M_relative); - properties.clear_mouse_mode(); - break; - - case WindowProperties::M_confined: - break; - } - } - - if (osxdisplay_cat.is_debug()) { - osxdisplay_cat.debug() - << "set_properties_now Out....." << _properties << "\n"; - } - - return; -} - -/** - * - */ -void osxGraphicsWindow:: -local_point_to_system_point(Point &local_point) { - if (_osx_window != NULL) { - GrafPtr save_port; - Boolean port_changed = QDSwapPort(GetWindowPort(_osx_window), &save_port); - - LocalToGlobal(&local_point); - - if (port_changed) { - QDSwapPort(save_port, NULL); - } - } -} - -/** - * detaches mouse. Only mouse delta from now on. - */ -void osxGraphicsWindow:: -mouse_mode_relative() { - CGAssociateMouseAndMouseCursorPosition(false); -} - - -/** - * reattaches mouse to location - */ -void osxGraphicsWindow:: -mouse_mode_absolute() { - CGAssociateMouseAndMouseCursorPosition(true); -} diff --git a/panda/src/osxdisplay/p3osxdisplay_composite1.cxx b/panda/src/osxdisplay/p3osxdisplay_composite1.cxx deleted file mode 100644 index 6247d97d78..0000000000 --- a/panda/src/osxdisplay/p3osxdisplay_composite1.cxx +++ /dev/null @@ -1,4 +0,0 @@ -#include "config_osxdisplay.cxx" -#include "osxGraphicsBuffer.cxx" -#include "osxGraphicsPipe.cxx" -#include "osxGraphicsStateGuardian.cxx" diff --git a/panda/src/osxdisplay/resize_box.rgb b/panda/src/osxdisplay/resize_box.rgb deleted file mode 100644 index 216efe10d9..0000000000 Binary files a/panda/src/osxdisplay/resize_box.rgb and /dev/null differ diff --git a/panda/src/osxdisplay/resize_box.rgb.c b/panda/src/osxdisplay/resize_box.rgb.c deleted file mode 100644 index eecce8c1e5..0000000000 --- a/panda/src/osxdisplay/resize_box.rgb.c +++ /dev/null @@ -1,167 +0,0 @@ - -/* - * This table was generated by the command: - * - * bin2c -static -string -n resize_box -o resize_box.rgb.c resize_box.rgb - */ - -#include - -static const unsigned char resize_box[] = { - 0x01, 0xda, 0x01, 0x01, 0x00, 0x03, 0x00, 0x10, 0x00, 0x10, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x81, 0x00, - 0x00, 0x06, 0x38, 0x00, 0x00, 0x05, 0xf2, 0x00, 0x00, 0x05, 0xb0, - 0x00, 0x00, 0x05, 0x7a, 0x00, 0x00, 0x05, 0x41, 0x00, 0x00, 0x05, - 0x0b, 0x00, 0x00, 0x04, 0xd9, 0x00, 0x00, 0x04, 0xb3, 0x00, 0x00, - 0x04, 0x8a, 0x00, 0x00, 0x04, 0x64, 0x00, 0x00, 0x04, 0x42, 0x00, - 0x00, 0x04, 0x30, 0x00, 0x00, 0x04, 0x1e, 0x00, 0x00, 0x04, 0x0c, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x86, 0x00, 0x00, 0x06, - 0x4b, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x05, 0xc1, 0x00, 0x00, - 0x05, 0x88, 0x00, 0x00, 0x05, 0x50, 0x00, 0x00, 0x05, 0x19, 0x00, - 0x00, 0x04, 0xe6, 0x00, 0x00, 0x04, 0xbd, 0x00, 0x00, 0x04, 0x95, - 0x00, 0x00, 0x04, 0x6e, 0x00, 0x00, 0x04, 0x4b, 0x00, 0x00, 0x04, - 0x35, 0x00, 0x00, 0x04, 0x23, 0x00, 0x00, 0x04, 0x11, 0x00, 0x00, - 0x04, 0x03, 0x00, 0x00, 0x06, 0x8b, 0x00, 0x00, 0x06, 0x5e, 0x00, - 0x00, 0x06, 0x16, 0x00, 0x00, 0x05, 0xd2, 0x00, 0x00, 0x05, 0x96, - 0x00, 0x00, 0x05, 0x5f, 0x00, 0x00, 0x05, 0x27, 0x00, 0x00, 0x04, - 0xf3, 0x00, 0x00, 0x04, 0xc7, 0x00, 0x00, 0x04, 0xa0, 0x00, 0x00, - 0x04, 0x78, 0x00, 0x00, 0x04, 0x54, 0x00, 0x00, 0x04, 0x3a, 0x00, - 0x00, 0x04, 0x28, 0x00, 0x00, 0x04, 0x16, 0x00, 0x00, 0x04, 0x06, - 0x00, 0x00, 0x06, 0x90, 0x00, 0x00, 0x06, 0x71, 0x00, 0x00, 0x06, - 0x28, 0x00, 0x00, 0x05, 0xe3, 0x00, 0x00, 0x05, 0xa4, 0x00, 0x00, - 0x05, 0x6e, 0x00, 0x00, 0x05, 0x35, 0x00, 0x00, 0x05, 0x00, 0x00, - 0x00, 0x04, 0xd1, 0x00, 0x00, 0x04, 0xab, 0x00, 0x00, 0x04, 0x82, - 0x00, 0x00, 0x04, 0x5d, 0x00, 0x00, 0x04, 0x3f, 0x00, 0x00, 0x04, - 0x2d, 0x00, 0x00, 0x04, 0x1b, 0x00, 0x00, 0x04, 0x09, 0x00, 0x00, - 0x00, 0x05, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, - 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, - 0x00, 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, - 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, - 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0a, 0x00, - 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, - 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, - 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, - 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, - 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x03, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x0f, 0xec, 0x01, 0x00, 0x00, 0x0f, 0xec, 0x01, 0x00, - 0x00, 0x0f, 0xec, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0f, 0xec, - 0x01, 0x00, 0x00, 0x0f, 0xec, 0x01, 0x00, 0x00, 0x0f, 0xec, 0x01, - 0x00, 0x00, 0x10, 0x00, 0x00, 0x0f, 0xec, 0x01, 0x00, 0x00, 0x0f, - 0xec, 0x01, 0x00, 0x00, 0x0f, 0xec, 0x01, 0x00, 0x00, 0x10, 0x00, - 0x00, 0x0d, 0xec, 0x81, 0xcf, 0x01, 0xec, 0x01, 0x00, 0x00, 0x0d, - 0xec, 0x81, 0xcf, 0x01, 0xec, 0x01, 0x00, 0x00, 0x0d, 0xec, 0x81, - 0xcf, 0x01, 0xec, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x81, 0xff, 0x02, - 0x00, 0x00, 0x0c, 0xec, 0x82, 0xcf, 0x80, 0x01, 0xec, 0x01, 0x00, - 0x00, 0x0c, 0xec, 0x82, 0xcf, 0x80, 0x01, 0xec, 0x01, 0x00, 0x00, - 0x0c, 0xec, 0x82, 0xcf, 0x80, 0x01, 0xec, 0x01, 0x00, 0x00, 0x0c, - 0x00, 0x82, 0xff, 0xff, 0x02, 0x00, 0x00, 0x0b, 0xec, 0x83, 0xca, - 0x7e, 0xec, 0x01, 0xec, 0x01, 0x00, 0x00, 0x0b, 0xec, 0x83, 0xca, - 0x7e, 0xec, 0x01, 0xec, 0x01, 0x00, 0x00, 0x0b, 0xec, 0x83, 0xca, - 0x7e, 0xec, 0x01, 0xec, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x82, 0xff, - 0xff, 0x03, 0x00, 0x00, 0x0a, 0xec, 0x82, 0xca, 0x7e, 0x03, 0xec, - 0x01, 0x00, 0x00, 0x0a, 0xec, 0x82, 0xca, 0x7e, 0x03, 0xec, 0x01, - 0x00, 0x00, 0x0a, 0xec, 0x82, 0xca, 0x7e, 0x03, 0xec, 0x01, 0x00, - 0x00, 0x0a, 0x00, 0x82, 0xff, 0xff, 0x04, 0x00, 0x00, 0x09, 0xec, - 0x85, 0xcc, 0x80, 0xec, 0xec, 0xcf, 0x01, 0xec, 0x01, 0x00, 0x00, - 0x09, 0xec, 0x85, 0xcc, 0x80, 0xec, 0xec, 0xcf, 0x01, 0xec, 0x01, - 0x00, 0x00, 0x09, 0xec, 0x85, 0xcc, 0x80, 0xec, 0xec, 0xcf, 0x01, - 0xec, 0x01, 0x00, 0x00, 0x09, 0x00, 0x85, 0xff, 0xff, 0x00, 0x00, - 0xff, 0x02, 0x00, 0x00, 0x08, 0xec, 0x86, 0xca, 0x80, 0xec, 0xec, - 0xcf, 0x80, 0x01, 0xec, 0x01, 0x00, 0x00, 0x08, 0xec, 0x86, 0xca, - 0x80, 0xec, 0xec, 0xcf, 0x80, 0x01, 0xec, 0x01, 0x00, 0x00, 0x08, - 0xec, 0x86, 0xca, 0x80, 0xec, 0xec, 0xcf, 0x80, 0x01, 0xec, 0x01, - 0x00, 0x00, 0x08, 0x00, 0x86, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x02, 0x00, 0x00, 0x07, 0xec, 0x87, 0xc6, 0x7e, 0xec, 0xec, 0xc9, - 0x7e, 0xec, 0x01, 0xec, 0x01, 0x00, 0x00, 0x07, 0xec, 0x87, 0xc6, - 0x7e, 0xec, 0xec, 0xc9, 0x7e, 0xec, 0x01, 0xec, 0x01, 0x00, 0x00, - 0x07, 0xec, 0x87, 0xc6, 0x7e, 0xec, 0xec, 0xc9, 0x7e, 0xec, 0x01, - 0xec, 0x01, 0x00, 0x00, 0x07, 0x00, 0x86, 0xff, 0xff, 0x00, 0x00, - 0xff, 0xff, 0x03, 0x00, 0x00, 0x06, 0xec, 0x86, 0xc6, 0x7e, 0xec, - 0xec, 0xc9, 0x7e, 0x03, 0xec, 0x01, 0x00, 0x00, 0x06, 0xec, 0x86, - 0xc6, 0x7e, 0xec, 0xec, 0xc9, 0x7e, 0x03, 0xec, 0x01, 0x00, 0x00, - 0x06, 0xec, 0x86, 0xc6, 0x7e, 0xec, 0xec, 0xc9, 0x7e, 0x03, 0xec, - 0x01, 0x00, 0x00, 0x06, 0x00, 0x86, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x04, 0x00, 0x00, 0x05, 0xec, 0x89, 0xc8, 0x80, 0xec, 0xec, - 0xcd, 0x80, 0xec, 0xec, 0xcd, 0x01, 0xec, 0x01, 0x00, 0x00, 0x05, - 0xec, 0x89, 0xc8, 0x80, 0xec, 0xec, 0xcd, 0x80, 0xec, 0xec, 0xcd, - 0x01, 0xec, 0x01, 0x00, 0x00, 0x05, 0xec, 0x89, 0xc8, 0x80, 0xec, - 0xec, 0xcd, 0x80, 0xec, 0xec, 0xcd, 0x01, 0xec, 0x01, 0x00, 0x00, - 0x05, 0x00, 0x89, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, - 0xff, 0x02, 0x00, 0x00, 0x04, 0xec, 0x8a, 0xcd, 0x80, 0xec, 0xec, - 0xcd, 0x80, 0xec, 0xec, 0xcd, 0x80, 0x01, 0xec, 0x01, 0x00, 0x00, - 0x04, 0xec, 0x8a, 0xcd, 0x80, 0xec, 0xec, 0xcd, 0x80, 0xec, 0xec, - 0xcd, 0x80, 0x01, 0xec, 0x01, 0x00, 0x00, 0x04, 0xec, 0x8a, 0xcd, - 0x80, 0xec, 0xec, 0xcd, 0x80, 0xec, 0xec, 0xcd, 0x80, 0x01, 0xec, - 0x01, 0x00, 0x00, 0x04, 0x00, 0x8a, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x03, 0xec, 0x8b, - 0xc9, 0x7e, 0xec, 0xec, 0xc9, 0x7e, 0xec, 0xec, 0xc9, 0x7e, 0xec, - 0x01, 0xec, 0x01, 0x00, 0x00, 0x03, 0xec, 0x8b, 0xc9, 0x7e, 0xec, - 0xec, 0xc9, 0x7e, 0xec, 0xec, 0xc9, 0x7e, 0xec, 0x01, 0xec, 0x01, - 0x00, 0x00, 0x03, 0xec, 0x8b, 0xc9, 0x7e, 0xec, 0xec, 0xc9, 0x7e, - 0xec, 0xec, 0xc9, 0x7e, 0xec, 0x01, 0xec, 0x01, 0x00, 0x00, 0x03, - 0x00, 0x8a, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x03, 0x00, 0x00, 0x0f, 0xec, 0x01, 0x00, 0x00, 0x0f, 0xec, - 0x01, 0x00, 0x00, 0x0f, 0xec, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00 -}; - -static const size_t resize_box_len = 1683; - diff --git a/panda/src/pandabase/pandasymbols.h b/panda/src/pandabase/pandasymbols.h index 3f529dab27..88857c7f0c 100644 --- a/panda/src/pandabase/pandasymbols.h +++ b/panda/src/pandabase/pandasymbols.h @@ -53,14 +53,6 @@ #define EXPTP_FRAMEWORK IMPORT_TEMPL #endif -#ifdef BUILDING_MILES_AUDIO - #define EXPCL_MILES_AUDIO EXPORT_CLASS - #define EXPTP_MILES_AUDIO EXPORT_TEMPL -#else - #define EXPCL_MILES_AUDIO IMPORT_CLASS - #define EXPTP_MILES_AUDIO IMPORT_TEMPL -#endif - #ifdef BUILDING_OPENAL_AUDIO #define EXPCL_OPENAL_AUDIO EXPORT_CLASS #define EXPTP_OPENAL_AUDIO EXPORT_TEMPL @@ -122,7 +114,6 @@ #define BUILDING_PANDA_COCOADISPLAY #define BUILDING_PANDA_GLGSG #define BUILDING_PANDA_GLXDISPLAY - #define BUILDING_PANDA_OSXDISPLAY #define BUILDING_PANDA_WGLDISPLAY #endif @@ -340,14 +331,6 @@ #define EXPTP_PANDA_NET IMPORT_TEMPL #endif -#ifdef BUILDING_PANDA_OSXDISPLAY - #define EXPCL_PANDA_OSXDISPLAY EXPORT_CLASS - #define EXPTP_PANDA_OSXDISPLAY EXPORT_TEMPL -#else - #define EXPCL_PANDA_OSXDISPLAY IMPORT_CLASS - #define EXPTP_PANDA_OSXDISPLAY IMPORT_TEMPL -#endif - #ifdef BUILDING_PANDA_PARAMETRICS #define EXPCL_PANDA_PARAMETRICS EXPORT_CLASS #define EXPTP_PANDA_PARAMETRICS EXPORT_TEMPL diff --git a/panda/src/pgraph/clipPlaneAttrib.cxx b/panda/src/pgraph/clipPlaneAttrib.cxx index c17a17b272..961bc7f5d2 100644 --- a/panda/src/pgraph/clipPlaneAttrib.cxx +++ b/panda/src/pgraph/clipPlaneAttrib.cxx @@ -45,8 +45,7 @@ public: * Constructs a new ClipPlaneAttrib object that enables (or disables, * according to op) the indicated plane(s). * - * This method is now deprecated. Use add_on_plane() or add_off_plane() - * instead. + * @deprecated Use add_on_plane() or add_off_plane() instead. */ CPT(RenderAttrib) ClipPlaneAttrib:: make(ClipPlaneAttrib::Operation op, PlaneNode *plane) { @@ -80,8 +79,7 @@ make(ClipPlaneAttrib::Operation op, PlaneNode *plane) { * Constructs a new ClipPlaneAttrib object that turns on (or off, according to * op) the indicate plane(s). * - * This method is now deprecated. Use add_on_plane() or add_off_plane() - * instead. + * @deprecated Use add_on_plane() or add_off_plane() instead. */ CPT(RenderAttrib) ClipPlaneAttrib:: make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2) { @@ -118,8 +116,7 @@ make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2) { * Constructs a new ClipPlaneAttrib object that turns on (or off, according to * op) the indicate plane(s). * - * This method is now deprecated. Use add_on_plane() or add_off_plane() - * instead. + * @deprecated Use add_on_plane() or add_off_plane() instead. */ CPT(RenderAttrib) ClipPlaneAttrib:: make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2, @@ -160,8 +157,7 @@ make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2, * Constructs a new ClipPlaneAttrib object that turns on (or off, according to * op) the indicate plane(s). * - * This method is now deprecated. Use add_on_plane() or add_off_plane() - * instead. + * @deprecated Use add_on_plane() or add_off_plane() instead. */ CPT(RenderAttrib) ClipPlaneAttrib:: make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2, @@ -217,9 +213,9 @@ make_default() { * were already on, and if O_remove, the planes here are removed from the set * of planes that were on. * - * This method is now deprecated. ClipPlaneAttribs nowadays have a separate - * list of on_planes and off_planes, so this method doesn't make sense. Query - * the lists independently. + * @deprecated ClipPlaneAttribs nowadays have a separate list of on_planes and + * off_planes, so this method no longer makes sense. Query the lists + * independently. */ ClipPlaneAttrib::Operation ClipPlaneAttrib:: get_operation() const { @@ -240,9 +236,9 @@ get_operation() const { /** * Returns the number of planes listed in the attribute. * - * This method is now deprecated. ClipPlaneAttribs nowadays have a separate - * list of on_planes and off_planes, so this method doesn't make sense. Query - * the lists independently. + * @deprecated ClipPlaneAttribs nowadays have a separate list of on_planes and + * off_planes, so this method no longer makes sense. Query the lists + * independently. */ int ClipPlaneAttrib:: get_num_planes() const { @@ -259,9 +255,9 @@ get_num_planes() const { /** * Returns the nth plane listed in the attribute. * - * This method is now deprecated. ClipPlaneAttribs nowadays have a separate - * list of on_planes and off_planes, so this method doesn't make sense. Query - * the lists independently. + * @deprecated ClipPlaneAttribs nowadays have a separate list of on_planes and + * off_planes, so this method no longer makes sense. Query the lists + * independently. */ PlaneNode *ClipPlaneAttrib:: get_plane(int n) const { @@ -279,9 +275,9 @@ get_plane(int n) const { * Returns true if the indicated plane is listed in the attrib, false * otherwise. * - * This method is now deprecated. ClipPlaneAttribs nowadays have a separate - * list of on_planes and off_planes, so this method doesn't make sense. Query - * the lists independently. + * @deprecated ClipPlaneAttribs nowadays have a separate list of on_planes and + * off_planes, so this method no longer makes sense. Query the lists + * independently. */ bool ClipPlaneAttrib:: has_plane(PlaneNode *plane) const { @@ -299,8 +295,7 @@ has_plane(PlaneNode *plane) const { * Returns a new ClipPlaneAttrib, just like this one, but with the indicated * plane added to the list of planes. * - * This method is now deprecated. Use add_on_plane() or add_off_plane() - * instead. + * @deprecated Use add_on_plane() or add_off_plane() instead. */ CPT(RenderAttrib) ClipPlaneAttrib:: add_plane(PlaneNode *plane) const { @@ -318,8 +313,7 @@ add_plane(PlaneNode *plane) const { * Returns a new ClipPlaneAttrib, just like this one, but with the indicated * plane removed from the list of planes. * - * This method is now deprecated. Use remove_on_plane() or remove_off_plane() - * instead. + * @deprecated Use remove_on_plane() or remove_off_plane() instead. */ CPT(RenderAttrib) ClipPlaneAttrib:: remove_plane(PlaneNode *plane) const { diff --git a/panda/src/pgraph/colorBlendAttrib.cxx b/panda/src/pgraph/colorBlendAttrib.cxx index f9a24d96bf..51b2798137 100644 --- a/panda/src/pgraph/colorBlendAttrib.cxx +++ b/panda/src/pgraph/colorBlendAttrib.cxx @@ -35,8 +35,9 @@ make_off() { } /** - * Constructs a new ColorBlendAttrib object. This constructor is deprecated; - * use the one below, which takes three or four parameters, instead. + * Constructs a new ColorBlendAttrib object. + * + * @deprecated Use the three- or four-parameter constructor instead. */ CPT(RenderAttrib) ColorBlendAttrib:: make(ColorBlendAttrib::Mode mode) { diff --git a/panda/src/pgraph/cullTraverser.I b/panda/src/pgraph/cullTraverser.I index bb7bb35e36..98bc781b50 100644 --- a/panda/src/pgraph/cullTraverser.I +++ b/panda/src/pgraph/cullTraverser.I @@ -116,6 +116,8 @@ get_camera_mask() const { /** * Specifies the bounding volume that corresponds to the view frustum. Any * primitives that fall entirely outside of this volume are not drawn. + * + * Nowadays, this gets set automatically by set_scene(). */ INLINE void CullTraverser:: set_view_frustum(GeometricBoundingVolume *view_frustum) { diff --git a/panda/src/pgraph/cullTraverser.cxx b/panda/src/pgraph/cullTraverser.cxx index 0ecf27666a..ae0b7e8014 100644 --- a/panda/src/pgraph/cullTraverser.cxx +++ b/panda/src/pgraph/cullTraverser.cxx @@ -97,6 +97,8 @@ set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsg, _camera_mask = camera->get_camera_mask(); _effective_incomplete_render = _gsg->get_incomplete_render() && dr_incomplete_render; + + _view_frustum = scene_setup->get_view_frustum(); } /** diff --git a/panda/src/pgraph/cullTraverserData.h b/panda/src/pgraph/cullTraverserData.h index e275dcfd93..eca5f142dc 100644 --- a/panda/src/pgraph/cullTraverserData.h +++ b/panda/src/pgraph/cullTraverserData.h @@ -67,6 +67,8 @@ PUBLISHED: void apply_transform_and_state(CullTraverser *trav); void apply_transform(const TransformState *node_transform); + MAKE_PROPERTY(node_path, get_node_path); + private: // We store a chain leading all the way to the root, so that we can compose // a NodePath. We may be able to eliminate this requirement in the future. diff --git a/panda/src/pgraph/light.I b/panda/src/pgraph/light.I index 4b84c409aa..6dd0428836 100644 --- a/panda/src/pgraph/light.I +++ b/panda/src/pgraph/light.I @@ -78,6 +78,8 @@ set_color(const LColor &color) { /** * Returns true if the color was specified as a temperature in kelvins, and * get_color_temperature is defined. + * + * @since 1.10.0 */ INLINE bool Light:: has_color_temperature() const { @@ -87,6 +89,8 @@ has_color_temperature() const { /** * Returns the basic color temperature of the light, assuming * has_color_temperature() returns true. + * + * @since 1.10.0 */ INLINE PN_stdfloat Light:: get_color_temperature() const { diff --git a/panda/src/pgraph/light.cxx b/panda/src/pgraph/light.cxx index 82b53f5757..8631cb5b0b 100644 --- a/panda/src/pgraph/light.cxx +++ b/panda/src/pgraph/light.cxx @@ -70,6 +70,8 @@ is_ambient_light() const { * * The default value is 6500 K, corresponding to a perfectly white light * assuming a D65 white point. + * + * @since 1.10.0 */ void Light:: set_color_temperature(PN_stdfloat temperature) { diff --git a/panda/src/pgraph/lightAttrib.cxx b/panda/src/pgraph/lightAttrib.cxx index bdd1553a5e..9bc99674f8 100644 --- a/panda/src/pgraph/lightAttrib.cxx +++ b/panda/src/pgraph/lightAttrib.cxx @@ -89,8 +89,7 @@ LightAttrib:: * Constructs a new LightAttrib object that turns on (or off, according to op) * the indicated light(s). * - * This method is now deprecated. Use add_on_light() or add_off_light() - * instead. + * @deprecated Use add_on_light() or add_off_light() instead. */ CPT(RenderAttrib) LightAttrib:: make(LightAttrib::Operation op, Light *light) { @@ -124,8 +123,7 @@ make(LightAttrib::Operation op, Light *light) { * Constructs a new LightAttrib object that turns on (or off, according to op) * the indicate light(s). * - * This method is now deprecated. Use add_on_light() or add_off_light() - * instead. + * @deprecated Use add_on_light() or add_off_light() instead. */ CPT(RenderAttrib) LightAttrib:: make(LightAttrib::Operation op, Light *light1, Light *light2) { @@ -162,8 +160,7 @@ make(LightAttrib::Operation op, Light *light1, Light *light2) { * Constructs a new LightAttrib object that turns on (or off, according to op) * the indicate light(s). * - * This method is now deprecated. Use add_on_light() or add_off_light() - * instead. + * @deprecated Use add_on_light() or add_off_light() instead. */ CPT(RenderAttrib) LightAttrib:: make(LightAttrib::Operation op, Light *light1, Light *light2, @@ -204,8 +201,7 @@ make(LightAttrib::Operation op, Light *light1, Light *light2, * Constructs a new LightAttrib object that turns on (or off, according to op) * the indicate light(s). * - * This method is now deprecated. Use add_on_light() or add_off_light() - * instead. + * @deprecated Use add_on_light() or add_off_light() instead. */ CPT(RenderAttrib) LightAttrib:: make(LightAttrib::Operation op, Light *light1, Light *light2, @@ -261,9 +257,9 @@ make_default() { * already on, and if O_remove, the lights here are removed from the set of * lights that were on. * - * This method is now deprecated. LightAttribs nowadays have a separate list - * of on_lights and off_lights, so this method doesn't make sense. Query the - * lists independently. + * @deprecated LightAttribs nowadays have a separate list of on_lights and + * off_lights, so this method no longer makes sense. Query the lists + * independently. */ LightAttrib::Operation LightAttrib:: get_operation() const { @@ -284,9 +280,9 @@ get_operation() const { /** * Returns the number of lights listed in the attribute. * - * This method is now deprecated. LightAttribs nowadays have a separate list - * of on_lights and off_lights, so this method doesn't make sense. Query the - * lists independently. + * @deprecated LightAttribs nowadays have a separate list of on_lights and + * off_lights, so this method no longer makes sense. Query the lists + * independently. */ int LightAttrib:: get_num_lights() const { @@ -303,9 +299,9 @@ get_num_lights() const { /** * Returns the nth light listed in the attribute. * - * This method is now deprecated. LightAttribs nowadays have a separate list - * of on_lights and off_lights, so this method doesn't make sense. Query the - * lists independently. + * @deprecated LightAttribs nowadays have a separate list of on_lights and + * off_lights, so this method no longer makes sense. Query the lists + * independently. */ Light *LightAttrib:: get_light(int n) const { @@ -323,9 +319,9 @@ get_light(int n) const { * Returns true if the indicated light is listed in the attrib, false * otherwise. * - * This method is now deprecated. LightAttribs nowadays have a separate list - * of on_lights and off_lights, so this method doesn't make sense. Query the - * lists independently. + * @deprecated LightAttribs nowadays have a separate list of on_lights and + * off_lights, so this method no longer makes sense. Query the lists + * independently. */ bool LightAttrib:: has_light(Light *light) const { @@ -343,8 +339,7 @@ has_light(Light *light) const { * Returns a new LightAttrib, just like this one, but with the indicated light * added to the list of lights. * - * This method is now deprecated. Use add_on_light() or add_off_light() - * instead. + * @deprecated Use add_on_light() or add_off_light() instead. */ CPT(RenderAttrib) LightAttrib:: add_light(Light *light) const { @@ -362,8 +357,7 @@ add_light(Light *light) const { * Returns a new LightAttrib, just like this one, but with the indicated light * removed from the list of lights. * - * This method is now deprecated. Use remove_on_light() or remove_off_light() - * instead. + * @deprecated Use remove_on_light() or remove_off_light() instead. */ CPT(RenderAttrib) LightAttrib:: remove_light(Light *light) const { diff --git a/panda/src/pgraph/loaderFileTypeRegistry.cxx b/panda/src/pgraph/loaderFileTypeRegistry.cxx index c7df6731eb..2a8d321323 100644 --- a/panda/src/pgraph/loaderFileTypeRegistry.cxx +++ b/panda/src/pgraph/loaderFileTypeRegistry.cxx @@ -113,6 +113,41 @@ register_deferred_type(const string &extension, const string &library) { _deferred_types[dcextension] = library; } +/** + * Removes a type previously registered using register_type. + */ +void LoaderFileTypeRegistry:: +unregister_type(LoaderFileType *type) { + Types::iterator it = find(_types.begin(), _types.end(), type); + if (it == _types.end()) { + if (loader_cat.is_debug()) { + loader_cat.debug() + << "Attempt to unregister LoaderFileType " << type->get_name() + << " (" << type->get_type() << "), which was not registered.\n"; + } + return; + } + + _types.erase(it); + + { + std::string dcextension = downcase(type->get_extension()); + Extensions::iterator ei = _extensions.find(dcextension); + if (ei != _extensions.end() && ei->second == type) { + _extensions.erase(ei); + } + } + + vector_string words; + extract_words(type->get_additional_extensions(), words); + for (const std::string &word : words) { + Extensions::iterator ei = _extensions.find(downcase(word)); + if (ei != _extensions.end() && ei->second == type) { + _extensions.erase(ei); + } + } +} + /** * Returns the total number of types registered. */ diff --git a/panda/src/pgraph/loaderFileTypeRegistry.h b/panda/src/pgraph/loaderFileTypeRegistry.h index 1cc1cd8f6f..cccb3af333 100644 --- a/panda/src/pgraph/loaderFileTypeRegistry.h +++ b/panda/src/pgraph/loaderFileTypeRegistry.h @@ -35,7 +35,14 @@ public: void register_type(LoaderFileType *type); void register_deferred_type(const std::string &extension, const std::string &library); + void unregister_type(LoaderFileType *type); + PUBLISHED: + EXTENSION(void register_type(PyObject *type)); + EXTENSION(void register_deferred_type(PyObject *entry_point)); + + EXTENSION(void unregister_type(PyObject *type)); + int get_num_types() const; LoaderFileType *get_type(int n) const; MAKE_SEQ(get_types, get_num_types, get_type); diff --git a/panda/src/pgraph/loaderFileTypeRegistry_ext.cxx b/panda/src/pgraph/loaderFileTypeRegistry_ext.cxx new file mode 100644 index 0000000000..20450e9195 --- /dev/null +++ b/panda/src/pgraph/loaderFileTypeRegistry_ext.cxx @@ -0,0 +1,115 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file loaderFileTypeRegistry_ext.cxx + * @author rdb + * @date 2019-07-30 + */ + +#include "loaderFileTypeRegistry_ext.h" + +#ifdef HAVE_PYTHON + +#include "pythonLoaderFileType.h" + +extern struct Dtool_PyTypedObject Dtool_LoaderFileType; + +/** + * Registers a loader file type that is implemented in Python. + */ +void Extension:: +register_type(PyObject *type) { + PythonLoaderFileType *loader = new PythonLoaderFileType(); + + if (loader->init(type)) { + _this->register_type(loader); + } else { + delete loader; + } +} + +/** + * Registers a loader file type from a pkg_resources.EntryPoint object, which + * will be loaded when a file with the extension is encountered. + */ +void Extension:: +register_deferred_type(PyObject *entry_point) { + // The "name" attribute holds the extension. + PyObject *name = PyObject_GetAttrString(entry_point, "name"); + if (name == nullptr) { + Dtool_Raise_TypeError("entry_point argument is missing name attribute"); + return; + } + + const char *name_str; + Py_ssize_t name_len; +#if PY_MAJOR_VERSION >= 3 + name_str = PyUnicode_AsUTF8AndSize(name, &name_len); +#else + if (PyString_AsStringAndSize(name, (char **)&name_str, &name_len) == -1) { + name_str = nullptr; + } +#endif + Py_DECREF(name); + + if (name_str == nullptr) { + Dtool_Raise_TypeError("entry_point.name is expected to be str"); + return; + } + + PythonLoaderFileType *loader = new PythonLoaderFileType(std::string(name_str, name_len), entry_point); + _this->register_type(loader); +} + +/** + * If the given loader type is registered, unregisters it. + */ +void Extension:: +unregister_type(PyObject *type) { + // Are we passing in a C++ file type object? + LoaderFileType *extracted_type; + if (DtoolInstance_GetPointer(type, extracted_type, Dtool_LoaderFileType)) { + _this->unregister_type(extracted_type); + return; + } + + // If not, we may be passing in a Python file type. + PyObject *load_func = PyObject_GetAttrString(type, "load_file"); + PyObject *save_func = PyObject_GetAttrString(type, "save_file"); + PyErr_Clear(); + + if (load_func == nullptr && save_func == nullptr) { + Dtool_Raise_TypeError("expected loader type"); + return; + } + + // Keep looping until we've removed all instances of it. + bool found_any; + do { + found_any = false; + size_t num_types = _this->get_num_types(); + for (size_t i = 0; i < num_types; ++i) { + LoaderFileType *type = _this->get_type(i); + if (type->is_of_type(PythonLoaderFileType::get_class_type())) { + PythonLoaderFileType *python_type = (PythonLoaderFileType *)type; + if (python_type->_load_func == load_func && + python_type->_save_func == save_func) { + _this->unregister_type(python_type); + delete python_type; + found_any = true; + break; + } + } + } + } while (found_any); + + Py_XDECREF(load_func); + Py_XDECREF(save_func); +} + +#endif diff --git a/panda/src/pgraph/loaderFileTypeRegistry_ext.h b/panda/src/pgraph/loaderFileTypeRegistry_ext.h new file mode 100644 index 0000000000..9c9815c20d --- /dev/null +++ b/panda/src/pgraph/loaderFileTypeRegistry_ext.h @@ -0,0 +1,40 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file loaderFileTypeRegistry_ext.h + * @author rdb + * @date 2019-07-30 + */ + +#ifndef LOADERFILETYPEREGISTRYEXT_H +#define LOADERFILETYPEREGISTRYEXT_H + +#include "pandabase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "loaderFileTypeRegistry.h" +#include "py_panda.h" + +/** + * This class defines the extension methods for LoaderFileTypeRegistry, which are called + * instead of any C++ methods with the same prototype. + */ +template<> +class Extension : public ExtensionBase { +public: + void register_type(PyObject *type); + void register_deferred_type(PyObject *entry_point); + + void unregister_type(PyObject *type); +}; + +#endif // HAVE_PYTHON + +#endif diff --git a/panda/src/pgraph/logicOpAttrib.h b/panda/src/pgraph/logicOpAttrib.h index f5bd2ce5d9..7d6b0f5b6e 100644 --- a/panda/src/pgraph/logicOpAttrib.h +++ b/panda/src/pgraph/logicOpAttrib.h @@ -24,6 +24,8 @@ class FactoryParams; * If enabled, specifies that a custom logical operation be performed instead * of any color blending. Setting it to a value other than M_none will cause * color blending to be disabled and the given logic operation to be performed. + * + * @since 1.10.0 */ class EXPCL_PANDA_PGRAPH LogicOpAttrib : public RenderAttrib { PUBLISHED: diff --git a/panda/src/pgraph/modelPool.I b/panda/src/pgraph/modelPool.I index 717c3eedba..efa8fa99f8 100644 --- a/panda/src/pgraph/modelPool.I +++ b/panda/src/pgraph/modelPool.I @@ -63,8 +63,7 @@ load_model(const Filename &filename, const LoaderOptions &options) { * Adds the indicated already-loaded model to the pool. The model will * replace any previously-loaded model in the pool that had the same filename. * - * This two-parameter version of this method is deprecated; use the one- - * parameter add_model(model) instead. + * @deprecated Use the one-parameter add_model(model) instead. */ INLINE void ModelPool:: add_model(const Filename &filename, ModelRoot *model) { @@ -77,8 +76,7 @@ add_model(const Filename &filename, ModelRoot *model) { * called, a reference count will be maintained on every model every loaded, * and models will never be freed. * - * This version of this method is deprecated; use release_model(model) - * instead. + * @deprecated Use release_model(model) instead. */ INLINE void ModelPool:: release_model(const Filename &filename) { diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 8732168212..1748942679 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -3136,6 +3136,21 @@ get_texture(TextureStage *stage) const { return nullptr; } +/** + * Recursively searches the scene graph for references to the given texture, + * and replaces them with the new texture. + * + * @since 1.10.4 + */ +void NodePath:: +replace_texture(Texture *tex, Texture *new_tex) { + nassertv_always(!is_empty()); + nassertv(tex != nullptr); + nassertv(new_tex != nullptr); + + r_replace_texture(node(), tex, new_tex); +} + /** * Returns the sampler state that has been given for the base-level texture * that has been set on this particular node. If no sampler state was given, @@ -6425,6 +6440,53 @@ r_find_all_textures(PandaNode *node, TextureStage *stage, } } +/** + * Recursively replaces references to the given texture on this section of the + * scene graph with the given other texture. + */ +void NodePath:: +r_replace_texture(PandaNode *node, Texture *tex, Texture *new_tex) { + // Consider the state of the node itself. + { + CPT(RenderState) node_state = node->get_state(); + const TextureAttrib *ta; + if (node_state->get_attrib(ta)) { + CPT(RenderAttrib) new_ta = ta->replace_texture(tex, new_tex); + if (new_ta != ta) { + node->set_state(node_state->set_attrib(new_ta)); + } + } + } + + // If this is a GeomNode, consider the state of any of its Geoms. + if (node->is_geom_node()) { + GeomNode *gnode; + DCAST_INTO_V(gnode, node); + + int num_geoms = gnode->get_num_geoms(); + for (int i = 0; i < num_geoms; i++) { + CPT(RenderState) geom_state = gnode->get_geom_state(i); + + // Look for a TextureAttrib on the state. + const TextureAttrib *ta; + if (geom_state->get_attrib(ta)) { + CPT(RenderAttrib) new_ta = ta->replace_texture(tex, new_tex); + if (new_ta != ta) { + gnode->set_geom_state(i, geom_state->set_attrib(new_ta)); + } + } + } + } + + // Now consider children. + PandaNode::Children cr = node->get_children(); + size_t num_children = cr.get_num_children(); + for (size_t i = 0; i < num_children; ++i) { + PandaNode *child = cr.get_child(i); + r_replace_texture(child, tex, new_tex); + } +} + /** * */ diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index 3d5d4a6aa3..eaec935a29 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -41,6 +41,8 @@ #include "pta_LVecBase2.h" #include "stl_compares.h" #include "shaderInput.h" +#include "internalNameCollection.h" +#include "materialCollection.h" #include "textureCollection.h" #include "textureStageCollection.h" @@ -49,13 +51,9 @@ class FindApproxPath; class FindApproxLevelEntry; class Light; class PolylightNode; -class InternalNameCollection; class Texture; class TextureStage; -class TextureCollection; -class TextureStageCollection; class Material; -class MaterialCollection; class Fog; class GlobPattern; class PreparedGraphicsObjects; @@ -620,6 +618,7 @@ PUBLISHED: bool has_texture_off(TextureStage *stage) const; Texture *get_texture() const; Texture *get_texture(TextureStage *stage) const; + void replace_texture(Texture *tex, Texture *new_tex); const SamplerState &get_texture_sampler() const; const SamplerState &get_texture_sampler(TextureStage *stage) const; @@ -1003,6 +1002,7 @@ private: Texture *r_find_texture(PandaNode *node, TextureStage *stage) const; void r_find_all_textures(PandaNode *node, TextureStage *stage, Textures &textures) const; + static void r_replace_texture(PandaNode *node, Texture *tex, Texture *new_tex); typedef phash_set TextureStages; TextureStage *r_find_texture_stage(PandaNode *node, const RenderState *state, @@ -1052,6 +1052,8 @@ private: INLINE std::ostream &operator << (std::ostream &out, const NodePath &node_path); +#include "nodePathCollection.h" + #include "nodePath.I" #endif diff --git a/panda/src/pgraph/p3pgraph_ext_composite.cxx b/panda/src/pgraph/p3pgraph_ext_composite.cxx index d16f600c0a..37b9012c76 100644 --- a/panda/src/pgraph/p3pgraph_ext_composite.cxx +++ b/panda/src/pgraph/p3pgraph_ext_composite.cxx @@ -1,6 +1,8 @@ +#include "loaderFileTypeRegistry_ext.cxx" #include "nodePath_ext.cxx" #include "nodePathCollection_ext.cxx" #include "pandaNode_ext.cxx" +#include "pythonLoaderFileType.cxx" #include "renderState_ext.cxx" #include "shaderAttrib_ext.cxx" #include "shaderInput_ext.cxx" diff --git a/panda/src/pgraph/paramNodePath.I b/panda/src/pgraph/paramNodePath.I index 89d6a9e67b..67a9d24ea6 100644 --- a/panda/src/pgraph/paramNodePath.I +++ b/panda/src/pgraph/paramNodePath.I @@ -11,24 +11,6 @@ * @date 2015-02-25 */ -/** - * Creates a new ParamNodePath storing the given node path object. - */ -INLINE ParamNodePath:: -ParamNodePath(const NodePath &node_path) : - _node_path(node_path) -{ -} - -/** - * Creates a new ParamNodePath storing the given node path object. - */ -INLINE ParamNodePath:: -ParamNodePath(NodePath &&node_path) noexcept : - _node_path(std::move(node_path)) -{ -} - /** * Returns NodePath::get_class_type(). */ @@ -40,7 +22,7 @@ get_value_type() const { /** * Retrieves the NodePath stored in the parameter. */ -INLINE const NodePath &ParamNodePath:: +INLINE NodePath ParamNodePath:: get_value() const { return _node_path; } diff --git a/panda/src/pgraph/paramNodePath.cxx b/panda/src/pgraph/paramNodePath.cxx index 72a3563b22..45bfe0c12f 100644 --- a/panda/src/pgraph/paramNodePath.cxx +++ b/panda/src/pgraph/paramNodePath.cxx @@ -14,9 +14,41 @@ #include "paramNodePath.h" #include "dcast.h" #include "pandaNode.h" +#include "light.h" TypeHandle ParamNodePath::_type_handle; +/** + * Creates a new ParamNodePath storing the given node path object. + */ +ParamNodePath:: +ParamNodePath(NodePath node_path) : + _node_path(std::move(node_path)) +{ + // The reason to construct a ParamNodePath is often to apply a light to a + // shader input, so we want to keep track of the fact that the light is in + // use. + if (!_node_path.is_empty()) { + Light *light = _node_path.node()->as_light(); + if (light != nullptr) { + light->attrib_ref(); + } + } +} + +/** + * + */ +ParamNodePath:: +~ParamNodePath() { + if (!_node_path.is_empty()) { + Light *light = _node_path.node()->as_light(); + if (light != nullptr) { + light->attrib_unref(); + } + } +} + /** * */ @@ -68,6 +100,13 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { _node_path = NodePath(DCAST(PandaNode, p_list[pi++])); } + if (!_node_path.is_empty()) { + Light *light = _node_path.node()->as_light(); + if (light != nullptr) { + light->attrib_ref(); + } + } + return pi; } @@ -96,6 +135,14 @@ void ParamNodePath:: fillin(DatagramIterator &scan, BamReader *manager) { ParamValueBase::fillin(scan, manager); + if (!_node_path.is_empty()) { + Light *light = _node_path.node()->as_light(); + if (light != nullptr) { + light->attrib_unref(); + } + _node_path.clear(); + } + if (manager->get_file_minor_ver() >= 40) { _node_path.fillin(scan, manager); } else { diff --git a/panda/src/pgraph/paramNodePath.h b/panda/src/pgraph/paramNodePath.h index bd0967d5f8..63798cc4a8 100644 --- a/panda/src/pgraph/paramNodePath.h +++ b/panda/src/pgraph/paramNodePath.h @@ -26,11 +26,11 @@ protected: INLINE ParamNodePath() {}; PUBLISHED: - INLINE ParamNodePath(const NodePath &node_path); - INLINE ParamNodePath(NodePath &&node_path) noexcept; + ParamNodePath(NodePath node_path); + virtual ~ParamNodePath(); INLINE virtual TypeHandle get_value_type() const; - INLINE const NodePath &get_value() const; + INLINE NodePath get_value() const; virtual void output(std::ostream &out) const; diff --git a/panda/src/pgraph/polylightNode.cxx b/panda/src/pgraph/polylightNode.cxx index 73242aab04..508a1c97cf 100644 --- a/panda/src/pgraph/polylightNode.cxx +++ b/panda/src/pgraph/polylightNode.cxx @@ -134,7 +134,9 @@ LColor PolylightNode::flicker() const { b = color[2]; } */ - pgraph_cat.debug() << "Color R:" << r << "; G:" << g << "; B:" << b << std::endl; + if (pgraph_cat.is_debug()) { + pgraph_cat.debug() << "Color R:" << r << "; G:" << g << "; B:" << b << std::endl; + } return LColor(r,g,b,1.0); } diff --git a/panda/src/pgraph/portalClipper.I b/panda/src/pgraph/portalClipper.I index 6dd31056e2..1e5a7c5b8f 100644 --- a/panda/src/pgraph/portalClipper.I +++ b/panda/src/pgraph/portalClipper.I @@ -140,7 +140,9 @@ get_reduced_viewport(LPoint2& min, LPoint2& max) const { */ INLINE bool PortalClipper:: is_facing_view(const LPlane &portal_plane) { - portal_cat.debug() << "portal plane check value: " << portal_plane[3] << "\n"; + if (portal_cat.is_debug()) { + portal_cat.debug() << "portal plane check value: " << portal_plane[3] << "\n"; + } return (portal_plane[3] > 0); } @@ -161,7 +163,9 @@ is_whole_portal_in_view(const LMatrix4 &cmat) { int result = _reduced_frustum->contains(gbv); - portal_cat.spam() << "1st level test if portal: " << *_reduced_frustum << " is in view " << result << std::endl; + if (portal_cat.is_spam()) { + portal_cat.spam() << "1st level test if portal: " << *_reduced_frustum << " is in view " << result << std::endl; + } return (result != 0); } @@ -179,7 +183,9 @@ is_partial_portal_in_view() { for (int j=0; j<_num_vert; ++j) { result |= _reduced_frustum->contains(_coords[j]); } - portal_cat.spam() << "frustum: " << *_reduced_frustum << " contains(coord) result = " << result << endl; + if (portal_cat.is_spam()) { + portal_cat.spam() << "frustum: " << *_reduced_frustum << " contains(coord) result = " << result << endl; + } return (result != 0); } @@ -193,9 +199,11 @@ INLINE PN_stdfloat PortalClipper:: get_plane_depth(PN_stdfloat x, PN_stdfloat z, LPlane *portal_plane) { PN_stdfloat y = 0.0; // Plane equation: Ax + By + Cz + D = 0 y = (Ax + Cz + D) -B - portal_cat.spam() << *portal_plane << endl; - portal_cat.spam() << portal_plane->_v.v._0 << " " << portal_plane->_v.v._1 << " " - << portal_plane->_v.v._2 << " " << portal_plane->_v.v._3 << endl; + if (portal_cat.is_spam()) { + portal_cat.spam() << *portal_plane << endl; + portal_cat.spam() << portal_plane->_v.v._0 << " " << portal_plane->_v.v._1 << " " + << portal_plane->_v.v._2 << " " << portal_plane->_v.v._3 << endl; + } if (portal_plane->_v.v._1 != 0.0) { y = (((portal_plane->_v.v._0*x)+(portal_plane->_v.v._2*z)+portal_plane->_v.v._3) diff --git a/panda/src/pgraph/portalClipper.cxx b/panda/src/pgraph/portalClipper.cxx index d9d1af727d..22d032a728 100644 --- a/panda/src/pgraph/portalClipper.cxx +++ b/panda/src/pgraph/portalClipper.cxx @@ -210,14 +210,18 @@ prepare_portal(const NodePath &node_path) } // Get the geometry from the portal - portal_cat.spam() << *_portal_node << endl; + if (portal_cat.is_spam()) { + portal_cat.spam() << *_portal_node << endl; + } // Get the camera transformation matrix CPT(TransformState) ctransform = node_path.get_transform(_scene_setup->get_cull_center()); // CPT(TransformState) ctransform = // node_path.get_transform(_scene_setup->get_camera_path()); LMatrix4 cmat = ctransform->get_mat(); - portal_cat.spam() << cmat << endl; + if (portal_cat.is_spam()) { + portal_cat.spam() << cmat << endl; + } LVertex temp[4]; temp[0] = _portal_node->get_vertex(0); @@ -225,11 +229,13 @@ prepare_portal(const NodePath &node_path) temp[2] = _portal_node->get_vertex(2); temp[3] = _portal_node->get_vertex(3); - portal_cat.spam() << "before transformation to camera space" << endl; - portal_cat.spam() << temp[0] << endl; - portal_cat.spam() << temp[1] << endl; - portal_cat.spam() << temp[2] << endl; - portal_cat.spam() << temp[3] << endl; + if (portal_cat.is_spam()) { + portal_cat.spam() << "before transformation to camera space" << endl; + portal_cat.spam() << temp[0] << endl; + portal_cat.spam() << temp[1] << endl; + portal_cat.spam() << temp[2] << endl; + portal_cat.spam() << temp[3] << endl; + } temp[0] = temp[0]*cmat; temp[1] = temp[1]*cmat; @@ -238,15 +244,19 @@ prepare_portal(const NodePath &node_path) LPlane portal_plane(temp[0], temp[1], temp[2]); if (!is_facing_view(portal_plane)) { - portal_cat.debug() << "portal failed 1st level test (isn't facing the camera)\n"; + if (portal_cat.is_debug()) { + portal_cat.debug() << "portal failed 1st level test (isn't facing the camera)\n"; + } return false; } - portal_cat.spam() << "after transformation to camera space" << endl; - portal_cat.spam() << temp[0] << endl; - portal_cat.spam() << temp[1] << endl; - portal_cat.spam() << temp[2] << endl; - portal_cat.spam() << temp[3] << endl; + if (portal_cat.is_spam()) { + portal_cat.spam() << "after transformation to camera space" << endl; + portal_cat.spam() << temp[0] << endl; + portal_cat.spam() << temp[1] << endl; + portal_cat.spam() << temp[2] << endl; + portal_cat.spam() << temp[3] << endl; + } // check if the portal intersects with the cameras 0 point (center of // projection). In that case the portal will invert itself. portals @@ -270,7 +280,9 @@ prepare_portal(const NodePath &node_path) (temp[1][forward_axis] * forward[forward_axis] <= 0) || (temp[2][forward_axis] * forward[forward_axis] <= 0) || (temp[3][forward_axis] * forward[forward_axis] <= 0)) { - portal_cat.debug() << "portal intersects with center of projection.." << endl; + if (portal_cat.is_debug()) { + portal_cat.debug() << "portal intersects with center of projection.." << endl; + } return true; } @@ -281,11 +293,13 @@ prepare_portal(const NodePath &node_path) lens->project(temp[2], projected_coords[2]); lens->project(temp[3], projected_coords[3]); - portal_cat.spam() << "after projection to 2d" << endl; - portal_cat.spam() << projected_coords[0] << endl; - portal_cat.spam() << projected_coords[1] << endl; - portal_cat.spam() << projected_coords[2] << endl; - portal_cat.spam() << projected_coords[3] << endl; + if (portal_cat.is_spam()) { + portal_cat.spam() << "after projection to 2d" << endl; + portal_cat.spam() << projected_coords[0] << endl; + portal_cat.spam() << projected_coords[1] << endl; + portal_cat.spam() << projected_coords[2] << endl; + portal_cat.spam() << projected_coords[3] << endl; + } // calculate axis aligned bounding box of the portal PN_stdfloat min_x, max_x, min_y, max_y; @@ -294,7 +308,9 @@ prepare_portal(const NodePath &node_path) min_y = min(min(min(projected_coords[0][1], projected_coords[1][1]), projected_coords[2][1]), projected_coords[3][1]); max_y = max(max(max(projected_coords[0][1], projected_coords[1][1]), projected_coords[2][1]), projected_coords[3][1]); - portal_cat.spam() << "min_x " << min_x << ";max_x " << max_x << ";min_y " << min_y << ";max_y " << max_y << endl; + if (portal_cat.is_spam()) { + portal_cat.spam() << "min_x " << min_x << ";max_x " << max_x << ";min_y " << min_y << ";max_y " << max_y << endl; + } // clip the minima and maxima against the viewport min_x = max(min_x, _reduced_viewport_min[0]); @@ -302,11 +318,15 @@ prepare_portal(const NodePath &node_path) max_x = min(max_x, _reduced_viewport_max[0]); max_y = min(max_y, _reduced_viewport_max[1]); - portal_cat.spam() << "after clipping: min_x " << min_x << ";max_x " << max_x << ";min_y " << min_y << ";max_y " << max_y << endl; + if (portal_cat.is_spam()) { + portal_cat.spam() << "after clipping: min_x " << min_x << ";max_x " << max_x << ";min_y " << min_y << ";max_y " << max_y << endl; + } if ((min_x >= max_x) || (min_y >= max_y)) { + if (portal_cat.is_debug()) { portal_cat.debug() << "portal got clipped away \n"; - return false; + } + return false; } // here we know the portal is in view and we have its clipped extents @@ -325,7 +345,9 @@ prepare_portal(const NodePath &node_path) _reduced_frustum = new BoundingHexahedron(far_point[0], far_point[1], far_point[2], far_point[3], near_point[0], near_point[1], near_point[2], near_point[3]); - portal_cat.debug() << *_reduced_frustum << endl; + if (portal_cat.is_debug()) { + portal_cat.debug() << *_reduced_frustum << endl; + } // do debug rendering, if requested if (debug_portal_cull) { diff --git a/panda/src/pgraph/pythonLoaderFileType.cxx b/panda/src/pgraph/pythonLoaderFileType.cxx new file mode 100644 index 0000000000..bbb0577106 --- /dev/null +++ b/panda/src/pgraph/pythonLoaderFileType.cxx @@ -0,0 +1,427 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file pythonLoaderFileType.cxx + * @author rdb + * @date 2019-07-29 + */ + +#include "pythonLoaderFileType.h" + +#ifdef HAVE_PYTHON + +#include "bamCacheRecord.h" +#include "modelRoot.h" +#include "pythonThread.h" +#include "py_panda.h" +#include "virtualFileSystem.h" + +extern struct Dtool_PyTypedObject Dtool_BamCacheRecord; +extern struct Dtool_PyTypedObject Dtool_Filename; +extern struct Dtool_PyTypedObject Dtool_LoaderOptions; +extern struct Dtool_PyTypedObject Dtool_PandaNode; +extern struct Dtool_PyTypedObject Dtool_PythonLoaderFileType; + +TypeHandle PythonLoaderFileType::_type_handle; + +/** + * This constructor expects init() to be called manually. + */ +PythonLoaderFileType:: +PythonLoaderFileType() { + init_type(); +} + +/** + * This constructor expects a single pkg_resources.EntryPoint argument for a + * deferred loader. + */ +PythonLoaderFileType:: +PythonLoaderFileType(std::string extension, PyObject *entry_point) : + _extension(std::move(extension)), + _entry_point(entry_point) { + + init_type(); + Py_INCREF(entry_point); +} + +/** + * Destructor. + */ +PythonLoaderFileType:: +~PythonLoaderFileType() { + if (_entry_point != nullptr || _load_func != nullptr || _save_func != nullptr) { +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); +#endif + + Py_CLEAR(_entry_point); + Py_CLEAR(_load_func); + Py_CLEAR(_save_func); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyGILState_Release(gstate); +#endif + } +} + +/** + * Initializes the fields from the given Python loader object. + */ +bool PythonLoaderFileType:: +init(PyObject *loader) { + nassertr(loader != nullptr, false); + nassertr(_load_func == nullptr, false); + nassertr(_save_func == nullptr, false); + + // Check the extensions member. If we already have a registered extension, + // it must occur in the list. + PyObject *extensions = PyObject_GetAttrString(loader, "extensions"); + if (extensions != nullptr) { + if (PyUnicode_Check(extensions) +#if PY_MAJOR_VERSION < 3 + || PyString_Check(extensions) +#endif + ) { + Dtool_Raise_TypeError("extensions list should be a list or tuple"); + Py_DECREF(extensions); + return false; + } + + PyObject *sequence = PySequence_Fast(extensions, "extensions must be a sequence"); + PyObject **items = PySequence_Fast_ITEMS(sequence); + Py_ssize_t num_items = PySequence_Fast_GET_SIZE(sequence); + Py_DECREF(extensions); + + if (num_items == 0) { + PyErr_SetString(PyExc_ValueError, "extensions list may not be empty"); + Py_DECREF(sequence); + return false; + } + + bool found_extension = false; + + for (Py_ssize_t i = 0; i < num_items; ++i) { + PyObject *extension = items[i]; + const char *extension_str; + Py_ssize_t extension_len; + #if PY_MAJOR_VERSION >= 3 + extension_str = PyUnicode_AsUTF8AndSize(extension, &extension_len); + #else + if (PyString_AsStringAndSize(extension, (char **)&extension_str, &extension_len) == -1) { + extension_str = nullptr; + } + #endif + + if (extension_str == nullptr) { + Py_DECREF(sequence); + return false; + } + + if (_extension.empty()) { + _extension.assign(extension_str, extension_len); + found_extension = true; + } else { + std::string new_extension(extension_str, extension_len); + if (_extension == new_extension) { + found_extension = true; + } else { + if (!_additional_extensions.empty()) { + _additional_extensions += ' '; + } + _additional_extensions += new_extension; + } + } + } + Py_DECREF(sequence); + + if (!found_extension) { + PyObject *repr = PyObject_Repr(loader); + loader_cat.error() + << "Registered extension '" << _extension + << "' does not occur in extensions list of " +#if PY_MAJOR_VERSION >= 3 + << PyUnicode_AsUTF8(repr) << "\n"; +#else + << PyString_AsString(repr) << "\n"; +#endif + Py_DECREF(repr); + return false; + } + } else { + return false; + } + + PyObject *supports_compressed = PyObject_GetAttrString(loader, "supports_compressed"); + if (supports_compressed != nullptr) { + if (supports_compressed == Py_True) { + _supports_compressed = true; + } + else if (supports_compressed == Py_False) { + _supports_compressed = false; + } + else { + Dtool_Raise_TypeError("supports_compressed must be a bool"); + Py_DECREF(supports_compressed); + return false; + } + Py_DECREF(supports_compressed); + } + + _load_func = PyObject_GetAttrString(loader, "load_file"); + _save_func = PyObject_GetAttrString(loader, "save_file"); + PyErr_Clear(); + + if (_load_func == nullptr && _save_func == nullptr) { +#if PY_MAJOR_VERSION >= 3 + PyErr_Format(PyExc_TypeError, + "loader plug-in %R does not define load_file or save_file function", + loader); +#else + PyObject *repr = PyObject_Repr(loader); + PyErr_Format(PyExc_TypeError, + "loader plug-in %s does not define load_file or save_file function", + PyString_AsString(repr)); + Py_DECREF(repr); +#endif + return false; + } + + // We don't need this any more. + Py_CLEAR(_entry_point); + + return true; +} + +/** + * Ensures that the referenced Python module is loaded. + */ +bool PythonLoaderFileType:: +ensure_loaded() const { + if (_load_func != nullptr || _save_func != nullptr) { + return true; + } + nassertr_always(_entry_point != nullptr, false); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); +#endif + + if (loader_cat.is_info()) { + PyObject *repr = PyObject_Repr(_entry_point); + + loader_cat.info() + << "loading file type module: " +#if PY_MAJOR_VERSION >= 3 + << PyUnicode_AsUTF8(repr) << "\n"; +#else + << PyString_AsString(repr) << "\n"; +#endif + Py_DECREF(repr); + } + + PyObject *result = PyObject_CallMethod(_entry_point, (char *)"load", nullptr); + + bool success = false; + if (result != nullptr) { + success = ((PythonLoaderFileType *)this)->init(result); + } else { + PyErr_Clear(); + PyObject *repr = PyObject_Repr(_entry_point); + + loader_cat.error() + << "unable to load " +#if PY_MAJOR_VERSION >= 3 + << PyUnicode_AsUTF8(repr) << "\n"; +#else + << PyString_AsString(repr) << "\n"; +#endif + Py_DECREF(repr); + } + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyGILState_Release(gstate); +#endif + + return success; +} + +/** + * + */ +std::string PythonLoaderFileType:: +get_name() const { + return "Python loader"; +} + +/** + * + */ +std::string PythonLoaderFileType:: +get_extension() const { + return _extension; +} + +/** + * Returns a space-separated list of extension, in addition to the one + * returned by get_extension(), that are recognized by this converter. + */ +std::string PythonLoaderFileType:: +get_additional_extensions() const { + return _additional_extensions; +} + +/** + * Returns true if this file type can transparently load compressed files + * (with a .pz or .gz extension), false otherwise. + */ +bool PythonLoaderFileType:: +supports_compressed() const { + return ensure_loaded() && _supports_compressed; +} + +/** + * Returns true if the file type can be used to load files, and load_file() is + * supported. Returns false if load_file() is unimplemented and will always + * fail. + */ +bool PythonLoaderFileType:: +supports_load() const { + return ensure_loaded() && _load_func != nullptr; +} + +/** + * Returns true if the file type can be used to save files, and save_file() is + * supported. Returns false if save_file() is unimplemented and will always + * fail. + */ +bool PythonLoaderFileType:: +supports_save() const { + return ensure_loaded() && _save_func != nullptr; +} + +/** + * + */ +PT(PandaNode) PythonLoaderFileType:: +load_file(const Filename &path, const LoaderOptions &options, + BamCacheRecord *record) const { + // Let's check whether the file even exists before calling Python. + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + PT(VirtualFile) vfile = vfs->get_file(path); + if (vfile == nullptr) { + return nullptr; + } + + if (!supports_load()) { + return nullptr; + } + + if (record != nullptr) { + record->add_dependent_file(vfile); + } + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); +#endif + + // Wrap the arguments. + PyObject *args = PyTuple_New(3); + PyTuple_SET_ITEM(args, 0, DTool_CreatePyInstance((void *)&path, Dtool_Filename, false, true)); + PyTuple_SET_ITEM(args, 1, DTool_CreatePyInstance((void *)&options, Dtool_LoaderOptions, false, true)); + if (record != nullptr) { + record->ref(); + PyTuple_SET_ITEM(args, 2, DTool_CreatePyInstanceTyped((void *)record, Dtool_BamCacheRecord, true, false, record->get_type_index())); + } else { + PyTuple_SET_ITEM(args, 2, Py_None); + Py_INCREF(Py_None); + } + + PT(PandaNode) node; + + PyObject *result = PythonThread::call_python_func(_load_func, args); + if (result != nullptr) { + if (DtoolInstance_Check(result)) { + node = (PandaNode *)DtoolInstance_UPCAST(result, Dtool_PandaNode); + } + Py_DECREF(result); + } + + Py_DECREF(args); + + if (node == nullptr) { + PyObject *exc_type = _PyErr_OCCURRED(); + if (!exc_type) { + loader_cat.error() + << "load_file must return valid PandaNode or raise exception\n"; + } else { + loader_cat.error() + << "Loading " << path.get_basename() + << " failed with " << ((PyTypeObject *)exc_type)->tp_name << " exception.\n"; + PyErr_Clear(); + } + } + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyGILState_Release(gstate); +#endif + + if (node != nullptr && node->is_of_type(ModelRoot::get_class_type())) { + ModelRoot *model_root = DCAST(ModelRoot, node.p()); + model_root->set_fullpath(path); + model_root->set_timestamp(vfile->get_timestamp()); + } + + return node; +} + +/** + * + */ +bool PythonLoaderFileType:: +save_file(const Filename &path, const LoaderOptions &options, + PandaNode *node) const { + if (!supports_save()) { + return false; + } + + nassertr(node != nullptr, false); + node->ref(); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); +#endif + + // Wrap the arguments. + PyObject *args = PyTuple_New(3); + PyTuple_SET_ITEM(args, 0, DTool_CreatePyInstance((void *)&path, Dtool_Filename, false, true)); + PyTuple_SET_ITEM(args, 1, DTool_CreatePyInstance((void *)&options, Dtool_LoaderOptions, false, true)); + PyTuple_SET_ITEM(args, 2, DTool_CreatePyInstanceTyped((void *)node, Dtool_PandaNode, true, false, node->get_type_index())); + + PyObject *result = PythonThread::call_python_func(_load_func, args); + Py_DECREF(args); + if (result != nullptr) { + Py_DECREF(result); + } else { + PyErr_Clear(); + loader_cat.error() + << "save_file failed with an exception.\n"; + } + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyGILState_Release(gstate); +#endif + + return (result != nullptr); +} + +#endif // HAVE_PYTHON diff --git a/panda/src/pgraph/pythonLoaderFileType.h b/panda/src/pgraph/pythonLoaderFileType.h new file mode 100644 index 0000000000..6532d1b6e1 --- /dev/null +++ b/panda/src/pgraph/pythonLoaderFileType.h @@ -0,0 +1,88 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file pythonLoaderFileType.h + * @author rdb + * @date 2019-07-29 + */ + +#ifndef PYTHONLOADERFILETYPE_H +#define PYTHONLOADERFILETYPE_H + +#include "pandabase.h" + +#ifdef HAVE_PYTHON + +#include "loaderFileType.h" +#include "extension.h" + +class LoaderFileTypeRegistry; + +/** + * This defines a Python-based loader plug-in. An instance of this can be + * constructed by inheritance and explicitly registered, or it can be created + * by passing in a pkg_resources.EntryPoint instance. + * + * @since 1.10.4 + */ +class PythonLoaderFileType : public LoaderFileType { +public: + PythonLoaderFileType(); + PythonLoaderFileType(std::string extension, PyObject *entry_point); + ~PythonLoaderFileType(); + + bool init(PyObject *loader); + bool ensure_loaded() const; + + virtual std::string get_name() const override; + virtual std::string get_extension() const override; + virtual std::string get_additional_extensions() const override; + virtual bool supports_compressed() const override; + + virtual bool supports_load() const override; + virtual bool supports_save() const override; + + virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &options, + BamCacheRecord *record) const override; + virtual bool save_file(const Filename &path, const LoaderOptions &options, + PandaNode *node) const override; + +private: + std::string _extension; + std::string _additional_extensions; + PyObject *_entry_point = nullptr; + PyObject *_load_func = nullptr; + PyObject *_save_func = nullptr; + bool _supports_compressed = false; + + friend class Extension; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + LoaderFileType::init_type(); + register_type(_type_handle, "PythonLoaderFileType", + LoaderFileType::get_class_type()); + } + virtual TypeHandle get_type() const override { + return get_class_type(); + } + virtual TypeHandle force_init_type() override { + init_type(); + return get_class_type(); + } + +private: + static TypeHandle _type_handle; +}; + +#endif // HAVE_PYTHON + +#endif diff --git a/panda/src/pgraph/renderAttrib.cxx b/panda/src/pgraph/renderAttrib.cxx index 9bac4e666a..4abde672dd 100644 --- a/panda/src/pgraph/renderAttrib.cxx +++ b/panda/src/pgraph/renderAttrib.cxx @@ -21,7 +21,7 @@ using std::ostream; LightReMutex *RenderAttrib::_attribs_lock = nullptr; -RenderAttrib::Attribs *RenderAttrib::_attribs = nullptr; +RenderAttrib::Attribs RenderAttrib::_attribs; TypeHandle RenderAttrib::_type_handle; size_t RenderAttrib::_garbage_index = 0; @@ -33,7 +33,7 @@ PStatCollector RenderAttrib::_garbage_collect_pcollector("*:State Cache:Garbage */ RenderAttrib:: RenderAttrib() { - if (_attribs == nullptr) { + if (_attribs_lock == nullptr) { init_attribs(); } _saved_entry = -1; @@ -45,8 +45,6 @@ RenderAttrib() { */ RenderAttrib:: ~RenderAttrib() { - LightReMutexHolder holder(*_attribs_lock); - // unref() should have cleared this. nassertv(_saved_entry == -1); } @@ -156,11 +154,7 @@ write(ostream &out, int indent_level) const { int RenderAttrib:: get_num_attribs() { LightReMutexHolder holder(*_attribs_lock); - - if (_attribs == nullptr) { - return 0; - } - return _attribs->get_num_entries(); + return _attribs.get_num_entries(); } /** @@ -172,10 +166,10 @@ void RenderAttrib:: list_attribs(ostream &out) { LightReMutexHolder holder(*_attribs_lock); - size_t size = _attribs->get_num_entries(); + size_t size = _attribs.get_num_entries(); out << size << " attribs:\n"; for (size_t si = 0; si < size; ++si) { - const RenderAttrib *attrib = _attribs->get_key(si); + const RenderAttrib *attrib = _attribs.get_key(si); attrib->write(out, 2); } } @@ -186,16 +180,16 @@ list_attribs(ostream &out) { */ int RenderAttrib:: garbage_collect() { - if (_attribs == nullptr || !garbage_collect_states) { + if (!garbage_collect_states) { return 0; } LightReMutexHolder holder(*_attribs_lock); PStatTimer timer(_garbage_collect_pcollector); - size_t orig_size = _attribs->get_num_entries(); + size_t orig_size = _attribs.get_num_entries(); #ifdef _DEBUG - nassertr(_attribs->validate(), 0); + nassertr(_attribs.validate(), 0); #endif // How many elements to process this pass? @@ -214,7 +208,7 @@ garbage_collect() { size_t stop_at_element = (si + num_this_pass) % size; do { - RenderAttrib *attrib = (RenderAttrib *)_attribs->get_key(si); + RenderAttrib *attrib = (RenderAttrib *)_attribs.get_key(si); if (attrib->get_ref_count() == 1) { // This attrib has recently been unreffed to 1 (the one we added when // we stored it in the cache). Now it's time to delete it. This is @@ -238,15 +232,15 @@ garbage_collect() { } while (si != stop_at_element); _garbage_index = si; - nassertr(_attribs->get_num_entries() == size, 0); + nassertr(_attribs.get_num_entries() == size, 0); #ifdef _DEBUG - nassertr(_attribs->validate(), 0); + nassertr(_attribs.validate(), 0); #endif // If we just cleaned up a lot of attribs, see if we can reduce the table in // size. This will help reduce iteration overhead in the future. - _attribs->consider_shrink_table(); + _attribs.consider_shrink_table(); return (int)orig_size - (int)size; } @@ -259,17 +253,17 @@ garbage_collect() { bool RenderAttrib:: validate_attribs() { LightReMutexHolder holder(*_attribs_lock); - if (_attribs->is_empty()) { + if (_attribs.is_empty()) { return true; } - if (!_attribs->validate()) { + if (!_attribs.validate()) { pgraph_cat.error() << "RenderAttrib::_attribs cache is invalid!\n"; - size_t size = _attribs->get_num_entries(); + size_t size = _attribs.get_num_entries(); for (size_t si = 0; si < size; ++si) { - const RenderAttrib *attrib = _attribs->get_key(si); + const RenderAttrib *attrib = _attribs.get_key(si); //cerr << si << ": " << attrib << "\n"; attrib->write(std::cerr, 2); } @@ -277,16 +271,16 @@ validate_attribs() { return false; } - size_t size = _attribs->get_num_entries(); + size_t size = _attribs.get_num_entries(); size_t si = 0; nassertr(si < size, false); - nassertr(_attribs->get_key(si)->get_ref_count() >= 0, false); + nassertr(_attribs.get_key(si)->get_ref_count() >= 0, false); size_t snext = si; ++snext; while (snext < size) { - nassertr(_attribs->get_key(snext)->get_ref_count() >= 0, false); - const RenderAttrib *ssi = _attribs->get_key(si); - const RenderAttrib *ssnext = _attribs->get_key(snext); + nassertr(_attribs.get_key(snext)->get_ref_count() >= 0, false); + const RenderAttrib *ssi = _attribs.get_key(si); + const RenderAttrib *ssnext = _attribs.get_key(snext); int c = ssi->compare_to(*ssnext); int ci = ssnext->compare_to(*ssi); if ((ci < 0) != (c > 0) || @@ -356,19 +350,19 @@ return_unique(RenderAttrib *attrib) { LightReMutexHolder holder(*_attribs_lock); if (attrib->_saved_entry != -1) { - // This attrib is already in the cache. nassertr(_attribs->find(attrib) + // This attrib is already in the cache. nassertr(_attribs.find(attrib) // == attrib->_saved_entry, attrib); return attrib; } - int si = _attribs->find(attrib); + int si = _attribs.find(attrib); if (si != -1) { // There's an equivalent attrib already in the set. Return it. If this // is a newly created RenderAttrib, though, be sure to delete it. if (attrib->get_ref_count() == 0) { delete attrib; } - return _attribs->get_key(si); + return _attribs.get_key(si); } // Not already in the set; add it. @@ -378,7 +372,7 @@ return_unique(RenderAttrib *attrib) { // deleted while it's in it. attrib->ref(); } - si = _attribs->store(attrib, nullptr); + si = _attribs.store(attrib, nullptr); // Save the index and return the input attrib. attrib->_saved_entry = si; @@ -495,7 +489,7 @@ release_new() { if (_saved_entry != -1) { _saved_entry = -1; - nassertv_always(_attribs->remove(this)); + nassertv_always(_attribs.remove(this)); } } @@ -508,8 +502,6 @@ release_new() { */ void RenderAttrib:: init_attribs() { - _attribs = new Attribs; - // TODO: we should have a global Panda mutex to allow us to safely create // _attribs_lock without a startup race condition. For the meantime, this // is OK because we guarantee that this method is called at static init diff --git a/panda/src/pgraph/renderAttrib.h b/panda/src/pgraph/renderAttrib.h index 98b9de315c..15f090f43d 100644 --- a/panda/src/pgraph/renderAttrib.h +++ b/panda/src/pgraph/renderAttrib.h @@ -186,7 +186,7 @@ private: // This mutex protects _attribs. static LightReMutex *_attribs_lock; typedef SimpleHashMap > Attribs; - static Attribs *_attribs; + static Attribs _attribs; int _saved_entry; size_t _hash; diff --git a/panda/src/pgraph/renderAttribRegistry.cxx b/panda/src/pgraph/renderAttribRegistry.cxx index 7919d75799..d79c6484a5 100644 --- a/panda/src/pgraph/renderAttribRegistry.cxx +++ b/panda/src/pgraph/renderAttribRegistry.cxx @@ -91,9 +91,9 @@ register_slot(TypeHandle type_handle, int sort, RenderAttrib *default_attrib) { if (default_attrib->_saved_entry == -1) { // If this attribute was already registered, something odd is going on. - nassertr(RenderAttrib::_attribs->find(default_attrib) == -1, 0); + nassertr(RenderAttrib::_attribs.find(default_attrib) == -1, 0); default_attrib->_saved_entry = - RenderAttrib::_attribs->store(default_attrib, nullptr); + RenderAttrib::_attribs.store(default_attrib, nullptr); } // It effectively lives forever. Might as well make it official. diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index 5ce1527247..636e960a8c 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -39,7 +39,7 @@ using std::ostream; LightReMutex *RenderState::_states_lock = nullptr; -RenderState::States *RenderState::_states = nullptr; +RenderState::States RenderState::_states; const RenderState *RenderState::_empty_state = nullptr; UpdateSeq RenderState::_last_cycle_detect; size_t RenderState::_garbage_index = 0; @@ -68,7 +68,7 @@ RenderState() : _flags(0), _lock("RenderState") { - if (_states == nullptr) { + if (_states_lock == nullptr) { init_states(); } _saved_entry = -1; @@ -716,11 +716,8 @@ get_max_priority() { */ int RenderState:: get_num_states() { - if (_states == nullptr) { - return 0; - } LightReMutexHolder holder(*_states_lock); - return _states->get_num_entries(); + return _states.get_num_entries(); } /** @@ -738,9 +735,6 @@ get_num_states() { */ int RenderState:: get_num_unused_states() { - if (_states == nullptr) { - return 0; - } LightReMutexHolder holder(*_states_lock); // First, we need to count the number of times each RenderState object is @@ -748,9 +742,9 @@ get_num_unused_states() { typedef pmap StateCount; StateCount state_count; - size_t size = _states->get_num_entries(); + size_t size = _states.get_num_entries(); for (size_t si = 0; si < size; ++si) { - const RenderState *state = _states->get_key(si); + const RenderState *state = _states.get_key(si); size_t i; size_t cache_size = state->_composition_cache.get_num_entries(); @@ -821,13 +815,10 @@ get_num_unused_states() { */ int RenderState:: clear_cache() { - if (_states == nullptr) { - return 0; - } LightReMutexHolder holder(*_states_lock); PStatTimer timer(_cache_update_pcollector); - int orig_size = _states->get_num_entries(); + int orig_size = _states.get_num_entries(); // First, we need to copy the entire set of states to a temporary vector, // reference-counting each object. That way we can walk through the copy, @@ -838,9 +829,9 @@ clear_cache() { TempStates temp_states; temp_states.reserve(orig_size); - size_t size = _states->get_num_entries(); + size_t size = _states.get_num_entries(); for (size_t si = 0; si < size; ++si) { - const RenderState *state = _states->get_key(si); + const RenderState *state = _states.get_key(si); temp_states.push_back(state); } @@ -879,7 +870,7 @@ clear_cache() { // the various objects' caches will go away. } - int new_size = _states->get_num_entries(); + int new_size = _states.get_num_entries(); return orig_size - new_size; } @@ -895,14 +886,14 @@ int RenderState:: garbage_collect() { int num_attribs = RenderAttrib::garbage_collect(); - if (_states == nullptr || !garbage_collect_states) { + if (!garbage_collect_states) { return num_attribs; } LightReMutexHolder holder(*_states_lock); PStatTimer timer(_garbage_collect_pcollector); - size_t orig_size = _states->get_num_entries(); + size_t orig_size = _states.get_num_entries(); // How many elements to process this pass? size_t size = orig_size; @@ -922,7 +913,7 @@ garbage_collect() { size_t stop_at_element = (si + num_this_pass) % size; do { - RenderState *state = (RenderState *)_states->get_key(si); + RenderState *state = (RenderState *)_states.get_key(si); if (break_and_uniquify) { if (state->get_cache_ref_count() > 0 && state->get_ref_count() == state->get_cache_ref_count()) { @@ -959,15 +950,15 @@ garbage_collect() { } while (si != stop_at_element); _garbage_index = si; - nassertr(_states->get_num_entries() == size, 0); + nassertr(_states.get_num_entries() == size, 0); #ifdef _DEBUG - nassertr(_states->validate(), 0); + nassertr(_states.validate(), 0); #endif // If we just cleaned up a lot of states, see if we can reduce the table in // size. This will help reduce iteration overhead in the future. - _states->consider_shrink_table(); + _states.consider_shrink_table(); return (int)orig_size - (int)size + num_attribs; } @@ -980,9 +971,9 @@ void RenderState:: clear_munger_cache() { LightReMutexHolder holder(*_states_lock); - size_t size = _states->get_num_entries(); + size_t size = _states.get_num_entries(); for (size_t si = 0; si < size; ++si) { - RenderState *state = (RenderState *)(_states->get_key(si)); + RenderState *state = (RenderState *)(_states.get_key(si)); state->_mungers.clear(); state->_munged_states.clear(); state->_last_mi = -1; @@ -1004,18 +995,15 @@ clear_munger_cache() { */ void RenderState:: list_cycles(ostream &out) { - if (_states == nullptr) { - return; - } LightReMutexHolder holder(*_states_lock); typedef pset VisitedStates; VisitedStates visited; CompositionCycleDesc cycle_desc; - size_t size = _states->get_num_entries(); + size_t size = _states.get_num_entries(); for (size_t si = 0; si < size; ++si) { - const RenderState *state = _states->get_key(si); + const RenderState *state = _states.get_key(si); bool inserted = visited.insert(state).second; if (inserted) { @@ -1081,16 +1069,12 @@ list_cycles(ostream &out) { */ void RenderState:: list_states(ostream &out) { - if (_states == nullptr) { - out << "0 states:\n"; - return; - } LightReMutexHolder holder(*_states_lock); - size_t size = _states->get_num_entries(); + size_t size = _states.get_num_entries(); out << size << " states:\n"; for (size_t si = 0; si < size; ++si) { - const RenderState *state = _states->get_key(si); + const RenderState *state = _states.get_key(si); state->write(out, 2); } } @@ -1103,33 +1087,29 @@ list_states(ostream &out) { */ bool RenderState:: validate_states() { - if (_states == nullptr) { - return true; - } - PStatTimer timer(_state_validate_pcollector); LightReMutexHolder holder(*_states_lock); - if (_states->is_empty()) { + if (_states.is_empty()) { return true; } - if (!_states->validate()) { + if (!_states.validate()) { pgraph_cat.error() << "RenderState::_states cache is invalid!\n"; return false; } - size_t size = _states->get_num_entries(); + size_t size = _states.get_num_entries(); size_t si = 0; nassertr(si < size, false); - nassertr(_states->get_key(si)->get_ref_count() >= 0, false); + nassertr(_states.get_key(si)->get_ref_count() >= 0, false); size_t snext = si; ++snext; while (snext < size) { - nassertr(_states->get_key(snext)->get_ref_count() >= 0, false); - const RenderState *ssi = _states->get_key(si); - const RenderState *ssnext = _states->get_key(snext); + nassertr(_states.get_key(snext)->get_ref_count() >= 0, false); + const RenderState *ssi = _states.get_key(si); + const RenderState *ssnext = _states.get_key(snext); int c = ssi->compare_to(*ssnext); int ci = ssnext->compare_to(*ssi); if ((ci < 0) != (c > 0) || @@ -1299,7 +1279,7 @@ return_unique(RenderState *state) { LightReMutexHolder holder(*_states_lock); if (state->_saved_entry != -1) { - // This state is already in the cache. nassertr(_states->find(state) == + // This state is already in the cache. nassertr(_states.find(state) == // state->_saved_entry, pt_state); return state; } @@ -1318,7 +1298,7 @@ return_unique(RenderState *state) { } } - int si = _states->find(state); + int si = _states.find(state); if (si != -1) { // There's an equivalent state already in the set. Return it. The state // that was passed may be newly created and therefore may not be @@ -1326,7 +1306,7 @@ return_unique(RenderState *state) { if (state->get_ref_count() == 0) { delete state; } - return _states->get_key(si); + return _states.get_key(si); } // Not already in the set; add it. @@ -1336,7 +1316,7 @@ return_unique(RenderState *state) { // deleted while it's in it. state->cache_ref(); } - si = _states->store(state, nullptr); + si = _states.store(state, nullptr); // Save the index and return the input state. state->_saved_entry = si; @@ -1612,7 +1592,7 @@ release_new() { if (_saved_entry != -1) { _saved_entry = -1; - nassertv_always(_states->remove(this)); + nassertv_always(_states.remove(this)); } } @@ -1849,8 +1829,6 @@ update_pstats(int old_referenced_bits, int new_referenced_bits) { */ void RenderState:: init_states() { - _states = new States; - // TODO: we should have a global Panda mutex to allow us to safely create // _states_lock without a startup race condition. For the meantime, this is // OK because we guarantee that this method is called at static init time, @@ -1863,7 +1841,7 @@ init_states() { // is declared globally, and lives forever. RenderState *state = new RenderState; state->local_object(); - state->_saved_entry = _states->store(state, nullptr); + state->_saved_entry = _states.store(state, nullptr); _empty_state = state; } diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index c9e3719082..db4ebbd634 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -232,7 +232,7 @@ private: // _invert_composition_cache. static LightReMutex *_states_lock; typedef SimpleHashMap > States; - static States *_states; + static States _states; static const RenderState *_empty_state; // This iterator records the entry corresponding to this RenderState object diff --git a/panda/src/pgraph/renderState_ext.cxx b/panda/src/pgraph/renderState_ext.cxx index 2b7e3a4791..d0afb0718a 100644 --- a/panda/src/pgraph/renderState_ext.cxx +++ b/panda/src/pgraph/renderState_ext.cxx @@ -118,18 +118,15 @@ get_invert_composition_cache() const { PyObject *Extension:: get_states() { extern struct Dtool_PyTypedObject Dtool_RenderState; - if (RenderState::_states == nullptr) { - return PyList_New(0); - } LightReMutexHolder holder(*RenderState::_states_lock); - size_t num_states = RenderState::_states->get_num_entries(); + size_t num_states = RenderState::_states.get_num_entries(); PyObject *list = PyList_New(num_states); size_t i = 0; - size_t size = RenderState::_states->get_num_entries(); + size_t size = RenderState::_states.get_num_entries(); for (size_t si = 0; si < size; ++si) { - const RenderState *state = RenderState::_states->get_key(si); + const RenderState *state = RenderState::_states.get_key(si); state->ref(); PyObject *a = DTool_CreatePyInstanceTyped((void *)state, Dtool_RenderState, @@ -142,6 +139,4 @@ get_states() { return list; } - - #endif // HAVE_PYTHON diff --git a/panda/src/pgraph/sceneSetup.I b/panda/src/pgraph/sceneSetup.I index cf420ee132..a81271cd02 100644 --- a/panda/src/pgraph/sceneSetup.I +++ b/panda/src/pgraph/sceneSetup.I @@ -182,6 +182,23 @@ get_cull_bounds() const { return _lens->make_bounds(); } +/** + * Returns the camera's cull bounds transformed to world space, or null if + * view frustum culling is disabled. + */ +INLINE void SceneSetup:: +set_view_frustum(PT(GeometricBoundingVolume) view_frustum) { + _view_frustum = std::move(view_frustum); +} + +/** + * Returns the initial state as set by a previous call to set_initial_state(). + */ +INLINE GeometricBoundingVolume *SceneSetup:: +get_view_frustum() const { + return _view_frustum; +} + /** * Sets the initial state which is applied to all nodes in the scene, as if it * were set at the top of the scene graph. diff --git a/panda/src/pgraph/sceneSetup.h b/panda/src/pgraph/sceneSetup.h index d2ad0c0edb..b6622eaee7 100644 --- a/panda/src/pgraph/sceneSetup.h +++ b/panda/src/pgraph/sceneSetup.h @@ -22,6 +22,7 @@ #include "transformState.h" #include "lens.h" #include "pointerTo.h" +#include "geometricBoundingVolume.h" class DisplayRegion; @@ -59,6 +60,9 @@ PUBLISHED: INLINE const NodePath &get_cull_center() const; INLINE PT(BoundingVolume) get_cull_bounds() const; + INLINE void set_view_frustum(PT(GeometricBoundingVolume) view_frustum); + INLINE GeometricBoundingVolume *get_view_frustum() const; + INLINE void set_initial_state(const RenderState *initial_state); INLINE const RenderState *get_initial_state() const; @@ -88,6 +92,7 @@ private: CPT(TransformState) _world_transform; CPT(TransformState) _cs_transform; CPT(TransformState) _cs_world_transform; + PT(GeometricBoundingVolume) _view_frustum; public: static TypeHandle get_class_type() { diff --git a/panda/src/pgraph/scissorAttrib.cxx b/panda/src/pgraph/scissorAttrib.cxx index 4e137eec5b..f84bab3fc0 100644 --- a/panda/src/pgraph/scissorAttrib.cxx +++ b/panda/src/pgraph/scissorAttrib.cxx @@ -101,7 +101,7 @@ int ScissorAttrib:: compare_to_impl(const RenderAttrib *other) const { const ScissorAttrib *ta = (const ScissorAttrib *)other; - if (!_off && !ta->_off) { + if (_off && ta->_off) { return 0; } diff --git a/panda/src/pgraph/shaderAttrib.cxx b/panda/src/pgraph/shaderAttrib.cxx index e519289b28..934773ad49 100644 --- a/panda/src/pgraph/shaderAttrib.cxx +++ b/panda/src/pgraph/shaderAttrib.cxx @@ -224,6 +224,29 @@ set_shader_input(ShaderInput &&input) const { return return_new(result); } +/** + * Returns a new ShaderAttrib with the given shader inputs set. This is a + * more efficient way to set multiple shader inputs than calling + * set_shader_input multiple times. + */ +CPT(RenderAttrib) ShaderAttrib:: +set_shader_inputs(const pvector &inputs) const { + ShaderAttrib *result = new ShaderAttrib(*this); + + size_t num_inputs = inputs.size(); + for (size_t i = 0; i < num_inputs; i++) { + const ShaderInput &input = inputs[i]; + Inputs::iterator itr = result->_inputs.find(input.get_name()); + if (itr == result->_inputs.end()) { + result->_inputs.insert(Inputs::value_type(input.get_name(), input)); + } else { + itr->second = input; + } + } + + return return_new(result); +} + /** * Sets the geometry instance count. Do not confuse this with instanceTo, * which is used for animation instancing, and has nothing to do with this. A @@ -291,7 +314,7 @@ get_shader_input(const std::string &id) const { * Returns the ShaderInput as a nodepath. Assertion fails if there is none, * or if it is not a nodepath. */ -const NodePath &ShaderAttrib:: +NodePath ShaderAttrib:: get_shader_input_nodepath(const InternalName *id) const { static NodePath resfail; Inputs::const_iterator i = _inputs.find(id); @@ -399,6 +422,57 @@ get_shader_input_ptr(const InternalName *id) const { } } +/** + * Returns the ShaderInput as a ShaderPtrData struct. Assertion fails if + * there is none. or if it is not a PTA(double/float) + */ +bool ShaderAttrib:: +get_shader_input_ptr(const InternalName *id, Shader::ShaderPtrData &data) const { + Inputs::const_iterator i = _inputs.find(id); + if (i != _inputs.end()) { + const ShaderInput &p = (*i).second; + if (p.get_value_type() == ShaderInput::M_numeric || + p.get_value_type() == ShaderInput::M_vector) { + + data = p.get_ptr(); + return (data._ptr != nullptr); + } + if (p.get_value_type() == ShaderInput::M_param) { + // Temporary solution until the new param system + TypedWritableReferenceCount *param = p.get_value(); + if (param != nullptr) { + if (param->is_of_type(ParamVecBase4f::get_class_type())) { + data._ptr = (void *)((const ParamVecBase4f *)param)->get_value().get_data(); + data._size = 4; + data._type = Shader::SPT_float; + return true; + } + else if (param->is_of_type(ParamVecBase4i::get_class_type())) { + data._ptr = (void *)((const ParamVecBase4i *)param)->get_value().get_data(); + data._size = 4; + data._type = Shader::SPT_int; + return true; + } + else if (param->is_of_type(ParamVecBase4d::get_class_type())) { + data._ptr = (void *)((const ParamVecBase4d *)param)->get_value().get_data(); + data._size = 4; + data._type = Shader::SPT_float; + return true; + } + } + } + ostringstream strm; + strm << "Shader input " << id->get_name() << " was given an incompatible parameter type.\n"; + nassert_raise(strm.str()); + return false; + } else { + ostringstream strm; + strm << "Shader input " << id->get_name() << " is not present.\n"; + nassert_raise(strm.str()); + return false; + } +} + /** * Returns the ShaderInput as a texture. Assertion fails if there is none, or * if it is not a texture. diff --git a/panda/src/pgraph/shaderAttrib.h b/panda/src/pgraph/shaderAttrib.h index 6cd04c6d99..fb0cc6e996 100644 --- a/panda/src/pgraph/shaderAttrib.h +++ b/panda/src/pgraph/shaderAttrib.h @@ -91,6 +91,8 @@ public: INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, const LMatrix3 &v, int priority=0) const; INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, double n1=0, double n2=0, double n3=0, double n4=1, int priority=0) const; + + CPT(RenderAttrib) set_shader_inputs(const pvector &inputs) const; PUBLISHED: EXTENSION(CPT(RenderAttrib) set_shader_input(CPT_InternalName, PyObject *, int priority=0) const); @@ -113,10 +115,11 @@ PUBLISHED: const ShaderInput &get_shader_input(const InternalName *id) const; const ShaderInput &get_shader_input(const std::string &id) const; - const NodePath &get_shader_input_nodepath(const InternalName *id) const; + NodePath get_shader_input_nodepath(const InternalName *id) const; LVecBase4 get_shader_input_vector(InternalName *id) const; Texture *get_shader_input_texture(const InternalName *id, SamplerState *sampler=nullptr) const; const Shader::ShaderPtrData *get_shader_input_ptr(const InternalName *id) const; + bool get_shader_input_ptr(const InternalName *id, Shader::ShaderPtrData &data) const; const LMatrix4 &get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const; ShaderBuffer *get_shader_input_buffer(const InternalName *id) const; diff --git a/panda/src/pgraph/shaderInput.cxx b/panda/src/pgraph/shaderInput.cxx index b017045b22..ddb390f05d 100644 --- a/panda/src/pgraph/shaderInput.cxx +++ b/panda/src/pgraph/shaderInput.cxx @@ -89,7 +89,7 @@ add_hash(size_t hash) const { * Warning: no error checking is done. This *will* crash if get_value_type() * is not M_nodepath. */ -const NodePath &ShaderInput:: +NodePath ShaderInput:: get_nodepath() const { return DCAST(ParamNodePath, _value)->get_value(); } diff --git a/panda/src/pgraph/shaderInput.h b/panda/src/pgraph/shaderInput.h index 39ae9ba835..dbbf617ff0 100644 --- a/panda/src/pgraph/shaderInput.h +++ b/panda/src/pgraph/shaderInput.h @@ -119,7 +119,7 @@ PUBLISHED: INLINE const LVecBase4 &get_vector() const; INLINE const Shader::ShaderPtrData &get_ptr() const; - const NodePath &get_nodepath() const; + NodePath get_nodepath() const; Texture *get_texture() const; const SamplerState &get_sampler() const; diff --git a/panda/src/pgraph/stencilAttrib.h b/panda/src/pgraph/stencilAttrib.h index 6382bc3cd3..ffc205179c 100644 --- a/panda/src/pgraph/stencilAttrib.h +++ b/panda/src/pgraph/stencilAttrib.h @@ -88,7 +88,7 @@ PUBLISHED: StencilOperation front_stencil_pass_z_pass_operation, unsigned int reference, unsigned int read_mask, - unsigned int write_mask); + unsigned int write_mask=~(unsigned int)0); static CPT(RenderAttrib) make_2_sided( bool front_enable, diff --git a/panda/src/pgraph/texMatrixAttrib.cxx b/panda/src/pgraph/texMatrixAttrib.cxx index f32e0a8b97..4ea5d03e84 100644 --- a/panda/src/pgraph/texMatrixAttrib.cxx +++ b/panda/src/pgraph/texMatrixAttrib.cxx @@ -48,6 +48,8 @@ make() { /** * Constructs a TexMatrixAttrib that applies the indicated matrix to the * default texture stage. This interface is deprecated. + * + * @deprecated Use the constructor that takes a TextureStage instead. */ CPT(RenderAttrib) TexMatrixAttrib:: make(const LMatrix4 &mat) { diff --git a/panda/src/pgraph/textureAttrib.I b/panda/src/pgraph/textureAttrib.I index 9ee2f69f62..c3d40bafbe 100644 --- a/panda/src/pgraph/textureAttrib.I +++ b/panda/src/pgraph/textureAttrib.I @@ -29,12 +29,12 @@ TextureAttrib() { INLINE TextureAttrib:: TextureAttrib(const TextureAttrib ©) : _on_stages(copy._on_stages), - _render_stages(copy._render_stages), - _render_ff_stages(copy._render_ff_stages), + _render_stages(), + _render_ff_stages(), _next_implicit_sort(copy._next_implicit_sort), _off_stages(copy._off_stages), _off_all_stages(copy._off_all_stages), - _sort_seq(copy._sort_seq), + _sort_seq(UpdateSeq::old()), _filtered_seq(UpdateSeq::old()) { } diff --git a/panda/src/pgraph/textureAttrib.cxx b/panda/src/pgraph/textureAttrib.cxx index 7f9c6ff8b8..0184f1825c 100644 --- a/panda/src/pgraph/textureAttrib.cxx +++ b/panda/src/pgraph/textureAttrib.cxx @@ -116,10 +116,6 @@ add_on_stage(TextureStage *stage, Texture *tex, int override) const { (*si)._has_sampler = false; ++(attrib->_next_implicit_sort); - // We now need to re-sort the attrib list. - attrib->_sort_seq = UpdateSeq::old(); - attrib->_filtered_seq = UpdateSeq::old(); - return return_new(attrib); } @@ -140,10 +136,6 @@ add_on_stage(TextureStage *stage, Texture *tex, const SamplerState &sampler, int (*si)._has_sampler = true; ++(attrib->_next_implicit_sort); - // We now need to re-sort the attrib list. - attrib->_sort_seq = UpdateSeq::old(); - attrib->_filtered_seq = UpdateSeq::old(); - return return_new(attrib); } @@ -158,9 +150,6 @@ remove_on_stage(TextureStage *stage) const { Stages::iterator si = attrib->_on_stages.find(StageNode(stage)); if (si != attrib->_on_stages.end()) { attrib->_on_stages.erase(si); - - attrib->_sort_seq = UpdateSeq::old(); - attrib->_filtered_seq = UpdateSeq::old(); } return return_new(attrib); @@ -182,8 +171,6 @@ add_off_stage(TextureStage *stage, int override) const { Stages::iterator si = attrib->_on_stages.find(sn); if (si != attrib->_on_stages.end()) { attrib->_on_stages.erase(si); - attrib->_sort_seq = UpdateSeq::old(); - attrib->_filtered_seq = UpdateSeq::old(); } } return return_new(attrib); @@ -250,6 +237,34 @@ unify_texture_stages(TextureStage *stage) const { return return_new(attrib); } +/** + * Returns a new TextureAttrib, just like this one, but with all references to + * the given texture replaced with the new texture. + * + * @since 1.10.4 + */ +CPT(RenderAttrib) TextureAttrib:: +replace_texture(Texture *tex, Texture *new_tex) const { + TextureAttrib *attrib = nullptr; + + for (size_t i = 0; i < _on_stages.size(); ++i) { + const StageNode &sn = _on_stages[i]; + if (sn._texture == tex) { + if (attrib == nullptr) { + attrib = new TextureAttrib(*this); + } + + attrib->_on_stages[i]._texture = new_tex; + } + } + + if (attrib != nullptr) { + return return_new(attrib); + } else { + return this; + } +} + /** * Returns a new TextureAttrib, very much like this one, but with the number * of on_stages reduced to be no more than max_texture_stages. The number of diff --git a/panda/src/pgraph/textureAttrib.h b/panda/src/pgraph/textureAttrib.h index 908e4c50af..0858cc68a7 100644 --- a/panda/src/pgraph/textureAttrib.h +++ b/panda/src/pgraph/textureAttrib.h @@ -89,6 +89,7 @@ PUBLISHED: CPT(RenderAttrib) add_off_stage(TextureStage *stage, int override = 0) const; CPT(RenderAttrib) remove_off_stage(TextureStage *stage) const; CPT(RenderAttrib) unify_texture_stages(TextureStage *stage) const; + CPT(RenderAttrib) replace_texture(Texture *tex, Texture *new_tex) const; public: CPT(TextureAttrib) filter_to_max(int max_texture_stages) const; diff --git a/panda/src/pgraph/transformState.cxx b/panda/src/pgraph/transformState.cxx index fc8b3de606..63adeb375d 100644 --- a/panda/src/pgraph/transformState.cxx +++ b/panda/src/pgraph/transformState.cxx @@ -27,7 +27,7 @@ using std::ostream; LightReMutex *TransformState::_states_lock = nullptr; -TransformState::States *TransformState::_states = nullptr; +TransformState::States TransformState::_states; CPT(TransformState) TransformState::_identity_state; CPT(TransformState) TransformState::_invalid_state; UpdateSeq TransformState::_last_cycle_detect; @@ -57,7 +57,7 @@ TypeHandle TransformState::_type_handle; */ TransformState:: TransformState() : _lock("TransformState") { - if (_states == nullptr) { + if (_states_lock == nullptr) { init_states(); } _saved_entry = -1; @@ -988,11 +988,8 @@ write_composition_cache(ostream &out, int indent_level) const { */ int TransformState:: get_num_states() { - if (_states == nullptr) { - return 0; - } LightReMutexHolder holder(*_states_lock); - return _states->get_num_entries(); + return _states.get_num_entries(); } /** @@ -1010,9 +1007,6 @@ get_num_states() { */ int TransformState:: get_num_unused_states() { - if (_states == nullptr) { - return 0; - } LightReMutexHolder holder(*_states_lock); // First, we need to count the number of times each TransformState object is @@ -1021,9 +1015,9 @@ get_num_unused_states() { typedef pmap StateCount; StateCount state_count; - size_t size = _states->get_num_entries(); + size_t size = _states.get_num_entries(); for (size_t si = 0; si < size; ++si) { - const TransformState *state = _states->get_key(si); + const TransformState *state = _states.get_key(si); size_t i; size_t cache_size = state->_composition_cache.get_num_entries(); @@ -1095,13 +1089,10 @@ get_num_unused_states() { */ int TransformState:: clear_cache() { - if (_states == nullptr) { - return 0; - } LightReMutexHolder holder(*_states_lock); PStatTimer timer(_cache_update_pcollector); - int orig_size = _states->get_num_entries(); + int orig_size = _states.get_num_entries(); // First, we need to copy the entire set of states to a temporary vector, // reference-counting each object. That way we can walk through the copy, @@ -1112,9 +1103,9 @@ clear_cache() { TempStates temp_states; temp_states.reserve(orig_size); - size_t size = _states->get_num_entries(); + size_t size = _states.get_num_entries(); for (size_t si = 0; si < size; ++si) { - const TransformState *state = _states->get_key(si); + const TransformState *state = _states.get_key(si); temp_states.push_back(state); } @@ -1153,7 +1144,7 @@ clear_cache() { // the various objects' caches will go away. } - int new_size = _states->get_num_entries(); + int new_size = _states.get_num_entries(); return orig_size - new_size; } @@ -1165,14 +1156,14 @@ clear_cache() { */ int TransformState:: garbage_collect() { - if (_states == nullptr || !garbage_collect_states) { + if (!garbage_collect_states) { return 0; } LightReMutexHolder holder(*_states_lock); PStatTimer timer(_garbage_collect_pcollector); - size_t orig_size = _states->get_num_entries(); + size_t orig_size = _states.get_num_entries(); // How many elements to process this pass? size_t size = orig_size; @@ -1192,7 +1183,7 @@ garbage_collect() { size_t stop_at_element = (si + num_this_pass) % size; do { - TransformState *state = (TransformState *)_states->get_key(si); + TransformState *state = (TransformState *)_states.get_key(si); if (break_and_uniquify) { if (state->get_cache_ref_count() > 0 && state->get_ref_count() == state->get_cache_ref_count()) { @@ -1229,15 +1220,15 @@ garbage_collect() { } while (si != stop_at_element); _garbage_index = si; - nassertr(_states->get_num_entries() == size, 0); + nassertr(_states.get_num_entries() == size, 0); #ifdef _DEBUG - nassertr(_states->validate(), 0); + nassertr(_states.validate(), 0); #endif // If we just cleaned up a lot of states, see if we can reduce the table in // size. This will help reduce iteration overhead in the future. - _states->consider_shrink_table(); + _states.consider_shrink_table(); return (int)orig_size - (int)size; } @@ -1257,18 +1248,15 @@ garbage_collect() { */ void TransformState:: list_cycles(ostream &out) { - if (_states == nullptr) { - return; - } LightReMutexHolder holder(*_states_lock); typedef pset VisitedStates; VisitedStates visited; CompositionCycleDesc cycle_desc; - size_t size = _states->get_num_entries(); + size_t size = _states.get_num_entries(); for (size_t si = 0; si < size; ++si) { - const TransformState *state = _states->get_key(si); + const TransformState *state = _states.get_key(si); bool inserted = visited.insert(state).second; if (inserted) { @@ -1334,16 +1322,12 @@ list_cycles(ostream &out) { */ void TransformState:: list_states(ostream &out) { - if (_states == nullptr) { - out << "0 states:\n"; - return; - } LightReMutexHolder holder(*_states_lock); - size_t size = _states->get_num_entries(); + size_t size = _states.get_num_entries(); out << size << " states:\n"; for (size_t si = 0; si < size; ++si) { - const TransformState *state = _states->get_key(si); + const TransformState *state = _states.get_key(si); state->write(out, 2); } } @@ -1356,36 +1340,32 @@ list_states(ostream &out) { */ bool TransformState:: validate_states() { - if (_states == nullptr) { - return true; - } - PStatTimer timer(_transform_validate_pcollector); LightReMutexHolder holder(*_states_lock); - if (_states->is_empty()) { + if (_states.is_empty()) { return true; } - if (!_states->validate()) { + if (!_states.validate()) { pgraph_cat.error() << "TransformState::_states cache is invalid!\n"; return false; } - size_t size = _states->get_num_entries(); + size_t size = _states.get_num_entries(); size_t si = 0; nassertr(si < size, false); - nassertr(_states->get_key(si)->get_ref_count() >= 0, false); + nassertr(_states.get_key(si)->get_ref_count() >= 0, false); size_t snext = si; ++snext; while (snext < size) { - nassertr(_states->get_key(snext)->get_ref_count() >= 0, false); - const TransformState *ssi = _states->get_key(si); + nassertr(_states.get_key(snext)->get_ref_count() >= 0, false); + const TransformState *ssi = _states.get_key(si); if (!ssi->validate_composition_cache()) { return false; } - const TransformState *ssnext = _states->get_key(snext); + const TransformState *ssnext = _states.get_key(snext); bool c = (*ssi) == (*ssnext); bool ci = (*ssnext) == (*ssi); if (c != ci) { @@ -1415,8 +1395,6 @@ validate_states() { */ void TransformState:: init_states() { - _states = new States; - ConfigVariableBool uniquify_matrix ("uniquify-matrix", true, PRC_DESC("Set this true to look up arbitrary 4x4 transform matrices in " @@ -1483,7 +1461,7 @@ return_unique(TransformState *state) { LightReMutexHolder holder(*_states_lock); if (state->_saved_entry != -1) { - // This state is already in the cache. nassertr(_states->find(state) == + // This state is already in the cache. nassertr(_states.find(state) == // state->_saved_entry, state); return state; } @@ -1492,10 +1470,10 @@ return_unique(TransformState *state) { // of this function if no one else uses it. CPT(TransformState) pt_state = state; - int si = _states->find(state); + int si = _states.find(state); if (si != -1) { // There's an equivalent state already in the set. Return it. - return _states->get_key(si); + return _states.get_key(si); } // Not already in the set; add it. @@ -1505,7 +1483,7 @@ return_unique(TransformState *state) { // deleted while it's in it. state->cache_ref(); } - si = _states->store(state, nullptr); + si = _states.store(state, nullptr); // Save the index and return the input state. state->_saved_entry = si; @@ -1893,7 +1871,7 @@ release_new() { if (_saved_entry != -1) { _saved_entry = -1; - nassertv_always(_states->remove(this)); + nassertv_always(_states.remove(this)); } } diff --git a/panda/src/pgraph/transformState.h b/panda/src/pgraph/transformState.h index 75b4fedb0e..370517de9c 100644 --- a/panda/src/pgraph/transformState.h +++ b/panda/src/pgraph/transformState.h @@ -253,7 +253,7 @@ private: // _invert_composition_cache. static LightReMutex *_states_lock; typedef SimpleHashMap > States; - static States *_states; + static States _states; static CPT(TransformState) _identity_state; static CPT(TransformState) _invalid_state; diff --git a/panda/src/pgraph/transformState_ext.cxx b/panda/src/pgraph/transformState_ext.cxx index 08a47b92cd..10b629ddff 100644 --- a/panda/src/pgraph/transformState_ext.cxx +++ b/panda/src/pgraph/transformState_ext.cxx @@ -132,18 +132,15 @@ get_invert_composition_cache() const { PyObject *Extension:: get_states() { extern struct Dtool_PyTypedObject Dtool_TransformState; - if (TransformState::_states == nullptr) { - return PyList_New(0); - } LightReMutexHolder holder(*TransformState::_states_lock); - size_t num_states = TransformState::_states->get_num_entries(); + size_t num_states = TransformState::_states.get_num_entries(); PyObject *list = PyList_New(num_states); size_t i = 0; - size_t size = TransformState::_states->get_num_entries(); + size_t size = TransformState::_states.get_num_entries(); for (size_t si = 0; si < size; ++si) { - const TransformState *state = TransformState::_states->get_key(si); + const TransformState *state = TransformState::_states.get_key(si); state->ref(); PyObject *a = DTool_CreatePyInstanceTyped((void *)state, Dtool_TransformState, @@ -163,15 +160,12 @@ get_states() { PyObject *Extension:: get_unused_states() { extern struct Dtool_PyTypedObject Dtool_TransformState; - if (TransformState::_states == nullptr) { - return PyList_New(0); - } LightReMutexHolder holder(*TransformState::_states_lock); PyObject *list = PyList_New(0); - size_t size = TransformState::_states->get_num_entries(); + size_t size = TransformState::_states.get_num_entries(); for (size_t si = 0; si < size; ++si) { - const TransformState *state = TransformState::_states->get_key(si); + const TransformState *state = TransformState::_states.get_key(si); if (state->get_cache_ref_count() == state->get_ref_count()) { state->ref(); PyObject *a = diff --git a/panda/src/pgraphnodes/rectangleLight.h b/panda/src/pgraphnodes/rectangleLight.h index 9fafe875d6..770f7f7d0d 100644 --- a/panda/src/pgraphnodes/rectangleLight.h +++ b/panda/src/pgraphnodes/rectangleLight.h @@ -22,6 +22,8 @@ /** * This is a type of area light that is an axis aligned rectangle, pointing * along the Y axis in the positive direction. + * + * @since 1.10.0 */ class EXPCL_PANDA_PGRAPHNODES RectangleLight : public LightLensNode { PUBLISHED: diff --git a/panda/src/pgraphnodes/sceneGraphAnalyzer.cxx b/panda/src/pgraphnodes/sceneGraphAnalyzer.cxx index 915845af86..9a71530919 100644 --- a/panda/src/pgraphnodes/sceneGraphAnalyzer.cxx +++ b/panda/src/pgraphnodes/sceneGraphAnalyzer.cxx @@ -425,8 +425,12 @@ collect_statistics(const Geom *geom) { CPT(GeomPrimitive) prim = geom->get_primitive(i); int num_vertices = prim->get_num_vertices(); + int strip_cut_index = prim->get_strip_cut_index(); for (int vi = 0; vi < num_vertices; ++vi) { - tracker._referenced_vertices.set_bit(prim->get_vertex(vi)); + int index = prim->get_vertex(vi); + if (index != strip_cut_index) { + tracker._referenced_vertices.set_bit(index); + } } if (prim->is_indexed()) { diff --git a/panda/src/pgraphnodes/sequenceNode.h b/panda/src/pgraphnodes/sequenceNode.h index db511dd4ae..96b7698131 100644 --- a/panda/src/pgraphnodes/sequenceNode.h +++ b/panda/src/pgraphnodes/sequenceNode.h @@ -35,6 +35,8 @@ PUBLISHED: virtual int get_num_frames() const; INLINE void set_frame_rate(double frame_rate); + MAKE_PROPERTY(frame_rate, get_frame_rate, set_frame_rate); + public: virtual PandaNode *make_copy() const; virtual bool safe_to_combine() const; diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index 16a7483478..5065a9c054 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -573,6 +573,8 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) { * Call this if certain state has changed in such a way as to require a rerun * of the shader generator. This should be rare because in most cases, the * shader generator will automatically regenerate shaders as necessary. + * + * @since 1.10.0 */ void ShaderGenerator:: rehash_generated_shaders() { @@ -580,9 +582,9 @@ rehash_generated_shaders() { // With uniquify-states turned on, we can actually go through all the states // and check whether their generated shader is still OK. - size_t size = RenderState::_states->get_num_entries(); + size_t size = RenderState::_states.get_num_entries(); for (size_t si = 0; si < size; ++si) { - const RenderState *state = RenderState::_states->get_key(si); + const RenderState *state = RenderState::_states.get_key(si); if (state->_generated_shader != nullptr) { ShaderKey key; @@ -614,14 +616,16 @@ rehash_generated_shaders() { /** * Removes all previously generated shaders, requiring all shaders to be * regenerated. Does not clear cache of compiled shaders. + * + * @since 1.10.0 */ void ShaderGenerator:: clear_generated_shaders() { LightReMutexHolder holder(*RenderState::_states_lock); - size_t size = RenderState::_states->get_num_entries(); + size_t size = RenderState::_states.get_num_entries(); for (size_t si = 0; si < size; ++si) { - const RenderState *state = RenderState::_states->get_key(si); + const RenderState *state = RenderState::_states.get_key(si); state->_generated_shader.clear(); } @@ -745,6 +749,11 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { bool need_world_normal = false; bool need_eye_position = key._lighting; bool need_eye_normal = !key._lights.empty() || ((key._outputs & AuxBitplaneAttrib::ABO_aux_normal) != 0); + bool need_tangents = ((key._texture_flags & ShaderKey::TF_map_normal) != 0); + + // If we have binormal/tangent and eye position, we can pack eye normal in + // the w channels of the others. + bool pack_eye_normal = need_eye_normal && need_tangents && need_eye_position; bool have_specular = false; if (key._lighting) { @@ -825,7 +834,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { map_index_gloss = i; } } - if (key._texture_flags & ShaderKey::TF_map_normal) { + if (need_tangents) { tangent_freg = alloc_freg(); binormal_freg = alloc_freg(); text << "\t out float4 l_tangent : " << tangent_freg << ",\n"; @@ -850,13 +859,15 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { text << "\t uniform float4x4 trans_model_to_view,\n"; eye_position_freg = alloc_freg(); text << "\t out float4 l_eye_position : " << eye_position_freg << ",\n"; - } else if (key._texture_flags & ShaderKey::TF_map_normal) { + } else if (need_tangents) { text << "\t uniform float4x4 trans_model_to_view,\n"; } if (need_eye_normal) { - eye_normal_freg = alloc_freg(); text << "\t uniform float4x4 tpose_view_to_model,\n"; - text << "\t out float3 l_eye_normal : " << eye_normal_freg << ",\n"; + if (!pack_eye_normal) { + eye_normal_freg = alloc_freg(); + text << "\t out float3 l_eye_normal : " << eye_normal_freg << ",\n"; + } } if ((key._texture_flags & ShaderKey::TF_map_height) != 0 || need_world_normal || need_eye_normal) { text << "\t in float3 vtx_normal : " << normal_vreg << ",\n"; @@ -865,20 +876,26 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { text << "\t uniform float4 mspos_view,\n"; text << "\t out float3 l_eyevec,\n"; } - for (size_t i = 0; i < key._lights.size(); ++i) { - const ShaderKey::LightInfo &light = key._lights[i]; - if (light._flags & ShaderKey::LF_has_shadows) { - lightcoord_fregs.push_back(alloc_freg()); - text << "\t uniform float4x4 mat_shadow_" << i << ",\n"; - text << "\t out float4 l_lightcoord" << i << " : " << lightcoord_fregs[i] << ",\n"; - } else { - lightcoord_fregs.push_back(nullptr); - } - } if (key._fog_mode != 0) { hpos_freg = alloc_freg(); text << "\t out float4 l_hpos : " << hpos_freg << ",\n"; } + for (size_t i = 0; i < key._lights.size(); ++i) { + const ShaderKey::LightInfo &light = key._lights[i]; + if (light._flags & ShaderKey::LF_has_shadows) { + if (_ftregs_used >= 8) { + // We ran out of TEXCOORD registers. That means we have to do this + // calculation in the fragment shader, which is slower. + lightcoord_fregs.push_back(nullptr); + } else { + lightcoord_fregs.push_back(alloc_freg()); + text << "\t uniform float4x4 mat_shadow_" << i << ",\n"; + text << "\t out float4 l_lightcoord" << i << " : " << lightcoord_fregs[i] << ",\n"; + } + } else { + lightcoord_fregs.push_back(nullptr); + } + } if (key._anim_spec.get_animation_type() == GeomEnums::AT_hardware && key._anim_spec.get_num_transforms() > 0) { int num_transforms; @@ -942,9 +959,6 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { if (need_eye_position) { text << "\t l_eye_position = mul(trans_model_to_view, vtx_position);\n"; } - if (need_eye_normal) { - text << "\t l_eye_normal = normalize(mul((float3x3)tpose_view_to_model, vtx_normal));\n"; - } pmap::const_iterator it; for (it = texcoord_fregs.begin(); it != texcoord_fregs.end(); ++it) { // Pass through all texcoord inputs as-is. @@ -954,7 +968,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { if (need_color && key._color_type == ColorAttrib::T_vertex) { text << "\t l_color = vtx_color;\n"; } - if (key._texture_flags & ShaderKey::TF_map_normal) { + if (need_tangents) { text << "\t l_tangent.xyz = normalize(mul((float3x3)trans_model_to_view, vtx_" << tangent_input << ".xyz));\n"; text << "\t l_tangent.w = 0;\n"; text << "\t l_binormal.xyz = normalize(mul((float3x3)trans_model_to_view, -vtx_" << binormal_input << ".xyz));\n"; @@ -962,7 +976,9 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { } for (size_t i = 0; i < key._lights.size(); ++i) { if (key._lights[i]._flags & ShaderKey::LF_has_shadows) { - text << "\t l_lightcoord" << i << " = mul(mat_shadow_" << i << ", l_eye_position);\n"; + if (lightcoord_fregs[i] != nullptr) { + text << "\t l_lightcoord" << i << " = mul(mat_shadow_" << i << ", l_eye_position);\n"; + } } } if (key._texture_flags & ShaderKey::TF_map_height) { @@ -972,6 +988,17 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { text << "\t l_eyevec.z = dot(vtx_normal, eyedir);\n"; text << "\t l_eyevec = normalize(l_eyevec);\n"; } + if (need_eye_normal) { + if (pack_eye_normal) { + // We can pack the normal into the w channels of these unused varyings. + text << "\t float3 eye_normal = normalize(mul((float3x3)tpose_view_to_model, vtx_normal));\n"; + text << "\t l_tangent.w = eye_normal.x;\n"; + text << "\t l_binormal.w = eye_normal.y;\n"; + text << "\t l_eye_position.w = eye_normal.z;\n"; + } else { + text << "\t l_eye_normal = normalize(mul((float3x3)tpose_view_to_model, vtx_normal));\n"; + } + } text << "}\n\n"; // Fragment shader @@ -991,7 +1018,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { if (need_eye_position) { text << "\t in float4 l_eye_position : " << eye_position_freg << ",\n"; } - if (need_eye_normal) { + if (need_eye_normal && !pack_eye_normal) { text << "\t in float3 l_eye_normal : " << eye_normal_freg << ",\n"; } for (it = texcoord_fregs.begin(); it != texcoord_fregs.end(); ++it) { @@ -1016,9 +1043,9 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { text << "\t uniform float4 texcolor_" << i << ",\n"; } } - if (key._texture_flags & ShaderKey::TF_map_normal) { - text << "\t in float3 l_tangent : " << tangent_freg << ",\n"; - text << "\t in float3 l_binormal : " << binormal_freg << ",\n"; + if (need_tangents) { + text << "\t in float4 l_tangent : " << tangent_freg << ",\n"; + text << "\t in float4 l_binormal : " << binormal_freg << ",\n"; } for (size_t i = 0; i < key._lights.size(); ++i) { text << "\t uniform float4x4 attr_light" << i << ",\n"; @@ -1032,7 +1059,11 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { } else { text << "\t uniform sampler2D shadow_" << i << ",\n"; } - text << "\t in float4 l_lightcoord" << i << " : " << lightcoord_fregs[i] << ",\n"; + if (lightcoord_fregs[i] != nullptr) { + text << "\t in float4 l_lightcoord" << i << " : " << lightcoord_fregs[i] << ",\n"; + } else { + text << "\t uniform float4x4 mat_shadow_" << i << ",\n"; + } } if (light._flags & ShaderKey::LF_has_specular_color) { text << "\t uniform float4 attr_lspec" << i << ",\n"; @@ -1074,6 +1105,12 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { text << "\t discard;\n"; text << "\t }\n"; } + + // Reconstruct a packed normal vector. + if (need_eye_normal && pack_eye_normal) { + text << "\t float3 l_eye_normal = float3(l_tangent.w, l_binormal.w, l_eye_position.w);\n"; + } + text << "\t float4 result;\n"; if (key._outputs & (AuxBitplaneAttrib::ABO_aux_normal | AuxBitplaneAttrib::ABO_aux_glow)) { text << "\t o_aux = float4(0, 0, 0, 0);\n"; @@ -1098,7 +1135,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { text << "\t float4 texcoord" << i << " = l_world_normal;\n"; break; case TexGenAttrib::M_eye_position: - text << "\t float4 texcoord" << i << " = l_eye_position;\n"; + text << "\t float4 texcoord" << i << " = float4(l_eye_position.xyz, 1.0f);\n"; break; case TexGenAttrib::M_eye_normal: text << "\t float4 texcoord" << i << " = float4(l_eye_normal, 1.0f);\n"; @@ -1195,7 +1232,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { text << "\t // Correct the surface normal for interpolation effects\n"; text << "\t l_eye_normal = normalize(l_eye_normal);\n"; } - if (key._texture_flags & ShaderKey::TF_map_normal) { + if (need_tangents) { text << "\t // Translate tangent-space normal in map to view-space.\n"; // Use Reoriented Normal Mapping to blend additional normal maps. @@ -1214,8 +1251,8 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { } } text << "\t l_eye_normal *= tsnormal.z;\n"; - text << "\t l_eye_normal += normalize(l_tangent) * tsnormal.x;\n"; - text << "\t l_eye_normal += normalize(l_binormal) * tsnormal.y;\n"; + text << "\t l_eye_normal += normalize(l_tangent.xyz) * tsnormal.x;\n"; + text << "\t l_eye_normal += normalize(l_binormal.xyz) * tsnormal.y;\n"; text << "\t l_eye_normal = normalize(l_eye_normal);\n"; } if (key._outputs & AuxBitplaneAttrib::ABO_aux_normal) { @@ -1247,6 +1284,15 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { } for (size_t i = 0; i < key._lights.size(); ++i) { const ShaderKey::LightInfo &light = key._lights[i]; + + if (light._flags & ShaderKey::LF_has_shadows) { + if (lightcoord_fregs[i] == nullptr) { + // We have to do this one in the fragment shader if we ran out of + // varyings. + text << "\t float4 l_lightcoord" << i << " = mul(mat_shadow_" << i << ", float4(l_eye_position.xyz, 1.0f));\n"; + } + } + if (light._type.is_derived_from(DirectionalLight::get_class_type())) { text << "\t // Directional Light " << i << "\n"; text << "\t lcolor = attr_light" << i << "[0];\n"; @@ -1638,6 +1684,11 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { } text << "}\n"; + if (pgraphnodes_cat.is_spam()) { + pgraphnodes_cat.spam() << "Generated shader:\n" + << text.str() << "\n"; + } + // Insert the shader into the shader attrib. PT(Shader) shader = Shader::make(text.str(), Shader::SL_Cg); nassertr(shader != nullptr, nullptr); diff --git a/panda/src/pgraphnodes/sphereLight.h b/panda/src/pgraphnodes/sphereLight.h index d4b1fb1c32..3717764b77 100644 --- a/panda/src/pgraphnodes/sphereLight.h +++ b/panda/src/pgraphnodes/sphereLight.h @@ -22,6 +22,8 @@ /** * A sphere light is like a point light, except that it represents a sphere * with a radius, rather than being an infinitely thin point in space. + * + * @since 1.10.0 */ class EXPCL_PANDA_PGRAPHNODES SphereLight : public PointLight { PUBLISHED: diff --git a/panda/src/pgui/pgButton.cxx b/panda/src/pgui/pgButton.cxx index d1eba186c0..09a91120d4 100644 --- a/panda/src/pgui/pgButton.cxx +++ b/panda/src/pgui/pgButton.cxx @@ -115,7 +115,11 @@ release(const MouseWatcherParameter ¶m, bool background) { if (has_click_button(param.get_button())) { _button_down = false; if (get_active()) { - if (param.is_outside()) { + // Note that a "click" may come from a keyboard button press. In that + // case, instead of checking that the mouse cursor is still over the + // button, we check whether the item has keyboard focus. + if (param.is_outside() && + (MouseButton::is_mouse_button(param.get_button()) || !get_focus())) { set_state(S_ready); } else { set_state(S_rollover); diff --git a/panda/src/physx/physxDebugGeomNode.cxx b/panda/src/physx/physxDebugGeomNode.cxx index 8b3bd2a1ca..a2684b0ea6 100644 --- a/panda/src/physx/physxDebugGeomNode.cxx +++ b/panda/src/physx/physxDebugGeomNode.cxx @@ -100,7 +100,9 @@ update(NxScene *scenePtr) { _prim_lines->close_primitive(); _prim_triangles->close_primitive(); - physx_cat.spam() << "Updated PhysxDebugGeomNode geometry\n"; + if (physx_cat.is_spam()) { + physx_cat.spam() << "Updated PhysxDebugGeomNode geometry\n"; + } } /** diff --git a/panda/src/pipeline/conditionVar.h b/panda/src/pipeline/conditionVar.h index fe90e22063..47ad3c973d 100644 --- a/panda/src/pipeline/conditionVar.h +++ b/panda/src/pipeline/conditionVar.h @@ -24,11 +24,6 @@ * condition variable can be used to "wake up" a thread when some arbitrary * condition has changed. * - * The ConditionVar class does not support the full semantics of POSIX - * condition variables. In particular, it does not support the broadcast or - * notify_all function. See ConditionVarFull for a more complete (but - * possibly more expensive) API. - * * A condition variable is associated with a single mutex, and several * condition variables may share the same mutex. * @@ -51,10 +46,7 @@ PUBLISHED: // These methods are inherited from the base class. //INLINE void wait(); //INLINE void notify(); - - // The notify_all() method is specifically *not* provided by ConditionVar. - // Use ConditionVarFull if you need to call this method. - void notify_all() = delete; + //INLINE void notify_all(); INLINE Mutex &get_mutex() const; }; diff --git a/panda/src/pipeline/conditionVarDebug.cxx b/panda/src/pipeline/conditionVarDebug.cxx index bbf9ce0d22..c38bdc6d52 100644 --- a/panda/src/pipeline/conditionVarDebug.cxx +++ b/panda/src/pipeline/conditionVarDebug.cxx @@ -78,8 +78,7 @@ wait() { << *current_thread << " waiting on " << *this << "\n"; } - nassertd(current_thread->_waiting_on_cvar == nullptr && - current_thread->_waiting_on_cvar_full == nullptr) { + nassertd(current_thread->_waiting_on_cvar == nullptr) { } current_thread->_waiting_on_cvar = this; @@ -128,8 +127,7 @@ wait(double timeout) { << ", with timeout " << timeout << "\n"; } - nassertd(current_thread->_waiting_on_cvar == nullptr && - current_thread->_waiting_on_cvar_full == nullptr) { + nassertd(current_thread->_waiting_on_cvar == nullptr) { } current_thread->_waiting_on_cvar = this; @@ -156,18 +154,15 @@ wait(double timeout) { * predict which one. It is possible that more than one thread will be woken * up. * - * The caller must be holding the mutex associated with the condition variable - * before making this call, which will not release the mutex. - * * If no threads are waiting, this is a no-op: the notify event is lost. */ void ConditionVarDebug:: notify() { _mutex._global_lock->lock(); - Thread *current_thread = Thread::get_current_thread(); - + /* if (!_mutex.do_debug_is_locked()) { + Thread *current_thread = Thread::get_current_thread(); ostringstream ostr; ostr << *current_thread << " attempted to notify " << *this << " without holding " << _mutex; @@ -175,8 +170,10 @@ notify() { _mutex._global_lock->unlock(); return; } + */ if (thread_cat->is_spam()) { + Thread *current_thread = Thread::get_current_thread(); thread_cat.spam() << *current_thread << " notifying " << *this << "\n"; } @@ -185,6 +182,38 @@ notify() { _mutex._global_lock->unlock(); } +/** + * Informs all of the other threads who are currently blocked on wait() that + * the relevant condition has changed. + * + * If no threads are waiting, this is a no-op: the notify event is lost. + */ +void ConditionVarDebug:: +notify_all() { + _mutex._global_lock->lock(); + + /* + if (!_mutex.do_debug_is_locked()) { + Thread *current_thread = Thread::get_current_thread(); + ostringstream ostr; + ostr << *current_thread << " attempted to notify " + << *this << " without holding " << _mutex; + nassert_raise(ostr.str()); + _mutex._global_lock->unlock(); + return; + } + */ + + if (thread_cat->is_spam()) { + Thread *current_thread = Thread::get_current_thread(); + thread_cat.spam() + << *current_thread << " notifying all " << *this << "\n"; + } + + _impl.notify_all(); + _mutex._global_lock->unlock(); +} + /** * This method is declared virtual in ConditionVarDebug, but non-virtual in * ConditionVarDirect. diff --git a/panda/src/pipeline/conditionVarDebug.h b/panda/src/pipeline/conditionVarDebug.h index 85da3fde2e..d7aa8e7ecb 100644 --- a/panda/src/pipeline/conditionVarDebug.h +++ b/panda/src/pipeline/conditionVarDebug.h @@ -43,6 +43,7 @@ PUBLISHED: BLOCKING void wait(); BLOCKING void wait(double timeout); void notify(); + void notify_all(); virtual void output(std::ostream &out) const; private: diff --git a/panda/src/pipeline/conditionVarDirect.I b/panda/src/pipeline/conditionVarDirect.I index bf8e42aea5..3a5074fd65 100644 --- a/panda/src/pipeline/conditionVarDirect.I +++ b/panda/src/pipeline/conditionVarDirect.I @@ -76,9 +76,6 @@ wait(double timeout) { * predict which one. It is possible that more than one thread will be woken * up. * - * The caller must be holding the mutex associated with the condition variable - * before making this call, which will not release the mutex. - * * If no threads are waiting, this is a no-op: the notify event is lost. */ INLINE void ConditionVarDirect:: @@ -86,3 +83,15 @@ notify() { TAU_PROFILE("ConditionVarDirect::notify()", " ", TAU_USER); _impl.notify(); } + +/** + * Informs all of the other threads who are currently blocked on wait() that + * the relevant condition has changed. + * + * If no threads are waiting, this is a no-op: the notify event is lost. + */ +INLINE void ConditionVarDirect:: +notify_all() { + TAU_PROFILE("ConditionVarDirect::notify()", " ", TAU_USER); + _impl.notify_all(); +} diff --git a/panda/src/pipeline/conditionVarDirect.h b/panda/src/pipeline/conditionVarDirect.h index 2a8c9197cb..17ecca68d9 100644 --- a/panda/src/pipeline/conditionVarDirect.h +++ b/panda/src/pipeline/conditionVarDirect.h @@ -43,6 +43,7 @@ PUBLISHED: BLOCKING INLINE void wait(); BLOCKING INLINE void wait(double timeout); INLINE void notify(); + INLINE void notify_all(); void output(std::ostream &out) const; private: diff --git a/panda/src/pipeline/conditionVarFull.I b/panda/src/pipeline/conditionVarFull.I deleted file mode 100644 index b796e7aed2..0000000000 --- a/panda/src/pipeline/conditionVarFull.I +++ /dev/null @@ -1,40 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file conditionVarFull.I - * @author drose - * @date 2006-08-28 - */ - -/** - * You must pass in a Mutex to the condition variable constructor. This mutex - * may be shared by other condition variables, if desired. It is the caller's - * responsibility to ensure the Mutex object does not destruct during the - * lifetime of the condition variable. - */ -INLINE ConditionVarFull:: -ConditionVarFull(Mutex &mutex) : -#ifdef DEBUG_THREADS - ConditionVarFullDebug(mutex) -#else - ConditionVarFullDirect(mutex) -#endif // DEBUG_THREADS -{ -} - -/** - * Returns the mutex associated with this condition variable. - */ -INLINE Mutex &ConditionVarFull:: -get_mutex() const { -#ifdef DEBUG_THREADS - return (Mutex &)ConditionVarFullDebug::get_mutex(); -#else - return (Mutex &)ConditionVarFullDirect::get_mutex(); -#endif // DEBUG_THREADS -} diff --git a/panda/src/pipeline/conditionVarFull.cxx b/panda/src/pipeline/conditionVarFull.cxx deleted file mode 100644 index b5a32d477c..0000000000 --- a/panda/src/pipeline/conditionVarFull.cxx +++ /dev/null @@ -1,14 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file conditionVarFull.cxx - * @author drose - * @date 2006-08-28 - */ - -#include "conditionVarFull.h" diff --git a/panda/src/pipeline/conditionVarFull.h b/panda/src/pipeline/conditionVarFull.h index 37a9af1e11..f7effac224 100644 --- a/panda/src/pipeline/conditionVarFull.h +++ b/panda/src/pipeline/conditionVarFull.h @@ -14,46 +14,8 @@ #ifndef CONDITIONVARFULL_H #define CONDITIONVARFULL_H -#include "pandabase.h" -#include "conditionVarFullDebug.h" -#include "conditionVarFullDirect.h" +#include "conditionVar.h" -/** - * This class implements a condition variable; see ConditionVar for a brief - * introduction to this class. The ConditionVarFull class provides a more - * complete implementation than ConditionVar; in particular, it provides the - * notify_all() method, which is guaranteed to wake up all threads currently - * waiting on the condition (whereas notify() is guaranteed to wake up at - * least one thread, but may or may not wake up all of them). - * - * This class exists because on certain platforms (e.g. Win32), implementing - * notify_all() requires more overhead, so you should use ConditionVar for - * cases when you do not require the notify_all() semantics. - * - * There are still some minor semantics that POSIX condition variables provide - * which this implementation does not. For instance, it is required (not - * optional) that the caller of notify() or notify_all() is holding the - * condition variable's mutex before the call. - * - * This class inherits its implementation either from ConditionVarFullDebug or - * ConditionVarFullDirect, depending on the definition of DEBUG_THREADS. - */ -#ifdef DEBUG_THREADS -class EXPCL_PANDA_PIPELINE ConditionVarFull : public ConditionVarFullDebug -#else -class EXPCL_PANDA_PIPELINE ConditionVarFull : public ConditionVarFullDirect -#endif // DEBUG_THREADS -{ -PUBLISHED: - INLINE explicit ConditionVarFull(Mutex &mutex); - ConditionVarFull(const ConditionVarFull ©) = delete; - ~ConditionVarFull() = default; - - ConditionVarFull &operator = (const ConditionVarFull ©) = delete; - - INLINE Mutex &get_mutex() const; -}; - -#include "conditionVarFull.I" +typedef ConditionVar ConditionVarFull; #endif diff --git a/panda/src/pipeline/conditionVarFullDebug.I b/panda/src/pipeline/conditionVarFullDebug.I deleted file mode 100644 index 5a09f03a3d..0000000000 --- a/panda/src/pipeline/conditionVarFullDebug.I +++ /dev/null @@ -1,20 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file conditionVarFullDebug.I - * @author drose - * @date 2006-08-28 - */ - -/** - * Returns the mutex associated with this condition variable. - */ -INLINE MutexDebug &ConditionVarFullDebug:: -get_mutex() const { - return _mutex; -} diff --git a/panda/src/pipeline/conditionVarFullDebug.cxx b/panda/src/pipeline/conditionVarFullDebug.cxx deleted file mode 100644 index 746991e79c..0000000000 --- a/panda/src/pipeline/conditionVarFullDebug.cxx +++ /dev/null @@ -1,230 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file conditionVarFullDebug.cxx - * @author drose - * @date 2006-08-28 - */ - -#include "conditionVarFullDebug.h" -#include "thread.h" -#include "config_pipeline.h" - -#ifdef DEBUG_THREADS - -using std::ostream; -using std::ostringstream; - -/** - * You must pass in a Mutex to the condition variable constructor. This mutex - * may be shared by other condition variables, if desired. It is the caller's - * responsibility to ensure the Mutex object does not destruct during the - * lifetime of the condition variable. - */ -ConditionVarFullDebug:: -ConditionVarFullDebug(MutexDebug &mutex) : - _mutex(mutex), - _impl(*mutex.get_global_lock()) -{ - nassertv(!_mutex._allow_recursion); -} - -/** - * - */ -ConditionVarFullDebug:: -~ConditionVarFullDebug() { -} - -/** - * Waits on the condition. The caller must already be holding the lock - * associated with the condition variable before calling this function. - * - * wait() will release the lock, then go to sleep until some other thread - * calls notify() on this condition variable. At that time at least one - * thread waiting on the same ConditionVarFullDebug will grab the lock again, - * and then return from wait(). - * - * It is possible that wait() will return even if no one has called notify(). - * It is the responsibility of the calling process to verify the condition on - * return from wait, and possibly loop back to wait again if necessary. - * - * Note the semantics of a condition variable: the mutex must be held before - * wait() is called, and it will still be held when wait() returns. However, - * it will be temporarily released during the wait() call itself. - */ -void ConditionVarFullDebug:: -wait() { - _mutex._global_lock->lock(); - - Thread *current_thread = Thread::get_current_thread(); - - if (!_mutex.do_debug_is_locked()) { - ostringstream ostr; - ostr << *current_thread << " attempted to wait on " - << *this << " without holding " << _mutex; - nassert_raise(ostr.str()); - _mutex._global_lock->unlock(); - return; - } - - if (thread_cat->is_spam()) { - thread_cat.spam() - << *current_thread << " waiting on " << *this << "\n"; - } - - nassertd(current_thread->_waiting_on_cvar == nullptr && - current_thread->_waiting_on_cvar_full == nullptr) { - } - current_thread->_waiting_on_cvar_full = this; - - _mutex.do_unlock(); - _impl.wait(); // temporarily releases _global_lock - _mutex.do_lock(current_thread); - - nassertd(current_thread->_waiting_on_cvar_full == this) { - } - current_thread->_waiting_on_cvar_full = nullptr; - - if (thread_cat.is_spam()) { - thread_cat.spam() - << *current_thread << " awake on " << *this << "\n"; - } - - _mutex._global_lock->unlock(); -} - -/** - * Waits on the condition, with a timeout. The function will return when the - * condition variable is notified, or the timeout occurs. There is no way to - * directly tell which happened, and it is possible that neither in fact - * happened (spurious wakeups are possible). - * - * See wait() with no parameters for more. - */ -void ConditionVarFullDebug:: -wait(double timeout) { - _mutex._global_lock->lock(); - - Thread *current_thread = Thread::get_current_thread(); - - if (!_mutex.do_debug_is_locked()) { - ostringstream ostr; - ostr << *current_thread << " attempted to wait on " - << *this << " without holding " << _mutex; - nassert_raise(ostr.str()); - _mutex._global_lock->unlock(); - return; - } - - if (thread_cat.is_spam()) { - thread_cat.spam() - << *current_thread << " waiting on " << *this - << ", with timeout " << timeout << "\n"; - } - - nassertd(current_thread->_waiting_on_cvar == nullptr && - current_thread->_waiting_on_cvar_full == nullptr) { - } - current_thread->_waiting_on_cvar_full = this; - - _mutex.do_unlock(); - _impl.wait(timeout); // temporarily releases _global_lock - _mutex.do_lock(current_thread); - - nassertd(current_thread->_waiting_on_cvar_full == this) { - } - current_thread->_waiting_on_cvar_full = nullptr; - - if (thread_cat.is_spam()) { - thread_cat.spam() - << *current_thread << " awake on " << *this << "\n"; - } - - _mutex._global_lock->unlock(); -} - -/** - * Informs one of the other threads who are currently blocked on wait() that - * the relevant condition has changed. If multiple threads are currently - * waiting, at least one of them will be woken up, although there is no way to - * predict which one. It is possible that more than one thread will be woken - * up. - * - * The caller must be holding the mutex associated with the condition variable - * before making this call, which will not release the mutex. - * - * If no threads are waiting, this is a no-op: the notify event is lost. - */ -void ConditionVarFullDebug:: -notify() { - _mutex._global_lock->lock(); - - Thread *current_thread = Thread::get_current_thread(); - - if (!_mutex.do_debug_is_locked()) { - ostringstream ostr; - ostr << *current_thread << " attempted to notify " - << *this << " without holding " << _mutex; - nassert_raise(ostr.str()); - _mutex._global_lock->unlock(); - return; - } - - if (thread_cat->is_spam()) { - thread_cat.spam() - << *current_thread << " notifying " << *this << "\n"; - } - - _impl.notify(); - _mutex._global_lock->unlock(); -} - -/** - * Informs all of the other threads who are currently blocked on wait() that - * the relevant condition has changed. - * - * The caller must be holding the mutex associated with the condition variable - * before making this call, which will not release the mutex. - * - * If no threads are waiting, this is a no-op: the notify event is lost. - */ -void ConditionVarFullDebug:: -notify_all() { - _mutex._global_lock->lock(); - - Thread *current_thread = Thread::get_current_thread(); - - if (!_mutex.do_debug_is_locked()) { - ostringstream ostr; - ostr << *current_thread << " attempted to notify " - << *this << " without holding " << _mutex; - nassert_raise(ostr.str()); - _mutex._global_lock->unlock(); - return; - } - - if (thread_cat->is_spam()) { - thread_cat.spam() - << *current_thread << " notifying all " << *this << "\n"; - } - - _impl.notify_all(); - _mutex._global_lock->unlock(); -} - -/** - * This method is declared virtual in ConditionVarFullDebug, but non-virtual - * in ConditionVarFullDirect. - */ -void ConditionVarFullDebug:: -output(ostream &out) const { - out << "ConditionVarFull " << (void *)this << " on " << _mutex; -} - -#endif // DEBUG_THREADS diff --git a/panda/src/pipeline/conditionVarFullDebug.h b/panda/src/pipeline/conditionVarFullDebug.h deleted file mode 100644 index cefb27cad7..0000000000 --- a/panda/src/pipeline/conditionVarFullDebug.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file conditionVarFullDebug.h - * @author drose - * @date 2006-08-28 - */ - -#ifndef CONDITIONVARFULLDEBUG_H -#define CONDITIONVARFULLDEBUG_H - -#include "pandabase.h" -#include "pmutex.h" -#include "conditionVarImpl.h" - -#ifdef DEBUG_THREADS - -/** - * A condition variable, usually used to communicate information about - * changing state to a thread that is waiting for something to happen. A - * condition variable can be used to "wake up" a thread when some arbitrary - * condition has changed. - * - * A condition variable is associated with a single mutex, and several - * condition variables may share the same mutex. - */ -class EXPCL_PANDA_PIPELINE ConditionVarFullDebug { -public: - explicit ConditionVarFullDebug(MutexDebug &mutex); - ConditionVarFullDebug(const ConditionVarFullDebug ©) = delete; - virtual ~ConditionVarFullDebug(); - - ConditionVarFullDebug &operator = (const ConditionVarFullDebug ©) = delete; - -PUBLISHED: - INLINE MutexDebug &get_mutex() const; - - BLOCKING void wait(); - BLOCKING void wait(double timeout); - void notify(); - void notify_all(); - virtual void output(std::ostream &out) const; - -private: - MutexDebug &_mutex; - ConditionVarFullImpl _impl; -}; - -INLINE std::ostream & -operator << (std::ostream &out, const ConditionVarFullDebug &cv) { - cv.output(out); - return out; -} - -#include "conditionVarFullDebug.I" - -#endif // DEBUG_THREADS - -#endif diff --git a/panda/src/pipeline/conditionVarFullDirect.I b/panda/src/pipeline/conditionVarFullDirect.I deleted file mode 100644 index 207cb01538..0000000000 --- a/panda/src/pipeline/conditionVarFullDirect.I +++ /dev/null @@ -1,103 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file conditionVarFullDirect.I - * @author drose - * @date 2006-08-28 - */ - -/** - * You must pass in a Mutex to the condition variable constructor. This mutex - * may be shared by other condition variables, if desired. It is the caller's - * responsibility to ensure the Mutex object does not destruct during the - * lifetime of the condition variable. - */ -INLINE ConditionVarFullDirect:: -ConditionVarFullDirect(MutexDirect &mutex) : - _mutex(mutex), - _impl(mutex._impl) -{ -} - -/** - * Returns the mutex associated with this condition variable. - */ -INLINE MutexDirect &ConditionVarFullDirect:: -get_mutex() const { - return _mutex; -} - -/** - * Waits on the condition. The caller must already be holding the lock - * associated with the condition variable before calling this function. - * - * wait() will release the lock, then go to sleep until some other thread - * calls notify() on this condition variable. At that time at least one - * thread waiting on the same ConditionVarFullDirect will grab the lock again, - * and then return from wait(). - * - * It is possible that wait() will return even if no one has called notify(). - * It is the responsibility of the calling process to verify the condition on - * return from wait, and possibly loop back to wait again if necessary. - * - * Note the semantics of a condition variable: the mutex must be held before - * wait() is called, and it will still be held when wait() returns. However, - * it will be temporarily released during the wait() call itself. - */ -INLINE void ConditionVarFullDirect:: -wait() { - TAU_PROFILE("ConditionVarFullDirect::wait()", " ", TAU_USER); - _impl.wait(); -} - -/** - * Waits on the condition, with a timeout. The function will return when the - * condition variable is notified, or the timeout occurs. There is no way to - * directly tell which happened, and it is possible that neither in fact - * happened (spurious wakeups are possible). - * - * See wait() with no parameters for more. - */ -void ConditionVarFullDirect:: -wait(double timeout) { - TAU_PROFILE("ConditionVarFullDirect::wait(double)", " ", TAU_USER); - _impl.wait(timeout); -} - -/** - * Informs one of the other threads who are currently blocked on wait() that - * the relevant condition has changed. If multiple threads are currently - * waiting, at least one of them will be woken up, although there is no way to - * predict which one. It is possible that more than one thread will be woken - * up. - * - * The caller must be holding the mutex associated with the condition variable - * before making this call, which will not release the mutex. - * - * If no threads are waiting, this is a no-op: the notify is lost. - */ -INLINE void ConditionVarFullDirect:: -notify() { - TAU_PROFILE("ConditionVarFullDirect::notify()", " ", TAU_USER); - _impl.notify(); -} - -/** - * Informs all of the other threads who are currently blocked on wait() that - * the relevant condition has changed. - * - * The caller must be holding the mutex associated with the condition variable - * before making this call, which will not release the mutex. - * - * If no threads are waiting, this is a no-op: the notify event is lost. - */ -INLINE void ConditionVarFullDirect:: -notify_all() { - TAU_PROFILE("ConditionVarFullDirect::notify()", " ", TAU_USER); - _impl.notify_all(); -} diff --git a/panda/src/pipeline/conditionVarFullDirect.cxx b/panda/src/pipeline/conditionVarFullDirect.cxx deleted file mode 100644 index 04f9c647ed..0000000000 --- a/panda/src/pipeline/conditionVarFullDirect.cxx +++ /dev/null @@ -1,27 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file conditionVarFullDirect.cxx - * @author drose - * @date 2006-08-28 - */ - -#include "conditionVarFullDirect.h" - -#ifndef DEBUG_THREADS - -/** - * This method is declared virtual in ConditionVarFullDebug, but non-virtual - * in ConditionVarFullDirect. - */ -void ConditionVarFullDirect:: -output(std::ostream &out) const { - out << "ConditionVarFull " << (void *)this << " on " << _mutex; -} - -#endif // !DEBUG_THREADS diff --git a/panda/src/pipeline/conditionVarFullDirect.h b/panda/src/pipeline/conditionVarFullDirect.h deleted file mode 100644 index 729185501b..0000000000 --- a/panda/src/pipeline/conditionVarFullDirect.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file conditionVarFullDirect.h - * @author drose - * @date 2006-08-28 - */ - -#ifndef CONDITIONVARFULLDIRECT_H -#define CONDITIONVARFULLDIRECT_H - -#include "pandabase.h" -#include "mutexDirect.h" -#include "conditionVarImpl.h" - -#ifndef DEBUG_THREADS - -/** - * A condition variable, usually used to communicate information about - * changing state to a thread that is waiting for something to happen. A - * condition variable can be used to "wake up" a thread when some arbitrary - * condition has changed. - * - * A condition variable is associated with a single mutex, and several - * condition variables may share the same mutex. - */ -class EXPCL_PANDA_PIPELINE ConditionVarFullDirect { -public: - INLINE explicit ConditionVarFullDirect(MutexDirect &mutex); - ConditionVarFullDirect(const ConditionVarFullDirect ©) = delete; - ~ConditionVarFullDirect() = default; - - ConditionVarFullDirect &operator = (const ConditionVarFullDirect ©) = delete; - -PUBLISHED: - INLINE MutexDirect &get_mutex() const; - - BLOCKING INLINE void wait(); - BLOCKING INLINE void wait(double timeout); - INLINE void notify(); - INLINE void notify_all(); - void output(std::ostream &out) const; - -private: - MutexDirect &_mutex; - ConditionVarFullImpl _impl; -}; - -INLINE std::ostream & -operator << (std::ostream &out, const ConditionVarFullDirect &cv) { - cv.output(out); - return out; -} - -#include "conditionVarFullDirect.I" - -#endif // !DEBUG_THREADS - -#endif diff --git a/panda/src/pipeline/conditionVarFullWin32Impl.I b/panda/src/pipeline/conditionVarFullWin32Impl.I deleted file mode 100644 index 386a9d5784..0000000000 --- a/panda/src/pipeline/conditionVarFullWin32Impl.I +++ /dev/null @@ -1,119 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file conditionVarFullWin32Impl.I - * @author drose - * @date 2006-08-28 - */ - -/** - * - */ -INLINE ConditionVarFullWin32Impl:: -ConditionVarFullWin32Impl(MutexWin32Impl &mutex) { - _external_mutex = &mutex._lock; - - // Create an auto-reset event and a manual-reset event. - _event_signal = CreateEvent(nullptr, false, false, nullptr); - _event_broadcast = CreateEvent(nullptr, true, false, nullptr); - - _waiters_count = 0; -} - -/** - * - */ -INLINE ConditionVarFullWin32Impl:: -~ConditionVarFullWin32Impl() { - CloseHandle(_event_signal); - CloseHandle(_event_broadcast); -} - -/** - * - */ -INLINE void ConditionVarFullWin32Impl:: -wait() { - AtomicAdjust::inc(_waiters_count); - - // It's ok to release the external_mutex here since Win32 manual-reset - // events maintain state when used with SetEvent(). This avoids the "lost - // wakeup" bug... - LeaveCriticalSection(_external_mutex); - - // Wait for either event to become signaled due to notify() being called or - // notify_all() being called. - int result = WaitForMultipleObjects(2, &_event_signal, FALSE, INFINITE); - - bool nonzero = AtomicAdjust::dec(_waiters_count); - bool last_waiter = (result == WAIT_OBJECT_0 + 1 && !nonzero); - - // Some thread called notify_all(). - if (last_waiter) { - // We're the last waiter to be notified or to stop waiting, so reset the - // manual event. - ResetEvent(_event_broadcast); - } - - // Reacquire the . - EnterCriticalSection(_external_mutex); -} - -/** - * - */ -INLINE void ConditionVarFullWin32Impl:: -wait(double timeout) { - AtomicAdjust::inc(_waiters_count); - - // It's ok to release the external_mutex here since Win32 manual-reset - // events maintain state when used with SetEvent(). This avoids the "lost - // wakeup" bug... - LeaveCriticalSection(_external_mutex); - - // Wait for either event to become signaled due to notify() being called or - // notify_all() being called. - int result = WaitForMultipleObjects(2, &_event_signal, FALSE, (DWORD)(timeout * 1000.0)); - - bool nonzero = AtomicAdjust::dec(_waiters_count); - bool last_waiter = (result == WAIT_OBJECT_0 + 1 && !nonzero); - - // Some thread called notify_all(). - if (last_waiter) { - // We're the last waiter to be notified or to stop waiting, so reset the - // manual event. - ResetEvent(_event_broadcast); - } - - // Reacquire the . - EnterCriticalSection(_external_mutex); -} - -/** - * - */ -INLINE void ConditionVarFullWin32Impl:: -notify() { - bool have_waiters = AtomicAdjust::get(_waiters_count) > 0; - - if (have_waiters) { - SetEvent(_event_signal); - } -} - -/** - * - */ -INLINE void ConditionVarFullWin32Impl:: -notify_all() { - bool have_waiters = AtomicAdjust::get(_waiters_count) > 0; - - if (have_waiters) { - SetEvent(_event_broadcast); - } -} diff --git a/panda/src/pipeline/conditionVarFullWin32Impl.cxx b/panda/src/pipeline/conditionVarFullWin32Impl.cxx deleted file mode 100644 index 6d664735d6..0000000000 --- a/panda/src/pipeline/conditionVarFullWin32Impl.cxx +++ /dev/null @@ -1,20 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file conditionVarFullWin32Impl.cxx - * @author drose - * @date 2006-08-28 - */ - -#include "selectThreadImpl.h" - -#if defined(WIN32_VC) || defined(WIN64_VC) - -#include "conditionVarFullWin32Impl.h" - -#endif // WIN32_VC diff --git a/panda/src/pipeline/conditionVarFullWin32Impl.h b/panda/src/pipeline/conditionVarFullWin32Impl.h deleted file mode 100644 index a42d4d86a8..0000000000 --- a/panda/src/pipeline/conditionVarFullWin32Impl.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file conditionVarFullWin32Impl.h - * @author drose - * @date 2006-08-28 - */ - -#ifndef CONDITIONVARFULLWIN32IMPL_H -#define CONDITIONVARFULLWIN32IMPL_H - -#include "pandabase.h" -#include "selectThreadImpl.h" - -#if defined(WIN32_VC) - -#include "mutexWin32Impl.h" -#include "pnotify.h" -#include "atomicAdjust.h" - -class MutexWin32Impl; - -/** - * Uses Windows native calls to implement a conditionVarFull. - * - * We follow the "SetEvent" implementation suggested by - * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html . This allows us to - * implement both notify() and notify_all(), but it has more overhead than the - * simpler implementation of ConditionVarWin32Impl. - * - * As described by the above reference, this implementation suffers from a few - * weaknesses; in particular, it does not necessarily wake up all threads - * fairly; and it may sometimes incorrectly wake up a thread that was not - * waiting at the time notify() was called. But we figure it's good enough - * for our purposes. - */ -class EXPCL_PANDA_PIPELINE ConditionVarFullWin32Impl { -public: - INLINE ConditionVarFullWin32Impl(MutexWin32Impl &mutex); - INLINE ~ConditionVarFullWin32Impl(); - - INLINE void wait(); - INLINE void wait(double timeout); - INLINE void notify(); - INLINE void notify_all(); - -private: - CRITICAL_SECTION *_external_mutex; - HANDLE _event_signal; - HANDLE _event_broadcast; - TVOLATILE AtomicAdjust::Integer _waiters_count; -}; - -#include "conditionVarFullWin32Impl.I" - -#endif // WIN32_VC - -#endif diff --git a/panda/src/pipeline/conditionVarImpl.h b/panda/src/pipeline/conditionVarImpl.h index 7837622ba6..774ab72753 100644 --- a/panda/src/pipeline/conditionVarImpl.h +++ b/panda/src/pipeline/conditionVarImpl.h @@ -21,35 +21,31 @@ #include "conditionVarDummyImpl.h" typedef ConditionVarDummyImpl ConditionVarImpl; -typedef ConditionVarDummyImpl ConditionVarFullImpl; #elif defined(THREAD_SIMPLE_IMPL) #include "conditionVarSimpleImpl.h" typedef ConditionVarSimpleImpl ConditionVarImpl; -typedef ConditionVarSimpleImpl ConditionVarFullImpl; #elif defined(MUTEX_SPINLOCK) #include "conditionVarSpinlockImpl.h" typedef ConditionVarSpinlockImpl ConditionVarImpl; -typedef ConditionVarSpinlockImpl ConditionVarFullImpl; #elif defined(THREAD_WIN32_IMPL) #include "conditionVarWin32Impl.h" -#include "conditionVarFullWin32Impl.h" typedef ConditionVarWin32Impl ConditionVarImpl; -typedef ConditionVarFullWin32Impl ConditionVarFullImpl; #elif defined(THREAD_POSIX_IMPL) #include "conditionVarPosixImpl.h" typedef ConditionVarPosixImpl ConditionVarImpl; -typedef ConditionVarPosixImpl ConditionVarFullImpl; #endif +typedef ConditionVarImpl ConditionVarFullImpl; + #if defined(WIN32_VC) #include "conditionVarWin32Impl.h" typedef ConditionVarWin32Impl TrueConditionVarImpl; diff --git a/panda/src/pipeline/conditionVarWin32Impl.I b/panda/src/pipeline/conditionVarWin32Impl.I index 5994c638d5..7f2b24a079 100644 --- a/panda/src/pipeline/conditionVarWin32Impl.I +++ b/panda/src/pipeline/conditionVarWin32Impl.I @@ -15,11 +15,7 @@ * */ INLINE ConditionVarWin32Impl:: -ConditionVarWin32Impl(MutexWin32Impl &mutex) { - _external_mutex = &mutex._lock; - - // Create an auto-reset event. - _event_signal = CreateEvent(nullptr, false, false, nullptr); +ConditionVarWin32Impl(MutexWin32Impl &mutex) : _mutex(mutex) { } /** @@ -27,7 +23,14 @@ ConditionVarWin32Impl(MutexWin32Impl &mutex) { */ INLINE ConditionVarWin32Impl:: ~ConditionVarWin32Impl() { - CloseHandle(_event_signal); + // These fields are only set in the Windows XP implementation, when these + // both contain events. + if (_cvar[1] != nullptr) { + CloseHandle(_cvar[1]); + } + if (_cvar[2] != nullptr) { + CloseHandle(_cvar[2]); + } } /** @@ -35,12 +38,7 @@ INLINE ConditionVarWin32Impl:: */ INLINE void ConditionVarWin32Impl:: wait() { - LeaveCriticalSection(_external_mutex); - - DWORD result = WaitForSingleObject(_event_signal, INFINITE); - nassertv(result == WAIT_OBJECT_0); - - EnterCriticalSection(_external_mutex); + MutexWin32Impl::_funcs._cvar_wait(_cvar, _mutex._lock, INFINITE, 0); } /** @@ -48,12 +46,7 @@ wait() { */ INLINE void ConditionVarWin32Impl:: wait(double timeout) { - LeaveCriticalSection(_external_mutex); - - DWORD result = WaitForSingleObject(_event_signal, (DWORD)(timeout * 1000.0)); - nassertv(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT); - - EnterCriticalSection(_external_mutex); + MutexWin32Impl::_funcs._cvar_wait(_cvar, _mutex._lock, (DWORD)(timeout * 1000.0), 0); } /** @@ -61,5 +54,13 @@ wait(double timeout) { */ INLINE void ConditionVarWin32Impl:: notify() { - SetEvent(_event_signal); + MutexWin32Impl::_funcs._cvar_notify_one(_cvar); +} + +/** + * + */ +INLINE void ConditionVarWin32Impl:: +notify_all() { + MutexWin32Impl::_funcs._cvar_notify_all(_cvar); } diff --git a/panda/src/pipeline/conditionVarWin32Impl.h b/panda/src/pipeline/conditionVarWin32Impl.h index 7ed6513d6e..213cacdf9e 100644 --- a/panda/src/pipeline/conditionVarWin32Impl.h +++ b/panda/src/pipeline/conditionVarWin32Impl.h @@ -25,14 +25,19 @@ class MutexWin32Impl; /** - * Uses Windows native calls to implement a conditionVar. + * Uses Windows native calls to implement a ConditionVar. * - * The Windows native synchronization primitives don't actually implement a - * full POSIX-style condition variable, but the Event primitive does a fair - * job if we disallow notify_all() (POSIX broadcast). See - * ConditionVarFullWin32Impl for a full implementation that includes - * notify_all(). This class is much simpler than that full implementation, so - * we can avoid the overhead required to support broadcast. + * On Windows Vista and above, we use the system's native condition variables. + * + * On Windows XP, we follow the "SetEvent" implementation suggested by + * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html . This allows us to + * implement both notify() and notify_all(). + * + * As described by the above reference, this implementation suffers from a few + * weaknesses; in particular, it does not necessarily wake up all threads + * fairly; and it may sometimes incorrectly wake up a thread that was not + * waiting at the time notify() was called. But we figure it's good enough + * for our purposes. */ class EXPCL_PANDA_PIPELINE ConditionVarWin32Impl { public: @@ -42,10 +47,15 @@ public: INLINE void wait(); INLINE void wait(double timeout); INLINE void notify(); + INLINE void notify_all(); private: - CRITICAL_SECTION *_external_mutex; - HANDLE _event_signal; + MutexWin32Impl &_mutex; + + // On Windows XP, the first field contains a Signal (auto-reset) event, + // the second field a broadcast (manual reset) event, third a waiter count. + // On Windows Vista and above, the first contains a PCONDITION_VARIABLE. + volatile PVOID _cvar[3] = {nullptr, nullptr, nullptr}; }; #include "conditionVarWin32Impl.I" diff --git a/panda/src/pipeline/mutexDebug.I b/panda/src/pipeline/mutexDebug.I index ae3ce74967..3259f2aa67 100644 --- a/panda/src/pipeline/mutexDebug.I +++ b/panda/src/pipeline/mutexDebug.I @@ -70,6 +70,8 @@ acquire(Thread *current_thread) const { /** * Returns immediately, with a true value indicating the mutex has been * acquired, and false indicating it has not. + * + * @deprecated Python users should use acquire(False), C++ users try_lock() */ INLINE bool MutexDebug:: try_acquire(Thread *current_thread) const { diff --git a/panda/src/pipeline/mutexDebug.h b/panda/src/pipeline/mutexDebug.h index 094f2eafc6..298fd9f7ca 100644 --- a/panda/src/pipeline/mutexDebug.h +++ b/panda/src/pipeline/mutexDebug.h @@ -83,7 +83,6 @@ private: static MutexTrueImpl *_global_lock; friend class ConditionVarDebug; - friend class ConditionVarFullDebug; }; INLINE std::ostream & diff --git a/panda/src/pipeline/mutexDirect.I b/panda/src/pipeline/mutexDirect.I index 71a26543a7..3daf53a179 100644 --- a/panda/src/pipeline/mutexDirect.I +++ b/panda/src/pipeline/mutexDirect.I @@ -60,6 +60,8 @@ acquire() const { /** * Returns immediately, with a true value indicating the mutex has been * acquired, and false indicating it has not. + * + * @deprecated Python users should use acquire(False), C++ users try_lock() */ INLINE bool MutexDirect:: try_acquire() const { diff --git a/panda/src/pipeline/mutexDirect.h b/panda/src/pipeline/mutexDirect.h index 068f5fc5c4..9e578abd52 100644 --- a/panda/src/pipeline/mutexDirect.h +++ b/panda/src/pipeline/mutexDirect.h @@ -57,7 +57,6 @@ private: mutable MutexTrueImpl _impl; friend class ConditionVarDirect; - friend class ConditionVarFullDirect; }; INLINE std::ostream & diff --git a/panda/src/pipeline/p3pipeline_composite1.cxx b/panda/src/pipeline/p3pipeline_composite1.cxx index 487c5ca0ad..5a9e929283 100644 --- a/panda/src/pipeline/p3pipeline_composite1.cxx +++ b/panda/src/pipeline/p3pipeline_composite1.cxx @@ -2,9 +2,6 @@ #include "conditionVarDebug.cxx" #include "conditionVarDirect.cxx" #include "conditionVarDummyImpl.cxx" -#include "conditionVarFull.cxx" -#include "conditionVarFullDebug.cxx" -#include "conditionVarFullDirect.cxx" #include "conditionVarPosixImpl.cxx" #include "conditionVarWin32Impl.cxx" #include "conditionVarSimpleImpl.cxx" diff --git a/panda/src/pipeline/pipeline.cxx b/panda/src/pipeline/pipeline.cxx index a3633226bc..641b2c6e2c 100644 --- a/panda/src/pipeline/pipeline.cxx +++ b/panda/src/pipeline/pipeline.cxx @@ -86,8 +86,8 @@ Pipeline:: void Pipeline:: cycle() { #ifdef THREADED_PIPELINE - if (pipeline_cat.is_debug()) { - pipeline_cat.debug() + if (pipeline_cat.is_spam()) { + pipeline_cat.spam() << "Beginning the pipeline cycle\n"; } diff --git a/panda/src/pipeline/pmutex.h b/panda/src/pipeline/pmutex.h index 2a47b7dbac..51b588a0c9 100644 --- a/panda/src/pipeline/pmutex.h +++ b/panda/src/pipeline/pmutex.h @@ -49,6 +49,10 @@ PUBLISHED: void operator = (const Mutex ©) = delete; + EXTENSION(bool acquire(bool blocking=true) const); + EXTENSION(bool __enter__()); + EXTENSION(void __exit__(PyObject *, PyObject *, PyObject *)); + public: // This is a global mutex set aside for the purpose of protecting Notify // messages from being interleaved between threads. diff --git a/panda/src/pipeline/pmutex_ext.I b/panda/src/pipeline/pmutex_ext.I new file mode 100644 index 0000000000..5f3ba1fbe2 --- /dev/null +++ b/panda/src/pipeline/pmutex_ext.I @@ -0,0 +1,53 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file pmutex_ext.h + * @author rdb + * @date 2019-05-12 + */ + +/** + * Acquires the mutex. + */ +INLINE bool Extension:: +acquire(bool blocking) const { + if (_this->try_lock()) { + return true; + } + + if (!blocking) { + return false; + } + + // Release the GIL while we are waiting for the lock. +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS + _this->lock(); + Py_BLOCK_THREADS +#else + _this->lock(); +#endif + return true; +} + +/** + * Acquires the mutex. + */ +INLINE bool Extension:: +__enter__() { + return acquire(true); +} + +/** + * Releases the mutex. + */ +INLINE void Extension:: +__exit__(PyObject *, PyObject *, PyObject *) { + _this->unlock(); +} diff --git a/panda/src/pipeline/pmutex_ext.h b/panda/src/pipeline/pmutex_ext.h new file mode 100644 index 0000000000..427d0c87f6 --- /dev/null +++ b/panda/src/pipeline/pmutex_ext.h @@ -0,0 +1,41 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file pmutex_ext.h + * @author rdb + * @date 2019-05-12 + */ + +#ifndef PMUTEX_EXT_H +#define PMUTEX_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "pmutex.h" +#include "py_panda.h" + +/** + * This class defines the extension methods for Mutex, which are called + * instead of any C++ methods with the same prototype. + */ +template<> +class Extension : public ExtensionBase { +public: + INLINE bool acquire(bool blocking) const; + INLINE bool __enter__(); + INLINE void __exit__(PyObject *, PyObject *, PyObject *); +}; + +#include "pmutex_ext.I" + +#endif // HAVE_PYTHON + +#endif // PMUTEX_EXT_H diff --git a/panda/src/pipeline/reMutex.h b/panda/src/pipeline/reMutex.h index bdf9031304..bb87953949 100644 --- a/panda/src/pipeline/reMutex.h +++ b/panda/src/pipeline/reMutex.h @@ -42,6 +42,10 @@ PUBLISHED: ~ReMutex() = default; void operator = (const ReMutex ©) = delete; + + EXTENSION(bool acquire(bool blocking=true) const); + EXTENSION(bool __enter__()); + EXTENSION(void __exit__(PyObject *, PyObject *, PyObject *)); }; #include "reMutex.I" diff --git a/panda/src/pipeline/reMutexDirect.I b/panda/src/pipeline/reMutexDirect.I index e7fa3a6fce..4e2fdb9abb 100644 --- a/panda/src/pipeline/reMutexDirect.I +++ b/panda/src/pipeline/reMutexDirect.I @@ -105,6 +105,8 @@ acquire(Thread *current_thread) const { /** * Returns immediately, with a true value indicating the mutex has been * acquired, and false indicating it has not. + * + * @deprecated Python users should use acquire(False), C++ users try_lock() */ INLINE bool ReMutexDirect:: try_acquire() const { @@ -119,6 +121,8 @@ try_acquire() const { /** * Returns immediately, with a true value indicating the mutex has been * acquired, and false indicating it has not. + * + * @deprecated Python users should use acquire(False), C++ users try_lock() */ INLINE bool ReMutexDirect:: try_acquire(Thread *current_thread) const { diff --git a/panda/src/pipeline/reMutex_ext.I b/panda/src/pipeline/reMutex_ext.I new file mode 100644 index 0000000000..8c28108f4c --- /dev/null +++ b/panda/src/pipeline/reMutex_ext.I @@ -0,0 +1,53 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file pmutex_ext.h + * @author rdb + * @date 2019-05-12 + */ + +/** + * Acquires the mutex. + */ +INLINE bool Extension:: +acquire(bool blocking) const { + if (_this->try_lock()) { + return true; + } + + if (!blocking) { + return false; + } + + // Release the GIL while we are waiting for the lock. +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS + _this->lock(); + Py_BLOCK_THREADS +#else + _this->lock(); +#endif + return true; +} + +/** + * Acquires the mutex. + */ +INLINE bool Extension:: +__enter__() { + return acquire(true); +} + +/** + * Releases the mutex. + */ +INLINE void Extension:: +__exit__(PyObject *, PyObject *, PyObject *) { + _this->unlock(); +} diff --git a/panda/src/pipeline/reMutex_ext.h b/panda/src/pipeline/reMutex_ext.h new file mode 100644 index 0000000000..72eb50305d --- /dev/null +++ b/panda/src/pipeline/reMutex_ext.h @@ -0,0 +1,41 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file remutex_ext.h + * @author rdb + * @date 2019-05-12 + */ + +#ifndef REMUTEX_EXT_H +#define REMUTEX_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "reMutex.h" +#include "py_panda.h" + +/** + * This class defines the extension methods for ReMutex, which are called + * instead of any C++ methods with the same prototype. + */ +template<> +class Extension : public ExtensionBase { +public: + INLINE bool acquire(bool blocking) const; + INLINE bool __enter__(); + INLINE void __exit__(PyObject *, PyObject *, PyObject *); +}; + +#include "reMutex_ext.I" + +#endif // HAVE_PYTHON + +#endif // REMUTEX_EXT_H diff --git a/panda/src/pipeline/thread.cxx b/panda/src/pipeline/thread.cxx index 80c32fa7f5..81152a8095 100644 --- a/panda/src/pipeline/thread.cxx +++ b/panda/src/pipeline/thread.cxx @@ -17,7 +17,6 @@ #include "config_pipeline.h" #include "mutexDebug.h" #include "conditionVarDebug.h" -#include "conditionVarFullDebug.h" Thread *Thread::_main_thread; Thread *Thread::_external_thread; @@ -52,7 +51,6 @@ Thread(const std::string &name, const std::string &sync_name) : #ifdef DEBUG_THREADS _blocked_on_mutex = nullptr; _waiting_on_cvar = nullptr; - _waiting_on_cvar_full = nullptr; #endif } @@ -63,8 +61,7 @@ Thread:: ~Thread() { #ifdef DEBUG_THREADS nassertv(_blocked_on_mutex == nullptr && - _waiting_on_cvar == nullptr && - _waiting_on_cvar_full == nullptr); + _waiting_on_cvar == nullptr); #endif } @@ -145,8 +142,6 @@ output_blocker(std::ostream &out) const { _blocked_on_mutex->output_with_holder(out); } else if (_waiting_on_cvar != nullptr) { out << *_waiting_on_cvar; - } else if (_waiting_on_cvar_full != nullptr) { - out << *_waiting_on_cvar_full; } #endif // DEBUG_THREADS } diff --git a/panda/src/pipeline/thread.h b/panda/src/pipeline/thread.h index 69b878725a..159b93c9df 100644 --- a/panda/src/pipeline/thread.h +++ b/panda/src/pipeline/thread.h @@ -31,7 +31,6 @@ class Mutex; class ReMutex; class MutexDebug; class ConditionVarDebug; -class ConditionVarFullDebug; class AsyncTask; /** @@ -156,7 +155,6 @@ private: #ifdef DEBUG_THREADS MutexDebug *_blocked_on_mutex; ConditionVarDebug *_waiting_on_cvar; - ConditionVarFullDebug *_waiting_on_cvar_full; #endif // DEBUG_THREADS private: @@ -184,7 +182,6 @@ private: friend class MutexDebug; friend class ConditionVarDebug; - friend class ConditionVarFullDebug; friend class ThreadDummyImpl; friend class ThreadWin32Impl; diff --git a/panda/src/pipeline/threadPosixImpl.cxx b/panda/src/pipeline/threadPosixImpl.cxx index 0d88c5ebe8..18e76cb8d2 100644 --- a/panda/src/pipeline/threadPosixImpl.cxx +++ b/panda/src/pipeline/threadPosixImpl.cxx @@ -58,6 +58,7 @@ ThreadPosixImpl:: void ThreadPosixImpl:: setup_main_thread() { _status = S_running; + _thread = pthread_self(); } /** @@ -180,7 +181,7 @@ join() { std::string ThreadPosixImpl:: get_unique_id() const { std::ostringstream strm; - strm << getpid() << "." << _thread; + strm << getpid() << "." << (uintptr_t)_thread; return strm.str(); } diff --git a/panda/src/pnmimage/pnmImage.I b/panda/src/pnmimage/pnmImage.I index fe589d3f9b..a82e6d6eda 100644 --- a/panda/src/pnmimage/pnmImage.I +++ b/panda/src/pnmimage/pnmImage.I @@ -71,6 +71,57 @@ clamp_val(int input_value) const { return (xelval)std::min(std::max(0, input_value), (int)get_maxval()); } +/** + * A handy function to scale non-alpha values from [0..1] to + * [0..get_maxval()]. Do not use this for alpha values, see to_alpha_val. + */ +INLINE xel PNMImage:: +to_val(const LRGBColorf &value) const { + xel col; + switch (_xel_encoding) { + case XE_generic: + case XE_generic_alpha: + { + LRGBColorf scaled = value * get_maxval() + 0.5f; + col.r = clamp_val((int)scaled[0]); + col.g = clamp_val((int)scaled[1]); + col.b = clamp_val((int)scaled[2]); + } + break; + + case XE_generic_sRGB: + case XE_generic_sRGB_alpha: + col.r = clamp_val((int) + (encode_sRGB_float(value[0]) * get_maxval() + 0.5f)); + col.g = clamp_val((int) + (encode_sRGB_float(value[1]) * get_maxval() + 0.5f)); + col.b = clamp_val((int) + (encode_sRGB_float(value[2]) * get_maxval() + 0.5f)); + break; + + case XE_uchar_sRGB: + case XE_uchar_sRGB_alpha: + encode_sRGB_uchar(LColorf(value, 0.0f), col); + break; + + case XE_uchar_sRGB_sse2: + case XE_uchar_sRGB_alpha_sse2: + encode_sRGB_uchar_sse2(LColorf(value, 0.0f), col); + break; + + case XE_scRGB: + case XE_scRGB_alpha: + { + LRGBColorf scaled = value * 8192.f + 4096.5f; + col.r = std::min(std::max(0, (int)scaled[0]), 65535); + col.g = std::min(std::max(0, (int)scaled[1]), 65535); + col.b = std::min(std::max(0, (int)scaled[2]), 65535); + } + break; + } + return col; +} + /** * A handy function to scale non-alpha values from [0..1] to * [0..get_maxval()]. Do not use this for alpha values, see to_alpha_val. @@ -112,6 +163,44 @@ to_alpha_val(float input_value) const { return clamp_val((int)(input_value * get_maxval() + 0.5)); } +/** + * A handy function to scale non-alpha values from [0..get_maxval()] to + * [0..1]. Do not use this for alpha values, see from_alpha_val. + */ +INLINE LRGBColorf PNMImage:: +from_val(const xel &col) const { + switch (_xel_encoding) { + case XE_generic: + case XE_generic_alpha: + return LRGBColorf(col.r, col.g, col.b) * _inv_maxval; + + case XE_generic_sRGB: + case XE_generic_sRGB_alpha: + return LRGBColorf( + decode_sRGB_float(col.r * _inv_maxval), + decode_sRGB_float(col.g * _inv_maxval), + decode_sRGB_float(col.b * _inv_maxval)); + + case XE_uchar_sRGB: + case XE_uchar_sRGB_alpha: + case XE_uchar_sRGB_sse2: + case XE_uchar_sRGB_alpha_sse2: + return LRGBColorf( + decode_sRGB_float((unsigned char)col.r), + decode_sRGB_float((unsigned char)col.g), + decode_sRGB_float((unsigned char)col.b)); + + case XE_scRGB: + case XE_scRGB_alpha: + return LRGBColorf((int)col.r - 4096, + (int)col.g - 4096, + (int)col.b - 4096) * (1.f / 8192.f); + + default: + return LRGBColorf(0); + } +} + /** * A handy function to scale non-alpha values from [0..get_maxval()] to * [0..1]. Do not use this for alpha values, see from_alpha_val. @@ -479,39 +568,7 @@ set_alpha_val(int x, int y, xelval a) { INLINE LRGBColorf PNMImage:: get_xel(int x, int y) const { nassertr(x >= 0 && x < _x_size && y >= 0 && y < _y_size, LRGBColorf::zero()); - - const xel &col = row(y)[x]; - - switch (_xel_encoding) { - case XE_generic: - case XE_generic_alpha: - return LRGBColorf(col.r, col.g, col.b) * _inv_maxval; - - case XE_generic_sRGB: - case XE_generic_sRGB_alpha: - return LRGBColorf( - decode_sRGB_float(col.r * _inv_maxval), - decode_sRGB_float(col.g * _inv_maxval), - decode_sRGB_float(col.b * _inv_maxval)); - - case XE_uchar_sRGB: - case XE_uchar_sRGB_alpha: - case XE_uchar_sRGB_sse2: - case XE_uchar_sRGB_alpha_sse2: - return LRGBColorf( - decode_sRGB_float((unsigned char)col.r), - decode_sRGB_float((unsigned char)col.g), - decode_sRGB_float((unsigned char)col.b)); - - case XE_scRGB: - case XE_scRGB_alpha: - return LRGBColorf((int)col.r - 4096, - (int)col.g - 4096, - (int)col.b - 4096) * (1.f / 8192.f); - - default: - return LRGBColorf(0); - } + return from_val(row(y)[x]); } /** @@ -521,50 +578,7 @@ get_xel(int x, int y) const { INLINE void PNMImage:: set_xel(int x, int y, const LRGBColorf &value) { nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size); - - xel &col = row(y)[x]; - - switch (_xel_encoding) { - case XE_generic: - case XE_generic_alpha: - { - LRGBColorf scaled = value * get_maxval() + 0.5f; - col.r = clamp_val((int)scaled[0]); - col.g = clamp_val((int)scaled[1]); - col.b = clamp_val((int)scaled[2]); - } - break; - - case XE_generic_sRGB: - case XE_generic_sRGB_alpha: - col.r = clamp_val((int) - (encode_sRGB_float(value[0]) * get_maxval() + 0.5f)); - col.g = clamp_val((int) - (encode_sRGB_float(value[1]) * get_maxval() + 0.5f)); - col.b = clamp_val((int) - (encode_sRGB_float(value[2]) * get_maxval() + 0.5f)); - break; - - case XE_uchar_sRGB: - case XE_uchar_sRGB_alpha: - encode_sRGB_uchar(LColorf(value, 0.0f), col); - break; - - case XE_uchar_sRGB_sse2: - case XE_uchar_sRGB_alpha_sse2: - encode_sRGB_uchar_sse2(LColorf(value, 0.0f), col); - break; - - case XE_scRGB: - case XE_scRGB_alpha: - { - LRGBColorf scaled = value * 8192.f + 4096.5f; - col.r = std::min(std::max(0, (int)scaled[0]), 65535); - col.g = std::min(std::max(0, (int)scaled[1]), 65535); - col.b = std::min(std::max(0, (int)scaled[2]), 65535); - } - break; - } + row(y)[x] = to_val(value); } /** diff --git a/panda/src/pnmimage/pnmImage.cxx b/panda/src/pnmimage/pnmImage.cxx index a26528f99a..a8deae0b6b 100644 --- a/panda/src/pnmimage/pnmImage.cxx +++ b/panda/src/pnmimage/pnmImage.cxx @@ -1928,6 +1928,51 @@ make_histogram(PNMImage::Histogram &histogram) { histogram.swap(pixels, hist_map); } +/** + * Reduces the number of unique colors in the image to (at most) the given + * count. Fewer colors than requested may be left in the image after this + * operation, but never more. + * + * At present, this is only supported on images without an alpha channel. + * + * @since 1.10.5 + */ +void PNMImage:: +quantize(size_t max_colors) { + nassertv(_array != nullptr); + nassertv(!has_alpha()); + size_t array_size = _x_size * _y_size; + + // Get all the unique colors in this image. + pmap color_map; + for (size_t i = 0; i < array_size; ++i) { + color_map[_array[i]]; + } + + size_t num_colors = color_map.size(); + if (num_colors <= max_colors) { + // We are already down to the requested number of colors. + return; + } + + // Collect all the colors into a contiguous array. + xel *colors = (xel *)alloca(num_colors * sizeof(xel)); + size_t i = 0; + for (pmap::const_iterator it = color_map.begin(); + it != color_map.end(); ++it) { + colors[i++] = it->first; + } + nassertv(i == num_colors); + + // Apply the median cut algorithm, which will give us a color map. + r_quantize(color_map, max_colors, colors, num_colors); + + // Replace all the existing colors with the corresponding bucket average. + for (size_t i = 0; i < array_size; ++i) { + _array[i] = color_map[_array[i]]; + } +} + /** * Fills the image with a grayscale perlin noise pattern based on the * indicated parameters. Uses set_xel to set the grayscale values. The sx @@ -2161,6 +2206,92 @@ setup_encoding() { } } +/** + * Recursive implementation of quantize() using the median cut algorithm. + */ +void PNMImage:: +r_quantize(pmap &color_map, size_t max_colors, + xel *colors, size_t num_colors) { + if (num_colors <= max_colors) { + // All points in this bucket can be preserved 1:1. + for (size_t i = 0; i < num_colors; ++i) { + const xel &col = colors[i]; + color_map[col] = col; + } + return; + } + else if (max_colors == 1) { + // We've reached the target. Calculate the average, in linear space. + LRGBColorf avg(0); + for (size_t i = 0; i < num_colors; ++i) { + avg += from_val(colors[i]); + } + avg *= 1.0f / num_colors; + xel avg_val = to_val(avg); + + // Map all colors in this bucket to the avg. + for (size_t i = 0; i < num_colors; ++i) { + color_map[colors[i]] = avg_val; + } + return; + } + else if (max_colors == 0) { + // Not sure how this happens, but we can't preserve any color here. + return; + } + + // Find the minimum/maximum RGB values. We should probably do this in + // linear space, but eh. + xelval min_r = _maxval; + xelval min_g = _maxval; + xelval min_b = _maxval; + xelval max_r = 0, max_g = 0, max_b = 0; + for (size_t i = 0; i < num_colors; ++i) { + const xel &col = colors[i]; + min_r = std::min(min_r, col.r); + max_r = std::max(max_r, col.r); + min_g = std::min(min_g, col.g); + max_g = std::max(max_g, col.g); + min_b = std::min(min_b, col.b); + max_b = std::max(max_b, col.b); + } + + int diff_r = max_r - min_r; + int diff_g = max_g - min_g; + int diff_b = max_b - min_b; + + auto sort_by_red = [](const xel &c1, const xel &c2) { + return c1.r < c2.r; + }; + auto sort_by_green = [](const xel &c1, const xel &c2) { + return c1.g < c2.g; + }; + auto sort_by_blue = [](const xel &c1, const xel &c2) { + return c1.b < c2.b; + }; + + // Sort by the component with the most variation. + if (diff_g >= diff_r) { + if (diff_g >= diff_b) { + std::sort(colors, colors + num_colors, sort_by_green); + } else { + std::sort(colors, colors + num_colors, sort_by_blue); + } + } else if (diff_r >= diff_b) { + std::sort(colors, colors + num_colors, sort_by_red); + } else { + std::sort(colors, colors + num_colors, sort_by_blue); + } + + // Subdivide the sorted colors into two buckets, and recurse. + size_t max_colors_1 = max_colors / 2; + size_t max_colors_2 = max_colors - max_colors_1; + size_t num_colors_1 = num_colors / 2; + size_t num_colors_2 = num_colors - num_colors_1; + r_quantize(color_map, max_colors_1, colors, num_colors_1); + r_quantize(color_map, max_colors_2, colors + num_colors_1, num_colors_2); +} + /** * Recursively fills in the minimum distance measured from a certain set of * points into the gray channel. diff --git a/panda/src/pnmimage/pnmImage.h b/panda/src/pnmimage/pnmImage.h index 494acb8029..da1e2d0553 100644 --- a/panda/src/pnmimage/pnmImage.h +++ b/panda/src/pnmimage/pnmImage.h @@ -68,8 +68,10 @@ PUBLISHED: INLINE ~PNMImage(); INLINE xelval clamp_val(int input_value) const; + INLINE xel to_val(const LRGBColorf &input_value) const; INLINE xelval to_val(float input_value) const; INLINE xelval to_alpha_val(float input_value) const; + INLINE LRGBColorf from_val(const xel &input_value) const; INLINE float from_val(xelval input_value) const; INLINE float from_alpha_val(xelval input_value) const; @@ -254,6 +256,7 @@ PUBLISHED: int xborder = 0, int yborder = 0); void make_histogram(Histogram &hist); + void quantize(size_t max_colors); BLOCKING void perlin_noise_fill(float sx, float sy, int table_size = 256, unsigned long seed = 0); void perlin_noise_fill(StackedPerlinNoise2 &perlin); @@ -346,6 +349,9 @@ private: void setup_rc(); void setup_encoding(); + void r_quantize(pmap &color_map, size_t max_colors, + xel *colors, size_t num_colors); + PUBLISHED: PNMImage operator ~() const; diff --git a/panda/src/pnmimage/pnmimage_base.cxx b/panda/src/pnmimage/pnmimage_base.cxx index 0fb9bedfc6..d5ef09e415 100644 --- a/panda/src/pnmimage/pnmimage_base.cxx +++ b/panda/src/pnmimage/pnmimage_base.cxx @@ -120,7 +120,7 @@ int pm_readbigshort(istream *in, short *sP) { StreamReader reader(in, false); *sP = reader.get_be_int16(); - return (!in->eof() && !in->fail()) ? 0 : -1; + return (!in->fail()) ? 0 : -1; } int @@ -134,7 +134,7 @@ int pm_readbiglong(istream *in, long *lP) { StreamReader reader(in, false); *lP = reader.get_be_int32(); - return (!in->eof() && !in->fail()) ? 0 : -1; + return (!in->fail()) ? 0 : -1; } int @@ -148,7 +148,7 @@ int pm_readlittleshort(istream *in, short *sP) { StreamReader reader(in, false); *sP = reader.get_int16(); - return (!in->eof() && !in->fail()) ? 0 : -1; + return (!in->fail()) ? 0 : -1; } int @@ -162,7 +162,7 @@ int pm_readlittlelong(istream *in, long *lP) { StreamReader reader(in, false); *lP = reader.get_int32(); - return (!in->eof() && !in->fail()) ? 0 : -1; + return (!in->fail()) ? 0 : -1; } int diff --git a/panda/src/pnmimage/pnmimage_base.h b/panda/src/pnmimage/pnmimage_base.h index ca1f356c48..aa5474e82a 100644 --- a/panda/src/pnmimage/pnmimage_base.h +++ b/panda/src/pnmimage/pnmimage_base.h @@ -59,6 +59,22 @@ PUBLISHED: void operator *= (const double mult) { r *= mult; g *= mult; b *= mult; } + bool operator == (const pixel &other) { + return r == other.r && g == other.g && r == other.r; + } + bool operator != (const pixel &other) { + return r != other.r || g != other.g || r != other.r; + } + bool operator < (const pixel &other) const { + if (r != other.r) { + return r < other.r; + } + if (g != other.g) { + return g < other.g; + } + return b < other.b; + } + #ifdef HAVE_PYTHON static int size() { return 3; } void output(std::ostream &out) { diff --git a/panda/src/pnmimagetypes/pnmFileTypePNG.cxx b/panda/src/pnmimagetypes/pnmFileTypePNG.cxx index 7d64faed50..e4b144dd95 100644 --- a/panda/src/pnmimagetypes/pnmFileTypePNG.cxx +++ b/panda/src/pnmimagetypes/pnmFileTypePNG.cxx @@ -227,10 +227,12 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) : } } - pnmimage_png_cat.debug() - << "width = " << width << " height = " << height << " bit_depth = " - << bit_depth << " color_type = " << color_type - << " color_space = " << _color_space << "\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "width = " << width << " height = " << height << " bit_depth = " + << bit_depth << " color_type = " << color_type + << " color_space = " << _color_space << "\n"; + } _x_size = width; _y_size = height; @@ -242,32 +244,42 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) : switch (color_type) { case PNG_COLOR_TYPE_GRAY: - pnmimage_png_cat.debug() - << "PNG_COLOR_TYPE_GRAY\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "PNG_COLOR_TYPE_GRAY\n"; + } _num_channels = 1; break; case PNG_COLOR_TYPE_GRAY_ALPHA: - pnmimage_png_cat.debug() - << "PNG_COLOR_TYPE_GRAY_ALPHA\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "PNG_COLOR_TYPE_GRAY_ALPHA\n"; + } _num_channels = 2; break; case PNG_COLOR_TYPE_RGB: - pnmimage_png_cat.debug() - << "PNG_COLOR_TYPE_RGB\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "PNG_COLOR_TYPE_RGB\n"; + } _num_channels = 3; break; case PNG_COLOR_TYPE_RGB_ALPHA: - pnmimage_png_cat.debug() - << "PNG_COLOR_TYPE_RGB_ALPHA\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "PNG_COLOR_TYPE_RGB_ALPHA\n"; + } _num_channels = 4; break; case PNG_COLOR_TYPE_PALETTE: - pnmimage_png_cat.debug() - << "PNG_COLOR_TYPE_PALETTE\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "PNG_COLOR_TYPE_PALETTE\n"; + } png_set_palette_to_rgb(_png); _maxval = 255; _num_channels = 3; @@ -566,8 +578,10 @@ write_data(xel *array, xelval *alpha_data) { if (png_palette) { if (png_bit_depth <= 8) { if (compute_palette(palette, array, alpha_data, png_max_palette)) { - pnmimage_png_cat.debug() - << palette.size() << " colors found.\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << palette.size() << " colors found.\n"; + } int palette_bit_depth = make_png_bit_depth(pm_maxvaltobits(palette.size() - 1)); @@ -581,10 +595,12 @@ write_data(xel *array, xelval *alpha_data) { if (palette_bit_depth < total_bits || _maxval != (1 << true_bit_depth) - 1) { - pnmimage_png_cat.debug() - << "palette bit depth of " << palette_bit_depth - << " improves on bit depth of " << total_bits - << "; making a palette image.\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "palette bit depth of " << palette_bit_depth + << " improves on bit depth of " << total_bits + << "; making a palette image.\n"; + } color_type = PNG_COLOR_TYPE_PALETTE; @@ -611,36 +627,40 @@ write_data(xel *array, xelval *alpha_data) { png_set_PLTE(_png, _info, png_palette_table, palette.size()); if (has_alpha()) { - pnmimage_png_cat.debug() - << "palette contains " << num_alpha << " transparent entries.\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "palette contains " << num_alpha << " transparent entries.\n"; + } png_set_tRNS(_png, _info, png_trans, num_alpha, nullptr); } - } else { + } else if (pnmimage_png_cat.is_debug()) { pnmimage_png_cat.debug() << "palette bit depth of " << palette_bit_depth << " does not improve on bit depth of " << total_bits << "; not making a palette image.\n"; } - } else { + } else if (pnmimage_png_cat.is_debug()) { pnmimage_png_cat.debug() << "more than " << png_max_palette << " colors found; not making a palette image.\n"; } - } else { + } else if (pnmimage_png_cat.is_debug()) { pnmimage_png_cat.debug() << "maxval exceeds 255; not making a palette image.\n"; } - } else { + } else if (pnmimage_png_cat.is_debug()) { pnmimage_png_cat.debug() << "palette images are not enabled.\n"; } - pnmimage_png_cat.debug() - << "width = " << _x_size << " height = " << _y_size - << " maxval = " << _maxval << " bit_depth = " - << png_bit_depth << " color_type = " << color_type - << " color_space = " << _color_space << "\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "width = " << _x_size << " height = " << _y_size + << " maxval = " << _maxval << " bit_depth = " + << png_bit_depth << " color_type = " << color_type + << " color_space = " << _color_space << "\n"; + } png_set_IHDR(_png, _info, _x_size, _y_size, png_bit_depth, color_type, PNG_INTERLACE_NONE, diff --git a/panda/src/pnmimagetypes/pnmFileTypePNM.cxx b/panda/src/pnmimagetypes/pnmFileTypePNM.cxx index cdc1e62f93..02411c0933 100644 --- a/panda/src/pnmimagetypes/pnmFileTypePNM.cxx +++ b/panda/src/pnmimagetypes/pnmFileTypePNM.cxx @@ -193,7 +193,7 @@ pm_getuint(istream * const ifP) { 'unsigned int'), issue an error message to stderr and abort the program. -----------------------------------------------------------------------------*/ - char ch; + int ch; unsigned int i; // skip whitespace diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx index a0ddb2ae38..606801e124 100644 --- a/panda/src/pstatclient/pStatClient.cxx +++ b/panda/src/pstatclient/pStatClient.cxx @@ -12,6 +12,8 @@ */ #include "pStatClient.h" +#include "pStatCollector.h" +#include "pStatThread.h" #ifdef DO_PSTATS // This file only defines anything interesting if DO_PSTATS is defined. @@ -19,8 +21,6 @@ #include "pStatClientImpl.h" #include "pStatClientControlMessage.h" #include "pStatServerControlMessage.h" -#include "pStatCollector.h" -#include "pStatThread.h" #include "config_pstatclient.h" #include "pStatProperties.h" #include "thread.h" @@ -1222,4 +1222,44 @@ InternalThread(const string &name, const string &sync_name) : { } +#else // DO_PSTATS + +PStatThread PStatClient:: +get_main_thread() const { + return PStatThread((PStatClient *)this, 0); +} + +PStatThread PStatClient:: +get_current_thread() const { + return get_main_thread(); +} + +PStatCollector PStatClient:: +make_collector_with_relname(int parent_index, std::string relname) { + return PStatCollector(); +} + +PStatClient *PStatClient:: +get_global_pstats() { + static PStatClient global_pstats; + return &global_pstats; +} + +void PStatClient:: +start(int collector_index, int thread_index) { +} + +void PStatClient:: +start(int collector_index, int thread_index, double as_of) { +} + +void PStatClient:: +stop(int collector_index, int thread_index) { +} + +void PStatClient:: +stop(int collector_index, int thread_index, double as_of) { +} + + #endif // DO_PSTATS diff --git a/panda/src/pstatclient/pStatClient.h b/panda/src/pstatclient/pStatClient.h index bdcdb4eb20..2d7e76f2bb 100644 --- a/panda/src/pstatclient/pStatClient.h +++ b/panda/src/pstatclient/pStatClient.h @@ -266,6 +266,12 @@ public: ~PStatClient() { } PUBLISHED: + std::string get_collector_name(int index) const { return std::string(); } + std::string get_collector_fullname(int index) const { return std::string(); } + + PStatThread get_main_thread() const; + PStatThread get_current_thread() const; + INLINE static bool connect(const std::string & = std::string(), int = -1) { return false; } INLINE static void disconnect() { } INLINE static bool is_connected() { return false; } @@ -273,6 +279,26 @@ PUBLISHED: INLINE static void main_tick() { } INLINE static void thread_tick(const std::string &) { } + + static PStatClient *get_global_pstats(); + +private: + // These are used by inline PStatCollector methods, so they need to be + // stubbed out for ABI compatibility. + PStatCollector make_collector_with_relname(int parent_index, std::string relname); + + bool is_active(int collector_index, int thread_index) const { return false; } + bool is_started(int collector_index, int thread_index) const { return false; } + + void start(int collector_index, int thread_index); + void start(int collector_index, int thread_index, double as_of); + void stop(int collector_index, int thread_index); + void stop(int collector_index, int thread_index, double as_of); + + void clear_level(int collector_index, int thread_index) { } + void set_level(int collector_index, int thread_index, double level) { } + void add_level(int collector_index, int thread_index, double increment) { } + double get_level(int collector_index, int thread_index) const { return 0.0; } }; #endif // DO_PSTATS diff --git a/panda/src/putil/bamCache.cxx b/panda/src/putil/bamCache.cxx index 4dbcf06966..b042dcfde2 100644 --- a/panda/src/putil/bamCache.cxx +++ b/panda/src/putil/bamCache.cxx @@ -676,21 +676,27 @@ do_read_index(const Filename &index_pathname) { DatagramInputFile din; if (!din.open(index_pathname)) { - util_cat.debug() - << "Could not read index file: " << index_pathname << "\n"; + if (util_cat.is_debug()) { + util_cat.debug() + << "Could not read index file: " << index_pathname << "\n"; + } return nullptr; } string head; if (!din.read_header(head, _bam_header.size())) { - util_cat.debug() - << index_pathname << " is not an index file.\n"; + if (util_cat.is_debug()) { + util_cat.debug() + << index_pathname << " is not an index file.\n"; + } return nullptr; } if (head != _bam_header) { - util_cat.debug() - << index_pathname << " is not an index file.\n"; + if (util_cat.is_debug()) { + util_cat.debug() + << index_pathname << " is not an index file.\n"; + } return nullptr; } diff --git a/panda/src/putil/datagramInputFile.cxx b/panda/src/putil/datagramInputFile.cxx index c4802ceed2..3c7da5d320 100644 --- a/panda/src/putil/datagramInputFile.cxx +++ b/panda/src/putil/datagramInputFile.cxx @@ -15,7 +15,6 @@ #include "temporaryFile.h" #include "numeric_types.h" #include "datagramIterator.h" -#include "profileTimer.h" #include "config_putil.h" #include "config_express.h" #include "virtualFileSystem.h" @@ -109,7 +108,7 @@ read_header(std::string &header, size_t num_bytes) { nassertr(buffer != nullptr, false); _in->read(buffer, num_bytes); - if (_in->fail() || _in->eof()) { + if (_in->fail()) { return false; } @@ -130,7 +129,7 @@ get_datagram(Datagram &data) { // First, get the size of the upcoming datagram. StreamReader reader(_in, false); uint32_t num_bytes_32 = reader.get_uint32(); - if (_in->fail() || _in->eof()) { + if (_in->fail()) { return false; } @@ -141,16 +140,29 @@ get_datagram(Datagram &data) { return true; } + if (_in->eof()) { + return false; + } + size_t num_bytes = (size_t)num_bytes_32; if (num_bytes_32 == (uint32_t)-1) { // Another special case for a value larger than 32 bits. uint64_t num_bytes_64 = reader.get_uint64(); - if (_in->fail() || _in->eof()) { + if (_in->fail()) { _error = true; return false; } + if (num_bytes_64 == 0) { + data.clear(); + return false; + } + + if (_in->eof()) { + return false; + } + num_bytes = (size_t)num_bytes_64; // Make sure we have a reasonable datagram size for putting into memory. @@ -169,6 +181,11 @@ get_datagram(Datagram &data) { while (bytes_read < num_bytes) { size_t bytes_left = num_bytes - bytes_read; + if (_in->eof()) { + _error = true; + return false; + } + // Hold up a second - datagrams >4MB are pretty large by bam/network // standards. Let's take it 4MB at a time just in case the length is // corrupt, so we don't allocate potentially a few GBs of RAM only to @@ -180,7 +197,7 @@ get_datagram(Datagram &data) { unsigned char *ptr = &buffer.p()[bytes_read]; _in->read((char *)ptr, (streamsize)bytes_left); - if (_in->fail() || _in->eof()) { + if (_in->fail()) { _error = true; return false; } @@ -210,7 +227,7 @@ save_datagram(SubfileInfo &info) { // First, get the size of the upcoming datagram. StreamReader reader(_in, false); size_t num_bytes_32 = reader.get_uint32(); - if (_in->fail() || _in->eof()) { + if (_in->fail() || (_in->eof() && num_bytes_32 > 0)) { return false; } diff --git a/panda/src/putil/sparseArray.cxx b/panda/src/putil/sparseArray.cxx index 6373e53039..5404728aa1 100644 --- a/panda/src/putil/sparseArray.cxx +++ b/panda/src/putil/sparseArray.cxx @@ -262,8 +262,8 @@ compare_to(const SparseArray &other) const { return -1; } - --ai; - --bi; + ++ai; + ++bi; } if (ai != _subranges.rend()) { @@ -440,9 +440,9 @@ do_remove_range(int begin, int end) { if (si == _subranges.end()) { if (!_subranges.empty()) { si = _subranges.begin() + _subranges.size() - 1; - if ((*si)._end >= begin) { + if ((*si)._end > begin) { // The new range shortens the last element of the array on the right. - end = std::min(end, (*si)._begin); + end = std::max(begin, (*si)._begin); (*si)._end = end; // It might also shorten it on the left; fall through. } else { @@ -462,10 +462,10 @@ do_remove_range(int begin, int end) { if (si != _subranges.begin()) { Subranges::iterator si2 = si; --si2; - if ((*si2)._end >= begin) { + if ((*si2)._end > begin) { // The new range shortens an element within the array on the right // (but does not intersect the next element). - end = std::min(end, (*si2)._begin); + end = std::max(begin, (*si2)._begin); (*si2)._end = end; // It might also shorten it on the left; fall through. si = si2; @@ -488,7 +488,7 @@ do_remove_range(int begin, int end) { } // Check if the new range removes any elements to the left. - while (begin <= (*si)._begin) { + while (begin <= (*si)._begin || (*si)._begin >= (*si)._end) { if (si == _subranges.begin()) { _subranges.erase(si); return; @@ -500,6 +500,7 @@ do_remove_range(int begin, int end) { } (*si)._end = std::min((*si)._end, begin); + nassertv((*si)._end > (*si)._begin); } /** diff --git a/panda/src/putil/uniqueIdAllocator.cxx b/panda/src/putil/uniqueIdAllocator.cxx index 6c4edd9795..19e3a8ee5a 100644 --- a/panda/src/putil/uniqueIdAllocator.cxx +++ b/panda/src/putil/uniqueIdAllocator.cxx @@ -173,6 +173,20 @@ initial_reserve_id(uint32_t id) { --_free; } +/** + * Checks the allocated state of an index. Returns true for + * indices that are currently allocated and in use. + */ +bool UniqueIdAllocator:: +is_allocated(uint32_t id) { + if (id < _min || id > _max) { + // This id is out of range, not allocated. + return false; + } + + uint32_t index = id - _min; // Convert to _table index. + return _table[index] == IndexAllocated; +} /** * Free an allocated index (index must be between _min and _max that were diff --git a/panda/src/putil/uniqueIdAllocator.h b/panda/src/putil/uniqueIdAllocator.h index 50db80b0b0..2f3e3b9e8f 100644 --- a/panda/src/putil/uniqueIdAllocator.h +++ b/panda/src/putil/uniqueIdAllocator.h @@ -43,6 +43,8 @@ PUBLISHED: uint32_t allocate(); void initial_reserve_id(uint32_t id); + bool is_allocated(uint32_t index); + void free(uint32_t index); PN_stdfloat fraction_used() const; diff --git a/panda/src/rocket/rocketFileInterface.cxx b/panda/src/rocket/rocketFileInterface.cxx index 5a54e31c8f..efdab87400 100644 --- a/panda/src/rocket/rocketFileInterface.cxx +++ b/panda/src/rocket/rocketFileInterface.cxx @@ -30,7 +30,9 @@ RocketFileInterface(VirtualFileSystem *vfs) : _vfs(vfs) { */ Rocket::Core::FileHandle RocketFileInterface:: Open(const Rocket::Core::String& path) { - rocket_cat.debug() << "Opening " << path.CString() << "\n"; + if (rocket_cat.is_debug()) { + rocket_cat.debug() << "Opening " << path.CString() << "\n"; + } Filename fn = Filename::from_os_specific(path.CString()); diff --git a/panda/src/rocket/rocketRegion.cxx b/panda/src/rocket/rocketRegion.cxx index 17fdc8f55f..3ae0b1bb47 100644 --- a/panda/src/rocket/rocketRegion.cxx +++ b/panda/src/rocket/rocketRegion.cxx @@ -44,9 +44,11 @@ RocketRegion(GraphicsOutput *window, const LVecBase4 &dr_dimensions, get_pixels(pl, pr, pb, pt); Rocket::Core::Vector2i dimensions (pr - pl, pt - pb); - rocket_cat.debug() - << "Setting initial context dimensions to (" - << dimensions.x << ", " << dimensions.y << ")\n"; + if (rocket_cat.is_debug()) { + rocket_cat.debug() + << "Setting initial context dimensions to (" + << dimensions.x << ", " << dimensions.y << ")\n"; + } _context = Rocket::Core::CreateContext(context_name.c_str(), dimensions, &_interface); @@ -112,8 +114,10 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup, Rocket::Core::Vector2i dimensions (pr - pl, pt - pb); if (_context->GetDimensions() != dimensions) { - rocket_cat.debug() << "Setting context dimensions to (" - << dimensions.x << ", " << dimensions.y << ")\n"; + if (rocket_cat.is_debug()) { + rocket_cat.debug() << "Setting context dimensions to (" + << dimensions.x << ", " << dimensions.y << ")\n"; + } _context->SetDimensions(dimensions); diff --git a/panda/src/text/textAssembler.cxx b/panda/src/text/textAssembler.cxx index 32cf41eb89..1453103fc2 100644 --- a/panda/src/text/textAssembler.cxx +++ b/panda/src/text/textAssembler.cxx @@ -1843,10 +1843,10 @@ get_character_glyphs(int character, const TextProperties *properties, // Maybe we should remap the character to something else--e.g. a small // capital. const UnicodeLatinMap::Entry *map_entry = - UnicodeLatinMap::look_up(character); + UnicodeLatinMap::look_up((char32_t)character); if (map_entry != nullptr) { if (properties->get_small_caps() && - map_entry->_toupper_character != character) { + map_entry->_toupper_character != (char32_t)character) { character = map_entry->_toupper_character; map_entry = UnicodeLatinMap::look_up(character); glyph_scale = properties->get_small_caps_scale(); @@ -1871,7 +1871,7 @@ get_character_glyphs(int character, const TextProperties *properties, got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph); } - if (!got_glyph && map_entry->_toupper_character != character) { + if (!got_glyph && map_entry->_toupper_character != (char32_t)character) { // If we still couldn't find it, try the uppercase equivalent. character = map_entry->_toupper_character; map_entry = UnicodeLatinMap::look_up(character); diff --git a/panda/src/text/textProperties.I b/panda/src/text/textProperties.I index 09bfc78dad..d56c354d46 100644 --- a/panda/src/text/textProperties.I +++ b/panda/src/text/textProperties.I @@ -801,6 +801,8 @@ get_text_scale() const { /** * Specifies the text direction. If none is specified, it will be guessed * based on the contents of the string. + * + * @since 1.10.0 */ INLINE void TextProperties:: set_direction(Direction direction) { @@ -811,6 +813,8 @@ set_direction(Direction direction) { /** * Clears the text direction setting. If no text direction is specified, it * will be guessed based on the contents of the string. + * + * @since 1.10.0 */ INLINE void TextProperties:: clear_direction() { @@ -819,7 +823,7 @@ clear_direction() { } /** - * + * @since 1.10.0 */ INLINE bool TextProperties:: has_direction() const { @@ -828,6 +832,8 @@ has_direction() const { /** * Returns the direction of the text as specified by set_direction(). + * + * @since 1.10.0 */ INLINE TextProperties::Direction TextProperties:: get_direction() const { diff --git a/panda/src/text/textProperties.cxx b/panda/src/text/textProperties.cxx index 42e7982638..1cbc8ddb9a 100644 --- a/panda/src/text/textProperties.cxx +++ b/panda/src/text/textProperties.cxx @@ -21,6 +21,7 @@ #include "colorAttrib.h" #include "cullBinAttrib.h" #include "transparencyAttrib.h" +#include "zStream.h" PT(TextFont) TextProperties::_default_font; bool TextProperties::_loaded_default_font = false; diff --git a/panda/src/tinydisplay/config_tinydisplay.cxx b/panda/src/tinydisplay/config_tinydisplay.cxx index 9740001c01..9b69b9bd00 100644 --- a/panda/src/tinydisplay/config_tinydisplay.cxx +++ b/panda/src/tinydisplay/config_tinydisplay.cxx @@ -16,8 +16,6 @@ #include "tinyXGraphicsWindow.h" #include "tinyWinGraphicsPipe.h" #include "tinyWinGraphicsWindow.h" -#include "tinyOsxGraphicsPipe.h" -#include "tinyOsxGraphicsWindow.h" #include "tinySDLGraphicsPipe.h" #include "tinySDLGraphicsWindow.h" #include "tinyOffscreenGraphicsPipe.h" @@ -40,26 +38,6 @@ ConfigureFn(config_tinydisplay) { init_libtinydisplay(); } -ConfigVariableBool show_resize_box -("show-resize-box", true, - PRC_DESC("When this variable is true, then resizable OSX Panda windows will " - "be rendered with a resize control in the lower-right corner. " - "This is specially handled by Panda, since otherwise the 3-d " - "window would completely hide any resize control drawn by the " - "OS. Set this variable false to allow this control to be hidden.")); - -ConfigVariableBool osx_disable_event_loop -("osx-disable-event-loop", false, - PRC_DESC("Set this true to disable the window event loop for the Panda " - "windows. This makes sense only in a publish environment where " - "the window event loop is already handled by another part of the " - "app.")); - -ConfigVariableInt osx_mouse_wheel_scale -("osx-mouse-wheel-scale", 1, - PRC_DESC("Specify the number of units to spin the Mac mouse wheel to " - "represent a single wheel_up or wheel_down message.")); - ConfigVariableBool td_ignore_mipmaps ("td-ignore-mipmaps", false, PRC_DESC("Configure this true to disable use of mipmaps on the " @@ -117,14 +95,6 @@ init_libtinydisplay() { ps->set_system_tag("TinyPanda", "native_window_system", "Win"); #endif -#if defined(IS_OSX) && !defined(BUILD_IPHONE) && defined(HAVE_CARBON) && !__LP64__ - TinyOsxGraphicsPipe::init_type(); - TinyOsxGraphicsWindow::init_type(); - selection->add_pipe_type(TinyOsxGraphicsPipe::get_class_type(), - TinyOsxGraphicsPipe::pipe_constructor); - ps->set_system_tag("TinyPanda", "native_window_system", "OSX"); -#endif - #ifdef HAVE_SDL TinySDLGraphicsPipe::init_type(); TinySDLGraphicsWindow::init_type(); @@ -150,10 +120,6 @@ get_pipe_type_p3tinydisplay() { return TinyWinGraphicsPipe::get_class_type().get_index(); #endif -#if defined(IS_OSX) && !defined(BUILD_IPHONE) && defined(HAVE_CARBON) && !__LP64__ - return TinyOsxGraphicsPipe::get_class_type().get_index(); -#endif - #ifdef HAVE_X11 return TinyXGraphicsPipe::get_class_type().get_index(); #endif diff --git a/panda/src/tinydisplay/config_tinydisplay.h b/panda/src/tinydisplay/config_tinydisplay.h index 2750d9cad3..d4d1225b94 100644 --- a/panda/src/tinydisplay/config_tinydisplay.h +++ b/panda/src/tinydisplay/config_tinydisplay.h @@ -25,10 +25,6 @@ NotifyCategoryDecl(tinydisplay, EXPCL_TINYDISPLAY, EXPTP_TINYDISPLAY); extern EXPCL_TINYDISPLAY void init_libtinydisplay(); extern "C" EXPCL_TINYDISPLAY int get_pipe_type_p3tinydisplay(); -extern ConfigVariableBool show_resize_box; -extern ConfigVariableBool osx_disable_event_loop; -extern ConfigVariableInt osx_mouse_wheel_scale; - extern ConfigVariableInt td_texture_ram; extern ConfigVariableBool td_ignore_mipmaps; extern ConfigVariableBool td_ignore_clamp; diff --git a/panda/src/tinydisplay/p3tinydisplay_composite2.cxx b/panda/src/tinydisplay/p3tinydisplay_composite2.cxx index 1d71c075aa..f020bbe8f1 100644 --- a/panda/src/tinydisplay/p3tinydisplay_composite2.cxx +++ b/panda/src/tinydisplay/p3tinydisplay_composite2.cxx @@ -1,11 +1,5 @@ -// We have to include this early, before anyone includes -// netinet/tcp.h, which will define TCP_NODELAY and other symbols and -// confuse the Apple system headers. -#include "tinyOsxGraphicsPipe.h" - #include "tinyGraphicsStateGuardian.cxx" #include "tinyOffscreenGraphicsPipe.cxx" -#include "tinyOsxGraphicsPipe.cxx" #include "tinySDLGraphicsPipe.cxx" #include "tinySDLGraphicsWindow.cxx" #include "tinyTextureContext.cxx" diff --git a/panda/src/tinydisplay/tinyOsxGraphicsPipe.I b/panda/src/tinydisplay/tinyOsxGraphicsPipe.I deleted file mode 100644 index ac88123073..0000000000 --- a/panda/src/tinydisplay/tinyOsxGraphicsPipe.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file tinyOsxGraphicsPipe.I - * @author drose - * @date 2008-05-12 - */ diff --git a/panda/src/tinydisplay/tinyOsxGraphicsPipe.cxx b/panda/src/tinydisplay/tinyOsxGraphicsPipe.cxx deleted file mode 100644 index 349fad4398..0000000000 --- a/panda/src/tinydisplay/tinyOsxGraphicsPipe.cxx +++ /dev/null @@ -1,252 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file tinyOsxGraphicsPipe.cxx - * @author drose - * @date 2008-05-12 - */ - -#include "pandabase.h" - -#if defined(IS_OSX) && !defined(BUILD_IPHONE) && defined(HAVE_CARBON) && !__LP64__ - -#include "tinyOsxGraphicsPipe.h" -#include "config_tinydisplay.h" -#include "tinyOsxGraphicsWindow.h" -#include "tinyGraphicsBuffer.h" -#include "pnmImage.h" -#include "subprocessWindow.h" -#include "nativeWindowHandle.h" - -TypeHandle TinyOsxGraphicsPipe::_type_handle; - -/** - * - */ -TinyOsxGraphicsPipe:: -TinyOsxGraphicsPipe() { - CGRect display_bounds = CGDisplayBounds(kCGDirectMainDisplay); - _display_width = CGRectGetWidth(display_bounds); - _display_height = CGRectGetHeight(display_bounds); -} - -/** - * - */ -TinyOsxGraphicsPipe:: -~TinyOsxGraphicsPipe() { -} - -/** - * Returns the name of the rendering interface associated with this - * GraphicsPipe. This is used to present to the user to allow him/her to - * choose between several possible GraphicsPipes available on a particular - * platform, so the name should be meaningful and unique for a given platform. - */ -std::string TinyOsxGraphicsPipe:: -get_interface_name() const { - return "TinyPanda"; -} - -/** - * This function is passed to the GraphicsPipeSelection object to allow the - * user to make a default TinyOsxGraphicsPipe. - */ -PT(GraphicsPipe) TinyOsxGraphicsPipe:: -pipe_constructor() { - return new TinyOsxGraphicsPipe; -} - -/** - * Creates a new Quartz bitmap image with the data in the indicated PNMImage. - * The caller should eventually free this image via CGImageRelease. - */ -CGImageRef TinyOsxGraphicsPipe:: -create_cg_image(const PNMImage &pnm_image) { - size_t width = pnm_image.get_x_size(); - size_t height = pnm_image.get_y_size(); - -#ifdef PGM_BIGGRAYS - size_t bytes_per_component = 2; -#else - size_t bytes_per_component = 1; -#endif - size_t bits_per_component = bytes_per_component * 8; - size_t num_components = pnm_image.get_num_channels(); - - size_t bits_per_pixel = num_components * bits_per_component; - size_t bytes_per_row = num_components * bytes_per_component * width; - - size_t num_bytes = bytes_per_row * height; - bool has_alpha; - bool is_grayscale; - - CFStringRef color_space_name = nullptr; - switch (pnm_image.get_color_type()) { - case PNMImage::CT_grayscale: - color_space_name = kCGColorSpaceGenericGray; - has_alpha = false; - is_grayscale = true; - break; - - case PNMImage::CT_two_channel: - color_space_name = kCGColorSpaceGenericGray; - has_alpha = true; - is_grayscale = true; - break; - - case PNMImage::CT_color: - color_space_name = kCGColorSpaceGenericRGB; - has_alpha = false; - is_grayscale = false; - break; - - case PNMImage::CT_four_channel: - color_space_name = kCGColorSpaceGenericRGB; - has_alpha = true; - is_grayscale = false; - break; - - case PNMImage::CT_invalid: - // Shouldn't get here. - nassertr(false, nullptr); - break; - } - nassertr(color_space_name != nullptr, nullptr); - - CGColorSpaceRef color_space = CGColorSpaceCreateWithName(color_space_name); - nassertr(color_space != nullptr, nullptr); - - CGBitmapInfo bitmap_info = 0; -#ifdef PGM_BIGGRAYS - bitmap_info |= kCGBitmapByteOrder16Host; -#endif - if (has_alpha) { - bitmap_info |= kCGImageAlphaLast; - } - - // Now convert the pixel data to a format friendly to CGImageCreate(). - char *char_array = (char *)PANDA_MALLOC_ARRAY(num_bytes); - - xelval *dp = (xelval *)char_array; - for (size_t yi = 0; yi < height; ++yi) { - for (size_t xi = 0; xi < width; ++xi) { - if (is_grayscale) { - *dp++ = (xelval)(pnm_image.get_gray(xi, yi) * PGM_MAXMAXVAL); - } else { - *dp++ = (xelval)(pnm_image.get_red(xi, yi) * PGM_MAXMAXVAL); - *dp++ = (xelval)(pnm_image.get_green(xi, yi) * PGM_MAXMAXVAL); - *dp++ = (xelval)(pnm_image.get_blue(xi, yi) * PGM_MAXMAXVAL); - } - if (has_alpha) { - *dp++ = (xelval)(pnm_image.get_alpha(xi, yi) * PGM_MAXMAXVAL); - } - } - } - nassertr((void *)dp == (void *)(char_array + num_bytes), nullptr); - - CGDataProviderRef provider = - CGDataProviderCreateWithData(nullptr, char_array, num_bytes, release_data); - nassertr(provider != nullptr, nullptr); - - CGImageRef image = CGImageCreate - (width, height, bits_per_component, bits_per_pixel, bytes_per_row, - color_space, bitmap_info, provider, - nullptr, false, kCGRenderingIntentDefault); - nassertr(image != nullptr, nullptr); - - CGColorSpaceRelease(color_space); - CGDataProviderRelease(provider); - - return image; -} - -/** - * This callback is assigned to delete the data array allocated within - * create_cg_image(). - */ -void TinyOsxGraphicsPipe:: -release_data(void *info, const void *data, size_t size) { - char *char_array = (char *)data; - PANDA_FREE_ARRAY(char_array); -} - -/** - * Creates a new window or buffer on the pipe, if possible. This routine is - * only called from GraphicsEngine::make_output. - */ -PT(GraphicsOutput) TinyOsxGraphicsPipe:: -make_output(const std::string &name, - const FrameBufferProperties &fb_prop, - const WindowProperties &win_prop, - int flags, - GraphicsEngine *engine, - GraphicsStateGuardian *gsg, - GraphicsOutput *host, - int retry, - bool &precertify) { - - if (!_is_valid) { - return nullptr; - } - - TinyGraphicsStateGuardian *tinygsg = 0; - if (gsg != 0) { - DCAST_INTO_R(tinygsg, gsg, nullptr); - } - - // First thing to try: a TinyOsxGraphicsWindow - - if (retry == 0) { - if (((flags&BF_require_parasite)!=0)|| - ((flags&BF_refuse_window)!=0)|| - ((flags&BF_resizeable)!=0)|| - ((flags&BF_size_track_host)!=0)|| - ((flags&BF_rtt_cumulative)!=0)|| - ((flags&BF_can_bind_color)!=0)|| - ((flags&BF_can_bind_every)!=0)) { - return nullptr; - } - if ((flags & BF_fb_props_optional)==0) { - if ((fb_prop.get_aux_rgba() > 0)|| - (fb_prop.get_aux_hrgba() > 0)|| - (fb_prop.get_aux_float() > 0)) { - return nullptr; - } - } - WindowHandle *window_handle = win_prop.get_parent_window(); - if (window_handle != nullptr) { - tinydisplay_cat.info() - << "Got parent_window " << *window_handle << "\n"; -#ifdef SUPPORT_SUBPROCESS_WINDOW - WindowHandle::OSHandle *os_handle = window_handle->get_os_handle(); - if (os_handle != nullptr && - os_handle->is_of_type(NativeWindowHandle::SubprocessHandle::get_class_type())) { - return new SubprocessWindow(engine, this, name, fb_prop, win_prop, - flags, gsg, host); - } -#endif // SUPPORT_SUBPROCESS_WINDOW - } - return new TinyOsxGraphicsWindow(engine, this, name, fb_prop, win_prop, - flags, gsg, host); - } - - // Second thing to try: a TinyGraphicsBuffer - if (retry == 1) { - if (((flags&BF_require_parasite)!=0)|| - ((flags&BF_require_window)!=0)) { - return nullptr; - } - return new TinyGraphicsBuffer(engine, this, name, fb_prop, win_prop, flags, gsg, host); - } - - // Nothing else left to try. - return nullptr; -} - -#endif // IS_OSX diff --git a/panda/src/tinydisplay/tinyOsxGraphicsPipe.h b/panda/src/tinydisplay/tinyOsxGraphicsPipe.h deleted file mode 100644 index 14cc0aded0..0000000000 --- a/panda/src/tinydisplay/tinyOsxGraphicsPipe.h +++ /dev/null @@ -1,79 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file tinyOsxGraphicsPipe.h - * @author drose - * @date 2008-05-12 - */ - -#ifndef TINYOSXGRAPHICSPIPE_H -#define TINYOSXGRAPHICSPIPE_H - -#include "pandabase.h" - -#if defined(IS_OSX) && !defined(BUILD_IPHONE) && defined(HAVE_CARBON) && !__LP64__ - -// We have to include this early, before anyone includes netinettcp.h, which -// will define TCP_NODELAY and other symbols and confuse the Apple system -// headers. -#include - -#include "graphicsPipe.h" -#include "tinyGraphicsStateGuardian.h" - -/** - * This graphics pipe represents the interface for creating TinyPanda graphics - * windows on a Mac client. - */ -class EXPCL_TINYDISPLAY TinyOsxGraphicsPipe : public GraphicsPipe { -public: - TinyOsxGraphicsPipe(); - virtual ~TinyOsxGraphicsPipe(); - - virtual std::string get_interface_name() const; - static PT(GraphicsPipe) pipe_constructor(); - - static CGImageRef create_cg_image(const PNMImage &pnm_image); - -private: - static void release_data(void *info, const void *data, size_t size); - -protected: - virtual PT(GraphicsOutput) make_output(const std::string &name, - const FrameBufferProperties &fb_prop, - const WindowProperties &win_prop, - int flags, - GraphicsEngine *engine, - GraphicsStateGuardian *gsg, - GraphicsOutput *host, - int retry, - bool &precertify); - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - GraphicsPipe::init_type(); - register_type(_type_handle, "TinyOsxGraphicsPipe", - GraphicsPipe::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; -}; - -#include "tinyOsxGraphicsPipe.I" - -#endif // IS_OSX - -#endif diff --git a/panda/src/tinydisplay/tinyOsxGraphicsWindow.I b/panda/src/tinydisplay/tinyOsxGraphicsWindow.I deleted file mode 100644 index 4f9c2c76f2..0000000000 --- a/panda/src/tinydisplay/tinyOsxGraphicsWindow.I +++ /dev/null @@ -1,20 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file tinyOsxGraphicsWindow.I - * @author drose - * @date 2008-05-12 - */ - -inline void TinyOsxGraphicsWindow::SendKeyEvent( ButtonHandle key, bool down) -{ - if(down) - _input_devices[0].button_down(key); - else - _input_devices[0].button_up(key); -} diff --git a/panda/src/tinydisplay/tinyOsxGraphicsWindow.h b/panda/src/tinydisplay/tinyOsxGraphicsWindow.h deleted file mode 100644 index 71682c9a3b..0000000000 --- a/panda/src/tinydisplay/tinyOsxGraphicsWindow.h +++ /dev/null @@ -1,146 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file tinyOsxGraphicsWindow.h - * @author drose - * @date 2008-05-12 - */ - -#ifndef TINYOSXGRAPHICSWINDOW_H -#define TINYOSXGRAPHICSWINDOW_H - -#include "pandabase.h" - -#if defined(IS_OSX) && !defined(BUILD_IPHONE) && defined(HAVE_CARBON) && !__LP64__ - -#include - -#include "graphicsWindow.h" -#include "buttonHandle.h" -#include "tinyGraphicsStateGuardian.h" - -/** - * Opens a window on OS X to display the TinyPanda software rendering. - */ -class TinyOsxGraphicsWindow : public GraphicsWindow { -public: - TinyOsxGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, - const std::string &name, - const FrameBufferProperties &fb_prop, - const WindowProperties &win_prop, - int flags, - GraphicsStateGuardian *gsg, - GraphicsOutput *host); - virtual ~TinyOsxGraphicsWindow(); - - virtual bool move_pointer(int device, int x, int y); - - virtual bool begin_frame(FrameMode mode, Thread *current_thread); - virtual void end_frame(FrameMode mode, Thread *current_thread); - virtual void begin_flip(); - virtual void process_events(); - virtual bool supports_pixel_zoom() const; - - virtual bool do_reshape_request(int x_origin, int y_origin, bool has_origin, - int x_size, int y_size); - - virtual void mouse_mode_absolute(); - virtual void mouse_mode_relative(); - - virtual void set_properties_now(WindowProperties &properties); - -private: - void ReleaseSystemResources(); - inline void SendKeyEvent( ButtonHandle key, bool down); - -protected: - virtual void close_window(); - virtual bool open_window(); - -private: - - bool OSOpenWindow(WindowProperties &properties); - - // a singleton .. for the events to find the right pipe to push the event - // into - - -public: // do not call direct .. - OSStatus handleKeyInput (EventHandlerCallRef myHandler, EventRef event, Boolean keyDown); - OSStatus handleTextInput (EventHandlerCallRef myHandler, EventRef event); - OSStatus handleWindowMouseEvents (EventHandlerCallRef myHandler, EventRef event); - ButtonHandle OSX_TranslateKey( UInt32 key, EventRef event ); - static TinyOsxGraphicsWindow * GetCurrentOSxWindow (WindowRef hint); - - void HandleModifireDeleta(UInt32 modifiers); - void HandleButtonDelta(UInt32 new_buttons); - void DoResize(void); - - OSStatus event_handler(EventHandlerCallRef myHandler, EventRef event); - - virtual void user_close_request(); - void SystemCloseWindow(); - void SystemSetWindowForground(bool forground); - void SystemPointToLocalPoint(Point &qdGlobalPoint); - void LocalPointToSystemPoint(Point &qdLocalPoint); - bool set_icon_filename(const Filename &icon_filename); - - void set_pointer_in_window(int x, int y); - void set_pointer_out_of_window(); - -private: - void create_frame_buffer(); - -private: - ZBuffer *_frame_buffer; - -private: - UInt32 _last_key_modifiers; - UInt32 _last_buttons; - WindowRef _osx_window; - bool _is_fullscreen; - - CGImageRef _pending_icon; - CGImageRef _current_icon; - - int _ID; - static TinyOsxGraphicsWindow *FullScreenWindow; - - CFDictionaryRef _originalMode; - - // True if _properties.get_cursor_hidden() is true. - bool _cursor_hidden; - - // True if the cursor is actually hidden right now via system calls. - bool _display_hide_cursor; - - SInt32 _wheel_delta; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - GraphicsWindow::init_type(); - register_type(_type_handle, "TinyOsxGraphicsWindow", - GraphicsWindow::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; -}; - -#include "tinyOsxGraphicsWindow.I" - -#endif // IS_OSX - -#endif diff --git a/panda/src/tinydisplay/tinyOsxGraphicsWindow.mm b/panda/src/tinydisplay/tinyOsxGraphicsWindow.mm deleted file mode 100644 index d1df628415..0000000000 --- a/panda/src/tinydisplay/tinyOsxGraphicsWindow.mm +++ /dev/null @@ -1,1734 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file tinyOsxGraphicsWindow.mm - */ - -#include "pandabase.h" - -#if defined(IS_OSX) && !defined(BUILD_IPHONE) && defined(HAVE_CARBON) && !__LP64__ - -#include -#include - -#include "tinyOsxGraphicsWindow.h" -#include "config_tinydisplay.h" -#include "tinyOsxGraphicsPipe.h" -#include "pStatTimer.h" -#include "keyboardButton.h" -#include "mouseButton.h" -#include "tinyGraphicsStateGuardian.h" -#include "throw_event.h" -#include "pnmImage.h" -#include "virtualFileSystem.h" -#include "config_putil.h" - -#include - -#include "pmutex.h" -// #include "mutexHolder.h" - - -Mutex &OSXGloablMutex() { - static Mutex m("OSXWIN_Mutex"); - return m; -} - -struct work1 { - volatile bool work_done; -}; - -#define PANDA_CREATE_WINDOW 101 -static void Post_Event_Wait(unsigned short type, unsigned int data1 , unsigned int data2 , int target_window ) { - work1 w; - w.work_done = false; - NSEvent *ev = [NSEvent otherEventWithType:NSApplicationDefined - location:NSZeroPoint - modifierFlags:0 - timestamp:0.0f - windowNumber:target_window - context:nil - subtype:type - data1:data1 - data2:(int)&w]; - - [NSApp postEvent:ev atStart:NO]; - while (!w.work_done) - usleep(10); - -} - - -TypeHandle TinyOsxGraphicsWindow::_type_handle; -TinyOsxGraphicsWindow * TinyOsxGraphicsWindow::FullScreenWindow = NULL; - - -#define USER_CONTAINER - -#include - -#ifdef USER_CONTAINER - -std::set< WindowRef > MyWindows; -void AddAWindow( WindowRef window) { - MyWindows.insert(window); -} -bool checkmywindow(WindowRef window) { - return MyWindows.find(window) != MyWindows.end(); -} -#else - -void AddAWindow( WindowRef window) { -} - -bool checkmywindow(WindowRef window) { - return true; -} - -#endif - - - -/** - * How to find the active window for events on osx.. - * - */ -TinyOsxGraphicsWindow* TinyOsxGraphicsWindow::GetCurrentOSxWindow(WindowRef window) { - if (FullScreenWindow != NULL) - return FullScreenWindow; - - if (NULL == window) // HID use this path - { - // Assume first we are a child window. If we cant find a window of - // that class, then we are standalone and can jsut grab the front - // window. - window = GetFrontWindowOfClass(kSimpleWindowClass, TRUE); - if (NULL == window) - window = FrontNonFloatingWindow(); - } - - if (window && checkmywindow(window)) { - return (TinyOsxGraphicsWindow *)GetWRefCon (window); - } else { - return NULL; - } -} - -/** - * The standard window event handler for non-fullscreen windows. - */ -OSStatus TinyOsxGraphicsWindow:: -event_handler(EventHandlerCallRef myHandler, EventRef event) { - - OSStatus result = eventNotHandledErr; - UInt32 the_class = GetEventClass(event); - UInt32 kind = GetEventKind(event); - - WindowRef window = NULL; - GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, - sizeof(window), NULL, &window); - - UInt32 attributes = 0; - GetEventParameter(event, kEventParamAttributes, typeUInt32, NULL, - sizeof(attributes), NULL, &attributes); - - if (tinydisplay_cat.is_spam()) { - tinydisplay_cat.spam() << ClockObject::get_global_clock()->get_real_time() << " event_handler: " << (void *)this << ", " << window << ", " << the_class << ", " << kind << "\n"; - } - - switch (the_class) { - case kEventClassMouse: - result = handleWindowMouseEvents (myHandler, event); - break; - - case kEventClassWindow: - switch (kind) { - case kEventWindowCollapsing: - /* - Rect r; - GetWindowPortBounds (window, &r); - CompositeGLBufferIntoWindow( get_context(), &r, GetWindowPort (window)); - UpdateCollapsedWindowDockTile (window); - */ - SystemSetWindowForground(false); - break; - case kEventWindowActivated: // called on click activation and initially - SystemSetWindowForground(true); - DoResize(); - break; - case kEventWindowDeactivated: - SystemSetWindowForground(false); - break; - case kEventWindowClose: // called when window is being closed (close box) - // This is a message from the window manager indicating that the - // user has requested to close the window. - user_close_request(); - result = noErr; - break; - case kEventWindowShown: // called on initial show (not on un-minimize) - if (window == FrontNonFloatingWindow ()) - SetUserFocusWindow (window); - break; - - case kEventWindowBoundsChanging: - // Gives us a chance to intercept resize attempts - if (attributes & kWindowBoundsChangeSizeChanged) { - // If the window is supposed to be fixed-size, enforce this. - if (_properties.get_fixed_size()) { - Rect bounds; - GetEventParameter(event, kEventParamCurrentBounds, - typeQDRectangle, NULL, sizeof(bounds), NULL, &bounds); - bounds.right = bounds.left + _properties.get_x_size(); - bounds.bottom = bounds.top + _properties.get_y_size(); - SetEventParameter(event, kEventParamCurrentBounds, - typeQDRectangle, sizeof(bounds), &bounds); result = noErr; - } - } - break; - - case kEventWindowBoundsChanged: // called for resize and moves (drag) - DoResize(); - break; - case kEventWindowZoomed: - break; - case kEventWindowCollapsed: - { - WindowProperties properties; - properties.set_minimized(true); - system_changed_properties(properties); - } - break; - case kEventWindowExpanded: - { - WindowProperties properties; - properties.set_minimized(false); - system_changed_properties(properties); - } - break; - } - break; - } - - return result; -} - -/** - * The user has requested to close the window, for instance with Cmd-W, or by - * clicking on the close button. - */ -void TinyOsxGraphicsWindow::user_close_request() { - string close_request_event = get_close_request_event(); - if (!close_request_event.empty()) { - // In this case, the app has indicated a desire to intercept the request - // and process it directly. - throw_event(close_request_event); - } else { - // In this case, the default case, the app does not intend to service the - // request, so we do by closing the window. - close_window(); - } -} - -/** - * The Windows is closed by a OS resource not by a internal request - * - */ -void TinyOsxGraphicsWindow::SystemCloseWindow() { - if (tinydisplay_cat.is_debug()) - tinydisplay_cat.debug() << "System Closing Window \n"; - ReleaseSystemResources(); -}; - -/** - * The C callback for Window Events .. - * - * We only hook this up for non fullscreen window... so we only handle system - * window events.. - * - */ -static pascal OSStatus windowEvtHndlr(EventHandlerCallRef myHandler, EventRef event, void *userData) { -#pragma unused (userData) - -// volatile().lock(); - - WindowRef window = NULL; - GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window); - - if (window != NULL) { - TinyOsxGraphicsWindow *osx_win = TinyOsxGraphicsWindow::GetCurrentOSxWindow(window); - if (osx_win != (TinyOsxGraphicsWindow *)NULL) { - // OSXGloablMutex().release(); - return osx_win->event_handler(myHandler, event); - } - } - - // OSXGloablMutex().release(); - return eventNotHandledErr; -} - -/** - * The C callback for Window Events .. - * - * We only hook this up for none fullscreen window... so we only handle system - * window events.. - * - */ -void TinyOsxGraphicsWindow::DoResize(void) { - tinydisplay_cat.info() << "In Resize....." << _properties << "\n"; - - // only in window mode .. not full screen - if (_osx_window != NULL && !_is_fullscreen && _properties.has_size()) { - Rect rectPort = {0,0,0,0}; - CGRect viewRect = {{0.0f, 0.0f}, {0.0f, 0.0f}}; - - GetWindowPortBounds (_osx_window, &rectPort); - viewRect.size.width = (PN_stdfloat) (rectPort.right - rectPort.left); - viewRect.size.height = (PN_stdfloat) (rectPort.bottom - rectPort.top); - // tell panda - WindowProperties properties; - properties.set_size((int)viewRect.size.width,(int)viewRect.size.height); - properties.set_origin((int) rectPort.left,(int)rectPort.top); - system_changed_properties(properties); - ZB_resize(_frame_buffer, NULL, _properties.get_x_size(), _properties.get_y_size()); - - if (tinydisplay_cat.is_debug()) { - tinydisplay_cat.debug() << " Resizing Window " << viewRect.size.width << " " << viewRect.size.height << "\n"; - } - } -}; - -/** - * The C callback for APlication Events.. - * - * Hooked once for application - * - */ -static pascal OSStatus appEvtHndlr (EventHandlerCallRef myHandler, EventRef event, void* userData) { -#pragma unused (myHandler) - OSStatus result = eventNotHandledErr; - { - - // OSXGloablMutex().lock(); - - TinyOsxGraphicsWindow *osx_win = NULL; - WindowRef window = NULL; - UInt32 the_class = GetEventClass (event); - UInt32 kind = GetEventKind (event); - - GetEventParameter(event, kEventParamWindowRef, typeWindowRef, NULL, sizeof(WindowRef), NULL, (void*) &window); - osx_win = TinyOsxGraphicsWindow::GetCurrentOSxWindow(window); - if (osx_win == NULL) { - // OSXGloablMutex().release(); - return eventNotHandledErr; - } - - switch (the_class) { - case kEventClassTextInput: - if (kind == kEventTextInputUnicodeForKeyEvent) { - osx_win->handleTextInput(myHandler, event); - } - // result = noErr; can not report handled .. the os will not sent the - // raw key strokes then if (osx_win->handleTextInput(myHandler, event) - // == noErr) result = noErr; - break; - case kEventClassKeyboard: - { - switch (kind) { - case kEventRawKeyRepeat: - case kEventRawKeyDown: - result = osx_win->handleKeyInput (myHandler, event, true); - break; - case kEventRawKeyUp: - result = osx_win->handleKeyInput (myHandler, event, false); - break; - case kEventRawKeyModifiersChanged: - { - UInt32 newModifiers; - OSStatus error = GetEventParameter(event, kEventParamKeyModifiers,typeUInt32, NULL,sizeof(UInt32), NULL, &newModifiers); - if (error == noErr) { - osx_win->HandleModifireDeleta(newModifiers); - result = noErr; - } - } - break; - } - } - break; - - case kEventClassMouse: - // tinydisplay_cat.info() << "Mouse movement handled by Application - // handler\n"; if (TinyOsxGraphicsWindow::FullScreenWindow != NULL) - result = osx_win->handleWindowMouseEvents (myHandler, event); - // result = noErr; - break; - } - - // OSXGloablMutex().release(); - } - - return result; -} - -/** - * Trap Unicode Input. - * - - * - */ -OSStatus TinyOsxGraphicsWindow::handleTextInput (EventHandlerCallRef myHandler, EventRef theTextEvent) { - UniChar *text = NULL; - UInt32 actualSize = 0; - - OSStatus ret = GetEventParameter (theTextEvent, kEventParamTextInputSendText, typeUnicodeText, NULL, 0, &actualSize, NULL); - if (ret != noErr) { - return ret; - } - - text = (UniChar*)NewPtr(actualSize); - if (text!= NULL) { - ret = GetEventParameter (theTextEvent, kEventParamTextInputSendText,typeUnicodeText, NULL, actualSize, NULL, text); - if (ret != noErr) { - return ret; - } - - for (unsigned int x = 0; x < actualSize/sizeof(UniChar); ++x) { - _input_devices[0]->keystroke(text[x]); - } - DisposePtr((char *)text); - } - - return ret; -} -/** - * Clean up the OS level messes.. - */ -void TinyOsxGraphicsWindow::ReleaseSystemResources() { - if (_is_fullscreen) { - _is_fullscreen = false; - FullScreenWindow = NULL; - - if (_originalMode != NULL) - CGDisplaySwitchToMode( kCGDirectMainDisplay, _originalMode ); - - CGDisplayRelease( kCGDirectMainDisplay ); - - _originalMode = NULL; - } - - if (_osx_window != NULL) { - SetWRefCon (_osx_window, (long int) NULL); - HideWindow (_osx_window); - DisposeWindow(_osx_window); - _osx_window = NULL; - } - - if (_pending_icon != NULL) { - CGImageRelease(_pending_icon); - _pending_icon = NULL; - } - if (_current_icon != NULL) { - CGImageRelease(_current_icon); - _current_icon = NULL; - } - - WindowProperties properties; - properties.set_foreground(false); - properties.set_open(false); - properties.set_cursor_filename(Filename()); - system_changed_properties(properties); - - _is_fullscreen = false; - _osx_window = NULL; -} - - -static int id_seed = 100; - -/** - * - */ -TinyOsxGraphicsWindow::TinyOsxGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, - const string &name, - const FrameBufferProperties &fb_prop, - const WindowProperties &win_prop, - int flags, - GraphicsStateGuardian *gsg, - GraphicsOutput *host) : - GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host), - _osx_window(NULL), - _is_fullscreen(false), - _pending_icon(NULL), - _current_icon(NULL), - _ID(id_seed++), - _originalMode(NULL) { - PT(InputDevice) device = - GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse"); - _input_devices.push_back(device); - device->set_pointer_in_window(0, 0); - _last_key_modifiers = 0; - _last_buttons = 0; - - _cursor_hidden = false; - _display_hide_cursor = false; - _wheel_delta = 0; - - _frame_buffer = NULL; - update_pixel_factor(); - - if (tinydisplay_cat.is_debug()) - tinydisplay_cat.debug() << "TinyOsxGraphicsWindow::TinyOsxGraphicsWindow() -" <<_ID << "\n"; -} - -/** - * - */ -TinyOsxGraphicsWindow::~TinyOsxGraphicsWindow() { - if (tinydisplay_cat.is_debug()) - tinydisplay_cat.debug() << "TinyOsxGraphicsWindow::~TinyOsxGraphicsWindow() -" <<_ID << "\n"; - - // Make sure the window callback won't come back to this (destructed) object - // any more. - if (_osx_window) { - SetWRefCon (_osx_window, (long) NULL); - } - - ReleaseSystemResources(); -} - -/** - * Called internally to load up an icon file that should be applied to the - * window. Returns true on success, false on failure. - */ -bool TinyOsxGraphicsWindow::set_icon_filename(const Filename &icon_filename) { - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - - Filename icon_pathname = icon_filename; - if (!vfs->resolve_filename(icon_pathname, get_model_path())) { - // The filename doesn't exist along the search path. - if (icon_pathname.is_fully_qualified() && vfs->exists(icon_pathname)) { - // But it does exist locally, so accept it. - } else { - tinydisplay_cat.warning() - << "Could not find icon filename " << icon_filename << "\n"; - return false; - } - } - - - PNMImage pnmimage; - if (!pnmimage.read(icon_pathname)) { - tinydisplay_cat.warning() - << "Could not read icon filename " << icon_pathname << "\n"; - return false; - } - - CGImageRef icon_image = TinyOsxGraphicsPipe::create_cg_image(pnmimage); - if (icon_image == NULL) { - return false; - } - - if (_pending_icon != NULL) { - CGImageRelease(_pending_icon); - _pending_icon = NULL; - } - _pending_icon = icon_image; - return true; -} - -/** - * Indicates the mouse pointer is seen within the window. - */ -void TinyOsxGraphicsWindow:: -set_pointer_in_window(int x, int y) { - _input_devices[0]->set_pointer_in_window(x, y); - - if (_cursor_hidden != _display_hide_cursor) { - if (_cursor_hidden) { - CGDisplayHideCursor(kCGDirectMainDisplay); - _display_hide_cursor = true; - } else { - CGDisplayShowCursor(kCGDirectMainDisplay); - _display_hide_cursor = false; - } - } -} - -/** - * Indicates the mouse pointer is no longer within the window. - */ -void TinyOsxGraphicsWindow:: -set_pointer_out_of_window() { - _input_devices[0]->set_pointer_out_of_window(); - - if (_display_hide_cursor) { - CGDisplayShowCursor(kCGDirectMainDisplay); - _display_hide_cursor = false; - } -} - - -/** - * This function will be called within the draw thread before beginning - * rendering for a given frame. It should do whatever setup is required, and - * return true if the frame should be rendered, or false if it should be - * skipped. - */ -bool TinyOsxGraphicsWindow::begin_frame(FrameMode mode, Thread *current_thread) { - PStatTimer timer(_make_current_pcollector); - - begin_frame_spam(mode); - if (_gsg == (GraphicsStateGuardian *)NULL || - (_osx_window == NULL && !_is_fullscreen)) { - // not powered up .. just abort.. - return false; - } - - // Now is a good time to apply the icon change that may have recently been - // requested. By this point, we should be able to get a handle to the dock - // context. - if (_pending_icon != NULL) { - CGContextRef context = BeginCGContextForApplicationDockTile(); - if (context != NULL) { - SetApplicationDockTileImage(_pending_icon); - EndCGContextForApplicationDockTile(context); - - if (_current_icon != NULL) { - CGImageRelease(_current_icon); - _current_icon = NULL; - } - _current_icon = _pending_icon; - _pending_icon = NULL; - } - } - - TinyGraphicsStateGuardian *tinygsg; - DCAST_INTO_R(tinygsg, _gsg, false); - - tinygsg->_current_frame_buffer = _frame_buffer; - tinygsg->reset_if_new(); - - _gsg->set_current_properties(&get_fb_properties()); - return _gsg->begin_frame(current_thread); -} - -/** - * This function will be called within the draw thread after rendering is - * completed for a given frame. It should do whatever finalization is - * required. - */ -void TinyOsxGraphicsWindow::end_frame(FrameMode mode, Thread *current_thread) { - end_frame_spam(mode); - - _gsg->end_frame(current_thread); - - if (mode == FM_render) { - - copy_to_textures(); - - trigger_flip(); - clear_cube_map_selection(); - } -} - -/** - * This function will be called within the draw thread after end_frame() has - * been called on all windows, to initiate the exchange of the front and back - * buffers. - * - * This should instruct the window to prepare for the flip at the next video - * sync, but it should not wait. - * - * We have the two separate functions, begin_flip() and end_flip(), to make it - * easier to flip all of the windows at the same time. - */ -void TinyOsxGraphicsWindow::begin_flip() { - if (_osx_window == NULL) { - return; - } - - QDErr err; - - // blit rendered framebuffer into window backing store - Rect src_rect = {0, 0, _frame_buffer->ysize, _frame_buffer->xsize}; - Rect ddrc_rect = {0, 0, _frame_buffer->ysize, _frame_buffer->xsize}; - if (get_pixel_factor() != 1.0) { - src_rect.right = get_fb_x_size(); - src_rect.bottom = get_fb_y_size(); - } - - // create a GWorld containing our image - GWorldPtr pGWorld; - err = NewGWorldFromPtr(&pGWorld, k32BGRAPixelFormat, &src_rect, 0, 0, 0, - (char *)_frame_buffer->pbuf, _frame_buffer->linesize); - if (err != noErr) { - tinydisplay_cat.error() - << " error in NewGWorldFromPtr, called from begin_flip()\n"; - return; - } - - GrafPtr out_port = GetWindowPort(_osx_window); - GrafPtr portSave = NULL; - Boolean portChanged = QDSwapPort(out_port, &portSave); - - { - static PStatCollector b2("Wait:Flip:Begin:CopyBits"); - PStatTimer t2(b2); - CopyBits(GetPortBitMapForCopyBits(pGWorld), - GetPortBitMapForCopyBits(out_port), - &src_rect, &ddrc_rect, srcCopy, 0); - } - - if (portChanged) { - QDSwapPort(portSave, NULL); - } - - DisposeGWorld(pGWorld); -} - -/** - * Closes the window right now. Called from the window thread. - */ -void TinyOsxGraphicsWindow::close_window() { - SystemCloseWindow(); - - WindowProperties properties; - properties.set_open(false); - system_changed_properties(properties); - - ReleaseSystemResources(); - _gsg.clear(); - GraphicsWindow::close_window(); -} - -/* - * HACK ALLERT ************ Undocumented OSX calls... I can not find any other - * way to get the mouse focus to a window in OSX.. extern "C" { struct - * CPSProcessSerNum { UInt32 lo; UInt32 hi; }; - */ - -// extern OSErr CPSGetCurrentProcess(CPSProcessSerNum *psn); extern OSErr -// CPSEnableForegroundOperation( struct CPSProcessSerNum *psn); extern OSErr -// CPSSetProcessName ( struct CPSProcessSerNum *psn, char *processname); -// extern OSErr CPSSetFrontProcess( struct CPSProcessSerNum *psn); }; - -/** - * Opens the window right now. Called from the window thread. Returns true - * if the window is successfully opened, or false if there was a problem. - */ -bool TinyOsxGraphicsWindow::open_window() { - // GSG CreationInitialization - TinyGraphicsStateGuardian *tinygsg; - if (_gsg == 0) { - // There is no old gsg. Create a new one. - tinygsg = new TinyGraphicsStateGuardian(_engine, _pipe, NULL); - _gsg = tinygsg; - } else { - DCAST_INTO_R(tinygsg, _gsg, false); - } - - create_frame_buffer(); - if (_frame_buffer == NULL) { - tinydisplay_cat.error() - << "Could not create frame buffer.\n"; - return false; - } - - tinygsg->_current_frame_buffer = _frame_buffer; - - tinygsg->reset_if_new(); - if (!tinygsg->is_valid()) { - close_window(); - return false; - } - - WindowProperties req_properties = _properties; - // OSXGloablMutex().lock(); - bool answer = OSOpenWindow(req_properties); - // OSXGloablMutex().release(); - return answer; -} - - -bool TinyOsxGraphicsWindow::OSOpenWindow(WindowProperties &req_properties) { - OSErr err = noErr; - - if (_current_icon != NULL && _pending_icon == NULL) { - // If we already have an icon specified, we'll need to reapply it when - // the window is successfully created. - _pending_icon = _current_icon; - _current_icon = NULL; - } - - static bool GlobalInits = false; - if (!GlobalInits) { - // one time aplication inits.. to get a window open from a standalone - // aplication.. - - EventHandlerRef application_event_ref_ref1; - EventTypeSpec list1[] = - { - // { kEventClassCommand, kEventProcessCommand }, { - // kEventClassCommand, kEventCommandUpdateStatus }, - { kEventClassMouse, kEventMouseDown },// handle trackball functionality globaly because there is only a single user - { kEventClassMouse, kEventMouseUp }, - { kEventClassMouse, kEventMouseMoved }, - { kEventClassMouse, kEventMouseDragged }, - { kEventClassMouse, kEventMouseWheelMoved } , - { kEventClassKeyboard, kEventRawKeyDown }, - { kEventClassKeyboard, kEventRawKeyUp } , - { kEventClassKeyboard, kEventRawKeyRepeat }, - { kEventClassKeyboard, kEventRawKeyModifiersChanged } , - { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent}, - }; - - EventHandlerUPP gEvtHandler = NewEventHandlerUPP(appEvtHndlr); - err = InstallApplicationEventHandler (gEvtHandler, GetEventTypeCount (list1) , list1, this, &application_event_ref_ref1 ); - GlobalInits = true; - - ProcessSerialNumber psn = { 0, kCurrentProcess }; - - // Determine if we're running from a bundle. - CFDictionaryRef dref = - ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask); - // If the dictionary doesn't have "BundlePath" (or the BundlePath is the - // same as the executable path), then we're not running from a bundle, and - // we need to call TransformProcessType to make the process a "foreground" - // application, with its own icon in the dock and such. - - bool has_bundle = false; - - CFStringRef bundle_path = (CFStringRef)CFDictionaryGetValue(dref, CFSTR("BundlePath")); - if (bundle_path != NULL) { - // OK, we have a bundle path. We're probably running in a bundle . . . - has_bundle = true; - - // . . . unless it turns out it's the same as the executable path. - CFStringRef exe_path = (CFStringRef)CFDictionaryGetValue(dref, kCFBundleExecutableKey); - if (exe_path != NULL) { - if (CFStringCompare(bundle_path, exe_path, kCFCompareCaseInsensitive) == kCFCompareEqualTo) { - has_bundle = false; - } - CFRelease(exe_path); - } - - CFRelease(bundle_path); - } - - if (!has_bundle) { - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - } - SetFrontProcess(&psn); - } - - - bool wants_fullscreen = req_properties.has_fullscreen() && req_properties.get_fullscreen(); - if (req_properties.get_minimized()) { - // A minimized window can't be fullscreen. - wants_fullscreen = false; - } - - if (wants_fullscreen) { - tinydisplay_cat.info() << "Creating full screen\n"; - - // capture the main display - CGDisplayCapture(kCGDirectMainDisplay); - // if sized try and switch it.. - if (req_properties.has_size()) { - _originalMode = CGDisplayCurrentMode(kCGDirectMainDisplay); - CFDictionaryRef newMode = CGDisplayBestModeForParameters(kCGDirectMainDisplay, 32, req_properties.get_x_size(), req_properties.get_y_size(), 0); - if (newMode == NULL) { - tinydisplay_cat.error() - << "Invalid fullscreen size: " << req_properties.get_x_size() - << ", " << req_properties.get_y_size() - << "\n"; - } else { - CGDisplaySwitchToMode(kCGDirectMainDisplay, newMode); - - // Set our new window size according to the size we actually got. - SInt32 width, height; - CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(newMode, kCGDisplayWidth), kCFNumberSInt32Type, &width); - CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(newMode, kCGDisplayHeight), kCFNumberSInt32Type, &height); - - _properties.set_size(width, height); - } - } - - _properties.set_fullscreen(true); - _properties.set_minimized(false); - _properties.set_foreground(true); - - _is_fullscreen = true; - FullScreenWindow = this; - req_properties.clear_fullscreen(); - } else { - int x_origin = 10; - int y_origin = 50; - if (req_properties.has_origin()) { - y_origin = req_properties.get_y_origin(); - x_origin = req_properties.get_x_origin(); - } - - int x_size = 512; - int y_size = 512; - if (req_properties.has_size()) { - x_size = req_properties.get_x_size(); - y_size = req_properties.get_y_size(); - } - - // A coordinate of -2 means to center the window on screen. - if (y_origin == -2 || x_origin == -2) { - if (y_origin == -2) { - y_origin = (_pipe->get_display_height() - y_size) / 2; - } - if (x_origin == -2) { - x_origin = (_pipe->get_display_width() - x_size) / 2; - } - } - - // A coordinate of -1 means a default location. - if (y_origin == -1) { - y_origin = 50; - } - if (x_origin == -1) { - x_origin = 10; - } - - _properties.set_origin(x_origin, y_origin); - _properties.set_size(x_size, y_size); - - Rect r; - r.top = y_origin; - r.left = x_origin; - r.right = r.left + x_size; - r.bottom = r.top + y_size; - - /* - if (req_properties.has_parent_window()) { - tinydisplay_cat.info() << "Creating child window\n"; - - CreateNewWindow(kSimpleWindowClass, kWindowNoAttributes, &r, &_osx_window); - AddAWindow(_osx_window); - - _properties.set_fixed_size(true); - tinydisplay_cat.info() << "Child window created\n"; - } - else */ - { - int attributes = kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute; - if (req_properties.has_fixed_size() && req_properties.get_fixed_size()) { - attributes &= ~kWindowResizableAttribute; - } - - if (req_properties.has_undecorated() && req_properties.get_undecorated()) { // create a unmovable .. no edge window.. - tinydisplay_cat.info() << "Creating undecorated window\n"; - - // We don't want a resize box either. - attributes &= ~kWindowResizableAttribute; - attributes |= kWindowNoTitleBarAttribute; - CreateNewWindow(kDocumentWindowClass, attributes, &r, &_osx_window); - } - else - { // create a window with crome and sizing and sucj - // In this case, we want to constrain the window to the available size. - Rect bounds; - GetAvailableWindowPositioningBounds(GetMainDevice(), &bounds); - - r.left = max(r.left, bounds.left); - r.right = min(r.right, bounds.right); - r.top = max(r.top, bounds.top); - r.bottom = min(r.bottom, bounds.bottom); - - tinydisplay_cat.info() << "Creating standard window\n"; - CreateNewWindow(kDocumentWindowClass, attributes, &r, &_osx_window); - AddAWindow(_osx_window); - } - } - - - if (_osx_window) { - - EventHandlerUPP gWinEvtHandler; // window event handler - EventTypeSpec list[] = - { - { kEventClassWindow, kEventWindowCollapsing }, - { kEventClassWindow, kEventWindowShown }, - { kEventClassWindow, kEventWindowActivated }, - { kEventClassWindow, kEventWindowDeactivated }, - { kEventClassWindow, kEventWindowClose }, - { kEventClassWindow, kEventWindowBoundsChanging }, - { kEventClassWindow, kEventWindowBoundsChanged }, - - { kEventClassWindow, kEventWindowCollapsed }, - { kEventClassWindow, kEventWindowExpanded }, - { kEventClassWindow, kEventWindowZoomed }, - { kEventClassWindow, kEventWindowClosed }, - }; - - SetWRefCon (_osx_window, (long) this); // point to the window record in the ref con of the window - gWinEvtHandler = NewEventHandlerUPP(windowEvtHndlr); - InstallWindowEventHandler(_osx_window, gWinEvtHandler, GetEventTypeCount(list), list, (void*)this, NULL); // add event handler - - /*if (!req_properties.has_parent_window()) */ - { - ShowWindow (_osx_window); - } - /* - else - { - - NSWindow* parentWindow = (NSWindow *)req_properties.get_parent_window(); - // NSView* aView = [[parentWindow contentView] - // viewWithTag:378]; NSRect aRect = [aView - // frame]; NSPoint origin = [parentWindow - // convertBaseToScreen:aRect.origin]; - - // NSWindow* childWindow = [[NSWindow alloc] - // initWithWindowRef:_osx_window]; - - - Post_Event_Wait(PANDA_CREATE_WINDOW,(unsigned long) _osx_window,1,[parentWindow windowNumber]); - - // [childWindow setFrameOrigin:origin]; [childWindow - // setAcceptsMouseMovedEvents:YES]; [childWindow - // setBackgroundColor:[NSColor blackColor]]; this seems to block till - // the parent accepts the connection ? [parentWindow - // addChildWindow:childWindow ordered:NSWindowAbove]; [childWindow - // orderFront:nil]; - - - _properties.set_parent_window(req_properties.get_parent_window()); - req_properties.clear_parent_window(); - - } */ - - if (req_properties.has_fullscreen()) { - _properties.set_fullscreen(false); - req_properties.clear_fullscreen(); - } - - if (req_properties.has_undecorated()) { - _properties.set_undecorated(req_properties.get_undecorated()); - req_properties.clear_undecorated(); - } - - _properties.set_minimized(false); - _properties.set_foreground(true); - - if (req_properties.has_minimized()) { - CollapseWindow(_osx_window, req_properties.get_minimized()); - _properties.set_minimized(req_properties.get_minimized()); - _properties.set_foreground(!req_properties.get_minimized()); - req_properties.clear_minimized(); - } - } - - // Now measure the size and placement of the window we actually ended up - // with. - Rect rectPort = {0,0,0,0}; - GetWindowPortBounds (_osx_window, &rectPort); - _properties.set_origin((int) rectPort.left, (int) rectPort.top); - _properties.set_size((int) (rectPort.right - rectPort.left), (int) (rectPort.bottom - rectPort.top)); - req_properties.clear_size(); - req_properties.clear_origin(); - } - - if (req_properties.has_icon_filename()) { - set_icon_filename(req_properties.get_icon_filename()); - } - - _properties.set_open(true); - - if (_properties.has_size()) - set_size_and_recalc(_properties.get_x_size(), _properties.get_y_size()); - - - return (err == noErr); -} - -/** - * Required Event upcall . Used to dispatch Window and Aplication Events back - * into panda - * - */ -void TinyOsxGraphicsWindow::process_events() { - GraphicsWindow::process_events(); - - if (!osx_disable_event_loop) { - EventRef theEvent; - EventTargetRef theTarget = GetEventDispatcherTarget(); - - /*if (!_properties.has_parent_window())*/ - { - while (ReceiveNextEvent(0, NULL, kEventDurationNoWait, true, &theEvent)== noErr) { - SendEventToEventTarget (theEvent, theTarget); - ReleaseEvent(theEvent); - } - } - } -}; - -/** - * Returns true if a call to set_pixel_zoom() will be respected, false if it - * will be ignored. If this returns false, then get_pixel_factor() will - * always return 1.0, regardless of what value you specify for - * set_pixel_zoom(). - * - * This may return false if the underlying renderer doesn't support pixel - * zooming, or if you have called this on a DisplayRegion that doesn't have - * both set_clear_color() and set_clear_depth() enabled. - */ -bool TinyOsxGraphicsWindow:: -supports_pixel_zoom() const { - return true; -} - -/** - * Required Event upcall . Used to dispatch Window and Aplication Events back - * into panda - */ -OSStatus TinyOsxGraphicsWindow::handleKeyInput (EventHandlerCallRef myHandler, EventRef event, Boolean keyDown) { - // key input handler - - if (tinydisplay_cat.is_debug()) { - UInt32 keyCode; - GetEventParameter (event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode); - - tinydisplay_cat.debug() - << ClockObject::get_global_clock()->get_real_time() - << " handleKeyInput: " << (void *)this << ", " << keyCode - << ", " << (int)keyDown << "\n"; - } - - // CallNextEventHandler(myHandler, event); - - // We don't check the result of the above function. In principle, this - // should return eventNotHandledErr if the key event is not handled by the - // OS, but in practice, testing this just seems to eat the Escape keypress - // meaninglessly. Keypresses like F11 that are already mapped in the - // desktop seem to not even come into this function in the first place. - UInt32 newModifiers = 0; - OSStatus error = GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &newModifiers); - if (error == noErr) { - HandleModifireDeleta(newModifiers); - } - - UInt32 keyCode; - GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode); - ButtonHandle button = OSX_TranslateKey(keyCode, event); - - if (keyDown) { - if ((newModifiers & cmdKey) != 0) { - if (button == KeyboardButton::ascii_key('q') || button == KeyboardButton::ascii_key('w')) { - // Command-Q or Command-W: quit the application or close the window, - // respectively. For now, we treat them both the same: close the - // window. - user_close_request(); - } - } - SendKeyEvent(button, true); - } else { - SendKeyEvent(button, false); - } - return CallNextEventHandler(myHandler, event); -// return noErr; -} - -/** - * - */ -void TinyOsxGraphicsWindow::SystemSetWindowForground(bool forground) { - WindowProperties properties; - properties.set_foreground(forground); - system_changed_properties(properties); -} - -/** - * - */ -void TinyOsxGraphicsWindow::SystemPointToLocalPoint(Point &qdGlobalPoint) { - if (_osx_window != NULL) { - GrafPtr savePort; - Boolean portChanged = QDSwapPort(GetWindowPort(_osx_window), &savePort); - - GlobalToLocal(&qdGlobalPoint); - - if (portChanged) - QDSwapPort(savePort, NULL); - } -} - -/** - * - */ - OSStatus TinyOsxGraphicsWindow::handleWindowMouseEvents (EventHandlerCallRef myHandler, EventRef event) { - WindowRef window = NULL; - OSStatus result = eventNotHandledErr; - UInt32 kind = GetEventKind (event); - Point qdGlobalPoint = {0, 0}; - UInt32 modifiers = 0; - Rect rectPort; - SInt32 this_wheel_delta; - EventMouseWheelAxis wheelAxis; - - // cerr <<" Start Mouse Event " << _ID << "\n"; - - // Mac OS X v10.1 and later should this be front window??? - GetEventParameter(event, kEventParamWindowRef, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window); - - if (!_is_fullscreen && (window == NULL || window != _osx_window )) { - if (kind == kEventMouseMoved) { - set_pointer_out_of_window(); - } - return eventNotHandledErr; - } - - - - GetWindowPortBounds (window, &rectPort); - - // result = CallNextEventHandler(myHandler, event); if (eventNotHandledErr - // == result) { only handle events not already handled (prevents wierd - // resize interaction) - switch (kind) { - // Whenever mouse button state changes, generate the appropriate Panda - // downup events to represent the change. - - case kEventMouseDown: - case kEventMouseUp: - { - GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers); - if (_properties.get_mouse_mode() == WindowProperties::M_relative) { - GetEventParameter(event, kEventParamMouseDelta,typeQDPoint, NULL, sizeof(Point),NULL , (void*) &qdGlobalPoint); - MouseData currMouse = get_pointer(0); - qdGlobalPoint.h += currMouse.get_x(); - qdGlobalPoint.v += currMouse.get_y(); - } - else - { - GetEventParameter(event, kEventParamMouseLocation,typeQDPoint, NULL, sizeof(Point),NULL , (void*) &qdGlobalPoint); - SystemPointToLocalPoint(qdGlobalPoint); - } - - set_pointer_in_window((int)qdGlobalPoint.h, (int)qdGlobalPoint.v); - - UInt32 new_buttons = GetCurrentEventButtonState(); - HandleButtonDelta(new_buttons); - } - result = noErr; - break; - - case kEventMouseMoved: - case kEventMouseDragged: - if (_properties.get_mouse_mode()==WindowProperties::M_relative) { - GetEventParameter(event, kEventParamMouseDelta,typeQDPoint, NULL, sizeof(Point),NULL , (void*) &qdGlobalPoint); - - MouseData currMouse=get_pointer(0); - qdGlobalPoint.h+=currMouse.get_x(); - qdGlobalPoint.v+=currMouse.get_y(); - } else { - GetEventParameter(event, kEventParamMouseLocation,typeQDPoint, NULL, sizeof(Point),NULL , (void*) &qdGlobalPoint); - SystemPointToLocalPoint(qdGlobalPoint); - } - if (kind == kEventMouseMoved && - (qdGlobalPoint.h < 0 || qdGlobalPoint.v < 0)) { - // Moving into the titlebar region. - set_pointer_out_of_window(); - } else { - // Moving within the window itself (or dragging anywhere). - set_pointer_in_window((int)qdGlobalPoint.h, (int)qdGlobalPoint.v); - } - result = noErr; - - break; - - case kEventMouseWheelMoved: - GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(this_wheel_delta), NULL, &this_wheel_delta); - GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(wheelAxis), NULL, &wheelAxis ); - GetEventParameter(event, kEventParamMouseLocation,typeQDPoint, NULL, sizeof(Point),NULL , (void*) &qdGlobalPoint); - SystemPointToLocalPoint(qdGlobalPoint); - - if (wheelAxis == kEventMouseWheelAxisY) { - set_pointer_in_window((int)qdGlobalPoint.h, (int)qdGlobalPoint.v); - _wheel_delta += this_wheel_delta; - SInt32 wheel_scale = osx_mouse_wheel_scale; - while (_wheel_delta > wheel_scale) { - _input_devices[0]->button_down(MouseButton::wheel_up()); - _input_devices[0]->button_up(MouseButton::wheel_up()); - _wheel_delta -= wheel_scale; - } - while (_wheel_delta < -wheel_scale) { - _input_devices[0]->button_down(MouseButton::wheel_down()); - _input_devices[0]->button_up(MouseButton::wheel_down()); - _wheel_delta += wheel_scale; - } - } - result = noErr; - break; - } - // result = noErr; - - return result; - } - -/** - * MAC Key Codes to Panda Key Codes - */ - ButtonHandle TinyOsxGraphicsWindow::OSX_TranslateKey(UInt32 key, EventRef event) { - - ButtonHandle nk = ButtonHandle::none(); - switch ( key ) { - case 0: nk = KeyboardButton::ascii_key('a'); break; - case 11: nk = KeyboardButton::ascii_key('b'); break; - case 8: nk = KeyboardButton::ascii_key('c'); break; - case 2: nk = KeyboardButton::ascii_key('d'); break; - case 14: nk = KeyboardButton::ascii_key('e'); break; - case 3: nk = KeyboardButton::ascii_key('f'); break; - case 5: nk = KeyboardButton::ascii_key('g'); break; - case 4: nk = KeyboardButton::ascii_key('h'); break; - case 34: nk = KeyboardButton::ascii_key('i'); break; - case 38: nk = KeyboardButton::ascii_key('j'); break; - case 40: nk = KeyboardButton::ascii_key('k'); break; - case 37: nk = KeyboardButton::ascii_key('l'); break; - case 46: nk = KeyboardButton::ascii_key('m'); break; - case 45: nk = KeyboardButton::ascii_key('n'); break; - case 31: nk = KeyboardButton::ascii_key('o'); break; - case 35: nk = KeyboardButton::ascii_key('p'); break; - case 12: nk = KeyboardButton::ascii_key('q'); break; - case 15: nk = KeyboardButton::ascii_key('r'); break; - case 1: nk = KeyboardButton::ascii_key('s'); break; - case 17: nk = KeyboardButton::ascii_key('t'); break; - case 32: nk = KeyboardButton::ascii_key('u'); break; - case 9: nk = KeyboardButton::ascii_key('v'); break; - case 13: nk = KeyboardButton::ascii_key('w'); break; - case 7: nk = KeyboardButton::ascii_key('x'); break; - case 16: nk = KeyboardButton::ascii_key('y'); break; - case 6: nk = KeyboardButton::ascii_key('z'); break; - - // top row numbers - case 29: nk = KeyboardButton::ascii_key('0'); break; - case 18: nk = KeyboardButton::ascii_key('1'); break; - case 19: nk = KeyboardButton::ascii_key('2'); break; - case 20: nk = KeyboardButton::ascii_key('3'); break; - case 21: nk = KeyboardButton::ascii_key('4'); break; - case 23: nk = KeyboardButton::ascii_key('5'); break; - case 22: nk = KeyboardButton::ascii_key('6'); break; - case 26: nk = KeyboardButton::ascii_key('7'); break; - case 28: nk = KeyboardButton::ascii_key('8'); break; - case 25: nk = KeyboardButton::ascii_key('9'); break; - - // key pad ... do they really map to the top number in panda ? - case 82: nk = KeyboardButton::ascii_key('0'); break; - case 83: nk = KeyboardButton::ascii_key('1'); break; - case 84: nk = KeyboardButton::ascii_key('2'); break; - case 85: nk = KeyboardButton::ascii_key('3'); break; - case 86: nk = KeyboardButton::ascii_key('4'); break; - case 87: nk = KeyboardButton::ascii_key('5'); break; - case 88: nk = KeyboardButton::ascii_key('6'); break; - case 89: nk = KeyboardButton::ascii_key('7'); break; - case 91: nk = KeyboardButton::ascii_key('8'); break; - case 92: nk = KeyboardButton::ascii_key('9'); break; - - - // case 36: nk = KeyboardButton::ret(); break; no - // return in panda ??? - case 49: nk = KeyboardButton::space(); break; - case 51: nk = KeyboardButton::backspace(); break; - case 48: nk = KeyboardButton::tab(); break; - case 53: nk = KeyboardButton::escape(); break; - case 76: nk = KeyboardButton::enter(); break; - case 36: nk = KeyboardButton::enter(); break; - - case 123: nk = KeyboardButton::left(); break; - case 124: nk = KeyboardButton::right(); break; - case 125: nk = KeyboardButton::down(); break; - case 126: nk = KeyboardButton::up(); break; - case 116: nk = KeyboardButton::page_up(); break; - case 121: nk = KeyboardButton::page_down(); break; - case 115: nk = KeyboardButton::home(); break; - case 119: nk = KeyboardButton::end(); break; - case 114: nk = KeyboardButton::help(); break; - case 117: nk = KeyboardButton::del(); break; - - // case 71: nk = KeyboardButton::num_lock() break; - - case 122: nk = KeyboardButton::f1(); break; - case 120: nk = KeyboardButton::f2(); break; - case 99: nk = KeyboardButton::f3(); break; - case 118: nk = KeyboardButton::f4(); break; - case 96: nk = KeyboardButton::f5(); break; - case 97: nk = KeyboardButton::f6(); break; - case 98: nk = KeyboardButton::f7(); break; - case 100: nk = KeyboardButton::f8(); break; - case 101: nk = KeyboardButton::f9(); break; - case 109: nk = KeyboardButton::f10(); break; - case 103: nk = KeyboardButton::f11(); break; - case 111: nk = KeyboardButton::f12(); break; - - case 105: nk = KeyboardButton::f13(); break; - case 107: nk = KeyboardButton::f14(); break; - case 113: nk = KeyboardButton::f15(); break; - case 106: nk = KeyboardButton::f16(); break; - - // shiftable chartablet - case 50: nk = KeyboardButton::ascii_key('`'); break; - case 27: nk = KeyboardButton::ascii_key('-'); break; - case 24: nk = KeyboardButton::ascii_key('='); break; - case 33: nk = KeyboardButton::ascii_key('['); break; - case 30: nk = KeyboardButton::ascii_key(']'); break; - case 42: nk = KeyboardButton::ascii_key('\\'); break; - case 41: nk = KeyboardButton::ascii_key(';'); break; - case 39: nk = KeyboardButton::ascii_key('\''); break; - case 43: nk = KeyboardButton::ascii_key(','); break; - case 47: nk = KeyboardButton::ascii_key('.'); break; - case 44: nk = KeyboardButton::ascii_key('/'); break; - - default: - if (tinydisplay_cat.is_debug()) { - tinydisplay_cat.debug() - << " Untranslated KeyCode: " << key - << " (0x" << hex << key << dec << ")\n"; - } - - // not sure this is right .. but no mapping for keypad and such this at - // least does a best gess.. - - char charCode = 0; - if (GetEventParameter( event, kEventParamKeyMacCharCodes, typeChar, nil, sizeof( charCode ), nil, &charCode ) == noErr) - nk = KeyboardButton::ascii_key(charCode); - } - return nk; - } -/** - * Used to emulate key events for the MAC key Modifiers.. - */ - void TinyOsxGraphicsWindow::HandleModifireDeleta(UInt32 newModifiers) { - UInt32 changed = _last_key_modifiers ^ newModifiers; - - if ((changed & (shiftKey | rightShiftKey)) != 0) - SendKeyEvent(KeyboardButton::shift(),(newModifiers & (shiftKey | rightShiftKey)) != 0) ; - - if ((changed & (optionKey | rightOptionKey)) != 0) - SendKeyEvent(KeyboardButton::alt(),(newModifiers & (optionKey | rightOptionKey)) != 0); - - - if ((changed & (controlKey | rightControlKey)) != 0) - SendKeyEvent(KeyboardButton::control(),(newModifiers & (controlKey | rightControlKey)) != 0); - - if ((changed & cmdKey) != 0) - SendKeyEvent(KeyboardButton::meta(),(newModifiers & cmdKey) != 0); - - if ((changed & alphaLock) != 0) - SendKeyEvent(KeyboardButton::caps_lock(),(newModifiers & alphaLock) != 0); - - // save current state - _last_key_modifiers = newModifiers; - }; - -/** - * Used to emulate buttons events - */ -void TinyOsxGraphicsWindow:: -HandleButtonDelta(UInt32 new_buttons) { - UInt32 changed = _last_buttons ^ new_buttons; - - if (changed & 0x01) { - if (new_buttons & 0x01) { - _input_devices[0]->button_down(MouseButton::one()); - } else { - _input_devices[0]->button_up(MouseButton::one()); - } - } - - if (changed & 0x04) { - if (new_buttons & 0x04) { - _input_devices[0]->button_down(MouseButton::two()); - } else { - _input_devices[0]->button_up(MouseButton::two()); - } - } - - if (changed & 0x02) { - if (new_buttons & 0x02) { - _input_devices[0]->button_down(MouseButton::three()); - } else { - _input_devices[0]->button_up(MouseButton::three()); - } - } - - _last_buttons = new_buttons; -} - -/** - * Forces the pointer to the indicated position within the window, if - * possible. - * - * Returns true if successful, false on failure. This may fail if the mouse - * is not currently within the window, or if the API doesn't support this - * operation. - */ -bool TinyOsxGraphicsWindow::move_pointer(int device, int x, int y) { - if (_osx_window == NULL) { - return false; - } - - if (tinydisplay_cat.is_debug()) { - tinydisplay_cat.debug() << "move_pointer " << device <<" "<< x <<" "<< y <<"\n"; - } - - Point pt = {0, 0}; - pt.h = x; - pt.v = y; - set_pointer_in_window(x, y); - - if (_properties.get_mouse_mode() == WindowProperties::M_absolute) { - LocalPointToSystemPoint(pt); - CGPoint newCursorPosition = {0, 0}; - newCursorPosition.x = pt.h; - newCursorPosition.y = pt.v; - mouse_mode_relative(); - CGWarpMouseCursorPosition(newCursorPosition); - mouse_mode_absolute(); - } - - return true; -}; - -bool TinyOsxGraphicsWindow::do_reshape_request(int x_origin, int y_origin, bool has_origin, int x_size, int y_size) { - tinydisplay_cat.info() << "Do Reshape\n"; - - if (_properties.get_fullscreen()) { - return false; - } - - // A coordinate of -2 means to center the window on screen. - if (x_origin == -2 || y_origin == -2 || x_origin == -1 || y_origin == -1) { - if (y_origin == -2) { - y_origin = (_pipe->get_display_height() - y_size) / 2; - } - if (x_origin == -2) { - x_origin = (_pipe->get_display_width() - x_size) / 2; - } - if (y_origin == -1) { - y_origin = 50; - } - if (x_origin == -1) { - x_origin = 10; - } - _properties.set_origin(x_origin, y_origin); - system_changed_properties(_properties); - } - - /* - if (_properties.has_parent_window()) { - if (has_origin) { - NSWindow* parentWindow = (NSWindow *)_properties.get_parent_window(); - NSRect parentFrame = [parentWindow frame]; - - MoveWindow(_osx_window, x_origin+parentFrame.origin.x, y_origin+parentFrame.origin.y, false); - } - } - else*/ - { - // We sometimes get a bogus origin of (0, 0). As a special hack, treat - // this as a special case, and ignore it. - if (has_origin) { - if (x_origin != 0 || y_origin != 0) { - MoveWindow(_osx_window, x_origin, y_origin, false); - } - } - } - - if (!_properties.get_undecorated()) { - // Constrain the window to the available desktop size. - Rect bounds; - GetAvailableWindowPositioningBounds(GetMainDevice(), &bounds); - - x_size = min(x_size, bounds.right - bounds.left); - y_size = min(y_size, bounds.bottom - bounds.top); - } - - SizeWindow(_osx_window, x_size, y_size, false); - - system_changed_size(x_size, y_size); - ZB_resize(_frame_buffer, NULL, _properties.get_x_size(), _properties.get_y_size()); - return true; -} - -/** - * Applies the requested set of properties to the window, if possible, for - * instance to request a change in size or minimization status. - * - * The window properties are applied immediately, rather than waiting until - * the next frame. This implies that this method may *only* be called from - * within the window thread. - * - * The properties that have been applied are cleared from the structure by - * this function; so on return, whatever remains in the properties structure - * are those that were unchanged for some reason (probably because the - * underlying interface does not support changing that property on an open - * window). - */ -void TinyOsxGraphicsWindow::set_properties_now(WindowProperties &properties) { - if (tinydisplay_cat.is_debug()) { - tinydisplay_cat.debug() - << "------------------------------------------------------\n"; - tinydisplay_cat.debug() - << "set_properties_now " << properties << "\n"; - } - - GraphicsWindow::set_properties_now(properties); - - if (tinydisplay_cat.is_debug()) { - tinydisplay_cat.debug() - << "set_properties_now After Base Class" << properties << "\n"; - } - - // for some changes .. a full rebuild is required for the OS layer Window. - // I think it is the crome atribute and full screen behaviour. - bool need_full_rebuild = false; - - // if we are not full and transitioning to full - if (properties.has_fullscreen() && - properties.get_fullscreen() != _properties.get_fullscreen()) { - need_full_rebuild = true; - } - - // If we are fullscreen and requesting a size change - if (_properties.get_fullscreen() && - (properties.has_size() && - (properties.get_x_size() != _properties.get_x_size() || - properties.get_y_size() != _properties.get_y_size()))) { - need_full_rebuild = true; - } - - // If we are fullscreen and requesting a minimize change - if (_properties.get_fullscreen() && - (properties.has_minimized() && - (properties.get_minimized() != _properties.get_minimized()))) { - need_full_rebuild = true; - } - - if (need_full_rebuild) { - // Logic here is .. take a union of the properties .. with the new - // allowed to overwrite the old states. and start a bootstrap of a new - // window .. - - // get a copy of my properties.. - WindowProperties req_properties(_properties); - ReleaseSystemResources(); - req_properties.add_properties(properties); - - OSOpenWindow(req_properties); - - // Now we've handled all of the requested properties. - properties.clear(); - } - - if (properties.has_title()) { - _properties.set_title(properties.get_title()); - if (_osx_window) { - SetWindowTitleWithCFString(_osx_window, - CFStringCreateWithCString(NULL,properties.get_title().c_str(), - kCFStringEncodingMacRoman)); - } - properties.clear_title(); - } - - // An icon filename means to load up the icon and save it. We can't - // necessarily apply it immediately; it will get applied later, in the - // window event handler. - if (properties.has_icon_filename()) { - if (set_icon_filename(properties.get_icon_filename())) { - properties.clear_icon_filename(); - } - } - - // decorated .. if this changes it reqires a new window - if (properties.has_undecorated()) { - _properties.set_undecorated(properties.get_undecorated()); - properties.clear_undecorated(); - } - - if (properties.has_cursor_hidden()) { - _properties.set_cursor_hidden(properties.get_cursor_hidden()); - _cursor_hidden = properties.get_cursor_hidden(); - if (_cursor_hidden && _input_devices[0]->has_pointer()) { - if (!_display_hide_cursor) { - CGDisplayHideCursor(kCGDirectMainDisplay); - _display_hide_cursor = true; - } - } else { - if (_display_hide_cursor) { - CGDisplayShowCursor(kCGDirectMainDisplay); - _display_hide_cursor = false; - } - } - properties.clear_cursor_hidden(); - } - - if (properties.has_minimized()) { - if (_properties.get_minimized() != properties.get_minimized()) { - CollapseWindow(_osx_window, properties.get_minimized()); - _properties.set_minimized(properties.get_minimized()); - _properties.set_foreground(!properties.get_minimized()); - } - properties.clear_minimized(); - } - - if (tinydisplay_cat.is_debug()) { - tinydisplay_cat.debug() - << "set_properties_now Out....." << _properties << "\n"; - } - - return; -} - -/** - - */ -void TinyOsxGraphicsWindow::LocalPointToSystemPoint(Point &qdLocalPoint) { - if (_osx_window != NULL) { - GrafPtr savePort; - Boolean portChanged = QDSwapPort(GetWindowPort(_osx_window), &savePort); - - LocalToGlobal( &qdLocalPoint ); - - if (portChanged) { - QDSwapPort(savePort, NULL); - } - } -} - -/** - * Detaches mouse. Only mouse delta from now on. - */ -void TinyOsxGraphicsWindow::mouse_mode_relative() { - CGAssociateMouseAndMouseCursorPosition(false); -} - -/** - * Reattaches mouse to location - */ -void TinyOsxGraphicsWindow::mouse_mode_absolute() { - CGAssociateMouseAndMouseCursorPosition(true); -} - -/** - * Creates a suitable frame buffer for the current window size. - */ -void TinyOsxGraphicsWindow:: -create_frame_buffer() { - if (_frame_buffer != NULL) { - ZB_close(_frame_buffer); - _frame_buffer = NULL; - } - - _frame_buffer = ZB_open(_properties.get_x_size(), _properties.get_y_size(), ZB_MODE_RGBA, 0, 0, 0, 0); -} - -#endif // IS_OSX diff --git a/panda/src/vision/openCVTexture.cxx b/panda/src/vision/openCVTexture.cxx index 58c233232a..be85aa6866 100644 --- a/panda/src/vision/openCVTexture.cxx +++ b/panda/src/vision/openCVTexture.cxx @@ -22,20 +22,26 @@ #include "bamCacheRecord.h" // This symbol is predefined by the Panda3D build system to select whether we -// are using the OpenCV 2.3 or later interface, or if it is not defined, we -// are using the original interface. -#ifdef OPENCV_VER_23 +// are using the OpenCV 3.x or later interface. +#if defined(OPENCV_VER_3) + +#include +#include + +// This checks for 2.3 or later. +#elif defined(OPENCV_VER_23) #include -// #include #include +// If neither of those are predefined, assume 1.x. #else + #include #include #include -#endif // OPENCV_VER_23 +#endif // OPENCV_VER_3 TypeHandle OpenCVTexture::_type_handle; diff --git a/panda/src/wgldisplay/wglGraphicsStateGuardian.cxx b/panda/src/wgldisplay/wglGraphicsStateGuardian.cxx index c98f12cf48..dc32a1ddcd 100644 --- a/panda/src/wgldisplay/wglGraphicsStateGuardian.cxx +++ b/panda/src/wgldisplay/wglGraphicsStateGuardian.cxx @@ -16,6 +16,7 @@ #include "wglGraphicsBuffer.h" #include "wglGraphicsPipe.h" #include "string_utils.h" +#include TypeHandle wglGraphicsStateGuardian::_type_handle; @@ -49,7 +50,6 @@ wglGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe, _wglCreateContextAttribsARB = nullptr; get_gamma_table(); - atexit(atexit_function); } /** @@ -609,9 +609,20 @@ make_context(HDC hdc) { attrib_list[n++] = gl_version[1]; } } + int flags = 0; if (gl_debug) { + flags |= WGL_CONTEXT_DEBUG_BIT_ARB; + } + if (gl_forward_compatible) { + flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + if (gl_version.get_num_words() == 0 || gl_version[0] < 2) { + wgldisplay_cat.error() + << "gl-forward-compatible requires gl-version >= 3 0\n"; + } + } + if (flags != 0) { attrib_list[n++] = WGL_CONTEXT_FLAGS_ARB; - attrib_list[n++] = WGL_CONTEXT_DEBUG_BIT_ARB; + attrib_list[n++] = flags; } #ifndef SUPPORT_FIXED_FUNCTION attrib_list[n++] = WGL_CONTEXT_PROFILE_MASK_ARB; @@ -875,6 +886,12 @@ static_set_gamma(bool restore, PN_stdfloat gamma) { if (SetDeviceGammaRamp (hdc, ramp)) { set = true; + + // Register an atexit handler + static std::atomic_flag gamma_modified = ATOMIC_FLAG_INIT; + if (!gamma_modified.test_and_set()) { + atexit(atexit_function); + } } ReleaseDC (nullptr, hdc); diff --git a/panda/src/windisplay/config_windisplay.cxx b/panda/src/windisplay/config_windisplay.cxx index 6e0ea2406d..62040c553a 100644 --- a/panda/src/windisplay/config_windisplay.cxx +++ b/panda/src/windisplay/config_windisplay.cxx @@ -86,6 +86,11 @@ ConfigVariableBool swapbuffer_framelock ("swapbuffer-framelock", false, PRC_DESC("Set this true to enable HW swapbuffer frame-lock on 3dlabs cards")); +ConfigVariableBool paste_emit_keystrokes +("paste-emit-keystrokes", true, + PRC_DESC("Handle paste events (Ctrl-V) as separate keystroke events for each " + "pasted character.")); + /** * Initializes the library. This must be called at least once before any of * the functions or classes in this library can be used. Normally it will be diff --git a/panda/src/windisplay/config_windisplay.h b/panda/src/windisplay/config_windisplay.h index 3256287f5b..7199b8603d 100644 --- a/panda/src/windisplay/config_windisplay.h +++ b/panda/src/windisplay/config_windisplay.h @@ -31,6 +31,7 @@ extern ConfigVariableBool ime_hide; extern ConfigVariableBool request_dxdisplay_information; extern ConfigVariableBool dpi_aware; extern ConfigVariableBool dpi_window_resize; +extern ConfigVariableBool paste_emit_keystrokes; extern EXPCL_PANDAWIN ConfigVariableBool swapbuffer_framelock; diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index 195189ac9e..2f6ce8881d 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -200,13 +200,17 @@ close_ime() { HIMC hIMC = ImmGetContext(_hWnd); if (hIMC != 0) { if (!ImmSetOpenStatus(hIMC, false)) { - windisplay_cat.debug() << "ImmSetOpenStatus failed\n"; + if (windisplay_cat.is_debug()) { + windisplay_cat.debug() << "ImmSetOpenStatus failed\n"; + } } ImmReleaseContext(_hWnd, hIMC); } _ime_open = false; - windisplay_cat.debug() << "success: closed ime window\n"; + if (windisplay_cat.is_debug()) { + windisplay_cat.debug() << "success: closed ime window\n"; + } return; } @@ -430,26 +434,8 @@ set_properties_now(WindowProperties &properties) { break; case WindowProperties::M_confined: - { - RECT clip; - - if (!GetWindowRect(_hWnd, &clip)) { - windisplay_cat.warning() - << "GetWindowRect() failed in set_properties_now. Cannot confine cursor.\n"; - } else { - windisplay_cat.info() - << "ClipCursor() to " << clip.left << "," << clip.top << " to " - << clip.right << "," << clip.bottom << endl; - - GetClipCursor(&_mouse_unconfined_cliprect); - if (!ClipCursor(&clip)) { - windisplay_cat.warning() - << "ClipCursor() failed in set_properties_now. Ignoring.\n"; - } else { - _properties.set_mouse_mode(WindowProperties::M_confined); - windisplay_cat.info() << "Confining cursor to window\n"; - } - } + if (confine_cursor()) { + _properties.set_mouse_mode(WindowProperties::M_confined); } break; } @@ -772,6 +758,12 @@ do_reshape_request(int x_origin, int y_origin, bool has_origin, view_rect.bottom - view_rect.top, flags); + // If we are in confined mode, we must update the clip region. + if (_properties.has_mouse_mode() && + _properties.get_mouse_mode() == WindowProperties::M_confined) { + confine_cursor(); + } + handle_reshape(); return true; } @@ -944,6 +936,12 @@ do_windowed_switch() { metrics.width, metrics.height, SWP_FRAMECHANGED | SWP_SHOWWINDOW); + // If we had a confined cursor, we must reconfine it now. + if (_properties.has_mouse_mode() && + _properties.get_mouse_mode() == WindowProperties::M_confined) { + confine_cursor(); + } + return true; } @@ -979,12 +977,12 @@ make_style(const WindowProperties &properties) { DWORD window_style = WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; - if (_properties.get_fullscreen()) { + if (properties.get_fullscreen()) { window_style |= WS_SYSMENU; - } else if (!_properties.get_undecorated()) { + } else if (!properties.get_undecorated()) { window_style |= (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX); - if (!_properties.get_fixed_size()) { + if (!properties.get_fixed_size()) { window_style |= (WS_SIZEBOX | WS_MAXIMIZEBOX); } else { window_style |= WS_BORDER; @@ -1309,6 +1307,31 @@ track_mouse_leaving(HWND hwnd) { _tracking_mouse_leaving = true; } +/** + * Confines the mouse cursor to the window. + */ +bool WinGraphicsWindow:: +confine_cursor() { + RECT clip; + if (!GetWindowRect(_hWnd, &clip)) { + windisplay_cat.warning() + << "GetWindowRect() failed, cannot confine cursor.\n"; + return false; + } else { + windisplay_cat.info() + << "ClipCursor() to " << clip.left << "," << clip.top << " to " + << clip.right << "," << clip.bottom << endl; + + if (!ClipCursor(&clip)) { + windisplay_cat.warning() + << "Failed to confine cursor to window.\n"; + return false; + } else { + return true; + } + } +} + /** * Attempts to set this window as the "focus" window, so that keyboard events * come here. @@ -1482,6 +1505,14 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { SetWindowPos(_hWnd, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOOWNERZORDER); fullscreen_restored(properties); } + + // If we had a confined cursor, we must reconfine it upon activation. + if (_properties.has_mouse_mode() && + _properties.get_mouse_mode() == WindowProperties::M_confined) { + if (!confine_cursor()) { + properties.set_mouse_mode(WindowProperties::M_absolute); + } + } } else { @@ -1516,7 +1547,16 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { break; case WM_EXITSIZEMOVE: - // handle_reshape(); + if (windisplay_cat.is_debug()) { + windisplay_cat.debug() + << "WM_EXITSIZEMOVE: " << hwnd << ", " << wparam << "\n"; + } + + // If we had a confined cursor, we must reconfine it upon a resize. + if (_properties.has_mouse_mode() && + _properties.get_mouse_mode() == WindowProperties::M_confined) { + confine_cursor(); + } break; case WM_WINDOWPOSCHANGED: @@ -1663,18 +1703,30 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if (!ime_hide) break; - windisplay_cat.debug() << "hwnd = " << hwnd << " and GetFocus = " << GetFocus() << endl; + if (windisplay_cat.is_debug()) { + windisplay_cat.debug() << "hwnd = " << hwnd << " and GetFocus = " << GetFocus() << endl; + } _ime_hWnd = ImmGetDefaultIMEWnd(hwnd); - if (::SendMessage(_ime_hWnd, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0)) + if (::SendMessage(_ime_hWnd, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0)) { // if (::SendMessage(hwnd, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0)) - windisplay_cat.debug() << "SendMessage failed for " << _ime_hWnd << endl; - else - windisplay_cat.debug() << "SendMessage Succeeded for " << _ime_hWnd << endl; + if (windisplay_cat.is_debug()) { + windisplay_cat.debug() << "SendMessage failed for " << _ime_hWnd << endl; + } + } else { + if (windisplay_cat.is_debug()) { + windisplay_cat.debug() << "SendMessage succeeded for " << _ime_hWnd << endl; + } + } - windisplay_cat.debug() << "wparam is " << wparam << ", lparam is " << lparam << endl; - lparam &= ~ISC_SHOWUIALL; - if (ImmIsUIMessage(_ime_hWnd, msg, wparam, lparam)) + if (windisplay_cat.is_debug()) { windisplay_cat.debug() << "wparam is " << wparam << ", lparam is " << lparam << endl; + } + lparam &= ~ISC_SHOWUIALL; + if (ImmIsUIMessage(_ime_hWnd, msg, wparam, lparam)) { + if (windisplay_cat.is_debug()) { + windisplay_cat.debug() << "wparam is " << wparam << ", lparam is " << lparam << endl; + } + } break; @@ -1693,16 +1745,18 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { CANDIDATEFORM canf; ImmGetCompositionWindow(hIMC, &comf); ImmGetCandidateWindow(hIMC, 0, &canf); - windisplay_cat.debug() << - "comf style " << comf.dwStyle << - " comf point: x" << comf.ptCurrentPos.x << ",y " << comf.ptCurrentPos.y << - " comf rect: l " << comf.rcArea.left << ",t " << comf.rcArea.top << ",r " << - comf.rcArea.right << ",b " << comf.rcArea.bottom << endl; - windisplay_cat.debug() << - "canf style " << canf.dwStyle << - " canf point: x" << canf.ptCurrentPos.x << ",y " << canf.ptCurrentPos.y << - " canf rect: l " << canf.rcArea.left << ",t " << canf.rcArea.top << ",r " << + if (windisplay_cat.is_debug()) { + windisplay_cat.debug() << + "comf style " << comf.dwStyle << + " comf point: x" << comf.ptCurrentPos.x << ",y " << comf.ptCurrentPos.y << + " comf rect: l " << comf.rcArea.left << ",t " << comf.rcArea.top << ",r " << + comf.rcArea.right << ",b " << comf.rcArea.bottom << endl; + windisplay_cat.debug() << + "canf style " << canf.dwStyle << + " canf point: x" << canf.ptCurrentPos.x << ",y " << canf.ptCurrentPos.y << + " canf rect: l " << canf.rcArea.left << ",t " << canf.rcArea.top << ",r " << canf.rcArea.right << ",b " << canf.rcArea.bottom << endl; + } comf.dwStyle = CFS_POINT; comf.ptCurrentPos.x = 2000; comf.ptCurrentPos.y = 2000; @@ -1723,11 +1777,17 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { comf.rcArea.bottom = 0; #endif - if (ImmSetCompositionWindow(hIMC, &comf)) - windisplay_cat.debug() << "set composition form: success\n"; + if (ImmSetCompositionWindow(hIMC, &comf)) { + if (windisplay_cat.is_debug()) { + windisplay_cat.debug() << "set composition form: success\n"; + } + } for (int i=0; i<3; ++i) { - if (ImmSetCandidateWindow(hIMC, &canf)) - windisplay_cat.debug() << "set candidate form: success\n"; + if (ImmSetCandidateWindow(hIMC, &canf)) { + if (windisplay_cat.is_debug()) { + windisplay_cat.debug() << "set candidate form: success\n"; + } + } canf.dwIndex++; } } @@ -1927,7 +1987,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { // Handle Cntrl-V paste from clipboard. Is there a better way to detect // this hotkey? if ((wparam=='V') && (GetKeyState(VK_CONTROL) < 0) && - !_input_devices.empty()) { + !_input_devices.empty() && paste_emit_keystrokes) { HGLOBAL hglb; char *lptstr; diff --git a/panda/src/windisplay/winGraphicsWindow.h b/panda/src/windisplay/winGraphicsWindow.h index 841e2b9854..083508087b 100644 --- a/panda/src/windisplay/winGraphicsWindow.h +++ b/panda/src/windisplay/winGraphicsWindow.h @@ -134,7 +134,7 @@ private: void initialize_input_devices(); void handle_raw_input(HRAWINPUT hraw); void track_mouse_leaving(HWND hwnd); - + bool confine_cursor(); void set_focus(); static void process_1_event(); diff --git a/panda/src/x11display/x11GraphicsPipe.cxx b/panda/src/x11display/x11GraphicsPipe.cxx index e5e8da18c8..6050911fb4 100644 --- a/panda/src/x11display/x11GraphicsPipe.cxx +++ b/panda/src/x11display/x11GraphicsPipe.cxx @@ -156,21 +156,55 @@ x11GraphicsPipe(const std::string &display) : void *xrandr = dlopen("libXrandr.so.2", RTLD_NOW | RTLD_LOCAL); if (xrandr != nullptr) { pfn_XRRQueryExtension _XRRQueryExtension = (pfn_XRRQueryExtension)dlsym(xrandr, "XRRQueryExtension"); + pfn_XRRQueryVersion _XRRQueryVersion = (pfn_XRRQueryVersion)dlsym(xrandr, "XRRQueryVersion"); + _XRRSizes = (pfn_XRRSizes)dlsym(xrandr, "XRRSizes"); _XRRRates = (pfn_XRRRates)dlsym(xrandr, "XRRRates"); _XRRGetScreenInfo = (pfn_XRRGetScreenInfo)dlsym(xrandr, "XRRGetScreenInfo"); _XRRConfigCurrentConfiguration = (pfn_XRRConfigCurrentConfiguration)dlsym(xrandr, "XRRConfigCurrentConfiguration"); _XRRSetScreenConfig = (pfn_XRRSetScreenConfig)dlsym(xrandr, "XRRSetScreenConfig"); + int event, error, major, minor; if (_XRRQueryExtension == nullptr || _XRRSizes == nullptr || _XRRRates == nullptr || _XRRGetScreenInfo == nullptr || _XRRConfigCurrentConfiguration == nullptr || - _XRRSetScreenConfig == nullptr) { + _XRRSetScreenConfig == nullptr || _XRRQueryVersion == nullptr) { _have_xrandr = false; x11display_cat.warning() << "libXrandr.so.2 does not provide required functions; resolution setting will not work.\n"; + } + else if (_XRRQueryExtension(_display, &event, &error) && + _XRRQueryVersion(_display, &major, &minor)) { + _have_xrandr = true; + if (x11display_cat.is_debug()) { + x11display_cat.debug() + << "Found RandR extension " << major << "." << minor << "\n"; + } + + if (major > 1 || (major == 1 && minor >= 2)) { + if (major > 1 || (major == 1 && minor >= 3)) { + _XRRGetScreenResourcesCurrent = (pfn_XRRGetScreenResources) + dlsym(xrandr, "XRRGetScreenResourcesCurrent"); + } else { + // Fall back to this slower version. + _XRRGetScreenResourcesCurrent = (pfn_XRRGetScreenResources) + dlsym(xrandr, "XRRGetScreenResources"); + } + + _XRRFreeScreenResources = (pfn_XRRFreeScreenResources)dlsym(xrandr, "XRRFreeScreenResources"); + _XRRGetCrtcInfo = (pfn_XRRGetCrtcInfo)dlsym(xrandr, "XRRGetCrtcInfo"); + _XRRFreeCrtcInfo = (pfn_XRRFreeCrtcInfo)dlsym(xrandr, "XRRFreeCrtcInfo"); + } else { + _XRRGetScreenResourcesCurrent = nullptr; + _XRRFreeScreenResources = nullptr; + _XRRGetCrtcInfo = nullptr; + _XRRFreeCrtcInfo = nullptr; + } } else { - int event, error; - _have_xrandr = _XRRQueryExtension(_display, &event, &error); + _have_xrandr = false; + if (x11display_cat.is_debug()) { + x11display_cat.debug() + << "RandR extension not supported; resolution setting will not work.\n"; + } } } else { _have_xrandr = false; @@ -182,29 +216,61 @@ x11GraphicsPipe(const std::string &display) : // Use Xrandr to fill in the supported resolution list. if (_have_xrandr) { - int num_sizes, num_rates; - XRRScreenSize *xrrs; - xrrs = _XRRSizes(_display, 0, &num_sizes); - _display_information->_total_display_modes = 0; - for (int i = 0; i < num_sizes; ++i) { - _XRRRates(_display, 0, i, &num_rates); - _display_information->_total_display_modes += num_rates; - } + // If we have XRRGetScreenResources, we prefer that. It seems to be more + // reliable than XRRSizes in multi-monitor set-ups. + if (auto res = get_screen_resources()) { + if (x11display_cat.is_debug()) { + x11display_cat.debug() + << "Using XRRScreenResources to obtain display modes\n"; + } + _display_information->_total_display_modes = res->nmode; + _display_information->_display_mode_array = new DisplayMode[res->nmode]; + for (int i = 0; i < res->nmode; ++i) { + XRRModeInfo &mode = res->modes[i]; - short *rates; - short counter = 0; - _display_information->_display_mode_array = new DisplayMode[_display_information->_total_display_modes]; - for (int i = 0; i < num_sizes; ++i) { - int num_rates; - rates = _XRRRates(_display, 0, i, &num_rates); - for (int j = 0; j < num_rates; ++j) { - DisplayMode* dm = _display_information->_display_mode_array + counter; - dm->width = xrrs[i].width; - dm->height = xrrs[i].height; - dm->refresh_rate = rates[j]; + DisplayMode *dm = _display_information->_display_mode_array + i; + dm->width = mode.width; + dm->height = mode.height; dm->bits_per_pixel = -1; dm->fullscreen_only = false; - ++counter; + + if (mode.hTotal && mode.vTotal) { + dm->refresh_rate = (double)mode.dotClock / + ((double)mode.hTotal * (double)mode.vTotal); + } else { + dm->refresh_rate = 0; + } + } + } else { + if (x11display_cat.is_debug()) { + x11display_cat.debug() + << "Using XRRSizes and XRRRates to obtain display modes\n"; + } + + int num_sizes, num_rates; + XRRScreenSize *xrrs; + xrrs = _XRRSizes(_display, 0, &num_sizes); + _display_information->_total_display_modes = 0; + for (int i = 0; i < num_sizes; ++i) { + _XRRRates(_display, 0, i, &num_rates); + _display_information->_total_display_modes += num_rates; + } + + short *rates; + short counter = 0; + _display_information->_display_mode_array = new DisplayMode[_display_information->_total_display_modes]; + for (int i = 0; i < num_sizes; ++i) { + int num_rates; + rates = _XRRRates(_display, 0, i, &num_rates); + for (int j = 0; j < num_rates; ++j) { + DisplayMode* dm = _display_information->_display_mode_array + counter; + dm->width = xrrs[i].width; + dm->height = xrrs[i].height; + dm->refresh_rate = rates[j]; + dm->bits_per_pixel = -1; + dm->fullscreen_only = false; + ++counter; + } } } } @@ -258,6 +324,69 @@ x11GraphicsPipe:: } } +/** + * Returns an XRRScreenResources object, or null if RandR 1.2 is not supported. + */ +std::unique_ptr x11GraphicsPipe:: +get_screen_resources() const { + XRRScreenResources *res = nullptr; + + if (_have_xrandr && _XRRGetScreenResourcesCurrent != nullptr) { + res = _XRRGetScreenResourcesCurrent(_display, _root); + } + + return std::unique_ptr(res, _XRRFreeScreenResources); +} + +/** + * Returns an XRRCrtcInfo object, or null if RandR 1.2 is not supported. + */ +std::unique_ptr x11GraphicsPipe:: +get_crtc_info(XRRScreenResources *res, RRCrtc crtc) const { + XRRCrtcInfo *info = nullptr; + + if (_have_xrandr && _XRRGetCrtcInfo != nullptr) { + info = _XRRGetCrtcInfo(_display, res, crtc); + } + + return std::unique_ptr(info, _XRRFreeCrtcInfo); +} + +/** + * Finds a CRTC for going fullscreen to, at the given origin. The new CRTC + * is returned, along with its x, y, width and height. + * + * If the required RandR extension is not supported, a value of None will be + * returned, but x, y, width and height will still be populated. + */ +RRCrtc x11GraphicsPipe:: +find_fullscreen_crtc(const LPoint2i &point, + int &x, int &y, int &width, int &height) { + x = 0; + y = 0; + width = DisplayWidth(_display, _screen); + height = DisplayHeight(_display, _screen); + + if (auto res = get_screen_resources()) { + for (int i = 0; i < res->ncrtc; ++i) { + RRCrtc crtc = res->crtcs[i]; + if (auto info = get_crtc_info(res.get(), crtc)) { + if (point[0] >= info->x && point[0] < info->x + info->width && + point[1] >= info->y && point[1] < info->y + info->height) { + + x = info->x; + y = info->y; + width = info->width; + height = info->height; + return crtc; + } + } + } + } + + return None; +} + /** * Returns an indication of the thread in which this GraphicsPipe requires its * window processing to be performed: typically either the app thread (e.g. diff --git a/panda/src/x11display/x11GraphicsPipe.h b/panda/src/x11display/x11GraphicsPipe.h index 3b8802e6e2..e1abf80b00 100644 --- a/panda/src/x11display/x11GraphicsPipe.h +++ b/panda/src/x11display/x11GraphicsPipe.h @@ -32,12 +32,61 @@ typedef struct _XcursorImages XcursorImages; typedef unsigned short Rotation; typedef unsigned short SizeID; +typedef unsigned long XRRModeFlags; +typedef XID RROutput; +typedef XID RRCrtc; +typedef XID RRMode; + typedef struct _XRRScreenConfiguration XRRScreenConfiguration; typedef struct { int width, height; int mwidth, mheight; } XRRScreenSize; +typedef struct _XRRModeInfo { + RRMode id; + unsigned int width; + unsigned int height; + unsigned long dotClock; + unsigned int hSyncStart; + unsigned int hSyncEnd; + unsigned int hTotal; + unsigned int hSkew; + unsigned int vSyncStart; + unsigned int vSyncEnd; + unsigned int vTotal; + char *name; + unsigned int nameLength; + XRRModeFlags modeFlags; +} XRRModeInfo; + +typedef struct _XRRScreenResources { + Time timestamp; + Time configTimestamp; + int ncrtc; + RRCrtc *crtcs; + int noutput; + RROutput *outputs; + int nmode; + XRRModeInfo *modes; +} XRRScreenResources; + +typedef struct _XRRCrtcInfo { + Time timestamp; + int x, y; + unsigned int width, height; + RRMode mode; + Rotation rotation; + int noutput; + RROutput *outputs; + Rotation rotations; + int npossible; + RROutput *possible; +} XRRCrtcInfo; + +typedef void (*pfn_XRRFreeScreenResources)(XRRScreenResources *resources); +typedef void (*pfn_XRRFreeCrtcInfo)(XRRCrtcInfo *crtcInfo); + class FrameBufferProperties; /** @@ -64,6 +113,12 @@ public: static INLINE int enable_x_error_messages(); static INLINE int get_x_error_count(); + std::unique_ptr get_screen_resources() const; + std::unique_ptr get_crtc_info(XRRScreenResources *res, RRCrtc crtc) const; + + RRCrtc find_fullscreen_crtc(const LPoint2i &point, + int &x, int &y, int &width, int &height); + public: virtual PreferredWindowThread get_preferred_window_thread() const; @@ -100,6 +155,7 @@ public: pfn_XcursorImageDestroy _XcursorImageDestroy; typedef Bool (*pfn_XRRQueryExtension)(X11_Display *, int*, int*); + typedef Status (*pfn_XRRQueryVersion)(X11_Display *, int*, int*); typedef XRRScreenSize *(*pfn_XRRSizes)(X11_Display*, int, int*); typedef short *(*pfn_XRRRates)(X11_Display*, int, int, int*); typedef XRRScreenConfiguration *(*pfn_XRRGetScreenInfo)(X11_Display*, X11_Window); @@ -126,6 +182,14 @@ protected: typedef Status (*pfn_XF86DGADirectVideo)(X11_Display *, int, int); pfn_XF86DGADirectVideo _XF86DGADirectVideo; + typedef XRRScreenResources *(*pfn_XRRGetScreenResources)(X11_Display*, X11_Window); + typedef XRRCrtcInfo *(*pfn_XRRGetCrtcInfo)(X11_Display *dpy, XRRScreenResources *resources, RRCrtc crtc); + + pfn_XRRGetScreenResources _XRRGetScreenResourcesCurrent; + pfn_XRRFreeScreenResources _XRRFreeScreenResources; + pfn_XRRGetCrtcInfo _XRRGetCrtcInfo; + pfn_XRRFreeCrtcInfo _XRRFreeCrtcInfo; + private: void make_hidden_cursor(); void release_hidden_cursor(); diff --git a/panda/src/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index df038a6dad..0a5d969bdf 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -579,20 +579,66 @@ set_properties_now(WindowProperties &properties) { bool is_fullscreen = _properties.has_fullscreen() && _properties.get_fullscreen(); bool want_fullscreen = properties.has_fullscreen() ? properties.get_fullscreen() : is_fullscreen; + if (want_fullscreen && properties.has_origin()) { + // If we're fullscreen, reject changes to the origin. + properties.clear_origin(); + } + if (is_fullscreen != want_fullscreen || (is_fullscreen && properties.has_size())) { if (want_fullscreen) { - if (x11_pipe->_have_xrandr) { - XRRScreenConfiguration* conf = _XRRGetScreenInfo(_display, x11_pipe->get_root()); + // OK, first figure out which CRTC the window is on. It may be on more + // than one, actually, so grab a point in the center in order to figure + // out which one it's more-or-less mostly on. + LPoint2i center(0, 0); + if (_properties.has_origin()) { + center = _properties.get_origin(); + if (_properties.has_size()) { + center += _properties.get_size() / 2; + } + } + int x, y, width, height; + x11_pipe->find_fullscreen_crtc(center, x, y, width, height); + + // Which size should we go fullscreen in? + int reqsizex, reqsizey; + if (properties.has_size()) { + reqsizex = properties.get_x_size(); + reqsizey = properties.get_y_size(); + } else if (_properties.has_size()) { + reqsizex = _properties.get_x_size(); + reqsizey = _properties.get_y_size(); + } else { + reqsizex = width; + reqsizey = height; + } + + // Are we passing in pipe.display_width/height? This is actually the + // size of the virtual desktop, which may not be a real resolution, so + // if that is passed in, we have to assume that the user means to just + // fullscreen without changing the screen resolution. + if ((reqsizex == x11_pipe->get_display_width() && + reqsizey == x11_pipe->get_display_height()) + || (width == reqsizex && height == reqsizey) + || !x11_pipe->_have_xrandr) { + + // Cover the current CRTC. + properties.set_origin(x, y); + properties.set_size(width, height); + + if (x11display_cat.is_debug()) { + x11display_cat.debug() + << "Setting window to fullscreen on CRTC " + << width << "x" << height << "+" << x << "+" << y << "\n"; + } + } else { + // We may need to change the screen resolution. The code below is + // suboptimal; in the future, we probably want to only touch the CRTC + // that the window is on. + XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, _xwindow ? _xwindow : x11_pipe->get_root()); SizeID old_size_id = x11_pipe->_XRRConfigCurrentConfiguration(conf, &_orig_rotation); SizeID new_size_id = (SizeID) -1; - int num_sizes = 0, reqsizex, reqsizey; - if (properties.has_size()) { - reqsizex = properties.get_x_size(); - reqsizey = properties.get_y_size(); - } else { - reqsizex = _properties.get_x_size(); - reqsizey = _properties.get_y_size(); - } + int num_sizes = 0; + XRRScreenSize *xrrs; xrrs = x11_pipe->_XRRSizes(_display, 0, &num_sizes); for (int i = 0; i < num_sizes; ++i) { @@ -605,21 +651,29 @@ set_properties_now(WindowProperties &properties) { x11display_cat.error() << "Videocard has no supported display resolutions at specified res (" << reqsizex << " x " << reqsizey << ")\n"; - } else { - if (new_size_id != old_size_id) { + // Just go fullscreen at native resolution, then. + properties.set_origin(x, y); + properties.set_size(width, height); + } else { + if (x11display_cat.is_debug()) { + x11display_cat.debug() + << "Switching to fullscreen with resolution " + << reqsizex << "x" << reqsizey << "\n"; + } + + if (new_size_id != old_size_id) { _XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), new_size_id, _orig_rotation, CurrentTime); if (_orig_size_id == (SizeID) -1) { // Remember the original resolution so we can switch back to it. _orig_size_id = old_size_id; } + + // Since the above changes the entire screen configuration, we + // have to set the origin to 0, 0. + properties.set_origin(0, 0); } } - } else { - // If we don't have Xrandr support, we fake the fullscreen support by - // setting the window size to the desktop size. - properties.set_size(x11_pipe->get_display_width(), - x11_pipe->get_display_height()); } } else { // Change the resolution back to what it was. Don't remove the SizeID @@ -962,34 +1016,6 @@ open_window() { // Make sure we are not making X11 calls from other threads. LightReMutexHolder holder(x11GraphicsPipe::_x_mutex); - if (_properties.get_fullscreen() && x11_pipe->_have_xrandr) { - XRRScreenConfiguration* conf = _XRRGetScreenInfo(_display, x11_pipe->get_root()); - if (_orig_size_id == (SizeID) -1) { - _orig_size_id = x11_pipe->_XRRConfigCurrentConfiguration(conf, &_orig_rotation); - } - int num_sizes, new_size_id = -1; - XRRScreenSize *xrrs; - xrrs = x11_pipe->_XRRSizes(_display, 0, &num_sizes); - for (int i = 0; i < num_sizes; ++i) { - if (xrrs[i].width == _properties.get_x_size() && - xrrs[i].height == _properties.get_y_size()) { - new_size_id = i; - } - } - if (new_size_id == -1) { - x11display_cat.error() - << "Videocard has no supported display resolutions at specified res (" - << _properties.get_x_size() << " x " << _properties.get_y_size() <<")\n"; - _orig_size_id = -1; - return false; - } - if (new_size_id != _orig_size_id) { - _XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), new_size_id, _orig_rotation, CurrentTime); - } else { - _orig_size_id = -1; - } - } - X11_Window parent_window = x11_pipe->get_root(); WindowHandle *window_handle = _properties.get_parent_window(); if (window_handle != nullptr) { @@ -1130,13 +1156,8 @@ set_wm_properties(const WindowProperties &properties, bool already_mapped) { size_hints_p = XAllocSizeHints(); if (size_hints_p != nullptr) { if (properties.has_origin()) { - if (_properties.get_fullscreen()) { - size_hints_p->x = 0; - size_hints_p->y = 0; - } else { - size_hints_p->x = properties.get_x_origin(); - size_hints_p->y = properties.get_y_origin(); - } + size_hints_p->x = properties.get_x_origin(); + size_hints_p->y = properties.get_y_origin(); size_hints_p->flags |= USPosition; } LVecBase2i size = _properties.get_size(); @@ -1976,6 +1997,8 @@ get_keyboard_map() const { // XkbGetMap(_display, XkbAllMapComponentsMask, XkbUseCoreKbd); ButtonMap *map = new ButtonMap; + LightReMutexHolder holder(x11GraphicsPipe::_x_mutex); + for (int k = 9; k <= 135; ++k) { ButtonHandle raw_button = map_raw_button(k); if (raw_button == ButtonHandle::none()) { diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index 8533fbaa64..9801d6b13b 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/pandatool/src/assimp/assimpLoader.cxx @@ -129,6 +129,16 @@ read(const Filename &filename) { if (assimp_flip_winding_order) { flags |= aiProcess_FlipWindingOrder; } + if (assimp_gen_normals) { + if (assimp_smooth_normal_angle == 0.0) { + flags |= aiProcess_GenNormals; + } + else { + flags |= aiProcess_GenSmoothNormals; + _importer.SetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, + assimp_smooth_normal_angle); + } + } _scene = _importer.ReadFile(_filename.c_str(), flags); if (_scene == nullptr) { @@ -169,6 +179,7 @@ build_graph() { // And then the meshes. _geoms = new PT(Geom)[_scene->mNumMeshes]; _geom_matindices = new unsigned int[_scene->mNumMeshes]; + _characters = new PT(Character)[_scene->mNumMeshes]; for (size_t i = 0; i < _scene->mNumMeshes; ++i) { load_mesh(i); } @@ -187,6 +198,7 @@ build_graph() { delete[] _mat_states; delete[] _geoms; delete[] _geom_matindices; + delete[] _characters; } /** @@ -221,8 +233,11 @@ load_texture(size_t index) { if (tex.mHeight == 0) { // Compressed texture. - assimp_cat.debug() - << "Reading embedded compressed texture with format " << tex.achFormatHint << " and size " << tex.mWidth << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Reading embedded compressed texture with format " + << tex.achFormatHint << " and size " << tex.mWidth << "\n"; + } stringstream str; str.write((char*) tex.pcData, tex.mWidth); @@ -248,8 +263,10 @@ load_texture(size_t index) { } } } else { - assimp_cat.debug() - << "Reading embedded raw texture with size " << tex.mWidth << "x" << tex.mHeight << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Reading embedded raw texture with size " << tex.mWidth << "x" << tex.mHeight << "\n"; + } ptex->setup_2d_texture(tex.mWidth, tex.mHeight, Texture::T_unsigned_byte, Texture::F_rgba); PTA_uchar data = ptex->modify_ram_image(); @@ -435,8 +452,10 @@ create_joint(Character *character, CharacterJointBundle *bundle, PartGroup *pare t.a4, t.b4, t.c4, t.d4); PT(CharacterJoint) joint = new CharacterJoint(character, bundle, parent, node.mName.C_Str(), mat); - assimp_cat.debug() - << "Creating joint for: " << node.mName.C_Str() << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Creating joint for: " << node.mName.C_Str() << "\n"; + } for (size_t i = 0; i < node.mNumChildren; ++i) { if (_bonemap.find(node.mChildren[i]->mName.C_Str()) != _bonemap.end()) { @@ -461,8 +480,10 @@ create_anim_channel(const aiAnimation &anim, AnimBundle *bundle, AnimGroup *pare } if (node_anim) { - assimp_cat.debug() - << "Found channel for node: " << node.mName.C_Str() << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Found channel for node: " << node.mName.C_Str() << "\n"; + } // assimp_cat.debug() << "Num Position Keys " << // node_anim->mNumPositionKeys << "\n"; assimp_cat.debug() << "Num // Rotation Keys " << node_anim->mNumRotationKeys << "\n"; @@ -510,7 +531,7 @@ create_anim_channel(const aiAnimation &anim, AnimBundle *bundle, AnimGroup *pare group->set_table('j', tablej); group->set_table('k', tablek); } - else { + else if (assimp_cat.is_debug()) { assimp_cat.debug() << "No channel found for node: " << node.mName.C_Str() << "\n"; } @@ -533,8 +554,11 @@ load_mesh(size_t index) { // Check if we need to make a Character PT(Character) character = nullptr; if (mesh.HasBones()) { - assimp_cat.debug() - << "Creating character for " << mesh.mName.C_Str() << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Creating character for mesh '" << mesh.mName.C_Str() << "' with " + << mesh.mNumBones << " bones\n"; + } // Find and add all bone nodes to the bone map for (size_t i = 0; i < mesh.mNumBones; ++i) { @@ -574,8 +598,10 @@ load_mesh(size_t index) { const aiBone &bone = *mesh.mBones[i]; CharacterJoint *joint = character->find_joint(bone.mName.C_Str()); if (joint == nullptr) { - assimp_cat.debug() - << "Could not find joint for bone: " << bone.mName.C_Str() << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Could not find joint for bone: " << bone.mName.C_Str() << "\n"; + } continue; } @@ -617,11 +643,17 @@ load_mesh(size_t index) { aiAnimation &ai_anim = *_scene->mAnimations[i]; bool convert_anim = false; - assimp_cat.debug() - << "Checking to see if anim (" << ai_anim.mName.C_Str() << ") matches character (" << mesh.mName.C_Str() << ")\n"; - for (size_t j = 0; j < ai_anim.mNumChannels; ++j) { + if (assimp_cat.is_debug()) { assimp_cat.debug() - << "Searching for " << ai_anim.mChannels[j]->mNodeName.C_Str() << " in bone map" << "\n"; + << "Checking to see if anim (" << ai_anim.mName.C_Str() + << ") matches character (" << mesh.mName.C_Str() << ")\n"; + } + for (size_t j = 0; j < ai_anim.mNumChannels; ++j) { + if (assimp_cat.is_spam()) { + assimp_cat.spam() + << "Searching for " << ai_anim.mChannels[j]->mNodeName.C_Str() + << " in bone map" << "\n"; + } if (_bonemap.find(ai_anim.mChannels[j]->mNodeName.C_Str()) != _bonemap.end()) { convert_anim = true; break; @@ -629,8 +661,11 @@ load_mesh(size_t index) { } if (convert_anim) { - assimp_cat.debug() - << "Found animation (" << ai_anim.mName.C_Str() << ") for character (" << mesh.mName.C_Str() << ")\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Found animation (" << ai_anim.mName.C_Str() + << ") for character (" << mesh.mName.C_Str() << ")\n"; + } // Now create the animation unsigned int frames = 0; @@ -646,10 +681,12 @@ load_mesh(size_t index) { } } PN_stdfloat fps = frames / (ai_anim.mTicksPerSecond * ai_anim.mDuration); - assimp_cat.debug() - << "FPS " << fps << "\n"; - assimp_cat.debug() - << "Frames " << frames << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "FPS " << fps << "\n"; + assimp_cat.debug() + << "Frames " << frames << "\n"; + } PT(AnimBundle) bundle = new AnimBundle(mesh.mName.C_Str(), fps, frames); PT(AnimGroup) skeleton = new AnimGroup(bundle, ""); @@ -800,38 +837,104 @@ load_mesh(size_t index) { _geom_matindices[index] = mesh.mMaterialIndex; if (character) { - _charmap[mesh.mName.C_Str()] = character; + _characters[index] = character; + + PT(GeomNode) gnode = new GeomNode(""); + gnode->add_geom(geom); + gnode->set_state(_mat_states[mesh.mMaterialIndex]); + character->add_child(gnode); } } /** - * Converts an aiNode into a PandaNode. + * Converts an aiNode into a PandaNode. Returns true if the node had anything + * of interest under it, false otherwise. */ -void AssimpLoader:: -load_node(const aiNode &node, PandaNode *parent) { +bool AssimpLoader:: +load_node(const aiNode &node, PandaNode *parent, bool under_joint) { PT(PandaNode) pnode; - PT(Character) character; - - // Skip nodes we've converted to joints - if (_bonemap.find(node.mName.C_Str()) != _bonemap.end()) { - return; - } - - // Create the node and give it a name. string name (node.mName.data, node.mName.length); - if (node.mNumMeshes > 0) { - pnode = new GeomNode(name); - } else { - pnode = new PandaNode(name); + + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Converting node '" << name << "' with " << node.mNumMeshes + << " meshes and " << node.mNumChildren << " children\n"; } - if (_charmap.find(node.mName.C_Str()) != _charmap.end()) { - character = _charmap[node.mName.C_Str()]; - parent->add_child(character); - } else { - parent->add_child(pnode); + if (!under_joint) { + under_joint = (_bonemap.find(node.mName.C_Str()) != _bonemap.end()); } + bool prune = false; + + if (node.mNumMeshes == 0) { + pnode = new PandaNode(name); + + // Possibly prune this if this is a joint or under a joint. + prune = under_joint; + } + else if (node.mNumMeshes == 1) { + size_t meshIndex = node.mMeshes[0]; + + Character *character = _characters[meshIndex]; + if (character != nullptr) { + pnode = new PandaNode(name); + pnode->add_child(character); + } else { + const RenderState *state = _mat_states[_geom_matindices[meshIndex]]; + PT(GeomNode) gnode = new GeomNode(name); + gnode->add_geom(_geoms[meshIndex]); + if (state != nullptr) { + // Only set the state on the GeomNode if there are no child nodes. + if (node.mNumChildren == 0) { + gnode->set_state(state); + } else { + gnode->set_geom_state(0, state); + } + } + pnode = gnode; + } + } + else { + // Do we have regular meshes or just animated meshes? + bool character_only = true; + + // First add all the regular meshes. + for (size_t i = 0; i < node.mNumMeshes; ++i) { + size_t meshIndex = node.mMeshes[i]; + + if (_characters[meshIndex] == nullptr) { + character_only = false; + break; + } + } + + PT(GeomNode) gnode; + if (character_only) { + pnode = new PandaNode(name); + } else { + gnode = new GeomNode(name); + pnode = gnode; + } + + for (size_t i = 0; i < node.mNumMeshes; ++i) { + size_t meshIndex = node.mMeshes[i]; + + Character *character = _characters[meshIndex]; + if (character != nullptr) { + // An animated mesh, which already is converted as Character with an + // attached GeomNode. + pnode->add_child(character); + } else { + // A non-animated mesh. + gnode->add_geom(_geoms[node.mMeshes[i]], + _mat_states[_geom_matindices[meshIndex]]); + } + } + } + + parent->add_child(pnode); + // Load in the transformation matrix. const aiMatrix4x4 &t = node.mTransformation; if (!t.IsIdentity()) { @@ -843,31 +946,21 @@ load_node(const aiNode &node, PandaNode *parent) { } for (size_t i = 0; i < node.mNumChildren; ++i) { - load_node(*node.mChildren[i], pnode); + if (load_node(*node.mChildren[i], pnode, under_joint)) { + prune = false; + } } - if (node.mNumMeshes > 0) { - // Remember, we created this as GeomNode earlier. - PT(GeomNode) gnode = DCAST(GeomNode, pnode); - size_t meshIndex; - - // If there's only mesh, don't bother using a per-geom state. - if (node.mNumMeshes == 1) { - meshIndex = node.mMeshes[0]; - gnode->add_geom(_geoms[meshIndex]); - gnode->set_state(_mat_states[_geom_matindices[meshIndex]]); - } else { - for (size_t i = 0; i < node.mNumMeshes; ++i) { - meshIndex = node.mMeshes[i]; - gnode->add_geom(_geoms[node.mMeshes[i]], - _mat_states[_geom_matindices[meshIndex]]); - } - } - - if (character) { - assimp_cat.debug() << "Adding char to geom\n"; - character->add_child(gnode); + if (prune) { + // This is an empty node in a hierarchy of joints, prune it. + parent->remove_child(pnode); + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Pruning node '" << name << "'\n"; } + return false; + } else { + return true; } } @@ -877,7 +970,9 @@ load_node(const aiNode &node, PandaNode *parent) { void AssimpLoader:: load_light(const aiLight &light) { string name (light.mName.data, light.mName.length); - assimp_cat.debug() << "Found light '" << name << "'\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() << "Found light '" << name << "'\n"; + } aiColor3D col; aiVector3D vec; diff --git a/pandatool/src/assimp/assimpLoader.h b/pandatool/src/assimp/assimpLoader.h index 3133fee377..807dc219fa 100644 --- a/pandatool/src/assimp/assimpLoader.h +++ b/pandatool/src/assimp/assimpLoader.h @@ -35,7 +35,6 @@ struct char_cmp { } }; typedef pmap BoneMap; -typedef pmap CharacterMap; /** * Class that interfaces with Assimp and builds Panda nodes to represent the @@ -67,7 +66,7 @@ private: PT(Geom) *_geoms; unsigned int *_geom_matindices; BoneMap _bonemap; - CharacterMap _charmap; + PT(Character) *_characters; const aiNode *find_node(const aiNode &root, const aiString &name); @@ -77,7 +76,7 @@ private: void create_joint(Character *character, CharacterJointBundle *bundle, PartGroup *parent, const aiNode &node); void create_anim_channel(const aiAnimation &anim, AnimBundle *bundle, AnimGroup *parent, const aiNode &node); void load_mesh(size_t index); - void load_node(const aiNode &node, PandaNode *parent); + bool load_node(const aiNode &node, PandaNode *parent, bool under_joint = false); void load_light(const aiLight &light); }; diff --git a/pandatool/src/assimp/config_assimp.cxx b/pandatool/src/assimp/config_assimp.cxx index 2dbf29fa16..2b84d2c63b 100644 --- a/pandatool/src/assimp/config_assimp.cxx +++ b/pandatool/src/assimp/config_assimp.cxx @@ -69,6 +69,23 @@ ConfigVariableBool assimp_flip_winding_order "the Assimp loader. Note that you may need to clear the model-cache " "after changing this.")); +ConfigVariableBool assimp_gen_normals +("assimp-gen-normals", false, + PRC_DESC("Set this true to generate normals (if absent from file) on import. " + "See assimp-smooth-normal-angle for more information. " + "Note that you may need to clear the model-cache after " + "changing this.")); + +ConfigVariableDouble assimp_smooth_normal_angle +("assimp-smooth-normal-angle", 0.0, + PRC_DESC("Set this to anything other than 0.0 in degrees (so 180.0 is PI) to " + "specify the maximum angle that may be between two face normals at " + "the same vertex position that are smoothed together. Sometimes " + "referred to as 'crease angle'. Only has effect if " + "assimp-gen-normals is set to true and the file does not contain " + "normals. Note that you may need to clear the model-cache after " + "changing this.")); + /** * Initializes the library. This must be called at least once before any of * the functions or classes in this library can be used. Normally it will be diff --git a/pandatool/src/assimp/config_assimp.h b/pandatool/src/assimp/config_assimp.h index 16efc8a754..e17e2caae6 100644 --- a/pandatool/src/assimp/config_assimp.h +++ b/pandatool/src/assimp/config_assimp.h @@ -15,7 +15,9 @@ #define CONFIG_ASSIMP_H #include "pandatoolbase.h" +#include "notifyCategoryProxy.h" #include "configVariableBool.h" +#include "configVariableDouble.h" #include "dconfig.h" ConfigureDecl(config_assimp, EXPCL_ASSIMP, EXPTP_ASSIMP); @@ -29,6 +31,8 @@ extern ConfigVariableBool assimp_fix_infacing_normals; extern ConfigVariableBool assimp_optimize_meshes; extern ConfigVariableBool assimp_optimize_graph; extern ConfigVariableBool assimp_flip_winding_order; +extern ConfigVariableBool assimp_gen_normals; +extern ConfigVariableDouble assimp_smooth_normal_angle; extern EXPCL_ASSIMP void init_libassimp(); diff --git a/pandatool/src/assimp/loaderFileTypeAssimp.cxx b/pandatool/src/assimp/loaderFileTypeAssimp.cxx index 73c27ee7d4..f19ce58e2f 100644 --- a/pandatool/src/assimp/loaderFileTypeAssimp.cxx +++ b/pandatool/src/assimp/loaderFileTypeAssimp.cxx @@ -15,6 +15,8 @@ #include "config_assimp.h" #include "assimpLoader.h" +#include + using std::string; TypeHandle LoaderFileTypeAssimp::_type_handle; @@ -23,7 +25,7 @@ TypeHandle LoaderFileTypeAssimp::_type_handle; * */ LoaderFileTypeAssimp:: -LoaderFileTypeAssimp() : _loader(new AssimpLoader) { +LoaderFileTypeAssimp() { } /** @@ -31,9 +33,6 @@ LoaderFileTypeAssimp() : _loader(new AssimpLoader) { */ LoaderFileTypeAssimp:: ~LoaderFileTypeAssimp() { - if (_loader != nullptr) { - delete _loader; - } } /** @@ -58,9 +57,22 @@ get_extension() const { */ string LoaderFileTypeAssimp:: get_additional_extensions() const { - string exts; - _loader->get_extensions(exts); - return exts; + aiString aexts; + aiGetExtensionList(&aexts); + + // The format is like: *.mdc;*.mdl;*.mesh.xml;*.mot + std::string ext; + char *sub = strtok(aexts.data, ";"); + while (sub != nullptr) { + ext += sub + 2; + sub = strtok(nullptr, ";"); + + if (sub != nullptr) { + ext += ' '; + } + } + + return ext; } /** @@ -82,10 +94,13 @@ load_file(const Filename &path, const LoaderOptions &options, assimp_cat.info() << "Reading " << path << "\n"; - if (!_loader->read(path)) { + AssimpLoader loader; + loader.local_object(); + + if (!loader.read(path)) { return nullptr; } - _loader->build_graph(); - return DCAST(PandaNode, _loader->_root); + loader.build_graph(); + return DCAST(PandaNode, loader._root); } diff --git a/pandatool/src/assimp/loaderFileTypeAssimp.h b/pandatool/src/assimp/loaderFileTypeAssimp.h index 45d810f964..033ab9affa 100644 --- a/pandatool/src/assimp/loaderFileTypeAssimp.h +++ b/pandatool/src/assimp/loaderFileTypeAssimp.h @@ -36,9 +36,6 @@ public: virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &options, BamCacheRecord *record) const; -public: - AssimpLoader *_loader; - public: static TypeHandle get_class_type() { return _type_handle; diff --git a/pandatool/src/assimp/pandaLogger.cxx b/pandatool/src/assimp/pandaLogger.cxx index 2b92cfbc17..c9897533cf 100644 --- a/pandatool/src/assimp/pandaLogger.cxx +++ b/pandatool/src/assimp/pandaLogger.cxx @@ -35,7 +35,9 @@ set_default() { * */ void PandaLogger::OnDebug(const char *message) { - assimp_cat.debug() << message << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() << message << "\n"; + } } /** diff --git a/pandatool/src/cvscopy/cvsCopy.cxx b/pandatool/src/cvscopy/cvsCopy.cxx deleted file mode 100644 index 17dd07eb66..0000000000 --- a/pandatool/src/cvscopy/cvsCopy.cxx +++ /dev/null @@ -1,501 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file cvsCopy.cxx - * @author drose - * @date 2000-10-31 - */ - -#include "cvsCopy.h" -#include "cvsSourceDirectory.h" - -#include "pnotify.h" -#include - -using std::string; - -/** - * - */ -CVSCopy:: -CVSCopy() { - _model_dirname = "."; - _key_filename = "Sources.pp"; - _cvs_binary = "cvs"; - _user_aborted = false; - _model_dir = nullptr; - _map_dir = nullptr; - - clear_runlines(); - add_runline("[opts] file [file ... ]"); - - add_option - ("f", "", 80, - "Force the copy to happen without any input from the user. If a file " - "with the same name exists anywhere in the source hierarchy, it will " - "be overwritten without prompting; if a file does not yet exist, it " - "will be created in the directory named by -d or by -m, as appropriate.", - &CVSCopy::dispatch_none, &_force); - - add_option - ("i", "", 80, - "The opposite of -f, this will prompt the user before each action. " - "The default is only to prompt the user when an action is ambiguous " - "or unusual.", - &CVSCopy::dispatch_none, &_interactive); - - add_option - ("d", "dirname", 80, - "Copy model files that are not already present somewhere in the tree " - "to the indicated directory. The default is the current directory.", - &CVSCopy::dispatch_filename, &_got_model_dirname, &_model_dirname); - - add_option - ("m", "dirname", 80, - "Copy texture map files to the indicated directory. The default " - "is src/maps from the root directory.", - &CVSCopy::dispatch_filename, &_got_map_dirname, &_map_dirname); - - add_option - ("root", "dirname", 80, - "Specify the root of the CVS source hierarchy. The default is to " - "use the ppremake convention of locating the directory above the -d " - "directory that contains a file called Package.pp.", - &CVSCopy::dispatch_filename, &_got_root_dirname, &_root_dirname); - - add_option - ("key", "filename", 80, - "Specify the name of the file that must exist in each directory for " - "it to be considered part of the CVS source hierarchy. The default " - "is the ppremake convention, \"Sources.pp\". Other likely candidates " - "are \"CVS\" to search a CVS hierarchy, or \".\" to include " - "all subdirectories indiscriminately.", - &CVSCopy::dispatch_filename, nullptr, &_key_filename); - - add_option - ("nc", "", 80, - "Do not attempt to add newly-created files to CVS. The default " - "is to add them.", - &CVSCopy::dispatch_none, &_no_cvs); - - add_option - ("cvs", "cvs_binary", 80, - "Specify how to run the cvs program for adding newly-created files. " - "The default is simply \"cvs\".", - &CVSCopy::dispatch_string, nullptr, &_cvs_binary); -} - -/** - * Checks for the given filename somewhere in the directory hierarchy, and - * chooses a place to import it. Copies the file by calling copy_file(). - * - * Extra_data may be NULL or a pointer to some user-defined structure; CVSCopy - * simply passes it unchanged to copy_file(). It presumably gives the class a - * hint as to how the file should be copied. Suggested_dir is the suggested - * directory in which to copy the file, if it does not already exist - * elsewhere. - * - * On success, returns the FilePath it was actually copied to. On failure, - * returns an invalid FilePath. - */ -CVSSourceTree::FilePath CVSCopy:: -import(const Filename &source, void *extra_data, - CVSSourceDirectory *suggested_dir) { - CopiedFiles::const_iterator ci; - ci = _copied_files.find(source); - if (ci != _copied_files.end()) { - // We have already copied this file. - return (*ci).second; - } - - if (!source.exists()) { - nout << "Source filename " << source << " does not exist!\n"; - return CVSSourceTree::FilePath(); - } - - string basename = filter_filename(source.get_basename()); - - CVSSourceTree::FilePath path = - _tree.choose_directory(basename, suggested_dir, _force, _interactive); - nassertr(path.is_valid(), path); - - _copied_files[source] = path; - Filename dest = path.get_fullpath(); - - bool new_file = !dest.exists(); - if (!new_file && verify_file(source, dest, path._dir, extra_data)) { - // The file is unchanged. - nout << path.get_path() << " is unchanged.\n"; - - } else { - // The file has changed. - nout << "Copying " << basename << " to " << path.get_path() << "\n"; - - if (!copy_file(source, dest, path._dir, extra_data, new_file)) { - if (!continue_after_error()) { - return CVSSourceTree::FilePath(); - } - } else { - if (new_file) { - cvs_add(dest); - } - } - } - - return path; -} - -/** - * Prompts the user (unless -f was specified) if he wants to continue the copy - * operation after some error has occurred. Returns true to continue, false - * otherwise. - */ -bool CVSCopy:: -continue_after_error() { - if (_force) { - return true; - } - if (_user_aborted) { - return false; - } - - while (true) { - string result = prompt("Error occurred during copy! Continue (y/n)? "); - nassertr(!result.empty(), false); - if (result.size() == 1) { - if (tolower(result[0]) == 'y') { - return true; - } else if (tolower(result[0]) == 'n') { - _user_aborted = true; - return false; - } - } - - nout << "*** Invalid response: " << result << "\n\n"; - } -} - - -/** - * Does something with the additional arguments on the command line (after all - * the -options have been parsed). Returns true if the arguments are good, - * false otherwise. - */ -bool CVSCopy:: -handle_args(Args &args) { - if (args.empty()) { - nout << "You must specify the file(s) to copy from on the command line.\n"; - return false; - } - - for (Args::const_iterator ai = args.begin(); - ai != args.end(); - ++ai) { - _source_files.push_back(Filename::from_os_specific(*ai)); - } - return true; -} - -/** - * This is called after the command line has been completely processed, and it - * gives the program a chance to do some last-minute processing and validation - * of the options and arguments. It should return true if everything is fine, - * false if there is an error. - */ -bool CVSCopy:: -post_command_line() { - if (!scan_hierarchy()) { - return false; - } - - _model_dir = _tree.find_directory(_model_dirname); - if (_model_dir == nullptr) { - if (_got_model_dirname) { - nout << "Warning: model directory " << _model_dirname - << " is not within the source hierarchy.\n"; - } - } - - if (_got_map_dirname) { - _map_dir = _tree.find_directory(_map_dirname); - - if (_map_dir == nullptr) { - nout << "Warning: map directory " << _map_dirname - << " is not within the source hierarchy.\n"; - } - - } else { - _map_dir = _tree.find_relpath("src/maps"); - - if (_map_dir == nullptr) { - nout << "Warning: no directory " << _tree.get_root_dirname() - << "/src/maps.\n"; - _map_dir = _model_dir; - } - } - - return true; -} - - -/** - * Verifies that the file is identical and does not need to be recopied. - * Returns true if the files are identical, false if they differ. - */ -bool CVSCopy:: -verify_file(const Filename &, const Filename &, - CVSSourceDirectory *, void *) { - return false; -} - -/** - * Verifies that the file is identical and does not need to be recopied. - * Returns true if the files are identical, false if they differ. - */ -bool CVSCopy:: -verify_binary_file(Filename source, Filename dest) { - if (source == dest) { - return true; - } - - source.set_binary(); - dest.set_binary(); - - std::ifstream s, d; - if (!source.open_read(s)) { - return false; - } - if (!dest.open_read(d)) { - return false; - } - - int cs, cd; - cs = s.get(); - cd = d.get(); - while (!s.eof() && !s.fail() && !d.eof() && !d.fail()) { - if (cs != cd) { - return false; - } - cs = s.get(); - cd = d.get(); - } - - if (s.fail() || d.fail()) { - // If we had some read error, call the files different. - return false; - } - - // If we haven't reached the end of one of the files yet, that file is - // longer than the other one, and the files are therefore different. - if (!s.eof() || !d.eof()) { - return false; - } - - // Otherwise, the files are the same. - return true; -} - -/** - * Copies a file without modifying it or scanning it in any way. This is - * particularly useful for copying textures. This is provided as a - * convenience function for derived programs because so many model file - * formats will also require copying textures or other black-box files. - */ -bool CVSCopy:: -copy_binary_file(Filename source, Filename dest) { - if (source == dest) { - return true; - } - - source.set_binary(); - dest.set_binary(); - - std::ifstream in; - std::ofstream out; - if (!source.open_read(in)) { - nout << "Cannot read " << source << "\n"; - return false; - } - - dest.unlink(); - if (!dest.open_write(out)) { - nout << "Cannot write " << dest << "\n"; - return false; - } - - int c; - c = in.get(); - while (!in.eof() && !in.fail() && !out.fail()) { - out.put(c); - c = in.get(); - } - - if (!in.eof() && in.fail()) { - nout << "Error reading " << source << "\n"; - return false; - } - if (out.fail()) { - nout << "Error writing " << dest << "\n"; - return false; - } - - return true; -} - -/** - * Invokes CVS to add the indicated filename to the repository, if the user so - * requested. Returns true if successful, false if there is an error. - */ -bool CVSCopy:: -cvs_add(const Filename &filename) { - if (_no_cvs) { - return true; - } - - if (!CVSSourceTree::temp_chdir(filename.get_dirname())) { - nout << "Invalid directory: " << filename.get_dirname() << "\n"; - return false; - } - - string command = _cvs_binary + " add -kb " + - protect_from_shell(filename.get_basename()); - nout << command << "\n"; - int result = system(command.c_str()); - - CVSSourceTree::restore_cwd(); - - if (result != 0) { - nout << "Failure invoking cvs.\n"; - return false; - } - return true; -} - -/** - * Inserts escape characters into the indicated source string to protect it - * from the shell, so that it may be given on the command line. Returns the - * modified string. - */ -string CVSCopy:: -protect_from_shell(const string &source) { - string result; - - for (string::const_iterator pi = source.begin(); pi != source.end(); ++pi) { - switch (*pi) { - case '\\': - case ' ': - case '\'': - case '"': - case '(': - case ')': - case '<': - case '>': - case '|': - case '&': - case '!': - case '$': - case '~': - case '*': - case '?': - case '[': - case ']': - case ';': - result += '\\'; - // fall through - - default: - result += *pi; - } - } - - return result; -} - -/** - * Given a source filename (including the basename only, without a dirname), - * return the appropriate corresponding filename within the source directory. - * This may be used by derived classes to, for instance, strip a version - * number from the filename. - */ -string CVSCopy:: -filter_filename(const string &source) { - return source; -} - -/** - * Starts the scan of the source hierarchy. This identifies all of the files - * in the source hierarchy we're to copy these into, so we can guess where - * referenced files should be placed. Returns true if everything is ok, false - * if there is an error. - */ -bool CVSCopy:: -scan_hierarchy() { - if (!_got_root_dirname) { - // If we didn't get a root directory name, find the directory above this - // one that contains the file "Package.pp". - if (!scan_for_root(_model_dirname)) { - return false; - } - } - - _tree.set_root(_root_dirname); - nout << "Root is " << _tree.get_root_fullpath() << "\n"; - - return _tree.scan(_key_filename); -} - -/** - * Searches for the root of the source directory by looking for the parent - * directory that contains "Package.pp". Returns true on success, false on - * failure. - */ -bool CVSCopy:: -scan_for_root(const string &dirname) { - Filename sources = dirname + "/Sources.pp"; - if (!sources.exists()) { - nout << "Couldn't find " << sources << " in source directory.\n"; - return false; - } - Filename package = dirname + "/Package.pp"; - if (package.exists()) { - // Here's the root! - _root_dirname = dirname; - return true; - } - - return scan_for_root(dirname + "/.."); -} - -/** - * Issues a prompt to the user and waits for a typed response. Returns the - * response (which will not be empty). - */ -string CVSCopy:: -prompt(const string &message) { - nout << std::flush; - while (true) { - std::cerr << message << std::flush; - std::string response; - std::getline(std::cin, response); - - // Remove leading and trailing whitespace. - size_t p = 0; - while (p < response.length() && isspace(response[p])) { - p++; - } - - size_t q = response.length(); - while (q > p && isspace(response[q - 1])) { - q--; - } - - if (q > p) { - return response.substr(p, q - p); - } - } -} diff --git a/pandatool/src/cvscopy/cvsCopy.h b/pandatool/src/cvscopy/cvsCopy.h deleted file mode 100644 index d820edf84d..0000000000 --- a/pandatool/src/cvscopy/cvsCopy.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file cvsCopy.h - * @author drose - * @date 2000-10-31 - */ - -#ifndef CVSCOPY_H -#define CVSCOPY_H - -#include "pandatoolbase.h" - -#include "cvsSourceTree.h" - -#include "programBase.h" -#include "filename.h" -#include "pvector.h" - -/** - * This is the base class for a family of programs that copy files, typically - * model files like .flt files and their associated textures, into a CVS- - * controlled source tree. - */ -class CVSCopy : public ProgramBase { -public: - CVSCopy(); - - CVSSourceTree::FilePath - import(const Filename &source, void *extra_data, - CVSSourceDirectory *suggested_dir); - - bool continue_after_error(); - -protected: - virtual bool handle_args(Args &args); - virtual bool post_command_line(); - - virtual bool verify_file(const Filename &source, const Filename &dest, - CVSSourceDirectory *dest_dir, - void *extra_data); - virtual bool copy_file(const Filename &source, const Filename &dest, - CVSSourceDirectory *dest_dir, - void *extra_data, bool new_file)=0; - - bool verify_binary_file(Filename source, Filename dest); - bool copy_binary_file(Filename source, Filename dest); - - bool cvs_add(const Filename &filename); - static std::string protect_from_shell(const std::string &source); - - virtual std::string filter_filename(const std::string &source); - -private: - bool scan_hierarchy(); - bool scan_for_root(const std::string &dirname); - std::string prompt(const std::string &message); - -protected: - bool _force; - bool _interactive; - bool _got_model_dirname; - Filename _model_dirname; - bool _got_map_dirname; - Filename _map_dirname; - bool _got_root_dirname; - Filename _root_dirname; - Filename _key_filename; - bool _no_cvs; - std::string _cvs_binary; - bool _user_aborted; - - typedef pvector SourceFiles; - SourceFiles _source_files; - - CVSSourceTree _tree; - CVSSourceDirectory *_model_dir; - CVSSourceDirectory *_map_dir; - - typedef pmap CopiedFiles; - CopiedFiles _copied_files; -}; - -#endif diff --git a/pandatool/src/cvscopy/cvsSourceDirectory.cxx b/pandatool/src/cvscopy/cvsSourceDirectory.cxx deleted file mode 100644 index 0eb741044d..0000000000 --- a/pandatool/src/cvscopy/cvsSourceDirectory.cxx +++ /dev/null @@ -1,237 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file cvsSourceDirectory.cxx - * @author drose - * @date 2000-10-31 - */ - -#include "cvsSourceDirectory.h" -#include "cvsSourceTree.h" -#include "string_utils.h" - -#include "pnotify.h" - -using std::string; - -/** - * - */ -CVSSourceDirectory:: -CVSSourceDirectory(CVSSourceTree *tree, CVSSourceDirectory *parent, - const string &dirname) : - _tree(tree), - _parent(parent), - _dirname(dirname) -{ - if (_parent == nullptr) { - _depth = 0; - } else { - _depth = _parent->_depth + 1; - } -} - -/** - * - */ -CVSSourceDirectory:: -~CVSSourceDirectory() { - Children::iterator ci; - for (ci = _children.begin(); ci != _children.end(); ++ci) { - delete (*ci); - } -} - -/** - * Returns the local name of this particular directory. - */ -string CVSSourceDirectory:: -get_dirname() const { - return _dirname; -} - -/** - * Returns the full pathname to this particular directory. - */ -Filename CVSSourceDirectory:: -get_fullpath() const { - if (_parent == nullptr) { - return _tree->get_root_fullpath(); - } - return Filename(_parent->get_fullpath(), _dirname); -} - -/** - * Returns the relative pathname to this particular directory, as seen from - * the root of the tree. - */ -Filename CVSSourceDirectory:: -get_path() const { - if (_parent == nullptr) { - return _dirname; - } - return Filename(_parent->get_path(), _dirname); -} - -/** - * Returns the relative path to the other directory from this one. This does - * not include a trailing slash. - */ -Filename CVSSourceDirectory:: -get_rel_to(const CVSSourceDirectory *other) const { - const CVSSourceDirectory *a = this; - const CVSSourceDirectory *b = other; - - if (a == b) { - return "."; - } - - string prefix, postfix; - while (a->_depth > b->_depth) { - prefix += "../"; - a = a->_parent; - nassertr(a != nullptr, string()); - } - - while (b->_depth > a->_depth) { - postfix = b->_dirname + "/" + postfix; - b = b->_parent; - nassertr(b != nullptr, string()); - } - - while (a != b) { - prefix += "../"; - postfix = b->_dirname + "/" + postfix; - a = a->_parent; - b = b->_parent; - nassertr(a != nullptr, string()); - nassertr(b != nullptr, string()); - } - - string result = prefix + postfix; - nassertr(!result.empty(), string()); - return result.substr(0, result.length() - 1); -} - -/** - * Returns the number of subdirectories below this directory. - */ -int CVSSourceDirectory:: -get_num_children() const { - return _children.size(); -} - -/** - * Returns the nth subdirectory below this directory. - */ -CVSSourceDirectory *CVSSourceDirectory:: -get_child(int n) const { - nassertr(n >= 0 && n < (int)_children.size(), nullptr); - return _children[n]; -} - -/** - * Returns the source directory that corresponds to the given relative path - * from this directory, or NULL if there is no match. - */ -CVSSourceDirectory *CVSSourceDirectory:: -find_relpath(const string &relpath) { - if (relpath.empty()) { - return this; - } - - size_t slash = relpath.find('/'); - string first = relpath.substr(0, slash); - string rest; - if (slash != string::npos) { - rest = relpath.substr(slash + 1); - } - - if (first.empty() || first == ".") { - return find_relpath(rest); - - } else if (first == "..") { - if (_parent != nullptr) { - return _parent->find_relpath(rest); - } - // Tried to back out past the root directory. - return nullptr; - } - - // Check for a child with the name indicated by first. - Children::const_iterator ci; - for (ci = _children.begin(); ci != _children.end(); ++ci) { - if (cmp_nocase((*ci)->get_dirname(), first) == 0) { - return (*ci)->find_relpath(rest); - } - } - - // No match. - return nullptr; -} - -/** - * Returns the source directory that corresponds to the given local directory - * name, or NULL if there is no match. - */ -CVSSourceDirectory *CVSSourceDirectory:: -find_dirname(const string &dirname) { - if (cmp_nocase(dirname, _dirname) == 0) { - return this; - } - - Children::const_iterator ci; - for (ci = _children.begin(); ci != _children.end(); ++ci) { - CVSSourceDirectory *result = (*ci)->find_dirname(dirname); - if (result != nullptr) { - return result; - } - } - - return nullptr; -} - -/** - * Recursively scans the contents of the source directory. Fullpath is the - * full path name to the directory; key_filename is the name of a file that - * must exist in each subdirectory for it to be considered part of the - * hierarchy. Returns true on success, false on failure. - */ -bool CVSSourceDirectory:: -scan(const Filename &directory, const string &key_filename) { - vector_string contents; - if (!directory.scan_directory(contents)) { - nout << "Unable to scan directory " << directory << "\n"; - return false; - } - - vector_string::const_iterator fi; - for (fi = contents.begin(); fi != contents.end(); ++fi) { - const string &basename = (*fi); - - // Is this possibly a subdirectory name? - Filename next_path(directory, basename); - Filename key(next_path, key_filename); - - if (key.exists()) { - CVSSourceDirectory *subdir = - new CVSSourceDirectory(_tree, this, basename); - _children.push_back(subdir); - - if (!subdir->scan(next_path, key_filename)) { - return false; - } - - } else { - // It's not a subdirectory; call it a regular file. - _tree->add_file(basename, this); - } - } - - return true; -} diff --git a/pandatool/src/cvscopy/cvsSourceDirectory.h b/pandatool/src/cvscopy/cvsSourceDirectory.h deleted file mode 100644 index eb452612e4..0000000000 --- a/pandatool/src/cvscopy/cvsSourceDirectory.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file cvsSourceDirectory.h - * @author drose - * @date 2000-10-31 - */ - -#ifndef CVSSOURCEDIRECTORY_H -#define CVSSOURCEDIRECTORY_H - -#include "pandatoolbase.h" -#include "filename.h" - -#include "pvector.h" - -class CVSSourceTree; - -/** - * This represents one particular directory in the hierarchy of source - * directory files. We must scan the source directory to identify where the - * related files have previously been copied. - * - * The tree is maintained in a case-insensitive manner, even on a non-Windows - * system, since you might want to eventually check out the CVS tree onto a - * Windows system--and if you do, you'll be sad if there are case conflicts - * within the tree. So we make an effort to ensure this doesn't happen by - * treating two files with a different case as the same file. - */ -class CVSSourceDirectory { -public: - CVSSourceDirectory(CVSSourceTree *tree, CVSSourceDirectory *parent, - const std::string &dirname); - ~CVSSourceDirectory(); - - std::string get_dirname() const; - Filename get_fullpath() const; - Filename get_path() const; - Filename get_rel_to(const CVSSourceDirectory *other) const; - - int get_num_children() const; - CVSSourceDirectory *get_child(int n) const; - - CVSSourceDirectory *find_relpath(const std::string &relpath); - CVSSourceDirectory *find_dirname(const std::string &dirname); - -public: - bool scan(const Filename &directory, const std::string &key_filename); - -private: - CVSSourceTree *_tree; - CVSSourceDirectory *_parent; - std::string _dirname; - int _depth; - - typedef pvector Children; - Children _children; -}; - -#endif diff --git a/pandatool/src/cvscopy/cvsSourceTree.cxx b/pandatool/src/cvscopy/cvsSourceTree.cxx deleted file mode 100644 index 8511afac6a..0000000000 --- a/pandatool/src/cvscopy/cvsSourceTree.cxx +++ /dev/null @@ -1,546 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file cvsSourceTree.cxx - * @author drose - * @date 2000-10-31 - */ - -#include "cvsSourceTree.h" -#include "cvsSourceDirectory.h" - -#include "filename.h" -#include "executionEnvironment.h" -#include "pnotify.h" -#include "string_utils.h" - -#include -#include -#include // for perror -#include - -#ifdef WIN32_VC -#include // for chdir -#endif - -using std::string; - -bool CVSSourceTree::_got_start_fullpath = false; -Filename CVSSourceTree::_start_fullpath; - -/** - * - */ -CVSSourceTree:: -CVSSourceTree() { - _root = nullptr; - _got_root_fullpath = false; -} - -/** - * - */ -CVSSourceTree:: -~CVSSourceTree() { - if (_root != nullptr) { - delete _root; - } -} - -/** - * Sets the root of the source directory. This must be called before scan(), - * and should not be called more than once. - */ -void CVSSourceTree:: -set_root(const Filename &root_path) { - nassertv(_path.empty()); - _path = root_path; -} - -/** - * Scans the complete source directory starting at the indicated pathname. It - * is an error to call this more than once. Returns true on success, false if - * there is an error. - */ -bool CVSSourceTree:: -scan(const Filename &key_filename) { - nassertr(_root == nullptr, false); - Filename root_fullpath = get_root_fullpath(); - _root = new CVSSourceDirectory(this, nullptr, root_fullpath.get_basename()); - return _root->scan(_path, key_filename); -} - -/** - * Returns the root directory of the hierarchy. - */ -CVSSourceDirectory *CVSSourceTree:: -get_root() const { - return _root; -} - -/** - * Returns the source directory that corresponds to the given path, or NULL if - * there is no such directory in the source tree. - */ -CVSSourceDirectory *CVSSourceTree:: -find_directory(const Filename &path) { - string root_fullpath = get_root_fullpath(); - string fullpath = get_actual_fullpath(path); - - // path is a subdirectory within the source hierarchy if and only if - // root_fullpath is an initial prefix of fullpath. - if (root_fullpath.length() > fullpath.length() || - cmp_nocase(fullpath.substr(0, root_fullpath.length()), root_fullpath) != 0) { - // Nope! - return nullptr; - } - - // The relative name is the part of fullpath not in root_fullpath. - Filename relpath = fullpath.substr(root_fullpath.length()); - - return _root->find_relpath(relpath); -} - -/** - * Returns the source directory that corresponds to the given relative path - * from the root, or NULL if there is no match. The relative path may or may - * not include the name of the root directory itself. - */ -CVSSourceDirectory *CVSSourceTree:: -find_relpath(const string &relpath) { - CVSSourceDirectory *result = _root->find_relpath(relpath); - if (result != nullptr) { - return result; - } - - // Check for the root dirname at the front of the path, and remove it if - // it's there. - size_t slash = relpath.find('/'); - Filename first = relpath.substr(0, slash); - Filename rest; - if (slash != string::npos) { - rest = relpath.substr(slash + 1); - } - - if (cmp_nocase(first, _root->get_dirname()) == 0) { - return _root->find_relpath(rest); - } - - return nullptr; -} - -/** - * Returns the source directory that corresponds to the given local directory - * name, or NULL if there is no match. - */ -CVSSourceDirectory *CVSSourceTree:: -find_dirname(const string &dirname) { - return _root->find_dirname(dirname); -} - -/** - * Determines where an externally referenced model file of the indicated name - * should go. It does this by looking for an existing model file of the same - * name; if a matching model is not found, or if multiple matching files are - * found, prompts the user for the directory, or uses suggested_dir. - */ -CVSSourceTree::FilePath CVSSourceTree:: -choose_directory(const string &basename, CVSSourceDirectory *suggested_dir, - bool force, bool interactive) { - static FilePaths empty_paths; - - Basenames::const_iterator bi; - bi = _basenames.find(downcase(basename)); - if (bi != _basenames.end()) { - // The filename already exists somewhere. - const FilePaths &paths = (*bi).second; - - return prompt_user(basename, suggested_dir, paths, - force, interactive); - } - - // Now we have to prompt the user for a suitable place to put it. - return prompt_user(basename, suggested_dir, empty_paths, - force, interactive); -} - -/** - * Returns the full path from the root to the top of the source hierarchy. - */ -Filename CVSSourceTree:: -get_root_fullpath() { - nassertr(!_path.empty(), Filename()); - if (!_got_root_fullpath) { - _root_fullpath = get_actual_fullpath(_path); - _got_root_fullpath = true; - } - return _root_fullpath; -} - -/** - * Returns the local directory name of the root of the tree. - */ -Filename CVSSourceTree:: -get_root_dirname() const { - nassertr(_root != nullptr, Filename()); - return _root->get_dirname(); -} - -/** - * Adds a new file to the set of known files. This is normally called from - * CVSSourceDirectory::scan() and should not be called directly by the user. - */ -void CVSSourceTree:: -add_file(const string &basename, CVSSourceDirectory *dir) { - FilePath file_path(dir, basename); - _basenames[downcase(basename)].push_back(file_path); -} - -/** - * Temporarily changes the current directory to the named path. Returns true - * on success, false on failure. Call restore_cwd() to restore to the - * original directory later. - */ -bool CVSSourceTree:: -temp_chdir(const Filename &path) { - // We have to call this first to guarantee that we have already determined - // our starting directory. - get_start_fullpath(); - - string os_path = path.to_os_specific(); - if (chdir(os_path.c_str()) < 0) { - return false; - } - return true; -} - -/** - * Restores the current directory after changing it from temp_chdir(). - */ -void CVSSourceTree:: -restore_cwd() { - Filename start_fullpath = get_start_fullpath(); - string os_path = start_fullpath.to_os_specific(); - - if (chdir(os_path.c_str()) < 0) { - // Hey! We can't get back to the directory we started from! - perror(os_path.c_str()); - nout << "Can't continue, aborting.\n"; - exit(1); - } -} - - -/** - * Prompts the user, if necessary, to choose a directory to import the given - * file into. - */ -CVSSourceTree::FilePath CVSSourceTree:: -prompt_user(const string &basename, CVSSourceDirectory *suggested_dir, - const CVSSourceTree::FilePaths &paths, - bool force, bool interactive) { - if (paths.size() == 1) { - // The file already exists in exactly one place. - if (!interactive) { - return paths[0]; - } - FilePath result = ask_existing(basename, paths[0]); - if (result.is_valid()) { - return result; - } - - } else if (paths.size() > 1) { - // The file already exists in multiple places. - if (force && !interactive) { - return paths[0]; - } - FilePath result = ask_existing(basename, paths, suggested_dir); - if (result.is_valid()) { - return result; - } - } - - // The file does not already exist, or the user declined to replace an - // existing file. - if (force && !interactive) { - return FilePath(suggested_dir, basename); - } - - // Is the file already in the suggested directory? If not, prompt the user - // to put it there. - bool found_dir = false; - FilePaths::const_iterator pi; - for (pi = paths.begin(); pi != paths.end(); ++pi) { - if ((*pi)._dir == suggested_dir) { - found_dir = true; - break; - } - } - - if (!found_dir) { - FilePath result = ask_new(basename, suggested_dir); - if (result.is_valid()) { - return result; - } - } - - // Ask the user where the damn thing should go. - return ask_any(basename, paths); -} - -/** - * Asks the user if he wants to replace an existing file. - */ -CVSSourceTree::FilePath CVSSourceTree:: -ask_existing(const string &basename, const CVSSourceTree::FilePath &path) { - while (true) { - nout << basename << " found in tree at " - << path.get_path() << ".\n"; - string result = prompt("Overwrite this file (y/n)? "); - nassertr(!result.empty(), FilePath()); - if (result.size() == 1) { - if (tolower(result[0]) == 'y') { - return path; - } else if (tolower(result[0]) == 'n') { - return FilePath(); - } - } - - nout << "*** Invalid response: " << result << "\n\n"; - } -} - -/** - * Asks the user which of several existing files he wants to replace. - */ -CVSSourceTree::FilePath CVSSourceTree:: -ask_existing(const string &basename, const CVSSourceTree::FilePaths &paths, - CVSSourceDirectory *suggested_dir) { - while (true) { - nout << basename << " found in tree at more than one place:\n"; - - bool any_suggested = false; - for (int i = 0; i < (int)paths.size(); i++) { - nout << " " << (i + 1) << ". " - << paths[i].get_path() << "\n"; - if (paths[i]._dir == suggested_dir) { - any_suggested = true; - } - } - - int next_option = paths.size() + 1; - int suggested_option = -1; - - if (!any_suggested) { - // If it wasn't already in the suggested directory, offer to put it - // there. - suggested_option = next_option; - next_option++; - nout << "\n" << suggested_option - << ". create " - << Filename(suggested_dir->get_path(), basename) - << "\n"; - } - - int other_option = next_option; - nout << other_option << ". Other\n"; - - string result = prompt("Choose an option: "); - nassertr(!result.empty(), FilePath()); - const char *nptr = result.c_str(); - char *endptr; - int option = strtol(nptr, &endptr, 10); - if (*endptr == '\0') { - if (option >= 1 && option <= (int)paths.size()) { - return paths[option - 1]; - - } else if (option == suggested_option) { - return FilePath(suggested_dir, basename); - - } else if (option == other_option) { - return FilePath(); - } - } - - nout << "*** Invalid response: " << result << "\n\n"; - } -} - -/** - * Asks the user if he wants to create a new file. - */ -CVSSourceTree::FilePath CVSSourceTree:: -ask_new(const string &basename, CVSSourceDirectory *dir) { - while (true) { - nout << basename << " will be created in " - << dir->get_path() << ".\n"; - string result = prompt("Create this file (y/n)? "); - nassertr(!result.empty(), FilePath()); - if (result.size() == 1) { - if (tolower(result[0]) == 'y') { - return FilePath(dir, basename); - } else if (tolower(result[0]) == 'n') { - return FilePath(); - } - } - - nout << "*** Invalid response: " << result << "\n\n"; - } -} - -/** - * Asks the user to type in the name of the directory in which to store the - * file. - */ -CVSSourceTree::FilePath CVSSourceTree:: -ask_any(const string &basename, - const CVSSourceTree::FilePaths &paths) { - while (true) { - string result = - prompt("Enter the name of the directory to copy " + basename + " to: "); - nassertr(!result.empty(), FilePath()); - - // The user might enter a fully-qualified path to the directory, or a - // relative path from the root (with or without the root's dirname), or - // the dirname of the particular directory. - CVSSourceDirectory *dir = find_directory(result); - if (dir == nullptr) { - dir = find_relpath(result); - } - if (dir == nullptr) { - dir = find_dirname(result); - } - - if (dir != nullptr) { - // If the file is already in this directory, we must preserve its - // existing case. - FilePaths::const_iterator pi; - for (pi = paths.begin(); pi != paths.end(); ++pi) { - if ((*pi)._dir == dir) { - return (*pi); - } - } - - // Otherwise, since we're creating a new file, keep the original case. - return FilePath(dir, basename); - } - - nout << "*** Not a valid directory name: " << result << "\n\n"; - } -} - -/** - * Issues a prompt to the user and waits for a typed response. Returns the - * response (which will not be empty). - */ -string CVSSourceTree:: -prompt(const string &message) { - nout << std::flush; - while (true) { - std::cerr << message << std::flush; - std::string response; - std::getline(std::cin, response); - - // Remove leading and trailing whitespace. - size_t p = 0; - while (p < response.length() && isspace(response[p])) { - p++; - } - - size_t q = response.length(); - while (q > p && isspace(response[q - 1])) { - q--; - } - - if (q > p) { - return response.substr(p, q - p); - } - } -} - -/** - * Determines the actual full path from the root to the named directory. - */ -Filename CVSSourceTree:: -get_actual_fullpath(const Filename &path) { - Filename canon = path; - canon.make_canonical(); - return canon; -} - - -/** - * Returns the full path from the root to the directory in which the user - * started the program. - */ -Filename CVSSourceTree:: -get_start_fullpath() { - if (!_got_start_fullpath) { - Filename cwd = ExecutionEnvironment::get_cwd(); - _start_fullpath = cwd.to_os_specific(); - } - return _start_fullpath; -} - - -/** - * Creates an invalid FilePath specification. - */ -CVSSourceTree::FilePath:: -FilePath() : - _dir(nullptr) -{ -} - -/** - * Creates a valid FilePath specification with the indicated directory and - * basename. - */ -CVSSourceTree::FilePath:: -FilePath(CVSSourceDirectory *dir, const string &basename) : - _dir(dir), - _basename(basename) -{ -} - -/** - * Returns true if this FilePath represents a valid file, or false if it - * represents an error return. - */ -bool CVSSourceTree::FilePath:: -is_valid() const { - return (_dir != nullptr); -} - -/** - * Returns the relative path to this file from the root of the source tree. - */ -Filename CVSSourceTree::FilePath:: -get_path() const { - nassertr(_dir != nullptr, Filename()); - return Filename(_dir->get_path(), _basename); -} - -/** - * Returns the full path to this file. - */ -Filename CVSSourceTree::FilePath:: -get_fullpath() const { - nassertr(_dir != nullptr, Filename()); - return Filename(_dir->get_fullpath(), _basename); -} - -/** - * Returns the relative path to this file as seen from the indicated source - * directory. - */ -Filename CVSSourceTree::FilePath:: -get_rel_from(const CVSSourceDirectory *other) const { - nassertr(_dir != nullptr, Filename()); - return Filename(other->get_rel_to(_dir), _basename); -} diff --git a/pandatool/src/cvscopy/cvsSourceTree.h b/pandatool/src/cvscopy/cvsSourceTree.h deleted file mode 100644 index 238431f78d..0000000000 --- a/pandatool/src/cvscopy/cvsSourceTree.h +++ /dev/null @@ -1,109 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file cvsSourceTree.h - * @author drose - * @date 2000-10-31 - */ - -#ifndef CVSSOURCETREE_H -#define CVSSOURCETREE_H - -#include "pandatoolbase.h" - -#include "pvector.h" -#include "pmap.h" -#include "filename.h" - -class CVSSourceDirectory; - -/** - * This represents the root of the tree of source directory files. - * - * The tree is maintained in a case-insensitive manner, even on a non-Windows - * system, since you might want to eventually check out the CVS tree onto a - * Windows system--and if you do, you'll be sad if there are case conflicts - * within the tree. So we make an effort to ensure this doesn't happen by - * treating two files with a different case as the same file. - */ -class CVSSourceTree { -public: - CVSSourceTree(); - ~CVSSourceTree(); - - void set_root(const Filename &root_path); - bool scan(const Filename &key_filename); - - CVSSourceDirectory *get_root() const; - CVSSourceDirectory *find_directory(const Filename &path); - CVSSourceDirectory *find_relpath(const std::string &relpath); - CVSSourceDirectory *find_dirname(const std::string &dirname); - - // This nested class represents the selection of a particular directory in - // which to place a given file, given its basename. The basename of the - // file is returned as part of the answer, because it might have changed in - // case from the original basename (in order to match the case of an - // existing file in the selected directory). - class FilePath { - public: - FilePath(); - FilePath(CVSSourceDirectory *dir, const std::string &basename); - bool is_valid() const; - Filename get_path() const; - Filename get_fullpath() const; - Filename get_rel_from(const CVSSourceDirectory *other) const; - - CVSSourceDirectory *_dir; - std::string _basename; - }; - - FilePath choose_directory(const std::string &basename, - CVSSourceDirectory *suggested_dir, - bool force, bool interactive); - - Filename get_root_fullpath(); - Filename get_root_dirname() const; - - static bool temp_chdir(const Filename &path); - static void restore_cwd(); - -public: - void add_file(const std::string &basename, CVSSourceDirectory *dir); - -private: - typedef pvector FilePaths; - - FilePath - prompt_user(const std::string &basename, CVSSourceDirectory *suggested_dir, - const FilePaths &paths, bool force, bool interactive); - - FilePath ask_existing(const std::string &filename, const FilePath &path); - FilePath ask_existing(const std::string &filename, const FilePaths &paths, - CVSSourceDirectory *suggested_dir); - FilePath ask_new(const std::string &filename, CVSSourceDirectory *dir); - FilePath ask_any(const std::string &filename, const FilePaths &paths); - - std::string prompt(const std::string &message); - - static Filename get_actual_fullpath(const Filename &path); - static Filename get_start_fullpath(); - -private: - Filename _path; - CVSSourceDirectory *_root; - - typedef pmap Basenames; - Basenames _basenames; - - static bool _got_start_fullpath; - static Filename _start_fullpath; - bool _got_root_fullpath; - Filename _root_fullpath; -}; - -#endif diff --git a/pandatool/src/cvscopy/p3cvscopy_composite1.cxx b/pandatool/src/cvscopy/p3cvscopy_composite1.cxx deleted file mode 100644 index 3447005d49..0000000000 --- a/pandatool/src/cvscopy/p3cvscopy_composite1.cxx +++ /dev/null @@ -1,5 +0,0 @@ - -#include "cvsCopy.cxx" -#include "cvsSourceDirectory.cxx" -#include "cvsSourceTree.cxx" - diff --git a/pandatool/src/cvscopy/testCopy.cxx b/pandatool/src/cvscopy/testCopy.cxx deleted file mode 100644 index 78010c58bb..0000000000 --- a/pandatool/src/cvscopy/testCopy.cxx +++ /dev/null @@ -1,65 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file testCopy.cxx - * @author drose - * @date 2000-10-31 - */ - -#include "testCopy.h" -#include "cvsSourceDirectory.h" - -/** - * - */ -TestCopy:: -TestCopy() { - set_program_brief("copy files into a CVS source hierarchy"); - set_program_description - ("This program copies one or more files into a CVS source hierarchy. " - "Rather than copying the named files immediately into the current " - "directory, it first scans the entire source hierarchy, identifying all " - "the already-existing files. If the named file to copy matches the " - "name of an already-existing file in the current directory or elsewhere " - "in the hierarchy, that file is overwritten.\n\n" - - "This is primarily useful as a test program for libcvscopy."); -} - -/** - * - */ -void TestCopy:: -run() { - SourceFiles::iterator fi; - for (fi = _source_files.begin(); fi != _source_files.end(); ++fi) { - CVSSourceDirectory *dest = import(*fi, 0, _model_dir); - if (dest == nullptr) { - exit(1); - } - } -} - -/** - * Called by import() if the timestamps indicate that a file needs to be - * copied. This does the actual copy of a file from source to destination. - * If new_file is true, then dest does not already exist. - */ -bool TestCopy:: -copy_file(const Filename &source, const Filename &dest, - CVSSourceDirectory *, void *, bool) { - return copy_binary_file(source, dest); -} - - -int main(int argc, char *argv[]) { - TestCopy prog; - prog.parse_command_line(argc, argv); - prog.run(); - return 0; -} diff --git a/pandatool/src/cvscopy/testCopy.h b/pandatool/src/cvscopy/testCopy.h deleted file mode 100644 index 3e1b770fee..0000000000 --- a/pandatool/src/cvscopy/testCopy.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file testCopy.h - * @author drose - * @date 2000-10-31 - */ - -#ifndef TESTCOPY_H -#define TESTCOPY_H - -#include "pandatoolbase.h" - -#include "cvsCopy.h" - -/** - * A program to copy ordinary files into the cvs tree. Mainly to test - * CVSCopy. - */ -class TestCopy : public CVSCopy { -public: - TestCopy(); - - void run(); - -protected: - virtual bool copy_file(const Filename &source, const Filename &dest, - CVSSourceDirectory *dir, void *extra_data, - bool new_file); -}; - -#endif diff --git a/pandatool/src/daeegg/daeMaterials.cxx b/pandatool/src/daeegg/daeMaterials.cxx index 36bf6fd895..ac221344d7 100644 --- a/pandatool/src/daeegg/daeMaterials.cxx +++ b/pandatool/src/daeegg/daeMaterials.cxx @@ -73,12 +73,16 @@ void DaeMaterials::add_material_instance(const FCDMaterialInstance* instance) { } // Handle the material stuff - daeegg_cat.spam() << "Trying to process material with semantic " << semantic << endl; + if (daeegg_cat.is_spam()) { + daeegg_cat.spam() << "Trying to process material with semantic " << semantic << endl; + } PT_EggMaterial egg_material = new EggMaterial(semantic); pvector egg_textures; const FCDEffect* effect = instance->GetMaterial()->GetEffect(); if (effect == nullptr) { - daeegg_cat.debug() << "Ignoring material (semantic: " << semantic << ") without assigned effect" << endl; + if (daeegg_cat.is_debug()) { + daeegg_cat.debug() << "Ignoring material (semantic: " << semantic << ") without assigned effect" << endl; + } } else { // Grab the common profile effect const FCDEffectStandard* effect_common = (FCDEffectStandard *)effect->FindProfile(FUDaeProfileType::COMMON); @@ -86,7 +90,9 @@ void DaeMaterials::add_material_instance(const FCDMaterialInstance* instance) { daeegg_cat.info() << "Ignoring effect referenced by material with semantic " << semantic << " because it has no common profile" << endl; } else { - daeegg_cat.spam() << "Processing effect, material semantic is " << semantic << endl; + if (daeegg_cat.is_spam()) { + daeegg_cat.spam() << "Processing effect, material semantic is " << semantic << endl; + } // Set the material parameters egg_material->set_amb(TO_COLOR(effect_common->GetAmbientColor())); // We already process transparency using blend modes LVecBase4 diffuse = @@ -115,7 +121,9 @@ void DaeMaterials::add_material_instance(const FCDMaterialInstance* instance) { // Find an tag to support some extra stuff from extensions process_extra(semantic, effect->GetExtra()); } - daeegg_cat.spam() << "Found " << egg_textures.size() << " textures in material" << endl; + if (daeegg_cat.is_spam()) { + daeegg_cat.spam() << "Found " << egg_textures.size() << " textures in material" << endl; + } _materials[semantic]->_egg_material = egg_material; } @@ -140,7 +148,9 @@ process_texture_bucket(const string semantic, const FCDEffectStandard* effect_co texpath = Filename::from_os_specific(FROM_FSTRING(image->GetFilename())); texpath.make_canonical(); texpath.make_relative_to(docpath.get_dirname(), true); - daeegg_cat.debug() << "Found texture with path " << texpath << endl; + if (daeegg_cat.is_debug()) { + daeegg_cat.debug() << "Found texture with path " << texpath << endl; + } } else { // Never mind. texpath = Filename::from_os_specific(FROM_FSTRING(image->GetFilename())); @@ -149,7 +159,9 @@ process_texture_bucket(const string semantic, const FCDEffectStandard* effect_co // Find a set of UV coordinates const FCDEffectParameterInt* uvset = effect_common->GetTexture(bucket, tx)->GetSet(); if (uvset != nullptr) { - daeegg_cat.debug() << "Texture has uv name '" << FROM_FSTRING(uvset->GetSemantic()) << "'\n"; + if (daeegg_cat.is_debug()) { + daeegg_cat.debug() << "Texture has uv name '" << FROM_FSTRING(uvset->GetSemantic()) << "'\n"; + } string uvset_semantic (FROM_FSTRING(uvset->GetSemantic())); // Only set the UV name if this UV set actually exists. @@ -216,7 +228,9 @@ apply_to_primitive(const string semantic, const PT(EggPrimitive) to) { if (_materials.count(semantic) > 0) { to->set_material(_materials[semantic]->_egg_material); for (pvector::iterator it = _materials[semantic]->_egg_textures.begin(); it != _materials[semantic]->_egg_textures.end(); ++it) { - daeegg_cat.spam() << "Applying texture " << (*it)->get_name() << " from material with semantic " << semantic << endl; + if (daeegg_cat.is_spam()) { + daeegg_cat.spam() << "Applying texture " << (*it)->get_name() << " from material with semantic " << semantic << endl; + } to->add_texture(*it); } to->set_bface_flag(_materials[semantic]->_double_sided); @@ -271,11 +285,15 @@ get_uvset_name(const string semantic, FUDaeGeometryInput::Semantic input_semanti // semantic TEXCOORD. for (size_t i = 0; i < _materials[semantic]->_uvsets.size(); ++i) { if (_materials[semantic]->_uvsets[i]->_input_set == input_set) { - daeegg_cat.debug() << "Using uv set with non-matching input semantic " << _materials[semantic]->_uvsets[i]->_semantic << "\n"; + if (daeegg_cat.is_debug()) { + daeegg_cat.debug() << "Using uv set with non-matching input semantic " << _materials[semantic]->_uvsets[i]->_semantic << "\n"; + } return _materials[semantic]->_uvsets[i]->_semantic; } } - daeegg_cat.debug() << "No uv set binding found for input set " << input_set << "\n"; + if (daeegg_cat.is_debug()) { + daeegg_cat.debug() << "No uv set binding found for input set " << input_set << "\n"; + } } } return ""; diff --git a/pandatool/src/daeegg/daeToEggConverter.cxx b/pandatool/src/daeegg/daeToEggConverter.cxx index a923a67264..7e5aa6673e 100644 --- a/pandatool/src/daeegg/daeToEggConverter.cxx +++ b/pandatool/src/daeegg/daeToEggConverter.cxx @@ -175,7 +175,9 @@ convert_file(const Filename &filename) { if (mesh != nullptr) { PT(DaeMaterials) materials = new DaeMaterials(character->_instance); - daeegg_cat.spam() << "Processing mesh for controller\n"; + if (daeegg_cat.is_spam()) { + daeegg_cat.spam() << "Processing mesh for controller\n"; + } process_mesh(character->_node_group, mesh, materials, character); } } @@ -373,7 +375,9 @@ void DAEToEggConverter:: process_node(EggGroupNode *parent, const FCDSceneNode* node, bool forced) { nassertv(node != nullptr); string node_id = FROM_FSTRING(node->GetDaeId()); - daeegg_cat.spam() << "Processing node with ID '" << node_id << "'" << endl; + if (daeegg_cat.is_spam()) { + daeegg_cat.spam() << "Processing node with ID '" << node_id << "'" << endl; + } // Create an egg group for this node PT(EggGroup) node_group = new EggGroup(FROM_FSTRING(node->GetDaeId())); @@ -470,7 +474,9 @@ process_mesh(EggGroup *parent, const FCDGeometryMesh* mesh, DaeMaterials *materials, DaeCharacter *character) { nassertv(mesh != nullptr); - daeegg_cat.debug() << "Processing mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << endl; + if (daeegg_cat.is_debug()) { + daeegg_cat.debug() << "Processing mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << endl; + } // Create the egg stuff to hold this mesh PT(EggGroup) mesh_group = new EggGroup(FROM_FSTRING(mesh->GetDaeId())); @@ -480,17 +486,23 @@ process_mesh(EggGroup *parent, const FCDGeometryMesh* mesh, // First retrieve the vertex source if (mesh->GetSourceCount() == 0) { - daeegg_cat.debug() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has no sources" << endl; + if (daeegg_cat.is_debug()) { + daeegg_cat.debug() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has no sources" << endl; + } return; } const FCDGeometrySource* vsource = mesh->FindSourceByType(FUDaeGeometryInput::POSITION); if (vsource == nullptr) { - daeegg_cat.debug() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has no source for POSITION data" << endl; + if (daeegg_cat.is_debug()) { + daeegg_cat.debug() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has no source for POSITION data" << endl; + } return; } // Loop through the polygon groups and add them - daeegg_cat.spam() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has " << mesh->GetPolygonsCount() << " polygon groups" << endl; + if (daeegg_cat.is_spam()) { + daeegg_cat.spam() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has " << mesh->GetPolygonsCount() << " polygon groups" << endl; + } if (mesh->GetPolygonsCount() == 0) return; // This is an array of pointers, I know. But since they are refcounted, I @@ -734,7 +746,9 @@ process_controller(EggGroup *parent, const FCDControllerInstance *instance) { const FCDGeometryMesh *mesh = controller->GetBaseGeometry()->GetMesh(); if (mesh != nullptr) { PT(DaeMaterials) materials = new DaeMaterials(instance); - daeegg_cat.spam() << "Processing mesh for controller\n"; + if (daeegg_cat.is_spam()) { + daeegg_cat.spam() << "Processing mesh for controller\n"; + } process_mesh(parent, mesh, materials); } } else { diff --git a/pandatool/src/deploy-stub/deploy-stub.c b/pandatool/src/deploy-stub/deploy-stub.c index 12079da46d..707694a8ca 100644 --- a/pandatool/src/deploy-stub/deploy-stub.c +++ b/pandatool/src/deploy-stub/deploy-stub.c @@ -75,6 +75,10 @@ static struct _inittab extensions[] = { #endif #endif +#ifdef _WIN32 +static wchar_t *log_pathw = NULL; +#endif + #if defined(_WIN32) && PY_VERSION_HEX < 0x03060000 static int supports_code_page(UINT cp) { if (cp == 0) { @@ -225,7 +229,8 @@ static int mkdir_parent(const char *path) { static int setup_logging(const char *path, int append) { #ifdef _WIN32 // Does it start with a tilde? Perform tilde expansion if so. - wchar_t pathw[MAX_PATH * 2]; + wchar_t *pathw = (wchar_t *)malloc(sizeof(wchar_t) * MAX_PATH); + pathw[0] = 0; size_t offset = 0; if (path[0] == '~' && (path[1] == 0 || path[1] == '/' || path[1] == '\\')) { // Strip off the tilde. @@ -233,6 +238,7 @@ static int setup_logging(const char *path, int append) { // Get the home directory path for the current user. if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, pathw))) { + free(pathw); return 0; } offset = wcslen(pathw); @@ -240,26 +246,30 @@ static int setup_logging(const char *path, int append) { // We need to convert the rest of the path from UTF-8 to UTF-16. if (MultiByteToWideChar(CP_UTF8, 0, path, -1, pathw + offset, - (int)(_countof(pathw) - offset)) == 0) { + (int)(MAX_PATH - offset)) == 0) { + free(pathw); return 0; } DWORD access = append ? FILE_APPEND_DATA : (GENERIC_READ | GENERIC_WRITE); int creation = append ? OPEN_ALWAYS : CREATE_ALWAYS; - HANDLE handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ, + HANDLE handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE) { // Make the parent directories first. mkdir_parent(pathw); - handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ, + handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); } if (handle == INVALID_HANDLE_VALUE) { + free(pathw); return 0; } + log_pathw = pathw; + if (append) { SetFilePointer(handle, 0, NULL, FILE_END); } @@ -282,7 +292,7 @@ static int setup_logging(const char *path, int append) { // Now replace the stdout and stderr file descriptors with one pointing to // our desired handle. - int fd = _open_osfhandle((intptr_t)handle, _O_WRONLY | _O_TEXT | (append ? _O_APPEND : 0)); + int fd = _open_osfhandle((intptr_t)handle, _O_WRONLY | _O_TEXT | _O_APPEND); _dup2(fd, _fileno(stdout)); _dup2(fd, _fileno(stderr)); _close(fd); @@ -421,6 +431,32 @@ int Py_FrozenMain(int argc, char **argv) #endif #if defined(MS_WINDOWS) && PY_VERSION_HEX < 0x03040000 + /* We can't rely on our overriding of the standard I/O to work on older + * versions of Python, since they are compiled with an incompatible CRT. + * The best solution I've found was to just replace sys.stdout/stderr with + * the log file reopened in append mode (which requires not locking it for + * write, and also passing in _O_APPEND above, and disabling buffering). + * It's not the most elegant solution, but it's better than crashing. */ +#if PY_MAJOR_VERSION < 3 + if (log_pathw != NULL) { + PyObject *uniobj = PyUnicode_FromWideChar(log_pathw, (Py_ssize_t)wcslen(log_pathw)); + PyObject *file = PyObject_CallFunction((PyObject*)&PyFile_Type, "Nsi", uniobj, "a", 0); + + if (file != NULL) { + PyFile_SetEncodingAndErrors(file, "utf-8", NULL); + + PySys_SetObject("stdout", file); + PySys_SetObject("stderr", file); + PySys_SetObject("__stdout__", file); + PySys_SetObject("__stderr__", file); + + /* Be sure to disable buffering, otherwise we'll get overlap */ + setbuf(stdout, (char *)NULL); + setbuf(stderr, (char *)NULL); + } + } + else +#endif if (!supports_code_page(GetConsoleOutputCP()) || !supports_code_page(GetConsoleCP())) { /* Same hack as before except for Python 2.7, which doesn't seem to have diff --git a/pandatool/src/egg-qtess/qtessInputFile.h b/pandatool/src/egg-qtess/qtessInputFile.h index d0e6b62464..c5cb5fab03 100644 --- a/pandatool/src/egg-qtess/qtessInputFile.h +++ b/pandatool/src/egg-qtess/qtessInputFile.h @@ -18,6 +18,7 @@ #include "qtessInputEntry.h" #include "filename.h" #include "pvector.h" +#include "vector_double.h" class QtessSurface; diff --git a/pandatool/src/flt/fltRecordReader.cxx b/pandatool/src/flt/fltRecordReader.cxx index c999001d64..eb3ba94840 100644 --- a/pandatool/src/flt/fltRecordReader.cxx +++ b/pandatool/src/flt/fltRecordReader.cxx @@ -131,20 +131,21 @@ advance(bool ok_eof) { // And now read the full record based on the length. int length = _next_record_length - header_size; - char *buffer = new char[length]; if (length > 0) { - _in.read(buffer, length); - } - _datagram = Datagram(buffer, length); - delete[] buffer; - - if (_in.eof()) { - _state = S_eof; - assert(!flt_error_abort); - return FE_end_of_file; + vector_uchar data((size_t)length); + _in.read((char *)&data[0], length); + _datagram = Datagram(std::move(data)); + } else { + _datagram = Datagram(); } if (_in.fail()) { + if (_in.eof()) { + _state = S_eof; + assert(!flt_error_abort); + return FE_end_of_file; + } + _state = S_error; assert(!flt_error_abort); return FE_read_error; @@ -162,20 +163,20 @@ advance(bool ok_eof) { _record_length += _next_record_length; length = _next_record_length - header_size; - buffer = new char[length]; if (length > 0) { + char *buffer = new char[length]; _in.read(buffer, length); - } - _datagram.append_data(buffer, length); - delete[] buffer; - - if (_in.eof()) { - _state = S_eof; - assert(!flt_error_abort); - return FE_end_of_file; + _datagram.append_data(buffer, length); + delete[] buffer; } if (_in.fail()) { + if (_in.eof()) { + _state = S_eof; + assert(!flt_error_abort); + return FE_end_of_file; + } + _state = S_error; assert(!flt_error_abort); return FE_read_error; @@ -221,11 +222,11 @@ read_next_header() { char bytes[header_size]; _in.read(bytes, header_size); - if (_in.eof()) { - _next_error = FE_end_of_file; - return; - - } else if (_in.fail()) { + if (_in.fail()) { + if (_in.eof()) { + _next_error = FE_end_of_file; + return; + } _next_error = FE_read_error; return; } diff --git a/pandatool/src/fltprogs/fltCopy.cxx b/pandatool/src/fltprogs/fltCopy.cxx deleted file mode 100644 index 6ecb3b7833..0000000000 --- a/pandatool/src/fltprogs/fltCopy.cxx +++ /dev/null @@ -1,237 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file fltCopy.cxx - * @author drose - * @date 2000-11-01 - */ - -#include "fltCopy.h" - -#include "cvsSourceDirectory.h" -#include "fltHeader.h" -#include "fltFace.h" -#include "fltExternalReference.h" -#include "fltError.h" -#include "dcast.h" - -/** - * - */ -FltCopy:: -FltCopy() { - set_program_brief("copy MultiGen .flt files into a CVS source hierarchy"); - set_program_description - ("fltcopy copies one or more MultiGen .flt files into a " - "CVS source hierarchy. " - "Rather than copying the named files immediately into the current " - "directory, it first scans the entire source hierarchy, identifying all " - "the already-existing files. If the named file to copy matches the " - "name of an already-existing file in the current directory or elsewhere " - "in the hierarchy, that file is overwritten. Other .flt files, as " - "well as texture files, that are externally referenced by the " - "named .flt file(s) are similarly copied."); - - clear_runlines(); - add_runline("[opts] file.flt [file.flt ... ]"); - - add_path_replace_options(); -} - -/** - * - */ -void FltCopy:: -run() { - SourceFiles::iterator fi; - for (fi = _source_files.begin(); fi != _source_files.end(); ++fi) { - ExtraData ed; - ed._type = FT_flt; - - CVSSourceTree::FilePath dest = import(*fi, &ed, _model_dir); - if (!dest.is_valid()) { - exit(1); - } - } -} - -/** - * Called by import() if verify_file() indicates that a file needs to be - * copied. This does the actual copy of a file from source to destination. - * If new_file is true, then dest does not already exist. - */ -bool FltCopy:: -copy_file(const Filename &source, const Filename &dest, - CVSSourceDirectory *dir, void *extra_data, bool new_file) { - ExtraData *ed = (ExtraData *)extra_data; - switch (ed->_type) { - case FT_flt: - return copy_flt_file(source, dest, dir); - - case FT_texture: - return copy_texture(source, dest, dir, ed->_texture, new_file); - } - - nout << "Internal error: invalid type " << (int)ed->_type << "\n"; - return false; -} - -/** - * - */ -bool FltCopy:: -copy_flt_file(const Filename &source, const Filename &dest, - CVSSourceDirectory *dir) { - PT(FltHeader) header = new FltHeader(_path_replace); - - // We don't want to automatically generate .attr files--we'd rather write - // them out explicitly. - header->set_auto_attr_update(FltHeader::AU_none); - - FltError result = header->read_flt(source); - if (result != FE_ok) { - nout << "Cannot read " << source << ": " << result << "\n"; - return false; - } - - header->check_version(); - - // Now scan the flt file for nested references. - Refs refs; - Textures textures; - scan_flt(header, refs, textures); - - Refs::const_iterator ri; - for (ri = refs.begin(); ri != refs.end(); ++ri) { - FltExternalReference *ref = (*ri); - Filename ref_filename = ref->get_ref_filename(); - - if (!ref_filename.exists()) { - nout << "*** Warning: external reference " << ref_filename - << " does not exist.\n"; - } else { - ExtraData ed; - ed._type = FT_flt; - - CVSSourceTree::FilePath ref_path = - import(ref_filename, &ed, _model_dir); - if (!ref_path.is_valid()) { - return false; - } - - // Update the reference to point to the new flt filename, relative to - // the base flt file. - ref->set_ref_filename(ref_path.get_rel_from(dir)); - } - } - - // Remove all the textures from the palette, and then add back only those we - // found in use. This way we don't copy a file that references bogus - // textures. - header->clear_textures(); - - Textures::const_iterator ti; - for (ti = textures.begin(); ti != textures.end(); ++ti) { - FltTexture *tex = (*ti); - Filename texture_filename = tex->get_texture_filename(); - - if (!texture_filename.exists()) { - nout << "*** Warning: texture " << texture_filename - << " does not exist.\n"; - } else { - ExtraData ed; - ed._type = FT_texture; - ed._texture = tex; - - CVSSourceTree::FilePath texture_path = - import(texture_filename, &ed, _map_dir); - if (!texture_path.is_valid()) { - return false; - } - - // Update the texture reference to point to the new texture filename, - // relative to the flt file. - tex->set_texture_filename(texture_path.get_rel_from(dir)); - header->add_texture(tex); - } - } - - // Finally, write the resulting file out. - result = header->write_flt(dest); - if (result != FE_ok) { - nout << "Cannot write " << dest << "\n"; - return false; - } - - return true; -} - -/** - * - */ -bool FltCopy:: -copy_texture(const Filename &source, const Filename &dest, - CVSSourceDirectory *dir, FltTexture *tex, bool new_file) { - if (!copy_binary_file(source, dest)) { - return false; - } - - // Also write out the .attr file. - Filename attr_filename = dest.get_fullpath() + ".attr"; - if (!attr_filename.exists()) { - new_file = true; - } - - tex->write_attr_data(attr_filename); - - if (new_file) { - cvs_add(attr_filename); - } - - return true; -} - -/** - * Recursively walks through the flt file hierarchy, looking for texture - * references and external flt file references. - */ -void FltCopy:: -scan_flt(FltRecord *record, FltCopy::Refs &refs, FltCopy::Textures &textures) { - if (record->is_of_type(FltFace::get_class_type())) { - FltFace *face; - DCAST_INTO_V(face, record); - if (face->has_texture()) { - textures.insert(face->get_texture()); - } - - } else if (record->is_of_type(FltExternalReference::get_class_type())) { - FltExternalReference *ref; - DCAST_INTO_V(ref, record); - - refs.insert(ref); - } - - int i; - int num_subfaces = record->get_num_subfaces(); - for (i = 0; i < num_subfaces; i++) { - scan_flt(record->get_subface(i), refs, textures); - } - - int num_children = record->get_num_children(); - for (i = 0; i < num_children; i++) { - scan_flt(record->get_child(i), refs, textures); - } -} - - -int main(int argc, char *argv[]) { - FltCopy prog; - prog.parse_command_line(argc, argv); - prog.run(); - return 0; -} diff --git a/pandatool/src/fltprogs/fltCopy.h b/pandatool/src/fltprogs/fltCopy.h deleted file mode 100644 index 2370fec605..0000000000 --- a/pandatool/src/fltprogs/fltCopy.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file fltCopy.h - * @author drose - * @date 2000-11-01 - */ - -#ifndef FLTCOPY_H -#define FLTCOPY_H - -#include "pandatoolbase.h" - -#include "cvsCopy.h" - -#include "dSearchPath.h" -#include "pointerTo.h" - -#include "pset.h" - -class FltRecord; -class FltTexture; -class FltExternalReference; - -/** - * A program to copy Multigen .flt files into the cvs tree. It copies the - * base file plus all externally referenced files as well as all textures. - */ -class FltCopy : public CVSCopy { -public: - FltCopy(); - - void run(); - -protected: - virtual bool copy_file(const Filename &source, const Filename &dest, - CVSSourceDirectory *dir, void *extra_data, - bool new_file); - -private: - enum FileType { - FT_flt, - FT_texture - }; - - class ExtraData { - public: - FileType _type; - FltTexture *_texture; - }; - - bool copy_flt_file(const Filename &source, const Filename &dest, - CVSSourceDirectory *dir); - bool copy_texture(const Filename &source, const Filename &dest, - CVSSourceDirectory *dir, FltTexture *tex, - bool new_file); - - - typedef pset< PT(FltExternalReference) > Refs; - typedef pset< PT(FltTexture) > Textures; - - void scan_flt(FltRecord *record, Refs &refs, Textures &textures); -}; - -#endif diff --git a/pandatool/src/maya/mayaShaders.cxx b/pandatool/src/maya/mayaShaders.cxx index dca3712366..3809574bcb 100644 --- a/pandatool/src/maya/mayaShaders.cxx +++ b/pandatool/src/maya/mayaShaders.cxx @@ -87,8 +87,10 @@ find_shader_for_node(MObject node, bool legacy_shader) { } // Well, we didn't find a ShadingEngine after all. Huh. - maya_cat.debug() - << node_fn.name().asChar() << " : no shading engine found.\n"; + if (maya_cat.is_debug()) { + maya_cat.debug() + << node_fn.name().asChar() << " : no shading engine found.\n"; + } return nullptr; } diff --git a/pandatool/src/mayaprogs/mayaCopy.cxx b/pandatool/src/mayaprogs/mayaCopy.cxx deleted file mode 100644 index d10ed8e0aa..0000000000 --- a/pandatool/src/mayaprogs/mayaCopy.cxx +++ /dev/null @@ -1,457 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file mayaCopy.cxx - * @author drose - * @date 2002-05-10 - * Modified 19Mar10 by ETC PandaSE team (see - * header comment for mayaToEgg.cxx for more details) - */ - -#include "mayaCopy.h" -#include "config_maya.h" -#include "cvsSourceDirectory.h" -#include "dcast.h" - -#include "pre_maya_include.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "post_maya_include.h" - -#include "mayaShader.h" - -using std::endl; -using std::string; - -/** - * - */ -MayaCopy:: -MayaCopy() { - set_program_brief("copy Maya .mb files into a CVS source hierarchy"); - set_program_description - ("mayacopy copies one or more Maya .mb files into a " - "CVS source hierarchy. " - "Rather than copying the named files immediately into the current " - "directory, it first scans the entire source hierarchy, identifying all " - "the already-existing files. If the named file to copy matches the " - "name of an already-existing file in the current directory or elsewhere " - "in the hierarchy, that file is overwritten. Other .mb files, as " - "well as texture files, that are externally referenced by the " - "named .mb file(s) are similarly copied."); - - clear_runlines(); - add_runline("[opts] file.mb [file.mb ... ]"); - - add_option - ("keepver", "", 0, - "Don't attempt to strip the Maya version number from the tail of the " - "source filename before it is copied into the tree.", - &CVSCopy::dispatch_none, &_keep_ver); - - /* - add_option - ("rp", "replace_prefix", 80, - "use these prefixes when replacing reference with the recently copied file from the " - "source filename before it is copied into the tree.", - &CVSCopy::dispatch_vector_string, NULL, &_replace_prefix); - */ - - add_option - ("omittex", "", 0, - "Character animation files do not need to copy the texures. " - "This option omits the textures of the models to be re-mayacopied", - &CVSCopy::dispatch_none, &_omit_tex); - - add_option - ("omitref", "", 0, - "Character animation files do not need to copy internal file references. " - "This option omits the references of the models to be re-mayacopied", - &CVSCopy::dispatch_none, &_omit_ref); - - add_option - ("ma", "", 0, - "Write a .ma file instead of a .mb file (regardless of input type)", - &CVSCopy::dispatch_none, &_maya_ascii); - - add_path_replace_options(); -} - -/** - * - */ -void MayaCopy:: -run() { - _maya = MayaApi::open_api(_program_name); - if (!_maya->is_valid()) { - nout << "Unable to initialize Maya.\n"; - exit(1); - } - - SourceFiles::iterator fi; - for (fi = _source_files.begin(); fi != _source_files.end(); ++fi) { - _curr_idx = 0; - ExtraData ed; - ed._type = FT_maya; - - CVSSourceTree::FilePath path = import(*fi, &ed, _model_dir); - if (!path.is_valid()) { - nout << "\nUnable to copy, aborting!\n\n"; - exit(1); - } - } -} - -/** - * Called by import() if verify_file() indicates that a file needs to be - * copied. This does the actual copy of a file from source to destination. - * If new_file is true, then dest does not already exist. - */ -bool MayaCopy:: -copy_file(const Filename &source, const Filename &dest, - CVSSourceDirectory *dir, void *extra_data, bool new_file) { - ExtraData *ed = (ExtraData *)extra_data; - switch (ed->_type) { - case FT_maya: - return copy_maya_file(source, dest, dir); - - case FT_texture: - if (_omit_tex) { - return true; - } - return copy_texture(source, dest, dir); - } - - nout << "Internal error: invalid type " << (int)ed->_type << "\n"; - return false; -} - -/** - * Given a source filename (including the basename only, without a dirname), - * return the appropriate corresponding filename within the source directory. - * This may be used by derived classes to, for instance, strip a version - * number from the filename. - */ -string MayaCopy:: -filter_filename(const string &source) { - if (_keep_ver) { - return source; - } - - size_t dot = source.rfind('.'); - size_t underscore = source.rfind("_v", dot); - if ((underscore != string::npos) && !isdigit(source.at(underscore+2))) - underscore = string::npos; - - string extension = source.substr(dot); - if (extension == ".ma" || extension == ".mb") { - // If we are reading a Maya file (as opposed to a texture image), then we - // always write ".mb" files out (unless -ma was specified on the command - // line). - if (_maya_ascii) { - extension = ".ma"; - } else { - extension = ".mb"; - } - } - - if (underscore == string::npos) { - // No version number appears to be present. - return source.substr(0, dot) + extension; - } else { - return source.substr(0, underscore) + extension; - } -} - -/** - * - */ -bool MayaCopy:: -copy_maya_file(const Filename &source, const Filename &dest, - CVSSourceDirectory *dir) { - if (!_maya->read(source)) { - maya_cat.error() - << "Unable to read " << source << "\n"; - return false; - } - - // Get the set of externally referenced Maya files. - MStringArray refs; - MStatus status = MFileIO::getReferences(refs); - if (status != MStatus::kSuccess) { - status.perror("MItDag constructor"); - return false; - } - - // Finally, copy in any referenced Maya files. - unsigned int num_refs = refs.length(); - - unsigned int ref_index; - maya_cat.info() << "num_refs = " << num_refs << endl; - for (ref_index = 0; ref_index < num_refs; ref_index++) { - maya_cat.info() << "curr_idx " << _curr_idx << endl; - string lookup = refs[ref_index].asChar(); - string blah = "file -q -referenceNode \"" + lookup + "\";"; - maya_cat.info() << blah << endl; - MString result; - status = MGlobal::executeCommand(MString(blah.c_str()), result); - maya_cat.info() << "result = " << result.asChar() << endl; - - // for multiple reference of the same model. maya throws in a {#} at the - // end, ignore that - size_t dup = lookup.find('{'); - if (dup != string::npos){ - lookup.erase(dup); - } - - // to check out this specific reference is actually loaded or not somehow - // this flag order of MEL script must be observed to guarantee proper - // working - string refNode = result.asChar(); - string refCheckCmd = "file -rfn " + refNode + " -q -dr;"; - int deferredRef; - status = MGlobal::executeCommand(MString(refCheckCmd.c_str()), deferredRef); - maya_cat.info() << "deferredRef = " << deferredRef << endl; - if (deferredRef == 1) { // means this reference is deferred, unloaded - continue; - } - - Filename filename = - _path_replace->convert_path(Filename::from_os_specific(lookup)); - - CVSSourceTree::FilePath path = - _tree.choose_directory(filename.get_basename(), dir, _force, _interactive); - Filename new_filename = path.get_rel_from(dir); - - if (maya_cat.is_spam()) { - maya_cat.spam() << "cvs dir " << dir->get_fullpath().to_os_generic() << endl; - maya_cat.spam() << "cvs path " << path.get_fullpath().to_os_generic() << endl; - } - MString result2; - - if (maya_cat.is_debug()) { - string cmdStr = "pwd"; - MString result3; - status = MGlobal::executeCommand(MString(cmdStr.c_str()), result3); - maya_cat.debug() << "result = " << result3.asChar() << "\n"; - } - _exec_string.push_back("file -loadReference \"" + string(result.asChar()) + "\" -type \"mayaBinary\" -options \"v=0\" \"" + new_filename.to_os_generic() + "\";"); - if (!_omit_ref) { - maya_cat.info() << "executing command: " << _exec_string[_curr_idx] << "\n"; - status = MGlobal::executeCommand(MString(_exec_string[_curr_idx].c_str())); - if (status != MStatus::kSuccess) { - status.perror("loadReference failed"); - } - } - _curr_idx++; - } - - if (!_omit_tex) { - // Get all the shaders so we can determine the set of textures. - _shaders.clear(); - collect_shaders(); - int num_shaders = _shaders.get_num_shaders(); - for (int i = 0; i < num_shaders; i++) { - MayaShader *shader = _shaders.get_shader(i); - for (size_t j = 0; j < shader->_all_maps.size(); j++) { - if (!extract_texture(*shader->_all_maps[j], dir)) { - return false; - } - } - } - } - - // Now write out the Maya file. - if (!_maya->write(dest)) { - maya_cat.error() - << "Cannot write " << dest << "\n"; - return false; - } - - for (ref_index = 0; ref_index < num_refs; ref_index++) { - if (1) { // we may want an option later to pull in all the referenced files - continue; - } - - string lookup = refs[ref_index].asChar(); - // for multiple reference of the same model. maya throws in a {#} at the - // end, ignore that - size_t dup = lookup.find('{'); - if (dup != string::npos){ - lookup.erase(dup); - } - - Filename filename = - _path_replace->convert_path(Filename::from_os_specific(lookup)); - - maya_cat.info() - << "External ref: " << filename << "\n"; - - // Now import the file - ExtraData ed; - ed._type = FT_maya; - - CVSSourceTree::FilePath path = import(filename, &ed, _model_dir); - if (!path.is_valid()) { - exit(1); - } - } - - return true; -} - -/** - * Gets the texture out of the indicated color channel and copies it in, - * updating the channel with the new texture filename. Returns true on - * success, false on failure. - */ -bool MayaCopy:: -extract_texture(MayaShaderColorDef &color_def, CVSSourceDirectory *dir) { - Filename texture_filename = - _path_replace->convert_path(color_def._texture_filename); - if (!texture_filename.exists()) { - nout << "*** Error: texture " << texture_filename - << " does not exist.\n"; - return false; - } else if (!texture_filename.is_regular_file()) { - nout << "*** Error: texture " << texture_filename - << " is not a regular file.\n"; - return false; - } else { - ExtraData ed; - ed._type = FT_texture; - - CVSSourceTree::FilePath texture_path = - import(texture_filename, &ed, _map_dir); - - if (!texture_path.is_valid()) { - return false; - } - - // Update the texture reference to point to the new texture filename, - // relative to the maya file. - Filename new_filename = texture_path.get_rel_from(dir); - color_def.reset_maya_texture(new_filename); - } - - return true; -} - -/** - * - */ -bool MayaCopy:: -copy_texture(const Filename &source, const Filename &dest, - CVSSourceDirectory *dir) { - if (!copy_binary_file(source, dest)) { - return false; - } - - return true; -} - -/** - * Recursively walks through the maya scene graph hierarchy, looking for - * shaders. - */ -bool MayaCopy:: -collect_shaders() { - MStatus status; - - MItDag dag_iterator(MItDag::kDepthFirst, MFn::kTransform, &status); - if (status != MStatus::kSuccess) { - status.perror("MItDag constructor"); - return false; - } - - // This while loop walks through the entire Maya hierarchy, one node at a - // time. Maya's MItDag object automatically performs a depth-first - // traversal of its scene graph. - bool all_ok = true; - while (!dag_iterator.isDone()) { - MDagPath dag_path; - status = dag_iterator.getPath(dag_path); - if (status != MStatus::kSuccess) { - status.perror("MItDag::getPath"); - } else { - if (!collect_shader_for_node(dag_path)) { - all_ok = false; - } - } - - dag_iterator.next(); - } - - if (!all_ok) { - nout << "Errors encountered in traversal.\n"; - return false; - } - - return true; -} - -/** - * Gets the relevant shader on the current node, if it has one. - */ -bool MayaCopy:: -collect_shader_for_node(const MDagPath &dag_path) { - MStatus status; - MFnDagNode dag_node(dag_path, &status); - if (status != MStatus::kSuccess) { - status.perror("MFnDagNode constructor"); - return false; - } - - if (dag_path.hasFn(MFn::kNurbsSurface)) { - MFnNurbsSurface surface(dag_path, &status); - if (status) { - _shaders.find_shader_for_node(surface.object(), false); - } - - } else if (dag_path.hasFn(MFn::kMesh)) { - MFnMesh mesh(dag_path, &status); - if (status) { - // Meshes may have multiple different shaders. - MObjectArray shaders; - MIntArray poly_shader_indices; - - status = mesh.getConnectedShaders(dag_path.instanceNumber(), - shaders, poly_shader_indices); - if (status) { - unsigned int num_shaders = shaders.length(); - for (unsigned int shader_index = 0; - shader_index < num_shaders; - shader_index++) { - MObject engine = shaders[shader_index]; - _shaders.find_shader_for_shading_engine(engine, false); - } - } - } - - } else { - // Ignoring other kinds of node. - } - - return true; -} - - -int main(int argc, char *argv[]) { - MayaCopy prog; - prog.parse_command_line(argc, argv); - prog.run(); - return 0; -} diff --git a/pandatool/src/mayaprogs/mayaCopy.h b/pandatool/src/mayaprogs/mayaCopy.h deleted file mode 100644 index 38f0aef9de..0000000000 --- a/pandatool/src/mayaprogs/mayaCopy.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file mayaCopy.h - * @author drose - * @date 2002-05-10 - */ - -#ifndef MAYACOPY_H -#define MAYACOPY_H - -#include "pandatoolbase.h" -#include "cvsCopy.h" -#include "mayaApi.h" -#include "dSearchPath.h" -#include "pointerTo.h" - -#include "pset.h" - -#include "pre_maya_include.h" -#include -#include "post_maya_include.h" - -#include "mayaShaders.h" - -class MayaShader; -class MayaShaderColorDef; - -/** - * A program to copy Maya .mb files into the cvs tree. - */ -class MayaCopy : public CVSCopy { -public: - MayaCopy(); - - void run(); - -protected: - virtual bool copy_file(const Filename &source, const Filename &dest, - CVSSourceDirectory *dir, void *extra_data, - bool new_file); - - virtual std::string filter_filename(const std::string &source); - -private: - enum FileType { - FT_maya, - FT_texture - }; - - class ExtraData { - public: - FileType _type; - MayaShader *_shader; - }; - - bool copy_maya_file(const Filename &source, const Filename &dest, - CVSSourceDirectory *dir); - bool extract_texture(MayaShaderColorDef &color_def, CVSSourceDirectory *dir); - bool copy_texture(const Filename &source, const Filename &dest, - CVSSourceDirectory *dir); - - bool collect_shaders(); - bool collect_shader_for_node(const MDagPath &dag_path); - - bool _keep_ver; - bool _omit_tex; - bool _omit_ref; - int _curr_idx; - bool _maya_ascii; - /* - vector_string _replace_prefix; - */ - - vector_string _exec_string; - - PT(MayaApi) _maya; - MayaShaders _shaders; -}; - -#endif diff --git a/pandatool/src/mayaprogs/mayapath.cxx b/pandatool/src/mayaprogs/mayapath.cxx index 441a22c0e2..526caa3afd 100644 --- a/pandatool/src/mayaprogs/mayapath.cxx +++ b/pandatool/src/mayaprogs/mayapath.cxx @@ -106,6 +106,7 @@ struct MayaVerInfo maya_versions[] = { { "MAYA20165", "2016.5"}, { "MAYA2017", "2017"}, { "MAYA2018", "2018"}, + { "MAYA2019", "2019"}, { 0, 0 }, }; @@ -442,11 +443,6 @@ main(int argc, char *argv[]) { #endif // IS_OSX - // When this is set, Panda3D will try not to use any functions from the - // CPython API. This is necessary because Maya links with its own copy of - // Python, which may be incompatible with ours. - putenv((char *)"PANDA_INCOMPATIBLE_PYTHON=1"); - // Now that we have set up the environment variables properly, chain to the // actual maya2egg_bin (or whichever) executable. diff --git a/pandatool/src/miscprogs/binToC.cxx b/pandatool/src/miscprogs/binToC.cxx index f963c08986..ae9fbc4090 100644 --- a/pandatool/src/miscprogs/binToC.cxx +++ b/pandatool/src/miscprogs/binToC.cxx @@ -99,9 +99,8 @@ run() { out << std::hex << std::setfill('0'); int count = 0; int col = 0; - unsigned int ch; - ch = in.get(); - while (!in.fail() && !in.eof()) { + int ch = in.get(); + while (!in.fail() && ch != EOF) { if (col == 0) { out << "\n "; } else if (col == col_width) { @@ -110,7 +109,7 @@ run() { } else { out << ", "; } - out << "0x" << std::setw(2) << ch; + out << "0x" << std::setw(2) << (unsigned int)ch; col++; count++; ch = in.get(); diff --git a/pandatool/src/ptloader/config_ptloader.h b/pandatool/src/ptloader/config_ptloader.h index b195083b2b..fc324dc939 100644 --- a/pandatool/src/ptloader/config_ptloader.h +++ b/pandatool/src/ptloader/config_ptloader.h @@ -15,7 +15,7 @@ #define CONFIG_PTLOADER_H #include "pandatoolbase.h" - +#include "notifyCategoryProxy.h" #include "dconfig.h" #include "distanceUnit.h" #include "configVariableEnum.h" diff --git a/pandatool/src/scripts/.cshrc b/pandatool/src/scripts/.cshrc deleted file mode 100644 index 2ed7a96675..0000000000 --- a/pandatool/src/scripts/.cshrc +++ /dev/null @@ -1,122 +0,0 @@ -set prompt='%m:%B%~%b> ' -alias ls 'ls -C -F' -alias dir ls -C -F -l -alias vi vim -alias fd ftp dumbo -alias tz tar zxvf -alias so1 setenv PANDA_OPTIMIZE 1 -alias so3 setenv PANDA_OPTIMIZE 3 -alias cpd 'cd $PANDA/src' -alias emacs xemacs -alias em xemacs -alias mi make install -alias ni nmake install -alias dh demo /install/models/herc-6000.egg /install/models/HB_1_HE1.egg -set nobeep=1 - -# my defaults for panda build -if (! $?PANDA_OPTIMIZE ) then -setenv PANDA_OPTIMIZE 1 -endif - -setenv CL_MAKE_BROWSE_INFO 0 -alias setbrowseinfo1 setenv CL_MAKE_BROWSE_INFO 1 -alias setbrowseinfo0 setenv CL_MAKE_BROWSE_INFO 0 - -setenv HAVE_GL yes -setenv HAVE_DX yes -setenv USE_NSPR t - -if (! $?USER ) then -setenv USER georges -endif - -if (! $?CYGWIN_ROOT) then -# may cause tcsh hang -setenv CYGWIN_ROOT `cygpath -w /` -endif - -setenv PENV_COMPILER WIN32_VC -setenv PENV WIN32 -setenv PANDA_ROOT C:/Cygwin - -setenv CVSROOT :pserver:georges@dimbo:/fit/cvs -#setenv CVS_PASSFILE ~/.cvspass - -# check out files read-only -setenv CVSREAD 1 - -if (! $?PPREMAKE_CONFIG) then -setenv PPREMAKE_CONFIG /usr/local/etc/Config.pp -endif - -# /usr/local/panda/bin for ppremake -if(! $?TCSH_NO_CHANGEPATH) then -set path=(/bin /contrib/bin $path ~/scripts $path /usr/local/panda/bin) -endif - - -if (! $?TCSH_NO_PANDA_ATTACH ) then -# setenv TOOL /install/tool -setenv MYTOOL ~/player/dtool -setenv MYTOOL_CSHRC $MYTOOL/etc/dtool.cshrc -#echo " checking for dtool.cshrc" - -if(-e $MYTOOL_CSHRC) then -setenv DTOOL $MYTOOL -#setenv CTATTACH_DEBUG 1 -#echo " found my dtool.cshrc" -source $DTOOL/etc/dtool.cshrc personal - -# else -# echo "WARNING: $TOOL_CSHRC doesnt exist, panda env setup failed!" -# cd /install -else - -setenv DTOOL /g/player/install/win2k/dtool -source $DTOOL/etc/dtool.cshrc install -endif - -cta panda personal - -if(! $?TCSH_NO_CSHRC_CHDIR ) then -cd $PANDA -endif - -endif - -# this version of emacs ignores cygwin root, cant give it cygwin paths -#set path=(/cygdrive/c/progra~1/XEmacs/XEmacs-21.1.9/i386-pc-win32 /bin /contrib/bin $path) -#set path=( /bin /contrib/bin /cygdrive/c/python16 $path ) -#set path=( /bin /contrib/bin $path ~/scripts /usr/sbin /contrib/sbin) for new cygwin - -#set path=($path /cygdrive/c/progra~1/micros~1/common/msdev98/bin /cygdrive/c/progra~1/micros~1/common/tools/winnt /cygdrive/c/progra~1/micros~1/common/tools /cygdrive/c/progra~1/micros~1/vc98/bin /cygdrive/c/winnt /cygdrive/c/winnt/system32 /cygdrive/c/bin) -#set lib = (/cygdrive/c/progra~1/micros~1/vc98/lib /cygdrive/c/progra~1/micros~1/vc98/mfc/lib) -# note: have to set vc envvars in sys env vars -#cd $HOME -unset owd - -alias mktool 'cd $DTOOL/src/build;./initialize make;cd $DTOOL;make install' -#alias cpdll 'cp -p $PANDA/src/all/pandadx/*dll $PANDA/src/all/panda/*dll $PANDA/src/all/pandaegg/*dll $PANDA/lib;cp -p $PANDA/src/all/pandadx/lib*pdb $PANDA/src/all/panda/lib*pdb $PANDA/src/all/pandaegg/lib*pdb $PANDA/lib' -alias mkdx 'cd dxgsg; make install; cd ../wdxdisplay; make install; cd ../../metalibs/pandadx; make install; cd wdxdisplay' -alias mkgl 'cd glgsg; make install; cd ../wgldisplay; make install; cd ../../metalibs/pandagl; make install; cd wgldisplay' -alias cpdxbak 'chmod a+w $PANDA/dx-bak/*;cp $PANDA/src/dxgsg/*.? $PANDA/src/wdxdisplay/*.? $PANDA/dx-bak' -alias tlinit 'cd $DTOOL/src/build; ./initialize make; cd $DTOOL' -alias setctdbg 'setenv CTATTACH_DEBUG 1' -alias update cvs update -alias cvhist cvs history -a -alias log cvs log -alias updn 'cvs -n update -R -d \!* |& egrep -v "Updating|^\?"' -alias cvdiff cvs diff -alias cvlog ~/scripts/cvs2cl.pl -alias updr 'cvs update -R -d \!* |& egrep -v "Updating|^\?"' -alias upd 'cvs update -d \!* |& egrep -v "Updating|^\?"' -alias lastchng ~/scripts/lastchng.pl -alias wnewdiff 'cvs update -p -r HEAD \!* > junk; windiff junk \!* &' -#only works if cur ver differs from checked in version -#alias newdiff 'cvs diff -r`log -h \!* | grep head | sed -e "s/head: //"` \!*' -# turns out all you need is this -alias newdiff 'cvs diff -r HEAD -w \!*' -# need version that picks out last 2 revs and does the diff -alias cvsclean "find \!* -path '*CVS*' -prune -or -not -name 'bldlog*' -and -not -type d -print | xargs --no-run-if-empty -n 40 rm" - diff --git a/pandatool/src/scripts/basedlls.pl b/pandatool/src/scripts/basedlls.pl deleted file mode 100755 index 05d01cca30..0000000000 --- a/pandatool/src/scripts/basedlls.pl +++ /dev/null @@ -1,111 +0,0 @@ -#!perl - -# generate address base file for panda dlls -# to simplify things, do it for Opt1 and reuse that for Opt2, -# and do it for Opt3 and use that for Opt4, since they will both be bigger than counterparts -# to run, in any directory, do 'perl basedlls.pl' and it will generate dllbase.txt and dllbase_d.txt in 'tmp' dir under current -# make sure Opt1 and Opt3 are current so current sizes are used in map - -$archive_root="\\\\dimbo\\panda\\win\\"; -#edit this to where rebase.exe is on your machine, must be cygwin-style path for cygwin perl -$rebase_path="/msvc7/Common7/Tools/bin"; -$ENV{'PATH'}=$rebase_path.":".$ENV{'PATH'}; - -# note: this is the order dlls will be laid out in memory -@dll_names=("libpanda","libwindisplay","libpandadx8","libpandadx7","libpandagl","libpandaexpress","libpandaphysics","libpandafx","libdtool","libdtoolconfig","libpystub","libdirect","libtoontown","libmiles_audio","libpandaegg","libframework"); - -# old audio dlls -# "libaudio_load_midi","libaudio_load_mp3","libaudio_load_st","libaudio_load_wav", - -@headerstrs=( -"This file is used by the MSVC linker to specify DLL base addresses to", -"prevent runtime relocation of DLLs by the windows loader. This speeds", -"loading. This file was generated using \$PANDATOOL/src/scripts/basedlls.pl", -"which uses rebase.exe from the MS platform SDK. It may additionally be", -"hand-hacked to simplify addresses for popular dlls to aid debugging.", -); - -$headerstr=""; -for($i=0;$i<=$#headerstrs;$i++) { - $headerstr.="; ".$headerstrs[$i]."\n"; -} - -@modules=("dtool","panda","direct","toontown"); - -$dll_names_nodbg=""; -$dll_names_dbg=""; - -foreach my $nm (@dll_names) { - $dll_names_nodbg.=" ".$nm.".dll"; - $dll_names_dbg.=" ".$nm."_d.dll"; -} - -# 0x60000000 is the recommended base address for user dll addr space -$baseaddr="0x60000000"; -$dllbasefile="dllbase.txt"; -$basedlllogfilename="log.txt"; - -# rebase will modify input dlls, so make tmp copies of them -if(!(-d "tmp")) { - system("mkdir tmp"); -} -chdir("tmp"); -unlink($basedlllogfilename); - -############################ -### do release - -$archivepath=$archive_root."release"; - -# need to remove existing files or rebase will just append to them -open(OUTFILE, ">$dllbasefile") || die "Couldn't open $dllbasefile!\n"; -print OUTFILE $headerstr; -print OUTFILE "\n; release dlls\n\n"; -close(OUTFILE); - -foreach my $dir1 (@modules) { - my @args=("cmd.exe","/c","copy ".$archivepath."\\$dir1\\lib\\*.dll ."); - system(@args); -} - -$argstr="-v -b $baseaddr -c $dllbasefile -l $basedlllogfilename $dll_names_nodbg"; - -#@args=("cmd.exe","/C","rebase.exe $argstr"); -#print $args[0]." ".$args[1]." ".$args[2]."\n"; -@args=("rebase.exe $argstr"); -print $args[0]."\n"; -system(@args); - -############################ -### do debug - -open(OUTFILE, ">>$dllbasefile") || die "Couldn't open $dllbasefile!\n"; -print OUTFILE "\n; debug dlls\n\n"; -close(OUTFILE); - -$archivepath=$archive_root."debug"; -foreach my $dir1 (@modules) { - my @args=("cmd.exe","/c","copy ".$archivepath."\\$dir1\\lib\\*.dll ."); - system(@args); -} - -$argstr="-v -b $baseaddr -c $dllbasefile -l $basedlllogfilename $dll_names_dbg"; -#@args=("cmd.exe","/C","rebase.exe $argstr"); -#print $args[0]." ".$args[1]." ".$args[2]."\n"; -@args=("rebase.exe $argstr"); -print $args[0]."\n"; -system(@args); - -#cygwin d2u seems to not work rite -$d2u_path=$ENV{'HOME'}."/player/wintools/bin"; -$ENV{'PATH'}=$d2u_path.":".$ENV{'PATH'}; -print $ENV{'PATH'},"\n"; -@args=("dos2unix",$dllbasefile); -print $args[0]." ".$args[1]." ".$args[2]."\n"; -system(@args); - -print "do cp tmp/dllbase.txt ~/player/dtool/src/dtoolbase\n"; - -exit(0); - - diff --git a/pandatool/src/scripts/binreplace.pl b/pandatool/src/scripts/binreplace.pl deleted file mode 100755 index 8bae8935c5..0000000000 --- a/pandatool/src/scripts/binreplace.pl +++ /dev/null @@ -1,42 +0,0 @@ -#!perl -if($#ARGV<2) { - print "Usage: perl binreplace.pl [-i] [findstr] [replacestr] [filename]\n"; - exit(1); -} - -$argnum=0; -$case_insensitive = 0; -if($ARGV[0] eq "-i") { - $argnum=1; - $case_insensitive = 1; -} -$findstr = $ARGV[$argnum]; $argnum++; -$replacestr = $ARGV[$argnum]; $argnum++; -$filename = $ARGV[$argnum]; $argnum++; - -$filenamebak = $filename.".bak"; -$retval = rename($filename,$filenamebak); -if(! $retval) { - die "rename(".$filename.",".$filenamebak.") failed!\n"; -} - -print "replacing '".$findstr."' with '".$replacestr."' in ".$filename."\n"; - -# for some reason, you need to double the $findstr \'s but not the replacestr ones - -$findstr =~ s/\\/\\\\/g; -#$replacestr =~ s/\\/\\\\/g; - -open IN, $filenamebak or die "can't read ".$filenamebak." $!"; -open OUT, ">$filename" or die "can't create ".$filename.": $!"; -binmode IN; -binmode OUT; - -if($case_insensitive) { - s/$findstr/$replacestr/gi, print OUT while ; -} else { - s/$findstr/$replacestr/g, print OUT while ; -} -close OUT; -close IN; -unlink($filenamebak); diff --git a/pandatool/src/scripts/builder.cshrc b/pandatool/src/scripts/builder.cshrc deleted file mode 100644 index e79489ad78..0000000000 --- a/pandatool/src/scripts/builder.cshrc +++ /dev/null @@ -1,207 +0,0 @@ -set prompt='%m:%B%~%b> ' -alias ls 'ls -C -F' -alias dir ls -C -F -l -alias vi vim -alias fd ftp dumbo -alias tz tar zxvf -alias spo1 setenv PANDA_OPTIMIZE 1 -alias spo2 setenv PANDA_OPTIMIZE 2 -alias spo3 setenv PANDA_OPTIMIZE 3 -alias spo4 setenv PANDA_OPTIMIZE 4 -alias snoi setenv NO_INTERROGATE 1 -alias usnoi unsetenv NO_INTERROGATE -alias set_intel setenv USE_COMPILER INTEL -alias set_msvc setenv USE_COMPILER MSVC - -alias cpd 'cd $PANDA/src' -#alias emacs xemacs -#alias em xemacs -alias mi make install -alias ni nmake install -alias dh demo /install/models/herc-6000.egg /install/models/HB_1_HE1.egg -set nobeep=1 - -if(! $?TCSH_NO_CHANGEPATH) then -# have to manually put '\' in front of spaces for this to work -# this is completely reset at file end -set oldpath=($path) -set path=(./ /bin /usr/local/bin /mscommon/Tools/WinNT /mscommon/MSDev98/Bin /mscommon/Tools /msvc98/Bin /mspsdk/Bin /mspsdk/Bin/WinNT /c/WINNT/System32/Wbem /c/PROGRA~1/TCL/bin /c/Program\ Files/Resource\ Pro\ Kit/ /c/Program\ Files/Resource\ Kit/ /c/WINNT/system32 /c/WINNT /c/bin /c/xemacs/bin/i686-pc-cygwin /emacs-20.7/bin) -endif - -# my defaults for panda build -if (! $?PANDA_OPTIMIZE ) then -setenv PANDA_OPTIMIZE 2 -endif - -setenv CL_MAKE_BROWSE_INFO 0 -alias setbrowseinfo1 setenv CL_MAKE_BROWSE_INFO 1 -alias setbrowseinfo0 setenv CL_MAKE_BROWSE_INFO 0 - -setenv HAVE_GL yes -setenv HAVE_DX9 yes -setenv USE_NSPR t - -if (! $?USER ) then -setenv USER georges -endif - -if (! $?CYGWIN_ROOT) then -# may cause tcsh hang -setenv CYGWIN_ROOT `cygpath -w /` -endif - -setenv PENV_COMPILER WIN32_VC -setenv PENV WIN32 -setenv PANDA_ROOT C:/Cygwin - -setenv CVSROOT :pserver:cxgeorge@dimbo:/fit/cvs -#setenv CVS_PASSFILE ~/.cvspass - -# check out files read-only -# setenv CVSREAD 1 - -if (! $?PANDA_BUILD_TYPE) then -setenv PANDA_BUILD_TYPE gmsvc -endif - -if (! $?PPREMAKE_CONFIG) then -#setenv PPREMAKE_CONFIG /usr/local/etc/Config.pp -#WINTOOLS may not be set, so do this instead -setenv PPREMAKE_CONFIG $HOME/player/wintools/panda/etc/Config.pp -endif - -# for ppremake -#set path=($path /usr/local/panda/bin) -# /usr/local/panda/bin for ppremake -if(! $?TCSH_NO_CHANGEPATH) then -# set path=(/bin /contrib/bin $path ~/scripts $path /usr/local/panda/bin) - -# builder.pl doesnt want initial path changed, except to add these things -set path=(~/scripts $path) -endif - - - -if (! $?TCSH_NO_PANDA_ATTACH ) then -# builder scripts don't attach (attach scripts too buggy on cygwin 1.0) -# they set paths manually -# setenv TOOL /install/tool -setenv MYTOOL ~/player/dtool -setenv MYTOOL_CSHRC $MYTOOL/src/attach/dtool.cshrc -#echo " checking for dtool.cshrc" - -if(-e $MYTOOL_CSHRC) then -setenv DTOOL $MYTOOL -#setenv CTATTACH_DEBUG 1 -#echo " found my dtool.cshrc" -source $DTOOL/etc/dtool.cshrc personal -cta wintools personal -cta panda personal -cta direct personal -cta toontown personal -cd ~/player/panda -# else -# echo "WARNING: $TOOL_CSHRC doesnt exist, panda env setup failed!" -# cd /install -else - -setenv DTOOL /g/player/install/win2k/dtool -source $DTOOL/src/attach/dtool.cshrc install -endif - -alias ctp "cta panda personal;cta wintools personal" -alias ctt "cta direct personal;cta toontown personal" -cta wintools personal -cta panda personal -cta direct personal -cta toontown personal - -if(! $?TCSH_NO_CSHRC_CHDIR ) then -cd $HOME/player/panda -endif - -else - if(! $?TCSH_NO_CHANGEPATH) then - set path=($oldpath $path) - endif -endif - -# this version of emacs ignores cygwin root, cant give it cygwin paths -#set path=(/cygdrive/c/progra~1/XEmacs/XEmacs-21.1.9/i386-pc-win32 /bin /contrib/bin $path) -#set path=( /bin /contrib/bin /cygdrive/c/python16 $path ) -#set path=( /bin /contrib/bin $path ~/scripts /usr/local/winpandatools/win98) -set path=( /bin /contrib/bin $path ~/scripts) -set path=(/intel/compiler60/ia32/bin /intel/iselect/bin /intel/edb $path) - -#set path=($path /cygdrive/c/progra~1/micros~1/common/msdev98/bin /cygdrive/c/progra~1/micros~1/common/tools/winnt /cygdrive/c/progra~1/micros~1/common/tools /cygdrive/c/progra~1/micros~1/vc98/bin /cygdrive/c/winnt /cygdrive/c/winnt/system32 /cygdrive/c/bin) -#set lib = (/cygdrive/c/progra~1/micros~1/vc98/lib /cygdrive/c/progra~1/micros~1/vc98/mfc/lib) -# note: have to set vc envvars in sys env vars -#cd $HOME -unset owd - - -alias md mkdir -alias pp ppremake -alias ppr ppremake -alias wlastchng ~/scripts/wlastchng.pl -alias ni nmake install -alias listincs-all '(grep -h include `(echo *cxx | sed -e "s/test_[^ ]*//g")` | sort | uniq ) | (grep -v \");(echo ""); (grep -h include `(echo *cxx | sed -e "s/test_[^ ]*//g")` | sort | uniq ) | (grep \");' -alias listincs-rep '(grep -h include `(echo *cxx | sed -e "s/test_[^ ]*//g")` | sort | uniq -d) | (grep -v \");(echo ""); (grep -h include `(echo *cxx | sed -e "s/test_[^ ]*//g")` | sort | uniq -d) | (grep \");' -alias listincs-sing '(grep include `(echo *cxx | sed -e "s/test_[^ \t]*//g")` | /bin/sort +1 | uniq -u -f 1 | /bin/sort);' -alias listincs-old '(grep -h include *cxx | sort | uniq ) | (grep -v \");echo ""; (grep -h include *cxx | sort | uniq ) | (grep \");' -alias listincs '(grep -h include *cxx | sort | uniq ) | (grep -v \"); (grep -h include *cxx | sort | uniq ) | (grep \");' -alias prproc 'echo $NUMBER_OF_PROCESSORS' -alias setproc1 setenv NUMBER_OF_PROCESSORS 1 -alias setproc2 setenv NUMBER_OF_PROCESSORS 2 -alias setproc4 setenv NUMBER_OF_PROCESSORS 4 -alias setgenass setenv GEN_ASSEMBLY 1 -alias usetgenass unsetenv GEN_ASSEMBLY -alias mroe more - -alias ctp "cta panda personal;cta wintools personal" -alias mktool 'cd $DTOOL/src/build;./initialize make;cd $DTOOL;make install' -#alias cpdll 'cp -p $PANDA/src/all/pandadx/*dll $PANDA/src/all/panda/*dll $PANDA/src/all/pandaegg/*dll $PANDA/lib;cp -p $PANDA/src/all/pandadx/lib*pdb $PANDA/src/all/panda/lib*pdb $PANDA/src/all/pandaegg/lib*pdb $PANDA/lib' -alias mkdx 'cd ~/player/panda/src/dxgsg; make install; cd ../wdxdisplay; make install; cd ../../metalibs/pandadx; make install; cd ~/player/panda/src/dxgsg' -alias mkdemo 'cd ~/player/panda/src/framework; make install; cd ../testbed; rm Opt*/demo.exe; make install; cd ~/player/panda/src/framework' -alias mkpp 'cd ~/player/panda/src/physics; make install; cd ../particlesystem; make install; cd ../../metalibs/pandaphysics; make install; cd ~/player/panda/src/dxgsg' -# alias mkdemo 'cd ~/player/panda/src/framework; make install; cd ../testbed; make clean; make install; cd ~/player/panda/src/framework' -alias mkdemo 'cd ~/player/panda/src/framework; make install; cd ../testbed; rm Opt*/*.exe; make install; cd ~/player/panda/src/framework' -alias mkgl 'cd ~/player/panda/src/glgsg; make install; cd ../wgldisplay; make install; cd ../../metalibs/pandagl; make install; cd ../../src/panda/wgldisplay' -alias mkegg 'cd ~/player/panda/src/egg; make install; cd ../builder; make install; cd ../egg2sg; make install; cd ../../metalibs/pandaegg; make install; cd ~/player/panda/src/dxgsg' -alias cpdxbak 'chmod a+w $PANDA/dx-bak/*;cp $PANDA/src/dxgsg/*.? $PANDA/src/wdxdisplay/*.? $PANDA/dx-bak' -alias tlinit 'cd $DTOOL/src/build; ./initialize make; cd $DTOOL' -alias setctdbg 'setenv CTATTACH_DEBUG 1' -alias update cvs update -alias cvhist cvs history -a -alias log cvs log -alias updn 'cvs -n update -R -d \!* |& egrep -v "Updating|^\?"' -alias cvdiff cvs diff -alias cvlog ~/scripts/cvlog.pl -alias updr 'cvs update -R -d \!* |& egrep -v "Updating|^\?"' -alias upd 'cvs update -d \!* |& egrep -v "Updating|^\?"' -alias chm 'chmod a+w *' -alias lastchng ~/scripts/lastchng.pl -alias wnewdiff 'cvs update -p -r HEAD \!* > junk ; windiff junk \!* &' -#only works if cur ver differs from checked in version -#alias newdiff 'cvs diff -r`log -h \!* | grep head | sed -e "s/head: //"` \!*' -# turns out all you need is this -alias newdiff 'cvs diff -r HEAD -w \!*' -# need version that picks out last 2 revs and does the diff -alias cvsclean "find \!* -path '*CVS*' -prune -or -not -name 'bldlog*' -and -not -type d -print | xargs --no-run-if-empty -n 40 rm" - -alias genpy4 '~/player/direct/bin/genPyCode win-publish' -alias genpy1 '~/player/direct/bin/genPyCode win-debug' -alias genpy2 '~/player/direct/bin/genPyCode win-debug' -alias genpy3 '~/player/direct/bin/genPyCode win-release' -alias nd newdiff - -# setenv LOCAL_PPREMAKE_CONFIG ~/player/MyConfig.pp - -alias ppy1 'ppython -d `cygpath -w $DIRECT/bin/generatePythonCode` -v -d `cygpath -w $DIRECT/lib/py` -e `cygpath -w $DIRECT/src/extensions` -i libdtoolconfig libpandaexpress libpanda libpandaphysics libdirect libtoontown' -setenv ZPAN /z/cygwin/home/builder/player/panda -setenv XPAN /x/cygwin/home/georges/player/panda -unalias emacs - -alias setcfgdbg setenv CONFIG_CONFIG ':configdbg=1:configpath=CFG_PATH:configpath=ETC_PATH' -alias usetcfgdbg setenv CONFIG_CONFIG 'configpath=CFG_PATH:configpath=ETC_PATH' -alias s source diff --git a/pandatool/src/scripts/builder.pl b/pandatool/src/scripts/builder.pl deleted file mode 100755 index 93338548b5..0000000000 --- a/pandatool/src/scripts/builder.pl +++ /dev/null @@ -1,761 +0,0 @@ -#!perl - -#NOTE: this script assumes you are running the Cygwin perl, which uses the -# Cygwin file paths (i.e. '/' corresponds to 'C:\cygwin') - -my $WIN_INSTALLDIR="\\\\dimbo\\panda\\win"; -my $scriptpath = "C:\\cygwin\\home\\builder\\scripts"; - -# $ENV{'USE_COMPILER'} = "INTEL"; # experiment - -# my $WIN_INSTALLDIR="\\\\cxgeorge-d01\\c\\win"; - -### DEBUG SETTINGS -# my $DEBUG_TREECOPY = 1; -# my $DEBUG_GENERATE_PYTHON_CODE_ONLY = 1; -# my $DEBUG_MAKEBSC_ONLY=1; - -if ($ENV{'DEBUG_GENERATE_PYTHON_CODE_ONLY'} ne '') { - $DEBUG_GENERATE_PYTHON_CODE_ONLY = 1; -} -# my $DO_ARCHIVE_AND_COPY_ONLY = 1; -my $DONT_ARCHIVE_OLD_BUILDS = 0; -my $BLD_DTOOL_ONLY=0; - -my $DIRPATH_SEPARATOR=':'; # set to ';' for non-cygwin NT perl - -my @inst_dirnames=("archive","debug","install","release","publish"); -my $ARCHIVENUM=0; -my $DEBUGNUM=1; -my $INSTALLNUM=2; -my $RELEASENUM=3; -my $PUBLISHNUM=4; -my @inst_dirs; -for(my $i=0;$i<=$#inst_dirnames;$i++) { - $inst_dirs[$i]=$WIN_INSTALLDIR."\\".$inst_dirnames[$i]; -} - -my $VC7_BINDIRS="/msvc7/Vc7/bin".$DIRPATH_SEPARATOR."/msvc7/Common7/Tools/Bin".$DIRPATH_SEPARATOR."/msvc7/Common7/Tools".$DIRPATH_SEPARATOR."/msvc7/Common7/IDE"; - -#if(! $DEBUG_GENERATE_PYTHON_CODE_ONLY) { -# $ENV{'PANDA_OPTIMIZE'}='4'; # var has meaning to my special Config.pp -#} - -$ENV{'WINTOOLS'} = '/home/builder/player/wintools'; -#$ENV{'PPREMAKE_CONFIG'} = '/usr/local/etc/Config.pp'; old location -$ENV{'PPREMAKE_CONFIG'} = $ENV{'WINTOOLS'}.'/panda/etc/Config.pp'; -$ENV{'PPREMAKE_PLATFORM'} = 'Win32'; # generate OptX-Win32 dirs instead of OptX-Cygwin -$ENV{'TCSH_NO_CSHRC_CHDIR'}='1'; -$ENV{'ENABLE_PROFILING'}='1'; # generate .map files - -# needed to add this to fix version.rc path in ppremake? -$ENV{'DTOOL'} = '/home/builder/player/dtool'; - -$ENV{'HOME'}="/home/builder"; -$ENV{'USER'}="builder"; -$ENV{'USERNAME'}=$ENV{'USER'}; - -my $DONT_ARCHIVE_OLD_BUILDS = (($ENV{'DONT_ARCHIVE_OLD_BUILDS'} ne "") || $DONT_ARCHIVE_OLD_BUILDS); - -sub logmsg() { - my $msg = shift; - print $msg."\n"; - open(LOGFILE,">>".$fulllogfilename) || die "can't open log file '$fulllogfilename'\n"; - print LOGFILE $msg."\n"; - close(LOGFILE); -} - -#could change this to use eval, but it would require doubling the '\''s again in filename -sub mychdir() { - my $newdir = shift; - my $retval = chdir($newdir); - &logmsg("chdir(".$newdir.")"); - if(! $retval) { - &logmsg("chdir(".$newdir.") failed!!"); - exit(1); - } -} - -sub mymkdir() { - my $newdir=shift; - if(!(-e $newdir)) { - if(!mkdir($newdir,0xfff)) { - &logmsg("cant create new dir '".$newdir."' !!"); - exit(1); - } - } -} - -sub myrename() { - my $n1 = shift; - my $n2 = shift; - my $retval; - - &logmsg("rename(".$n1.",".$n2.")"); - - if(-e $n2) { - - # find name we can move old target to - my $newnum=1; - while(-e $n2.".old.".$newnum) { - $newnum++; - } - - $newconflicttargetname=$n2.".old.".$newnum; - &logmsg("avoiding rename conflict, renaming old ".$n2." to ".$newconflicttargetname); - $retval = rename($n2,$newconflicttargetname); - if(! $retval) { - &logmsg("rename failing, giving up (check if files or dirs are open in other apps)"); - exit(1); - } - } - - $retval = rename($n1,$n2); - if(! $retval) { - &logmsg("rename(".$n1.",".$n2.") failed!!! (check if files or dirs are open in other apps)"); - exit(1); - } -} - -sub myexecstr() { - my $cmdstr = shift; - my $errstr = shift; - my $dologstr = shift; - my $exec_cshrc_type = shift; - - if($dologstr eq "DO_LOG") { - if($exec_cshrc_type eq "NT cmd") { - $cmdstr.=" >> ".$fulllogfilename_win." 2>&1"; # 2>&1 tells nt cmd.exe to redirect stderr to stdout - } else { - $cmdstr.=" >>& ".$fulllogfilename; # for tcsh - } - - &logmsg($cmdstr); - } - - my $savedhome = $ENV{'HOME'}; - - if( $exec_cshrc_type eq "NO_CSHRC") { - # change $HOME so .cshrc doesn't get sources by tcsh - $ENV{'HOME'}="/"; - } elsif( $exec_cshrc_type eq "NO_PANDA_ATTACH") { - $ENV{'TCSH_NO_PANDA_ATTACH'}="1"; - } - - if($exec_cshrc_type eq "NT cmd") { - my @args = ("cmd.exe", "/c", "$cmdstr"); - $retval = system(@args); - } else { - my @args = ("tcsh.exe", "-c", "$cmdstr"); - $retval = system(@args); - } - - if($retval!=0) { - if(($retval>=256) || ($retval<=-256)) { - $retval= $retval/256; # actual retval - } - if($errstr eq "") { - &logmsg($cmdstr." failed!!!!! continuing anyway...\nError return value=".$retval); - } elsif($errstr ne "nomsg") { - &logmsg($errstr."\nError return value=".$retval); - exit($retval); - } - } - - if($exec_cshrc_type eq "NO_CSHRC") { - $ENV{'HOME'}=$savedhome; - } elsif( $exec_cshrc_type eq "NO_PANDA_ATTACH") { - delete $ENV{'TCSH_NO_PANDA_ATTACH'}; - } -} - -sub gettimestr() { - my ($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime(time); - $mon++; - return $mon."/".$mday." ".$hour.":".($min<10 ? "0":"").$min; -} - -sub appendpath() { -# note cygwin perl.exe requires ':' instead of ';' as path dir separator - foreach $dir1 (@_) { - $ENV{'PATH'}.=$DIRPATH_SEPARATOR.$CYGBLDROOT.$dir1; - } -} - -sub make_bsc_file() { - &logmsg("*** Generating panda.bsc at ".&gettimestr()." ***"); - -# &mychdir($CYGBLDROOT."/debug"); -# $outputdir=$WINBLDROOT."\\debug"; - - &mychdir($CYGBLDROOT); - $outputdir=$WINBLDROOT; - - $outputname="panda.bsc"; - $outputfilepath=$outputdir."\\".$outputname; - $cmdfilepath=$outputdir."\\makebsc.txt"; - - #open(FILES, "where -r . *.sbr |") || die "Couldn't run 'where'!\n"; - #requires unix/cygwin style find.exe - - open(FILES, "find ".$dirstodostr." -name \"*.sbr\" -print |") || die "Couldn't run 'find'!\n"; - - open(OUTFILE, ">".$cmdfilepath) || die "Couldn't open ".$cmdfilepath."!\n"; - - $filename = ; #skip initial line - $filestr=""; - - $duline = ; - chop $duline; - $filestr=$duline; - - while ($duline = ) - { - chop $duline; # Remove the newline from the end of the filename - $filestr=$filestr."\n".$duline; - } - - print OUTFILE "/n /o ".$outputfilepath." \n"; - print OUTFILE "$filestr","\n"; - close(OUTFILE); - close(FILES); - - # vc7 dirs are not in path env-var by default - $ENV{'PATH'}=$VC7_BINDIRS.$DIRPATH_SEPARATOR.$ENV{'PATH'}; - print $ENV{'PATH'}, "\n"; - - $bscmake_str="bscmake /o ".$outputfilepath." @".$cmdfilepath."\n"; - &myexecstr($bscmake_str,"bscmake failed!!!","DO_LOG","NT cmd"); - - # edit binary so it looks for src on mountable Z drive, so src browsing - # works with Z:\ pointed to \\dragon - - $binreplace_str="perl ".$scriptpath."\\binreplace.pl -i C:\\cygwin Z:\\cygwin ".$outputfilepath."\n"; - &myexecstr($binreplace_str,"binreplace failed!!!","DO_LOG","NT cmd"); - - &myexecstr("copy ".$outputfilepath." ".$inst_dirs[$DEBUGNUM], "copy of ".$outputfilepath." failed!!", "DO_LOG","NT cmd"); - unlink($cmdfilepath); - &mychdir($CYGBLDROOT); - &logmsg("*** Finished generating panda.bsc at ".&gettimestr()." ***"); -} - -sub addpathsfromfile() { - my $dirname = shift; - my $envvarname = shift; - - my $pathfile=$WINBLDROOT."\\".$dirname."\\src\\configfiles\\".$dirname.".pth"; - if(!open(PTHFILE, $pathfile)) { - &logmsg("Couldn't open ".$pathfile."!"); - exit(1); - } - - my @filestrs=; - close(PTHFILE); - foreach my $i (@filestrs) { - chop($i); - $i =~ s/\//\\/g; # switch fwdslash to backslsh -# print $i,"\n"; - $ENV{$envvarname}.=";".$WINBLDROOT."\\".$dirname."\\".$i; - } - -} - -sub AddPckgPath() { - my $pckg = shift; - $ENV{'PATH'}.=":".$ENV{$pckg}."/bin:".$ENV{$pckg}."/lib"; -} - -sub gen_python_code() { - - &logmsg("*** GenPythonCode Start for ".$inst_dirnames[$treenum]." at ".&gettimestr()." ***"); - - &mychdir($CYGBLDROOT."/direct/bin"); - - # ETC_PATH required by generatePythonCode? - $ENV{'ETC_PATH'}='/home/builder/player/panda/etc /home/builder/player/direct/etc /home/builder/player/dtool/etc /home/builder/player/toontown/etc /home/builder/player/pandatool/etc'; - - my $origpath=$ENV{'PATH'}; # save now, restore path after done - - # start new path just for this fn - $ENV{'PATH'}="/bin"; - - &AddPckgPath('WINTOOLS'); - &AddPckgPath('DTOOL'); - &AddPckgPath('PANDATOOL'); - &AddPckgPath('PANDA'); - &AddPckgPath('DIRECT'); - &AddPckgPath('TOONTOWN'); - -# $ENV{'PATH'}=$ENV{'WINTOOLS'}."/bin:".$ENV{'WINTOOLS'}."/lib:/bin:/contrib/bin:/mscommon/Tools/WinNT:/mscommon/MSDev98/Bin:/mscommon/Tools:/msvc98/bin:/home/builder/player/dtool/bin:/home/builder/player/dtool/lib:/home/builder/player/direct/bin:/home/builder/player/direct/lib::/home/builder/player/toontown/bin:/home/builder/player/toontown/lib:/home/builder/player/panda/lib:/home/builder/player/panda/bin:/usr/local/bin:.:/c/WINNT/system32:/c/WINNT:/c/WINNT/System32/Wbem:/c/bin:/mspsdk/Bin/:/mspsdk/Bin/WinNT:/mscommon/Tools/WinNT:/mscommon/MSDev98/Bin:/mscommon/Tools:/msvc98/bin:/home/builder/scripts:/home/builder/player/pandatool/bin:/home/builder/player/pandatool/lib"; - $ENV{'PATH'}.=":/mscommon/Tools/WinNT:/mscommon/MSDev98/Bin:/mscommon/Tools:/msvc98/bin:/usr/local/bin:.:/c/WINNT/system32:/c/WINNT:/c/bin:/mspsdk/Bin/:/mspsdk/Bin/WinNT:/mscommon/Tools/WinNT:/mscommon/Tools:/home/builder/scripts:"; - $ENV{'PATH'}.=$ENV{'WINTOOLS'}."/sdk/python/Tcl/bin:".$ENV{'WINTOOLS'}."/sdk/python/Tcl/lib"; - - $ENV{'PYTHONPATH'}= $WINBLDROOT."\\direct\\src\\showbase;".$WINBLDROOT."\\panda\\lib;".$WINBLDROOT."\\dtool\\lib;".$WINBLDROOT."\\wintools\\bin;".$WINBLDROOT."\\wintools\\lib;"; - $ENV{'PYTHONPATH'}.= $WINBLDROOT."\\wintools\\sdk\\python\\Python-2.2\\Lib;".$WINBLDROOT."\\wintools\\sdk\\python\\Python-2.2\\DLLs;"; - # direct/src/showbase/sitecustomize.py will add paths based on CTPROJS - $ENV{'CTPROJS'}="TOONTOWN:personal DIRECT:personal PANDA:personal WINTOOLS:personal DTOOL:personal"; - -# my $directsrcroot=$WINBLDROOT."\\direct\\src"; - -# this should be unnecessary if sitecustomize.py sees CTPROJS -# # add stuff from etc/[dir].pth -# &addpathsfromfile("direct","PYTHONPATH"); -# &addpathsfromfile("toontown","PYTHONPATH"); - - $ENV{'TCSH_NO_CHANGEPATH'}='1'; - -# old way that tried to do genPyCode directly -# $outputdir = $WINBLDROOT."\\direct\\lib\\py"; -# &mymkdir($outputdir); -# $outputdir.= "\\Opt".$ENV{'PANDA_OPTIMIZE'}."-Win32"; -# &mymkdir($outputdir); -# now back to 1 build-type per tree -# -# my $genpyth_str; -# my $genargstr="-v -d"; -# -# if(($ENV{'PANDA_OPTIMIZE'} eq '1') || ($ENV{'PANDA_OPTIMIZE'} eq '2')) { -# $genpyth_str="python_d -v -OO "; -# $genargstr="-O ".$genargstr; -# } else { -# $genpyth_str="python -v -O "; -# $genargstr="-O ".$genargstr; -# } -# $genpyth_str.="generatePythonCode ".$genargstr." '".$outputdir."' -e '".$WINBLDROOT."\\direct\\src\\extensions' -i libdtoolconfig libpandaexpress libpanda libpandaphysics libdirect libtoontown"; -# &myexecstr($genpyth_str,"generate python code failed!!!","DO_LOG","NO_PANDA_ATTACH"); - - $genpy_args{'1'} = 'win-debug'; - $genpy_args{'2'} = 'win-debug'; - $genpy_args{'3'} = 'win-release'; - $genpy_args{'4'} = 'win-publish'; - - $genpy_arg1 = "-t ".$genpy_args{$ENV{'PANDA_OPTIMIZE'}}; - &myexecstr("genPyCode ".$genpy_arg1,"genPyCode failed","DO_LOG","NO_PANDA_ATTACH"); - - $ENV{'PATH'}=$origpath; - delete $ENV{'TCSH_NO_CHANGEPATH'}; - &mychdir($CYGBLDROOT); - &logmsg("*** GenPythonCode Finished for ".$inst_dirnames[$treenum]." at ".&gettimestr()." ***"); -} - -my $newdayarchivedirname; - -sub archivetree() { - $treenum=shift; - - if(!(-e $inst_dirs[$treenum])) { - return; - } - - if($newdayarchivename eq "") { - - ($devicenum,$inodenum,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) - = stat($inst_dirs[$treenum]."\\dtool\\Sources.pp"); - ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) - = localtime($mtime); - $mon++; - - $newdayarchivedirname=$inst_dirs[$ARCHIVENUM]."\\".($mon<10 ? "0":"").$mon."-".($mday<10 ? "0":"").$mday; - &mymkdir($newdayarchivedirname); - } - - &logmsg("*** Archiving ".$inst_dirnames[$treenum]." build on install server at ".&gettimestr()." ***"); - - my $archdirname=$newdayarchivedirname."\\".$inst_dirnames[$treenum]; - - &myrename($inst_dirs[$treenum],$archdirname); - - foreach my $dir1 (@dirstodolist) { - # copy DLL .pdb/.map up to lib dir so we can blow away metalibs subdir - # could do this is the makefiles instead - &myexecstr("( for /R ".$archdirname."\\".$dir1."\\metalibs %i in (lib*.pdb lib*.map) do copy %i ".$archdirname."\\".$dir1."\\lib )","nomsg","DO_LOG","NT cmd"); - - if($dir1 eq "panda") { - # dll/exe not under metalibs - &myexecstr("( for /R ".$archdirname."\\".$dir1."\\src\\audiotraits %i in (lib*.pdb lib*.map) do copy %i ".$archdirname."\\".$dir1."\\lib )","nomsg","DO_LOG","NT cmd"); - &myexecstr("( for /R ".$archdirname."\\".$dir1."\\src\\shader %i in (lib*.pdb lib*.map) do copy %i ".$archdirname."\\".$dir1."\\lib )","nomsg","DO_LOG","NT cmd"); - &myexecstr("( for /R ".$archdirname."\\".$dir1."\\src\\testbed %i in (*.pdb *.map) do copy %i ".$archdirname."\\".$dir1."\\bin )","nomsg","DO_LOG","NT cmd"); - } - - # NT cmd 'for' always returns 144 for some reason, impossible to detect error cond, so just don't check retval - # delete old objs/pdbs/etc out of archived trees (just blow away the Opt[Win32] dir) - # &myexecstr("( for /D /R ".$archdirname."\\".$dir1."\\src %i in (Opt*Win32) do rd /s /q %i )","nomsg","DO_LOG","NT cmd"); - - # instead blow away src,CVS,include,metalibs dirs completely - # doing every rd twice since windows-samba-RAID link seems to screw up and cause some files to not be deleted the 1st time - &myexecstr("rd /s /q ".$archdirname."\\".$dir1."\\src","nomsg","DO_LOG","NT cmd"); - &myexecstr("rd /s /q ".$archdirname."\\".$dir1."\\CVS","nomsg","DO_LOG","NT cmd"); - &myexecstr("rd /s /q ".$archdirname."\\".$dir1."\\include","nomsg","DO_LOG","NT cmd"); - &myexecstr("rd /s /q ".$archdirname."\\".$dir1."\\metalibs","nomsg","DO_LOG","NT cmd"); - - &myexecstr("rd /s /q ".$archdirname."\\".$dir1."\\src","nomsg","DO_LOG","NT cmd"); - &myexecstr("rd /s /q ".$archdirname."\\".$dir1."\\CVS","nomsg","DO_LOG","NT cmd"); - &myexecstr("rd /s /q ".$archdirname."\\".$dir1."\\include","nomsg","DO_LOG","NT cmd"); - &myexecstr("rd /s /q ".$archdirname."\\".$dir1."\\metalibs","nomsg","DO_LOG","NT cmd"); - - # del xtra files at root of subdirs - &myexecstr("del /Q /F ".$archdirname."\\".$dir1."\\*","nomsg","DO_LOG","NT cmd"); - } - - # delete old browse files - &myexecstr("del /q ".$archdirname."\\*.bsc","nomsg","DO_LOG","NT cmd"); - - # delete wintools, right now its too big to archive - &myexecstr("rd /s /q ".$archdirname."\\wintools","nomsg","DO_LOG","NT cmd"); - &myexecstr("rd /s /q ".$archdirname."\\wintools","nomsg","DO_LOG","NT cmd"); - - # could also move .pdb from metalibs to lib, then del metalibs, include, src dirs -} - -sub checkoutfiles { - my $existing_module_str=""; - my $nonexisting_module_str=""; - - &mychdir($CYGBLDROOT); - - foreach my $dir1 (@dirstodolist) { - if(-e $dir1) { - $existing_module_str.=$dir1." "; - } else { - $nonexisting_module_str.=$dir1." "; - } - } - - if($existing_module_str ne "") { - # flaw: will bomb if any CVS subdirs are missing - - &myexecstr("( for /D /R . %i in (Opt*Win32) do rd /s /q %i )","nomsg","DO_LOG","NT cmd"); - - &logmsg("*** REMOVING ALL FILES IN OLD SRC TREES ***"); - # use cvs update to grab new copy - # note: instead of blowing these away, may want to rename and save them - # also, I could just blow everything away and check out again - # cant blow away if people are sitting in those dirs, so we do this instead - $rmcmd="find ".$existing_module_str." -path '*CVS*' -prune -or -not -name 'bldlog*' -and -not -type d -print | xargs --no-run-if-empty -n 40 rm"; - #&myexecstr($rmcmd,"Removal of old files failed!","DO_LOG","NO_CSHRC"); - &myexecstr($rmcmd,"Removal of old files failed!","DO_LOG","NO_PANDA_ATTACH"); - - &myexecstr("cvs update -d -R ".$existing_module_str." |& egrep -v 'Updating|^\\?'", - "cvs update failed!","DO_LOG","NO_PANDA_ATTACH"); - } - - if($nonexisting_module_str ne "") { - &myexecstr("cvs checkout -R ".$nonexisting_module_str." |& egrep -v 'Updating|^\\?'", - "cvs checkout failed!","DO_LOG","NO_PANDA_ATTACH"); - } -} - -sub buildall() { - $treenum=shift; - - # DTOOL ppremake may have already run by DTOOL 'initialize make' - - if($DO_ARCHIVE_AND_COPY_ONLY) { - goto 'ARCHIVE_AND_COPY'; - } - - &logmsg("*** Starting ".uc($inst_dirnames[$treenum])." Build (Opt=".$ENV{'PANDA_OPTIMIZE'}.") at ".&gettimestr()." ***"); - - &checkoutfiles(); - - # cant do attachment, since that often hangs on NT - # must use non-attachment build system (BUILD_TYPE 'gmsvc', not 'stopgap', in $PPREMAKE_CONFIG) - - # hacks to fix multiproc build issue (cp file to dir occurs before dir creation, need to adjust makefiles to fix this) - foreach my $dir1 (@dirstodolist) { - &mymkdir($CYGBLDROOT."/".$dir1."/etc"); - &mymkdir($CYGBLDROOT."/".$dir1."/bin"); - &mymkdir($CYGBLDROOT."/".$dir1."/lib"); - &mymkdir($CYGBLDROOT."/".$dir1."/include"); - } - &mymkdir($CYGBLDROOT."/dtool/include/parser-inc"); # hack to fix makefile multiproc issue - &mymkdir($CYGBLDROOT."/pandatool/shared"); # hack to fix makefile multiproc issue - - foreach my $dir1 (@dirstodolist) { - my $dir1_upcase = uc($dir1); - - &logmsg("*** PPREMAKE ".$dir1_upcase." (BUILDTYPE=".$ENV{'PANDA_BUILD_TYPE'}.") ***"); - &mychdir($CYGBLDROOT."/".$dir1); - &myexecstr("ppremake",$dir1_upcase." ppremake failed!","DO_LOG","NO_PANDA_ATTACH"); - } - - # debug stuff - # &mychdir($CYGBLDROOT); - # &myexecstr("dir dtool","dir failed","DO_LOG","NT cmd"); - - $makecmd="make"; - $shellarg="NO_PANDA_ATTACH"; - - if($ENV{'PANDA_BUILD_TYPE'} eq 'msvc') { - $makecmd="nmake"; - $shellarg="NT cmd"; - } - - foreach my $dir1 (@dirstodolist) { - my $dir1_upcase = uc($dir1); - &logmsg("*** BUILDING ".$dir1_upcase." at ".&gettimestr()." ***"); - &mychdir($CYGBLDROOT."/".$dir1); - &myexecstr($makecmd." install",$dir1_upcase." ".$makecmd." install failed!","DO_LOG",$shellarg); - } - - &mychdir($CYGBLDROOT); # get out of src dirs to allow them to be moved/renamed - unlink($CYGBLDROOT."/dtool/dtool_config.h"); # fix freakish NTFS bug, this file is regenerated by ppremake anyway - - if($#dirstodolist>1) { - &gen_python_code(); # must run AFTER toontown bld - } - - ARCHIVE_AND_COPY: - - &mychdir($CYGBLDROOT); # get out of src dirs to allow them to be moved/renamed - - # archive old current srvr build & copy build to server, if its accessible - if(!(-e $WIN_INSTALLDIR)) { - &logmsg("ERROR: Cant access install directory!! (Is Server down??) ".$WIN_INSTALLDIR); - &logmsg("Skipping archive and copy-build-to-server steps for ".$inst_dirnames[$treenum]." build"); - return; - } - - if($DONT_ARCHIVE_OLD_BUILDS) { - &myexecstr("rd /s /q ".$inst_dirs[$treenum],"DO_LOG","NT cmd"); # don't bother checking errors here, probably just some shell has the dir cd'd to - } else { - &archivetree($treenum); - } - - &logmsg("*** Copying ".$inst_dirnames[$treenum]." build to install server at ".&gettimestr()." ***"); - &mymkdir($inst_dirs[$treenum]); - - my $xcopy_opt_str="/E /K /C /R /Y /H /I "; - - if($DEBUG_TREECOPY) { - $xcopy_opt_str.="/T "; #debug only - } - - if($treenum == $DEBUGNUM) { - # get rid of .sbr files before copying - &myexecstr("echo .sbr > C:\\junk","echo .sbr failed!!", "DO_LOG","NT cmd"); - $xcopy_opt_str.=" /EXCLUDE:C:\\junk"; - } - - # hopefully there are no extra dirs underneath - - # add wintools to dir copy list - # don't want to add wintools to global dirstodo, treat it separately - unshift(dirstodolist,"wintools"); - - foreach my $dir1 (@dirstodolist) { - &mymkdir($inst_dirs[$treenum]."\\".$dir1); - &myexecstr("\\WINNT\\system32\\xcopy ".$xcopy_opt_str." ".$WINBLDROOT."\\".$dir1."\\* ".$inst_dirs[$treenum]."\\".$dir1, - "xcopy of ".$inst_dirnames[$treenum]." tree failed!!", "DO_LOG","NT cmd"); - } - shift(dirstodolist); -} - -# assumes environment already attached to TOOL/PANDA/DIRECT/TOONTOWN -# assumes cygwin env, BLDROOT must use fwd slashes -if($ENV{'BLDROOT'} eq "") { - $ENV{'BLDROOT'} = "/home/builder/player"; -} - -if($ENV{'CYGWIN_ROOT'} eq "") { - $ENV{'CYGWIN_ROOT'} = "C:\\Cygwin"; -} - -$CYGROOT= $ENV{'CYGWIN_ROOT'}; -$CYGROOT =~ s/(.*)\\$/$1/; # get rid of trailing '\' -#$CYGROOT =~ s/\\/\//g; # switch backslash to fwdslash (setting up for cygwin) - -$CYGBLDROOT = $ENV{'BLDROOT'}; -print "\$CYGBLDROOT='",$CYGBLDROOT,"'\n"; - -if(($CYGBLDROOT eq "")||(!(-e $CYGBLDROOT))) { - die "Bad \$CYGBLDROOT !!\n"; -} - -$WINBLDROOT=$CYGROOT.$CYGBLDROOT; -$WINBLDROOT =~ s/\//\\/g; # switch fwdslash to backslash -print "\$WINBLDROOT='",$WINBLDROOT,"'\n"; - -my ($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime(time); -$mon++; -$logfilenamebase="bldlog-".($mon<10 ? "0":"").$mon."-".($mday<10 ? "0":"").$mday.".txt"; -$fulllogfilename = $CYGBLDROOT."/".$logfilenamebase; -$fulllogfilename_win = $WINBLDROOT."\\".$logfilenamebase; - -# recreate the log to blow away any old one -open(LOGFILE,">".$fulllogfilename) || die "can't open log file '$fulllogfilename'\n"; -close(LOGFILE); - -&logmsg("*** Panda Build Log Started at ".&gettimestr()." ***"); - -my @do_install_dir; -my @dir_build_type; - -for($ii=0;$ii<=$#PUBLISHNUM;$ii++) { - if($#ARGV<0) { - $do_install_dir=1; - } else { - $do_install_dir=0; - } - $dir_build_type=0; -} - -my $g_last_dirnum=0; - -for($ii=0;$ii<=$#ARGV;$ii++) { - if(uc($ARGV[$ii]) eq uc($inst_dirnames[$INSTALLNUM])) { - $g_last_dirnum=$INSTALLNUM; - } elsif(uc($ARGV[$ii]) eq uc($inst_dirnames[$DEBUGNUM])) { - $g_last_dirnum=$DEBUGNUM; - } elsif(uc($ARGV[$ii]) eq uc($inst_dirnames[$RELEASENUM])) { - $g_last_dirnum=$RELEASENUM; - } elsif(uc($ARGV[$ii]) eq uc($inst_dirnames[$PUBLISHNUM])) { - $g_last_dirnum=$PUBLISHNUM; - } elsif(uc($ARGV[$ii]) eq "NMAKE") { - $dir_build_type[$g_last_dirnum]=1; - next; - } else { - &logmsg("invalid argument '".$ARGV[0]."' to builder.pl. arg must be 'install','debug', or 'release', or 'publish'\n"); - exit(1); - } - $do_install_dir[$g_last_dirnum]=1; -} - -if(!(-e $WIN_INSTALLDIR)) { - &logmsg("ERROR: Cant access install directory!! ".$WIN_INSTALLDIR); - exit(1); -} - -&mychdir($CYGBLDROOT); - -if($BLD_DTOOL_ONLY) { - @dirstodolist=("dtool"); -} else { - @dirstodolist=("dtool","panda","pandatool","direct","toontown"); -} -$dirstodostr=""; -foreach my $dir1 (@dirstodolist) { - $dirstodostr.=$dir1." "; -} - -# makes ppremake build headers, libs in module dirs (panda/lib,dtool/bin,etc), not /usr/local/panda/inc... -foreach my $dir1 (@dirstodolist) { - my $dir1_upcase = uc($dir1); - $ENV{$dir1_upcase}=$CYGBLDROOT."/".$dir1; - - # need this since we are building in src dirs, not install dir - # so 'interrogate' needs to find its dlls when building panda, etc - &appendpath("/".$dir1."/bin","/".$dir1."/lib"); -} - -# pick up cygwin utils -$ENV{'PATH'}=$ENV{'WINTOOLS'}."/bin".$DIRPATH_SEPARATOR.$ENV{'WINTOOLS'}."/lib".$DIRPATH_SEPARATOR."/bin".$DIRPATH_SEPARATOR.$ENV{'PATH'}."/c/WINNT/system32".$DIRPATH_SEPARATOR."/c/WINNT"; - -# want build to pick up python dll's from /usr/lib before /c/python16 -# $ENV{'PATH'}="/usr/lib".$DIRPATH_SEPARATOR.$ENV{'PATH'}; not needed - -if($DEBUG_MAKEBSC_ONLY) { - &make_bsc_file(); - exit(0); -} - -if(! $DEBUG_GENERATE_PYTHON_CODE_ONLY) { - # update wintools - unlink("wintools\\bin\\dos2unix.exe"); # rm a file so egrep doesnt find 0 non-Updating/?? lines and return failure - &myexecstr("cvs update -d -R wintools |& egrep -v 'Updating|^\\?'", "cvs update failed!","DO_LOG","NO_PANDA_ATTACH"); -} - -if($DEBUG_TREECOPY) { - goto 'DBGTREECOPY'; -} - -# goto 'SKIP_REMOVE'; - -SKIP_REMOVE: - -# this doesnt work unless you can completely remove the dirs, since cvs checkout -# bombs if dirs exist but CVS dirs do not. - -if($do_install_dir[$INSTALLNUM]) { - if($dir_build_type[$INSTALLNUM]) { - $ENV{'PANDA_BUILD_TYPE'} = 'msvc'; - } else { - $ENV{'PANDA_BUILD_TYPE'} = 'gmsvc'; - } - - $ENV{'PANDA_OPTIMIZE'}='2'; - - if($DEBUG_GENERATE_PYTHON_CODE_ONLY) { - &gen_python_code(); - exit(0); - } - - &buildall($INSTALLNUM); -} - -if($do_install_dir[$RELEASENUM]) { - if($dir_build_type[$RELEASENUM]) { - $ENV{'PANDA_BUILD_TYPE'} = 'msvc'; - } else { - $ENV{'PANDA_BUILD_TYPE'} = 'gmsvc'; - } - $ENV{'PANDA_OPTIMIZE'}='3'; - - if($DEBUG_GENERATE_PYTHON_CODE_ONLY) { - &gen_python_code(); - exit(0); - } - - &buildall($RELEASENUM); -} - -BEFORE_DBGBUILD: - -if($do_install_dir[$DEBUGNUM]) { - if($dir_build_type[$DEBUGNUM]) { - $ENV{'PANDA_BUILD_TYPE'} = 'msvc'; - } else { - $ENV{'PANDA_BUILD_TYPE'} = 'gmsvc'; - } - $ENV{'USE_BROWSEINFO'}='1'; # make .sbr files - if(! $DEBUG_GENERATE_PYTHON_CODE_ONLY) { - $ENV{'PANDA_OPTIMIZE'}='1'; - } - - if($DEBUG_GENERATE_PYTHON_CODE_ONLY) { - &gen_python_code(); - exit(0); - } - - &buildall($DEBUGNUM); - &make_bsc_file(); - delete $ENV{'USE_BROWSEINFO'}; - - &myexecstr("del /s *.sbr","nomsg","DO_LOG","NT cmd"); -} - -AFTER_DBGBUILD: - -if($do_install_dir[$PUBLISHNUM]) { - if($dir_build_type[$PUBLISHNUM]) { - $ENV{'PANDA_BUILD_TYPE'} = 'msvc'; - } else { - $ENV{'PANDA_BUILD_TYPE'} = 'gmsvc'; - } - $ENV{'PANDA_OPTIMIZE'}='4'; - &buildall($PUBLISHNUM); -} - - -&logmsg("*** Panda Build Log Finished at ".&gettimestr()." ***"); - -# store log in 'debug' dir -&myexecstr("copy ".$fulllogfilename_win." ".$inst_dirs[$g_last_dirnum], "copy of ".$fulllogfilename_win." failed!!", "","NT cmd"); - -exit(0); - -# TODO: -# compress old archived blds? -# build DLLs with version stamp set by this script -# implement build-specific opttype mode - diff --git a/pandatool/src/scripts/cvlog.pl b/pandatool/src/scripts/cvlog.pl deleted file mode 100755 index 72f3315a19..0000000000 --- a/pandatool/src/scripts/cvlog.pl +++ /dev/null @@ -1,1849 +0,0 @@ -#!/bin/sh -exec perl -w -x $0 ${1+"$@"} # -*- mode: perl; perl-indent-level: 2; -*- -#!perl -w - -############################################################## -### ### -### cvs2cl.pl: produce ChangeLog(s) from `cvs log` output. ### -### ### -############################################################## - -## $Revision$ -## $Date$ -## $Author$ -## -## (C) 1999 Karl Fogel , under the GNU GPL. -## -## (Extensively hacked on by Melissa O'Neill .) -## -## cvs2cl.pl is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published by -## the Free Software Foundation; either version 2, or (at your option) -## any later version. -## -## cvs2cl.pl is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## -## You may have received a copy of the GNU General Public License -## along with cvs2cl.pl; see the file COPYING. If not, write to the -## Free Software Foundation, Inc., 59 Temple Place - Suite 330, -## Boston, MA 02111-1307, USA. - - - -use strict; -use Text::Wrap; -use Time::Local; -use File::Basename; - - -# The Plan: -# -# Read in the logs for multiple files, spit out a nice ChangeLog that -# mirrors the information entered during `cvs commit'. -# -# The problem presents some challenges. In an ideal world, we could -# detect files with the same author, log message, and checkin time -- -# each would be a changelog entry. -# We'd sort them; and spit them out. Unfortunately, CVS is *not atomic* -# so checkins can span a range of times. Also, the directory structure -# could be hierarchical. -# -# Another question is whether we really want to have the ChangeLog -# exactly reflect commits. An author could issue two related commits, -# with different log entries, reflecting a single logical change to the -# source. GNU style ChangeLogs group these under a single author/date. -# We try to do the same. -# -# So, we parse the output of `cvs log', storing log messages in a -# multilevel hash that stores the mapping: -# directory => author => time => message => filelist -# As we go, we notice "nearby" commit times and store them together -# (i.e., under the same timestamp), so they appear in the same log -# entry. -# -# When we've read all the logs, we twist this mapping into -# a time => author => message => filelist mapping for each directory. -# -# If we're not using the `--distributed' flag, the directory is always -# considered to be `./', even as descend into subdirectories. - - -############### Globals ################ - - -# What we run to generate it: -my $Log_Source_Command = "cvs log"; - -# In case we have to print it out: -my $VERSION = '$Revision$'; -$VERSION =~ s/\S+\s+(\S+)\s+\S+/$1/; - -## Vars set by options: - -# Print debugging messages? -my $Debug = 0; - -# Just show version and exit? -my $Print_Version = 0; - -# Just print usage message and exit? -my $Print_Usage = 0; - -# Single top-level ChangeLog, or one per subdirectory? -my $Distributed = 0; - -# What file should we generate (defaults to "ChangeLog")? -my $Log_File_Name = "ChangeLog"; - -# Expand usernames to email addresses based on a map file? -my $User_Map_File = ""; - -# Only list this many file changes per dir -my $Max_Fileline_Count = 15; - -# Only list changes with the last $Prev_Day_Count days -my $Prev_Day_Count = 30; - -# Output to a file or to stdout? -my $Output_To_Stdout = 1; - -# Eliminate empty log messages? -my $Prune_Empty_Msgs = 0; - -# Don't call Text::Wrap on the body of the message -my $No_Wrap = 1; - -# Separates header from log message. Code assumes it is either " " or -# "\n\n", so if there's ever an option to set it to something else, -# make sure to go through all conditionals that use this var. -my $After_Header = " "; - -# Format more for programs than for humans. -my $XML_Output = 0; - -# Do some special tweaks for log data that was written in FSF -# ChangeLog style. -my $FSF_Style = 0; - -# Show times in UTC instead of local time -my $UTC_Times = 0; - -# Show day of week in output? -my $Show_Day_Of_Week = 1; - -# Show revision numbers in output? -my $Show_Revisions = 1; - -# Show tags (symbolic names) in output? -my $Show_Tags = 0; - -# Show branches by symbolic name in output? -my $Show_Branches = 0; - -# Show only revisions on these branches or their ancestors. -my @Follow_Branches; - -# Don't bother with files matching this regexp. -my @Ignore_Files; - -# How exactly we match entries. We definitely want "o", -# and user might add "i" by using --case-insensitive option. -my $Case_Insensitive = 0; - -# Maybe only show log messages matching a certain regular expression. -my $Regexp_Gate = ""; - -# Pass this global option string along to cvs, to the left of `log': -my $Global_Opts = ""; - -# Pass this option string along to the cvs log subcommand: -my $Command_Opts = ""; - -# Read log output from stdin instead of invoking cvs log? -my $Input_From_Stdin = 0; - -# Don't show filenames in output. -my $Hide_Filenames = 0; - -# Max checkin duration. CVS checkin is not atomic, so we may have checkin -# times that span a range of time. We assume that checkins will last no -# longer than $Max_Checkin_Duration seconds, and that similarly, no -# checkins will happen from the same users with the same message less -# than $Max_Checkin_Duration seconds apart. -my $Max_Checkin_Duration = 300; - -# What to put at the front of [each] ChangeLog. -my $ChangeLog_Header = ""; - -## end vars set by options. - -# In 'cvs log' output, one long unbroken line of equal signs separates -# files: -my $file_separator = "=======================================" - . "======================================"; - -# In 'cvs log' output, a shorter line of dashes separates log messages -# within a file: -my $logmsg_separator = "----------------------------"; - -my @monthlengtharr=(31,28,31,30,31,30,31,31,30,31,30,31); - -############### End globals ############ - - -&parse_options (); -&derive_change_log (); - - -### Everything below is subroutine definitions. ### - -# Fills up a ChangeLog structure in the current directory. -sub derive_change_log () -{ - # See "The Plan" above for a full explanation. - - my %grand_poobah; - - my $file_full_path; - my $time; - my $revision; - my $author; - my $msg_txt; - my $detected_file_separator; - - # We might be expanding usernames - my %usermap; - - # In general, it's probably not very maintainable to use state - # variables like this to tell the loop what it's doing at any given - # moment, but this is only the first one, and if we never have more - # than a few of these, it's okay. - my $collecting_symbolic_names = 0; - my %symbolic_names; # Where tag names get stored. - my %branch_names; # We'll grab branch names while we're at it. - my %branch_numbers; # Save some revisions for @Follow_Branches - my @branch_roots; # For showing which files are branch ancestors. - - # Bleargh. Compensate for a deficiency of custom wrapping. - if (($After_Header ne " ") and $FSF_Style) - { - $After_Header .= "\t"; - } - - my ($ign1,$ign2,$ign3,$dayofmonth,$month,$year_,$ign4) = $UTC_Times ? gmtime(time()) : localtime(time()); - - my $logdayofmonth; - my $logmonth; - my $logyear; - - my $datestr= &FindPrevDayStr($Prev_Day_Count,$dayofmonth,$month,$year_); - $Log_Source_Command =~ s/(cvs log)/cvs log -d \">$datestr\"/; - - if (! $Input_From_Stdin) { - open (LOG_SOURCE, "$Log_Source_Command |") - or die "unable to run \"${Log_Source_Command}\""; - } - else { - open (LOG_SOURCE, "-") or die "unable to open stdin for reading"; - } - - %usermap = &maybe_read_user_map_file (); - - while () - { - # If on a new file and don't see filename, skip until we find it, and - # when we find it, grab it. - if ((! (defined $file_full_path)) and /^Working file: (.*)/) - { - $file_full_path = $1; - if (@Ignore_Files) - { - my $base; - ($base, undef, undef) = fileparse ($file_full_path); - # Ouch, I wish trailing operators in regexps could be - # evaluated on the fly! - if ($Case_Insensitive) { - if (grep ($file_full_path =~ m|$_|i, @Ignore_Files)) { - undef $file_full_path; - } - } - elsif (grep ($file_full_path =~ m|$_|, @Ignore_Files)) { - undef $file_full_path; - } - } - next; - } - - # Just spin wheels if no file defined yet. - next if (! $file_full_path); - - # Collect tag names in case we're asked to print them in the output. - if (/^symbolic names:$/) { - $collecting_symbolic_names = 1; - next; # There's no more info on this line, so skip to next - } - if ($collecting_symbolic_names) - { - # All tag names are listed with whitespace in front in cvs log - # output; so if see non-whitespace, then we're done collecting. - if (/^\S/) { - $collecting_symbolic_names = 0; - } - else # we're looking at a tag name, so parse & store it - { - # According to the Cederqvist manual, in node "Tags", tag - # names must start with an uppercase or lowercase letter and - # can contain uppercase and lowercase letters, digits, `-', - # and `_'. However, it's not our place to enforce that, so - # we'll allow anything CVS hands us to be a tag: - /^\s+([^:]+): ([\d.]+)$/; - my $tag_name = $1; - my $tag_rev = $2; - - # A branch number either has an odd number of digit sections - # (and hence an even number of dots), or has ".0." as the - # second-to-last digit section. Test for these conditions. - my $real_branch_rev = ""; - if (($tag_rev =~ /^(\d+\.\d+\.)+\d+$/) # Even number of dots... - and (! ($tag_rev =~ /^(1\.)+1$/))) # ...but not "1.[1.]1" - { - $real_branch_rev = $tag_rev; - } - elsif ($tag_rev =~ /(\d+\.(\d+\.)+)0.(\d+)/) # Has ".0." - { - $real_branch_rev = $1 . $3; - } - # If we got a branch, record its number. - if ($real_branch_rev) - { - $branch_names{$real_branch_rev} = $tag_name; - if (@Follow_Branches) { - if (grep ($_ eq $tag_name, @Follow_Branches)) { - $branch_numbers{$tag_name} = $real_branch_rev; - } - } - } - else { - # Else it's just a regular (non-branch) tag. - push (@{$symbolic_names{$tag_rev}}, $tag_name); - } - } - } - # End of code for collecting tag names. - - # If have file name, but not revision, and see revision, then grab - # it. (We collect unconditionally, even though we may or may not - # ever use it.) - if ((! (defined $revision)) and (/^revision (\d+\.[\d.]+)/)) - { - $revision = $1; - - if (@Follow_Branches) - { - foreach my $branch (@Follow_Branches) - { - # Special case for following trunk revisions - if (($branch =~ /^trunk$/i) and ($revision =~ /^[0-9]+\.[0-9]+$/)) - { - goto dengo; - } - - my $branch_number = $branch_numbers{$branch}; - if ($branch_number) - { - # Are we on one of the follow branches or an ancestor of - # same? - # - # If this revision is a prefix of the branch number, or - # possibly is less in the minormost number, OR if this - # branch number is a prefix of the revision, then yes. - # Otherwise, no. - # - # So below, we determine if any of those conditions are - # met. - - # Trivial case: is this revision on the branch? - # (Compare this way to avoid regexps that screw up Emacs - # indentation, argh.) - if ((substr ($revision, 0, ((length ($branch_number)) + 1))) - eq ($branch_number . ".")) - { - goto dengo; - } - # Non-trivial case: check if rev is ancestral to branch - elsif ((length ($branch_number)) > (length ($revision))) - { - $revision =~ /^((?:\d+\.)+)(\d+)$/; - my $r_left = $1; # still has the trailing "." - my $r_end = $2; - - $branch_number =~ /^((?:\d+\.)+)(\d+)\.\d+$/; - my $b_left = $1; # still has trailing "." - my $b_mid = $2; # has no trailing "." - - if (($r_left eq $b_left) - && ($r_end <= $b_mid)) - { - goto dengo; - } - } - } - } - } - else # (! @Follow_Branches) - { - next; - } - - # Else we are following branches, but this revision isn't on the - # path. So skip it. - undef $revision; - dengo: - next; - } - - # If we don't have a revision right now, we couldn't possibly - # be looking at anything useful. - if (! (defined ($revision))) { - $detected_file_separator = /^$file_separator$/o; - if ($detected_file_separator) { - # No revisions for this file; can happen, e.g. "cvs log -d DATE" - goto CLEAR; - } - else { - next; - } - } - - # If have file name but not date and author, and see date or - # author, then grab them: - unless (defined $time) - { - if (/^date: .*/) - { - ($time, $author) = &parse_date_and_author ($_); - if (defined ($usermap{$author}) and $usermap{$author}) { - $author = $usermap{$author}; - } - } - else { - $detected_file_separator = /^$file_separator$/o; - if ($detected_file_separator) { - # No revisions for this file; can happen, e.g. "cvs log -d DATE" - goto CLEAR; - } - } - # If the date/time/author hasn't been found yet, we couldn't - # possibly care about anything we see. So skip: - next; - } - - # A "branches: ..." line here indicates that one or more branches - # are rooted at this revision. If we're showing branches, then we - # want to show that fact as well, so we collect all the branches - # that this is the latest ancestor of and store them in - # @branch_roots. Just for reference, the format of the line we're - # seeing at this point is: - # - # branches: 1.5.2; 1.5.4; ...; - # - # Okay, here goes: - - if (/^branches:\s+(.*);$/) - { - if ($Show_Branches) - { - my $lst = $1; - $lst =~ s/(1\.)+1;|(1\.)+1$//; # ignore the trivial branch 1.1.1 - if ($lst) { - @branch_roots = split (/;\s+/, $lst); - } - else { - undef @branch_roots; - } - next; - } - else - { - # Ugh. This really bothers me. Suppose we see a log entry - # like this: - # - # ---------------------------- - # revision 1.1 - # date: 1999/10/17 03:07:38; author: jrandom; state: Exp; - # branches: 1.1.2; - # Intended first line of log message begins here. - # ---------------------------- - # - # The question is, how we can tell the difference between that - # log message and a *two*-line log message whose first line is - # - # "branches: 1.1.2;" - # - # See the problem? The output of "cvs log" is inherently - # ambiguous. - # - # For now, we punt: we liberally assume that people don't - # write log messages like that, and just toss a "branches:" - # line if we see it but are not showing branches. I hope no - # one ever loses real log data because of this. - next; - } - } - - # If have file name, time, and author, then we're just grabbing - # log message texts: - $detected_file_separator = /^$file_separator$/o; - if ($detected_file_separator && ! (defined $revision)) { - # No revisions for this file; can happen, e.g. "cvs log -d DATE" - goto CLEAR; - } - unless ($detected_file_separator || /^$logmsg_separator$/o) - { - $msg_txt .= $_; # Normally, just accumulate the message... - next; - } - # ... until a msg separator is encountered: - # Ensure the message contains something: - if ((! $msg_txt) - || ($msg_txt =~ /^\s*\.\s*$|^\s*$/) - || ($msg_txt =~ /\*\*\* empty log message \*\*\*/)) - { - if ($Prune_Empty_Msgs) { - goto CLEAR; - } - # else - $msg_txt = "[no log message]\n"; - } - - ### Store it all in the Grand Poobah: - { - my $dir_key; # key into %grand_poobah - my %qunk; # complicated little jobbie, see below - - # Each revision of a file has a little data structure (a `qunk') - # associated with it. That data structure holds not only the - # file's name, but any additional information about the file - # that might be needed in the output, such as the revision - # number, tags, branches, etc. The reason to have these things - # arranged in a data structure, instead of just appending them - # textually to the file's name, is that we may want to do a - # little rearranging later as we write the output. For example, - # all the files on a given tag/branch will go together, followed - # by the tag in parentheses (so trunk or otherwise non-tagged - # files would go at the end of the file list for a given log - # message). This rearrangement is a lot easier to do if we - # don't have to reparse the text. - # - # A qunk looks like this: - # - # { - # filename => "hello.c", - # revision => "1.4.3.2", - # time => a timegm() return value (moment of commit) - # tags => [ "tag1", "tag2", ... ], - # branch => "branchname" # There should be only one, right? - # branchroots => [ "branchtag1", "branchtag2", ... ] - # } - - if ($Distributed) { - # Just the basename, don't include the path. - ($qunk{'filename'}, $dir_key, undef) = fileparse ($file_full_path); - } - else { - $dir_key = "./"; - $qunk{'filename'} = $file_full_path; - } - - # This may someday be used in a more sophisticated calculation - # of what other files are involved in this commit. For now, we - # don't use it, because the common-commit-detection algorithm is - # hypothesized to be "good enough" as it stands. - $qunk{'time'} = $time; - - # We might be including revision numbers and/or tags and/or - # branch names in the output. Most of the code from here to - # loop-end deals with organizing these in qunk. - - $qunk{'revision'} = $revision; - - # Grab the branch, even though we may or may not need it: - $qunk{'revision'} =~ /((?:\d+\.)+)\d+/; - my $branch_prefix = $1; - $branch_prefix =~ s/\.$//; # strip off final dot - if ($branch_names{$branch_prefix}) { - $qunk{'branch'} = $branch_names{$branch_prefix}; - } - - # If there's anything in the @branch_roots array, then this - # revision is the root of at least one branch. We'll display - # them as branch names instead of revision numbers, the - # substitution for which is done directly in the array: - if (@branch_roots) { - my @roots = map { $branch_names{$_} } @branch_roots; - $qunk{'branchroots'} = \@roots; - } - - # Save tags too. - if (defined ($symbolic_names{$revision})) { - $qunk{'tags'} = $symbolic_names{$revision}; - delete $symbolic_names{$revision}; - } - - # Add this file to the list - # (We use many spoonfuls of autovivication magic. Hashes and arrays - # will spring into existence if they aren't there already.) - - &debug ("(pushing log msg for ${dir_key}$qunk{'filename'})\n"); - - # Store with the files in this commit. Later we'll loop through - # again, making sure that revisions with the same log message - # and nearby commit times are grouped together as one commit. - push (@{$grand_poobah{$dir_key}{$author}{$time}{$msg_txt}}, \%qunk); - } - - CLEAR: - # Make way for the next message - undef $msg_txt; - undef $time; - undef $revision; - undef $author; - undef @branch_roots; - - # Maybe even make way for the next file: - if ($detected_file_separator) { - undef $file_full_path; - undef %branch_names; - undef %branch_numbers; - } - } - - close (LOG_SOURCE); - - ### Process each ChangeLog - - while (my ($dir,$authorhash) = each %grand_poobah) - { - &debug ("DOING DIR: $dir\n"); - - # Here we twist our hash around, from being - # author => time => message => filelist - # in %$authorhash to - # time => author => message => filelist - # in %changelog. - # - # This is also where we merge entries. The algorithm proceeds - # through the timeline of the changelog with a sliding window of - # $Max_Checkin_Duration seconds; within that window, entries that - # have the same log message are merged. - # - # (To save space, we zap %$authorhash after we've copied - # everything out of it.) - - my %changelog; - while (my ($author,$timehash) = each %$authorhash) - { - my $lasttime; - my %stamptime; - foreach my $time (sort {$main::a <=> $main::b} (keys %$timehash)) - { - my $msghash = $timehash->{$time}; - while (my ($msg,$qunklist) = each %$msghash) - { - my $stamptime = $stamptime{$msg}; - if ((defined $stamptime) - and (($time - $stamptime) < $Max_Checkin_Duration) - and (defined $changelog{$stamptime}{$author}{$msg})) - { - push(@{$changelog{$stamptime}{$author}{$msg}}, @$qunklist); - } - else { - $changelog{$time}{$author}{$msg} = $qunklist; - $stamptime{$msg} = $time; - } - } - } - } - undef (%$authorhash); - - ### Now we can write out the ChangeLog! - - my ($logfile_here, $logfile_bak, $tmpfile); - - if (! $Output_To_Stdout) { - $logfile_here = $dir . $Log_File_Name; - $logfile_here =~ s/^\.\/\//\//; # fix any leading ".//" problem - $tmpfile = "${logfile_here}.cvs2cl$$.tmp"; - $logfile_bak = "${logfile_here}.bak"; - - open (LOG_OUT, ">$tmpfile") or die "Unable to open \"$tmpfile\""; - } - else { - open (LOG_OUT, ">-") or die "Unable to open stdout for writing"; - } - - print LOG_OUT $ChangeLog_Header; - - if ($XML_Output) { - print LOG_OUT "\n\n" - . "\n\n"; - } - - -# my $printed_fileline_count_left = 4; -# my $breakoutofloop = 0; - - foreach my $time (sort {$main::b <=> $main::a} (keys %changelog)) - { - my $printed_fileline_count_left = $Max_Fileline_Count; -# if($breakoutofloop) { -# last; -# } - my $authorhash = $changelog{$time}; - while (my ($author,$mesghash) = each %$authorhash) - { - # If XML, escape in outer loop to avoid compound quoting: - if ($XML_Output) { - $author = &xml_escape ($author); - } - - while (my ($msg,$qunklist) = each %$mesghash) - { - my $files = &pretty_file_list ($qunklist); - my $header_line; # date and author - my $body; # see below - my $wholething; # $header_line + $body - - # Set up the date/author line. - # kff todo: do some more XML munging here, on the header - # part of the entry: - my ($ignore,$min,$hour,$mday,$mon,$year,$wday) - = $UTC_Times ? gmtime($time) : localtime($time); - - # XML output includes everything else, we might as well make - # it always include Day Of Week too, for consistency. - if ($Show_Day_Of_Week or $XML_Output) { - $wday = ("Sunday", "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday")[$wday]; - $wday = ($XML_Output) ? "${wday}\n" : " $wday"; - } - else { - $wday = ""; - } - - if ($XML_Output) { - $header_line = - sprintf ("%4u-%02u-%02u\n" - . "${wday}" - . "\n" - . "%s\n", - $year+1900, $mon+1, $mday, $hour, $min, $author); - } - else { - $header_line = - sprintf ("%4u-%02u-%02u${wday} %02u:%02u %s\n", - $year+1900, $mon+1, $mday, $hour, $min, $author); - } - - # Reshape the body according to user preferences. - if ($XML_Output) - { - $msg = &preprocess_msg_text ($msg); - $body = $files . $msg; - } - elsif ($No_Wrap) - { - $msg = &preprocess_msg_text ($msg); -### $files = wrap ("\t", " ", "$files"); - - my $dirprepended = ($files =~ /:,/); - - if($dirprepended) { - $printed_fileline_count_left++; - } - # truncate to max fileline count - my @filearr = split(", ",$files,$printed_fileline_count_left+1); - $printed_fileline_count_left -= ($#filearr+1); - - if($printed_fileline_count_left<0) { - $filearr[$#filearr] = ""; - } - -# $breakoutofloop = ($printed_fileline_count_left<=0); - - $files = join(", ",@filearr); - - if($dirprepended) { - $files =~ s/, /,\n\t /g; - $files =~ s/:,/:/; - } else { - $files =~ s/, /,\n\t /g; - } - $files = "\t".$files; # I like this better (Georges) - $msg =~ s/\n(.*)/\n\t$1/g; - unless ($After_Header eq " ") { - $msg =~ s/^(.*)/\t$1/g; - } - $body = $files . $After_Header . $msg; - } - else # do wrapping, either FSF-style or regular - { - if ($FSF_Style) - { - $files = wrap ("\t", " ", "$files"); - - my $files_last_line_len = 0; - if ($After_Header eq " ") - { - $files_last_line_len = &last_line_len ($files); - $files_last_line_len += 1; # for $After_Header - } - - $msg = &wrap_log_entry - ($msg, "\t", 69 - $files_last_line_len, 69); - $body = $files . $After_Header . $msg; - } - else # not FSF-style - { - $msg = &preprocess_msg_text ($msg); - $body = $files . $After_Header . $msg; - $body = wrap ("\t", " ", "$body"); - } - } - - $wholething = $header_line . $body; - - if ($XML_Output) { - $wholething = "\n${wholething}\n"; - } - - # One last check: make sure it passes the regexp test, if the - # user asked for that. We have to do it here, so that the - # test can match against information in the header as well - # as in the text of the log message. - - # How annoying to duplicate so much code just because I - # can't figure out a way to evaluate scalars on the trailing - # operator portion of a regular expression. Grrr. - if ($Case_Insensitive) { - unless ($Regexp_Gate && ($wholething !~ /$Regexp_Gate/oi)) { - print LOG_OUT "${wholething}\n"; - } - } - else { - unless ($Regexp_Gate && ($wholething !~ /$Regexp_Gate/o)) { - print LOG_OUT "${wholething}\n"; - } - } - } - } - } - - if ($XML_Output) { - print LOG_OUT "\n"; - } - - close (LOG_OUT); - - if (! $Output_To_Stdout) - { - if (-f $logfile_here) { - rename ($logfile_here, $logfile_bak); - } - rename ($tmpfile, $logfile_here); - } - } -} - - -sub parse_date_and_author () -{ - # Parses the date/time and author out of a line like: - # - # date: 1999/02/19 23:29:05; author: apharris; state: Exp; - - my $line = shift; - - my ($year, $mon, $mday, $hours, $min, $secs, $author) = $line =~ - m#(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+);\s+author:\s+([^;]+);# - or die "Couldn't parse date ``$line''"; - die "Bad date or Y2K issues" unless ($year > 1969 and $year < 2258); - # Kinda arbitrary, but useful as a sanity check - my $time = timegm($secs,$min,$hours,$mday,$mon-1,$year-1900); - - return ($time, $author); -} - - -# Here we take a bunch of qunks and convert them into printed -# summary that will include all the information the user asked for. -sub pretty_file_list () -{ - if ($Hide_Filenames and (! $XML_Output)) { - return ""; - } - - my $qunksref = shift; - my @qunkrefs = @$qunksref; - my @filenames; - my $beauty = ""; # The accumulating header string for this entry. - my %non_unanimous_tags; # Tags found in a proper subset of qunks - my %unanimous_tags; # Tags found in all qunks - my %all_branches; # Branches found in any qunk - my $common_dir = undef; # Dir prefix common to all files ("" if none) - my $fbegun = 0; # Did we begin printing filenames yet? - - # First, loop over the qunks gathering all the tag/branch names. - # We'll put them all in non_unanimous_tags, and take out the - # unanimous ones later. - foreach my $qunkref (@qunkrefs) - { - # Keep track of whether all the files in this commit were in the - # same directory, and memorize it if so. We can make the output a - # little more compact by mentioning the directory only once. - if ((scalar (@qunkrefs)) > 1) - { - if (! (defined ($common_dir))) - { - my ($base, $dir); - ($base, $dir, undef) = fileparse ($$qunkref{'filename'}); - - if ((! (defined ($dir))) # this first case is sheer paranoia - or ($dir eq "") - or ($dir eq "./") - or ($dir eq ".\\")) - { - $common_dir = ""; - } - else - { - $common_dir = $dir; - } - } - elsif ($common_dir ne "") - { - # Already have a common dir prefix, so how much of it can we preserve? - $common_dir = &common_path_prefix ($$qunkref{'filename'}, $common_dir); - } - } - else # only one file in this entry anyway, so common dir not an issue - { - $common_dir = ""; - } - - if (defined ($$qunkref{'branch'})) { - $all_branches{$$qunkref{'branch'}} = 1; - } - if (defined ($$qunkref{'tags'})) { - foreach my $tag (@{$$qunkref{'tags'}}) { - $non_unanimous_tags{$tag} = 1; - } - } - } - - # Any tag held by all qunks will be printed specially... but only if - # there are multiple qunks in the first place! - if ((scalar (@qunkrefs)) > 1) { - foreach my $tag (keys (%non_unanimous_tags)) { - my $everyone_has_this_tag = 1; - foreach my $qunkref (@qunkrefs) { - if ((! (defined ($$qunkref{'tags'}))) - or (! (grep ($_ eq $tag, @{$$qunkref{'tags'}})))) { - $everyone_has_this_tag = 0; - } - } - if ($everyone_has_this_tag) { - $unanimous_tags{$tag} = 1; - delete $non_unanimous_tags{$tag}; - } - } - } - - if ($XML_Output) - { - # If outputting XML, then our task is pretty simple, because we - # don't have to detect common dir, common tags, branch prefixing, - # etc. We just output exactly what we have, and don't worry about - # redundancy or readability. - - foreach my $qunkref (@qunkrefs) - { - my $filename = $$qunkref{'filename'}; - my $revision = $$qunkref{'revision'}; - my $tags = $$qunkref{'tags'}; - my $branch = $$qunkref{'branch'}; - my $branchroots = $$qunkref{'branchroots'}; - - $filename = &xml_escape ($filename); # probably paranoia - $revision = &xml_escape ($revision); # definitely paranoia - - $beauty .= "\n"; - $beauty .= "${filename}\n"; - $beauty .= "${revision}\n"; - if ($branch) { - $branch = &xml_escape ($branch); # more paranoia - $beauty .= "${branch}\n"; - } - foreach my $tag (@$tags) { - $tag = &xml_escape ($tag); # by now you're used to the paranoia - $beauty .= "${tag}\n"; - } - foreach my $root (@$branchroots) { - $root = &xml_escape ($root); # which is good, because it will continue - $beauty .= "${root}\n"; - } - $beauty .= "\n"; - } - - # Theoretically, we could go home now. But as long as we're here, - # let's print out the common_dir and utags, as a convenience to - # the receiver (after all, earlier code calculated that stuff - # anyway, so we might as well take advantage of it). - - if ((scalar (keys (%unanimous_tags))) > 1) { - foreach my $utag ((keys (%unanimous_tags))) { - $utag = &xml_escape ($utag); # the usual paranoia - $beauty .= "${utag}\n"; - } - } - - if ($common_dir) { - $common_dir = &xml_escape ($common_dir); - $beauty .= "${common_dir}\n"; - } - - # That's enough for XML, time to go home: - return $beauty; - } - - # Else not XML output, so complexly compactify for chordate - # consumption. At this point we have enough global information - # about all the qunks to organize them non-redundantly for output. - - if ($common_dir) { - # Note that $common_dir still has its trailing slash -# $beauty .= "$common_dir: "; - $beauty .= "$common_dir:, "; - } - - if ($Show_Branches) - { - # For trailing revision numbers. - my @brevisions; - - foreach my $branch (keys (%all_branches)) - { - foreach my $qunkref (@qunkrefs) - { - if ((defined ($$qunkref{'branch'})) - and ($$qunkref{'branch'} eq $branch)) - { - if ($fbegun) { - # kff todo: comma-delimited in XML too? Sure. - $beauty .= ", "; - } - else { - $fbegun = 1; - } - my $fname = substr ($$qunkref{'filename'}, length ($common_dir)); - $beauty .= $fname; - $$qunkref{'printed'} = 1; # Just setting a mark bit, basically - - if ($Show_Tags && (defined @{$$qunkref{'tags'}})) { - my @tags = grep ($non_unanimous_tags{$_}, @{$$qunkref{'tags'}}); - if (@tags) { - $beauty .= " (tags: "; - $beauty .= join (', ', @tags); - $beauty .= ")"; - } - } - - if ($Show_Revisions) { - # Collect the revision numbers' last components, but don't - # print them -- they'll get printed with the branch name - # later. - $$qunkref{'revision'} =~ /.+\.([\d])+$/; - push (@brevisions, $1); - - # todo: we're still collecting branch roots, but we're not - # showing them anywhere. If we do show them, it would be - # nifty to just call them revision "0" on a the branch. - # Yeah, that's the ticket. - } - } - } - $beauty .= " ($branch"; - if (@brevisions) { - if ((scalar (@brevisions)) > 1) { - $beauty .= ".["; - $beauty .= (join (',', @brevisions)); - $beauty .= "]"; - } - else { - $beauty .= ".$brevisions[0]"; - } - } - $beauty .= ")"; - } - } - - # Okay; any qunks that were done according to branch are taken care - # of, and marked as printed. Now print everyone else. - - foreach my $qunkref (@qunkrefs) - { - next if (defined ($$qunkref{'printed'})); # skip if already printed - - if ($fbegun) { - $beauty .= ", "; - } - else { - $fbegun = 1; - } - $beauty .= substr ($$qunkref{'filename'}, length ($common_dir)); - # todo: Shlomo's change was this: - # $beauty .= substr ($$qunkref{'filename'}, - # (($common_dir eq "./") ? "" : length ($common_dir))); - $$qunkref{'printed'} = 1; # Set a mark bit. - - if ($Show_Revisions || $Show_Tags) - { - my $started_addendum = 0; - - if ($Show_Revisions) { - $started_addendum = 1; - $beauty .= " ("; - $beauty .= "$$qunkref{'revision'}"; - } - if ($Show_Tags && (defined $$qunkref{'tags'})) { - my @tags = grep ($non_unanimous_tags{$_}, @{$$qunkref{'tags'}}); - if ((scalar (@tags)) > 0) { - if ($started_addendum) { - $beauty .= ", "; - } - else { - $beauty .= " (tags: "; - } - $beauty .= join (', ', @tags); - $started_addendum = 1; - } - } - if ($started_addendum) { - $beauty .= ")"; - } - } - } - - # Unanimous tags always come last. - if ($Show_Tags && %unanimous_tags) - { - $beauty .= " (utags: "; - $beauty .= join (', ', keys (%unanimous_tags)); - $beauty .= ")"; - } - - # todo: still have to take care of branch_roots? - - $beauty = "* $beauty:"; - - return $beauty; -} - - -sub common_path_prefix () -{ - my $path1 = shift; - my $path2 = shift; - - my ($dir1, $dir2); - (undef, $dir1, undef) = fileparse ($path1); - (undef, $dir2, undef) = fileparse ($path2); - - # Transmogrify Windows filenames to look like Unix. - # (It is far more likely that someone is running cvs2cl.pl under - # Windows than that they would genuinely have backslashes in their - # filenames.) - $dir1 =~ tr#\\#/#; - $dir2 =~ tr#\\#/#; - - my $accum1 = ""; - my $accum2 = ""; - my $last_common_prefix = ""; - - while ($accum1 eq $accum2) - { - $last_common_prefix = $accum1; - last if ($accum1 eq $dir1); - my ($tmp1) = split (/\//, (substr ($dir1, length ($accum1)))); - my ($tmp2) = split (/\//, (substr ($dir2, length ($accum2)))); - $accum1 .= "$tmp1/" if ((defined ($tmp1)) and $tmp1); - $accum2 .= "$tmp2/" if ((defined ($tmp2)) and $tmp2); - } - - return $last_common_prefix; -} - - -sub preprocess_msg_text () -{ - my $text = shift; - - # Strip out carriage returns (as they probably result from DOSsy editors). - $text =~ s/\r\n/\n/g; - - # If it *looks* like two newlines, make it *be* two newlines: - $text =~ s/\n\s*\n/\n\n/g; - - if ($XML_Output) - { - $text = &xml_escape ($text); - $text = "${text}\n"; - } - elsif (! $No_Wrap) - { - # Strip off lone newlines, but only for lines that don't begin with - # whitespace or a mail-quoting character, since we want to preserve - # that kind of formatting. Also don't strip newlines that follow a - # period; we handle those specially next. And don't strip - # newlines that precede an open paren. - 1 while ($text =~ s/(^|\n)([^>\s].*[^.\n])\n([^>\n])/$1$2 $3/g); - - # If a newline follows a period, make sure that when we bring up the - # bottom sentence, it begins with two spaces. - 1 while ($text =~ s/(^|\n)([^>\s].*)\n([^>\n])/$1$2 $3/g); - } - - return $text; -} - - -sub last_line_len () -{ - my $files_list = shift; - my @lines = split (/\n/, $files_list); - my $last_line = pop (@lines); - return length ($last_line); -} - -sub FindPrevDayStr() { - my($prevdays,$dayofmonth,$month,$year) = @_; - use integer; - - $dayofmonth--; # make this zero-based - - #doesnt account for leap years well, but good enuff - - my $totalyeardays=365; - if($year % 4 == 0) { - $totalyeardays++; - $monthlengtharr[1]=29; - } - - my $prevyeardays = $prevdays % $totalyeardays; - my $yearsback = $prevdays / 365; - - my $m=0; - my $yearday = 0; - while($m<$month) { - $yearday+=$monthlengtharr[$m]; - $m++; - } - - $yearday+=$dayofmonth; - $yearday-=$prevyeardays; - - if($yearday<0) { - $yearsback++; - $yearday+=$totalyeardays; - } - - $year -= $yearsback; # also doesnt go lower than year 0 - - $m=0; - while($yearday>=$monthlengtharr[$m]) { - $yearday-=$monthlengtharr[$m]; - $m++; - } - - # mon/day are 0-based, conv to 1-based - my $retstr=($year+1900)."-".($m+1)."-".($yearday+1); - return $retstr; -} - -# A custom wrap function, sensitive to some common constructs used in -# log entries. -sub wrap_log_entry () -{ - my $text = shift; # The text to wrap. - my $left_pad_str = shift; # String to pad with on the left. - - # These do NOT take left_pad_str into account: - my $length_remaining = shift; # Amount left on current line. - my $max_line_length = shift; # Amount left for a blank line. - - my $wrapped_text = ""; # The accumulating wrapped entry. - my $indentation = ""; # Inherited indentation from prev line. - - my $first_time = 1; # Is this the first iteration of the loop? - - my @lines = split (/\n/, $text); - while (@lines) # Don't use `foreach' here, it won't work. - { - my $this_line = shift (@lines); - chomp $this_line; - - # First, if it matches any of the line-start regexps, then print a - # newline now... - if (($this_line =~ /^(\s*)\*\s+[a-zA-Z0-9]/) - || ($this_line =~ /^(\s*)\* [a-zA-Z0-9_\.\/\+-]+/) - || ($this_line =~ /^(\s*)\([a-zA-Z0-9_\.\/\+-]+(\)|,\s*)/) - || ($this_line =~ /^(\s*)- +/) - || ($this_line =~ /^(\s*)[^\s]+:\s*$/) - || ($this_line =~ /^(\s*)\*\) +/) - || ($this_line =~ /^(\s*)[a-zA-Z0-9](\)|\.|\:) +/)) - { - # Make a line break immediately, unless header separator is set - # and this line is the first line in the entry, in which case - # we're getting the blank line for free already and shouldn't - # add an extra one. - unless (($After_Header ne " ") and ($first_time)) { - $wrapped_text .= "\n${left_pad_str}"; - } - - $indentation = $1; - $length_remaining = $max_line_length - (length ($indentation)); - } - - # Now that any indentation has been preserved, strip off leading - # whitespace, so up-folding has no ugly side-effects. - $this_line =~ s/^\s*//; - - # Accumulate the line, and adjust parameters for next line. - my $this_len = length ($this_line); - if ($this_len == 0) - { - # Blank lines should be preserved, and should cancel any - # indentation level. - $this_line = "\n${left_pad_str}"; - $indentation = ""; - $length_remaining = $max_line_length; - } - elsif ($this_len >= $length_remaining) - { - # Walk backwards from the end. At first acceptable spot, break - # a new line. - my $idx = $length_remaining - 1; - if ($idx < 0) { $idx = 0 }; - while ($idx > 0) - { - if (substr ($this_line, $idx, 1) =~ /\s/) - { - my $line_now = substr ($this_line, 0, $idx); - my $next_line = substr ($this_line, $idx); - $this_line = $line_now; - - # Clean whitespace off the end. - chomp $this_line; - - # The current line is ready to be printed. - $this_line .= "\n${left_pad_str}"; - - # Make sure the next line is allowed full room. - $length_remaining = $max_line_length - (length ($indentation)); - - # Strip next_line, but then preserve any indentation. - $next_line =~ s/^\s*//; - $next_line = $indentation . $next_line; - - unshift (@lines, $next_line); - last; - } - else - { - $idx--; - } - } - - # If we just bottomed out, the line is too long, so just break - # it where the original text does. - if ($idx == 0) - { - if ($length_remaining == ($max_line_length - (length ($indentation)))) - { - # The line is simply too long -- there is no hope of ever - # breaking it nicely, so just insert it verbatim, with - # appropriate padding. - $this_line = "\n${left_pad_str}${this_line}"; - } - else - { - # Can't break it here, but may be able to on the next round. - unshift (@lines, $this_line); - $length_remaining = $max_line_length - (length ($indentation)); - $this_line = "\n${left_pad_str}"; - } - } - } - else # $this_len < $length_remaining, so tack on what we can. - { - # Leave a note for the next iteration. - $length_remaining = $length_remaining - $this_len; - - if ($this_line =~ /\.$/) - { - $this_line .= " "; - $length_remaining -= 2; - } - else # not a sentence end - { - $this_line .= " "; - $length_remaining -= 1; - } - } - - # Unconditionally indicate that loop has run at least once. - $first_time = 0; - - $wrapped_text .= "${this_line}"; - } - - # One last bit of padding. - $wrapped_text .= "\n"; - - return $wrapped_text; -} - - -sub xml_escape () -{ - my $txt = shift; - $txt =~ s/&/&/g; - $txt =~ s//>/g; - return $txt; -} - - -sub maybe_read_user_map_file () -{ - my %expansions; - - if ($User_Map_File) - { - open (MAPFILE, "<$User_Map_File") - or die ("Unable to open $User_Map_File ($!)"); - - while () - { - next if /^\s*#/; # Skip comment lines. - next if not /:/; # Skip lines without colons. - - # It is now safe to split on ':'. - my ($username, $expansion) = split ':'; - chomp $expansion; - $expansion =~ s/^'(.*)'$/$1/; - $expansion =~ s/^"(.*)"$/$1/; - - # If it looks like the expansion has a real name already, then - # we toss the username we got from CVS log. Otherwise, keep - # it to use in combination with the email address. - - if ($expansion =~ /^\s*<{0,1}\S+@.*/) { - # Also, add angle brackets if none present - if (! ($expansion =~ /<\S+@\S+>/)) { - $expansions{$username} = "$username <$expansion>"; - } - else { - $expansions{$username} = "$username $expansion"; - } - } - else { - $expansions{$username} = $expansion; - } - } - - close (MAPFILE); - } - - return %expansions; -} - - -sub parse_options () -{ - # Check this internally before setting the global variable. - my $output_file; - - # If this gets set, we encountered unknown options and will exit at - # the end of this subroutine. - my $exit_with_admonishment = 0; - - while (my $arg = shift (@ARGV)) - { - if ($arg =~ /^-h$|^-help$|^--help$|^--usage$|^-?$/) { - $Print_Usage = 1; - } - elsif ($arg =~ /^--debug$/) { # unadvertised option, heh - $Debug = 1; - } - elsif ($arg =~ /^--version$/) { - $Print_Version = 1; - } - elsif ($arg =~ /^-g$|^--global-opts$/) { - my $narg = shift (@ARGV) || die "$arg needs argument.\n"; - # Don't assume CVS is called "cvs" on the user's system: - $Log_Source_Command =~ s/(^\S*)/$1 $narg/; - } - elsif ($arg =~ /^-l$|^--log-opts$/) { - my $narg = shift (@ARGV) || die "$arg needs argument.\n"; - $Log_Source_Command .= " $narg"; - } - elsif ($arg =~ /^-f$|^--file$/) { - my $narg = shift (@ARGV) || die "$arg needs argument.\n"; - $output_file = $narg; - } - elsif ($arg =~ /^--fsf$/) { - $FSF_Style = 1; - } - elsif ($arg =~ /^-U$|^--usermap$/) { - my $narg = shift (@ARGV) || die "$arg needs argument.\n"; - $User_Map_File = $narg; - } - elsif ($arg =~ /^-W$|^--window$/) { - my $narg = shift (@ARGV) || die "$arg needs argument.\n"; - $Max_Checkin_Duration = $narg; - } - elsif ($arg =~ /^-I$|^--ignore$/) { - my $narg = shift (@ARGV) || die "$arg needs argument.\n"; - push (@Ignore_Files, $narg); - } - elsif ($arg =~ /^-C$|^--case-insensitive$/) { - $Case_Insensitive = 1; - } - elsif ($arg =~ /^-R$|^--regexp$/) { - my $narg = shift (@ARGV) || die "$arg needs argument.\n"; - $Regexp_Gate = $narg; - } - elsif ($arg =~ /^--stdout$/) { - $Output_To_Stdout = not $Output_To_Stdout; - } - elsif ($arg =~ /^--version$/) { - $Print_Version = 1; - } - elsif ($arg =~ /^--distributed$/) { - $Distributed = 1; - } - elsif ($arg =~ /^-P$|^--prune$/) { - $Prune_Empty_Msgs = 1; - } - elsif ($arg =~ /^-S$|^--separate-header$/) { - $After_Header = "\n\n"; - } - elsif ($arg =~ /^--no-wrap$/) { - $No_Wrap = not $No_Wrap; - } - elsif ($arg =~ /^--gmt$|^--utc$/) { - $UTC_Times = 1; - } - elsif ($arg =~ /^-w$|^--day-of-week$/) { - $Show_Day_Of_Week = not $Show_Day_Of_Week; - } - elsif ($arg =~ /^-r$|^--revisions$/) { - $Show_Revisions = not $Show_Revisions; - } - elsif ($arg =~ /^-t$|^--tags$/) { - $Show_Tags = not $Show_Tags; - } - elsif ($arg =~ /^-b$|^--branches$/) { - $Show_Branches = not $Show_Branches; - } - elsif ($arg =~ /^-c$|^--count$/) { - my $narg = shift (@ARGV) || die "$arg needs argument.\n"; - $Max_Fileline_Count = $narg; - } - elsif ($arg =~ /^-d$|^--days$/) { - my $narg = shift (@ARGV) || die "$arg needs argument.\n"; - $Prev_Day_Count = $narg; - } - elsif ($arg =~ /^-F$|^--follow$/) { - my $narg = shift (@ARGV) || die "$arg needs argument.\n"; - push (@Follow_Branches, $narg); - } - elsif ($arg =~ /^--stdin$/) { - $Input_From_Stdin = 1; - } - elsif ($arg =~ /^--header$/) { - my $narg = shift (@ARGV) || die "$arg needs argument.\n"; - $ChangeLog_Header = &slurp_file ($narg); - if (! defined ($ChangeLog_Header)) { - $ChangeLog_Header = ""; - } - } - elsif ($arg =~ /^--xml$/) { - $XML_Output = 1; - } - elsif ($arg =~ /^--hide-filenames$/) { - $Hide_Filenames = 1; - $After_Header = ""; - } - else { - # Just add a filename as argument to the log command - $Log_Source_Command .= " $arg"; - } - } - - ## Check for contradictions... - - if ($Output_To_Stdout && $Distributed) { - print STDERR "cannot pass both --stdout and --distributed\n"; - $exit_with_admonishment = 1; - } - - if ($Output_To_Stdout && $output_file) { - print STDERR "cannot pass both --stdout and --file\n"; - $exit_with_admonishment = 1; - } - - # Or if any other error message has already been printed out, we - # just leave now: - if ($exit_with_admonishment) { - &usage (); - exit (1); - } - elsif ($Print_Usage) { - &usage (); - exit (0); - } - elsif ($Print_Version) { - &version (); - exit (0); - } - - ## Else no problems, so proceed. - - if ($Output_To_Stdout) { - undef $Log_File_Name; # not actually necessary - } - elsif ($output_file) { - $Log_File_Name = $output_file; - } -} - - -sub slurp_file () -{ - my $filename = shift || die ("no filename passed to slurp_file()"); - my $retstr; - - open (SLURPEE, "<${filename}") or die ("unable to open $filename ($!)"); - my $saved_sep = $/; - undef $/; - $retstr = ; - $/ = $saved_sep; - close (SLURPEE); - return $retstr; -} - - -sub debug () -{ - if ($Debug) { - my $msg = shift; - print STDERR $msg; - } -} - - -sub version () -{ - print "cvs2cl.pl version ${VERSION}; distributed under the GNU GPL.\n"; -} - - -sub usage () -{ - &version (); - print <<'END_OF_INFO'; -Generate GNU-style ChangeLogs in CVS working copies. - -Notes about the output format(s): - - The default output of cvs2cl.pl is designed to be compact, formally - unambiguous, but still easy for humans to read. It is largely - self-explanatory, I hope; the one abbreviation that might not be - obvious is "utags". That stands for "universal tags" -- a - universal tag is one held by all the files in a given change entry. - - If you need output that's easy for a program to parse, use the - --xml option. Note that with XML output, just about all available - information is included with each change entry, whether you asked - for it or not, on the theory that your parser can ignore anything - it's not looking for. - -Notes about the options and arguments (the actual options are listed -last in this usage message): - - * The -I and -F options may appear multiple times. - - * To follow trunk revisions, use "-F trunk" ("-F TRUNK" also works). - This is okay because no would ever, ever be crazy enough to name a - branch "trunk", right? Right. - - * For the -U option, the UFILE should be formatted like - CVSROOT/users. That is, each line of UFILE looks like this - jrandom:jrandom@red-bean.com - or maybe even like this - jrandom:'Jesse Q. Random ' - Don't forget to quote the portion after the colon if necessary. - - * Many people want to filter by date. To do so, invoke cvs2cl.pl - like this: - cvs2cl.pl -l "-d'DATESPEC'" - where DATESPEC is any date specification valid for "cvs log -d". - (Note that CVS 1.10.7 and below requires there be no space between - -d and its argument). - -Options/Arguments: - -h, -help, --help, or -? Show this usage and exit - --version Show version and exit - -r, --revisions Show revision numbers in output - -b, --branches Show branch names in revisions when possible - -t, --tags Show tags (symbolic names) in output - --stdin Read from stdin, don't run cvs log - --stdout Output to stdout not to ChangeLog - -d, --distributed Put ChangeLogs in subdirs - -f FILE, --file FILE Write to FILE instead of "ChangeLog" - --fsf Use this if log data is in FSF ChangeLog style - -W SECS, --window SECS Window of time within which log entries unify - -U UFILE, --usermap UFILE Expand usernames to email addresses from UFILE - -R REGEXP, --regexp REGEXP Include only entries that match REGEXP - -I REGEXP, --ignore REGEXP Ignore files whose names match REGEXP - -C, --case-insensitive Any regexp matching is done case-insensitively - -F BRANCH, --follow BRANCH Show only revisions on or ancestral to BRANCH - -S, --separate-header Blank line between each header and log message - --no-wrap Don't auto-wrap log message (recommend -S also) - --gmt, --utc Show times in GMT/UTC instead of local time - -w, --day-of-week Show day of week - --header FILE Get ChangeLog header from FILE ("-" means stdin) - --xml Output XML instead of ChangeLog format - --hide-filenames Don't show filenames (ignored for XML output) - -P, --prune Don't show empty log messages - -g OPTS, --global-opts OPTS Invoke like this "cvs OPTS log ..." - -l OPTS, --log-opts OPTS Invoke like this "cvs ... log OPTS" - -c [#lines], --count [#lns] Print only #lines worth of file per dir (deflt 15) - -d [#days], --days [#days] Print only changes within the last [#days] days - FILE1 [FILE2 ...] Show only log information for the named FILE(s) - if no file or dir is specified, default to current dir logs only (no recursion) - -See http://www.red-bean.com/~kfogel/cvs2cl.shtml for maintenance and bug info. -END_OF_INFO -} - -__END__ - -=head1 NAME - -cvs2cl.pl - produces GNU-style ChangeLogs in CVS working copies, by - running "cvs log" and parsing the output. Shared log entries are - unified in an intuitive way. - -=head1 DESCRIPTION - -This script generates GNU-style ChangeLog files from CVS log -information. Basic usage: just run it inside a working copy and a -ChangeLog will appear. It requires repository access (i.e., 'cvs log' -must work). Run "cvs2cl.pl --help" to see more advanced options. - -See http://www.red-bean.com/~kfogel/cvs2cl.shtml for updates, and -for instructions on getting anonymous CVS access to this script. - -Maintainer: Karl Fogel -Please report bugs to . - -=head1 README - -This script generates GNU-style ChangeLog files from CVS log -information. Basic usage: just run it inside a working copy and a -ChangeLog will appear. It requires repository access (i.e., 'cvs log' -must work). Run "cvs2cl.pl --help" to see more advanced options. - -See http://www.red-bean.com/~kfogel/cvs2cl.shtml for updates, and -for instructions on getting anonymous CVS access to this script. - -Maintainer: Karl Fogel -Please report bugs to . - -=head1 PREREQUISITES - -This script requires C, C, and -C. -It also seems to require C or higher. - -=pod OSNAMES - -any - -=pod SCRIPT CATEGORIES - -Version_Control/CVS - -=cut - - --*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- - -Note about a bug-slash-opportunity: ------------------------------------ - -There's a bug in Text::Wrap, which affects cvs2cl. This script -reveals it: - - #!perl -w - - use Text::Wrap; - - my $test_text = - "This script demonstrates a bug in Text::Wrap. The very long line - following this paragraph will be relocated relative to the surrounding - text: - - ==================================================================== - - See? When the bug happens, we'll get the line of equal signs below - this paragraph, even though it should be above."; - - - # Print out the test text with no wrapping: - print "$test_text"; - print "\n"; - print "\n"; - - # Now print it out wrapped, and see the bug: - print wrap ("\t", " ", "$test_text"); - print "\n"; - print "\n"; - -If the line of equal signs were one shorter, then the bug doesn't -happen. Interesting. - -Anyway, rather than fix this in Text::Wrap, we might as well write a -new wrap() which has the following much-needed features: - -* initial indentation, like current Text::Wrap() -* subsequent line indentation, like current Text::Wrap() -* user chooses among: force-break long words, leave them alone, or die()? -* preserve existing indentation: chopped chunks from an indented line - are indented by same (like this line, not counting the asterisk!) -* optional list of things to preserve on line starts, default ">" - -Note that the last two are essentially the same concept, so unify in -implementation and give a good interface to controlling them. - -And how about: - -Optionally, when encounter a line pre-indented by same as previous -line, then strip the newline and refill, but indent by the same. -Yeah... diff --git a/pandatool/src/scripts/lastchng.pl b/pandatool/src/scripts/lastchng.pl deleted file mode 100755 index 09662e802a..0000000000 --- a/pandatool/src/scripts/lastchng.pl +++ /dev/null @@ -1,42 +0,0 @@ -#!perl - -# find last checked-in modification to [filename] - -if($#ARGV==-1) { - die "Usage: lastchng.pl [filename]\nfinds last checked-in modification to file\n"; -} - -if(!(-e $ARGV[0])) { - die "file '".$ARGV[0]."' doesnt exist!\n"; -} - -open(INPUT_STREAM,"cvs log $ARGV[0] |"); - -$firstrev=null; -$secondrev=null; - -while($line=) { - if($line =~ /^revision (\S*)$/) { -# print $line."\n"; - if($firstrev==null) { - $firstrev=$1; - } else { - $secondrev=$1; - last; - } - } -} -close(INPUT_STREAM); - -if($firstrev==null) { - die "Couldn't find first revision of $ARGV[0]!\n"; -} -$revstr="-r$firstrev"; -if($secondrev!=null) { -$revstr = "-r$secondrev ".$revstr; -} - -$cvsline="cvs diff $revstr ".$ARGV[0]; -print $cvsline,"\n"; -system($cvsline); -exit(0); diff --git a/pandatool/src/scripts/wlastchng.pl b/pandatool/src/scripts/wlastchng.pl deleted file mode 100755 index 4f6127477e..0000000000 --- a/pandatool/src/scripts/wlastchng.pl +++ /dev/null @@ -1,45 +0,0 @@ -#!perl - -# find last checked-in modification to [filename] - -if($#ARGV==-1) { - die "Usage: lastchng.pl [filename]\nfinds last checked-in modification to file\n"; -} - -if(!(-e $ARGV[0])) { - die "file '".$ARGV[0]."' doesnt exist!\n"; -} - -open(INPUT_STREAM,"cvs log $ARGV[0] |"); - -$firstrev=null; -$secondrev=null; - -while($line=) { - if($line =~ /^revision (\S*)$/) { -# print $line."\n"; - if($firstrev==null) { - $firstrev=$1; - } else { - $secondrev=$1; - last; - } - } -} -close(INPUT_STREAM); - -if($firstrev==null) { - die "Couldn't find first revision of $ARGV[0]!\n"; -} -$revstr="-r$firstrev"; -if($secondrev!=null) { -$revstr = "-r$secondrev ".$revstr; -} - -$cvsline="cvs update -p -r".$secondrev." ".$ARGV[0]." > junk"; -$windiffline="windiff junk ".$ARGV[0]; -print $cvsline,"\n"; -system($cvsline); -print $windiffline,"\n"; -system($windiffline); -exit(0); diff --git a/pandatool/src/win-stats/winStats.cxx b/pandatool/src/win-stats/winStats.cxx index 7818ea5ed8..e40830e189 100644 --- a/pandatool/src/win-stats/winStats.cxx +++ b/pandatool/src/win-stats/winStats.cxx @@ -16,6 +16,9 @@ #include "winStatsServer.h" #include "config_pstatclient.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include static const char *toplevel_class_name = "pstats"; diff --git a/pandatool/src/win-stats/winStatsChartMenu.h b/pandatool/src/win-stats/winStatsChartMenu.h index cc372f0a80..d96a807965 100644 --- a/pandatool/src/win-stats/winStatsChartMenu.h +++ b/pandatool/src/win-stats/winStatsChartMenu.h @@ -16,6 +16,9 @@ #include "pandatoolbase.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsMonitor; diff --git a/pandatool/src/win-stats/winStatsGraph.h b/pandatool/src/win-stats/winStatsGraph.h index a4f9a61467..82fb04f71d 100644 --- a/pandatool/src/win-stats/winStatsGraph.h +++ b/pandatool/src/win-stats/winStatsGraph.h @@ -18,6 +18,9 @@ #include "winStatsLabelStack.h" #include "pmap.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsMonitor; diff --git a/pandatool/src/win-stats/winStatsLabel.h b/pandatool/src/win-stats/winStatsLabel.h index 0317f0dcfb..5642cdb8d0 100644 --- a/pandatool/src/win-stats/winStatsLabel.h +++ b/pandatool/src/win-stats/winStatsLabel.h @@ -16,6 +16,9 @@ #include "pandatoolbase.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsMonitor; diff --git a/pandatool/src/win-stats/winStatsLabelStack.h b/pandatool/src/win-stats/winStatsLabelStack.h index afb8ecf6c7..e7fd1da41f 100644 --- a/pandatool/src/win-stats/winStatsLabelStack.h +++ b/pandatool/src/win-stats/winStatsLabelStack.h @@ -17,6 +17,9 @@ #include "pandatoolbase.h" #include "pvector.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsLabel; diff --git a/pandatool/src/win-stats/winStatsMonitor.h b/pandatool/src/win-stats/winStatsMonitor.h index de74aa0d68..c70a71507e 100644 --- a/pandatool/src/win-stats/winStatsMonitor.h +++ b/pandatool/src/win-stats/winStatsMonitor.h @@ -23,6 +23,9 @@ #include "pvector.h" #include "pmap.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsServer; diff --git a/pandatool/src/win-stats/winStatsPianoRoll.h b/pandatool/src/win-stats/winStatsPianoRoll.h index b0c9410003..b130c99b18 100644 --- a/pandatool/src/win-stats/winStatsPianoRoll.h +++ b/pandatool/src/win-stats/winStatsPianoRoll.h @@ -20,6 +20,9 @@ #include "pStatPianoRoll.h" #include "pointerTo.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsMonitor; diff --git a/pandatool/src/win-stats/winStatsStripChart.h b/pandatool/src/win-stats/winStatsStripChart.h index 59953fbe8c..9e6f91667f 100644 --- a/pandatool/src/win-stats/winStatsStripChart.h +++ b/pandatool/src/win-stats/winStatsStripChart.h @@ -20,6 +20,9 @@ #include "pStatStripChart.h" #include "pointerTo.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsMonitor; diff --git a/pandatool/src/xfile/xFile.cxx b/pandatool/src/xfile/xFile.cxx index c5534e0af1..3c0d7b3445 100644 --- a/pandatool/src/xfile/xFile.cxx +++ b/pandatool/src/xfile/xFile.cxx @@ -34,7 +34,7 @@ PT(XFile) XFile::_standard_templates; * */ XFile:: -XFile(bool keep_names) : XFileNode(this, "") { +XFile(bool keep_names) : XFileNode(this) { _major_version = 3; _minor_version = 2; _format_type = FT_text; diff --git a/pandatool/src/xfile/xFileNode.I b/pandatool/src/xfile/xFileNode.I index 6032a749e2..735c2dce3a 100644 --- a/pandatool/src/xfile/xFileNode.I +++ b/pandatool/src/xfile/xFileNode.I @@ -11,6 +11,16 @@ * @date 2004-10-03 */ +/** + * + */ +INLINE XFileNode:: +XFileNode(XFile *x_file) : + Namable(), + _x_file(x_file) +{ +} + /** * */ diff --git a/pandatool/src/xfile/xFileNode.h b/pandatool/src/xfile/xFileNode.h index a954b8f9fd..2cd9f6d3f6 100644 --- a/pandatool/src/xfile/xFileNode.h +++ b/pandatool/src/xfile/xFileNode.h @@ -38,6 +38,9 @@ class Filename; */ class XFileNode : public TypedObject, public Namable, virtual public ReferenceCount { +protected: + INLINE XFileNode(XFile *x_file); + public: XFileNode(XFile *x_file, const std::string &name); virtual ~XFileNode(); diff --git a/samples/asteroids/requirements.txt b/samples/asteroids/requirements.txt index a8e341eee1..0f30cd2bf9 100644 --- a/samples/asteroids/requirements.txt +++ b/samples/asteroids/requirements.txt @@ -1,2 +1 @@ ---pre --extra-index-url https://archive.panda3d.org/branches/deploy-ng panda3d diff --git a/samples/fireflies/main.py b/samples/fireflies/main.py index 41527beb88..f5f7c2e2a1 100755 --- a/samples/fireflies/main.py +++ b/samples/fireflies/main.py @@ -65,7 +65,7 @@ class FireflyDemo(ShowBase): # doesn't support the necessary OpenGL extensions. if self.modelbuffer is None or self.lightbuffer is None: - self.t = addTitle("Toon Shader: Video driver does not support " + self.t = addTitle("Firefly Demo: Video driver does not support " "multiple render targets") return diff --git a/samples/fractal-plants/main.py b/samples/fractal-plants/main.py index fb16a5b1bc..7c0492af85 100755 --- a/samples/fractal-plants/main.py +++ b/samples/fractal-plants/main.py @@ -141,11 +141,14 @@ def drawBody(nodePath, vdata, pos, vecList, radius=1, keepDrawing=True, numVerti drawReWriter.addData1f(keepDrawing) currAngle += angleSlice + if startRow == 0: + return + drawReader = GeomVertexReader(vdata, "drawFlag") drawReader.setRow(startRow - numVertices) # we cant draw quads directly so we use Tristrips - if (startRow != 0) & (drawReader.getData1f() != False): + if drawReader.getData1i() != 0: lines = GeomTristrips(Geom.UHStatic) half = int(numVertices * 0.5) for i in range(numVertices): diff --git a/samples/particles/advanced.py b/samples/particles/advanced.py new file mode 100755 index 0000000000..1521c1453d --- /dev/null +++ b/samples/particles/advanced.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python + +# This program shows a shader-based particle system. With this approach, you +# can define an inertial particle system with a moving emitter whose position +# can not be pre-determined. + +from array import array +from itertools import chain +from random import uniform +from math import pi, sin, cos +from panda3d.core import TextNode +from panda3d.core import AmbientLight, DirectionalLight +from panda3d.core import LVector3 +from panda3d.core import NodePath +from panda3d.core import GeomPoints +from panda3d.core import GeomEnums +from panda3d.core import GeomVertexFormat +from panda3d.core import GeomVertexData +from panda3d.core import GeomNode +from panda3d.core import Geom +from panda3d.core import OmniBoundingVolume +from panda3d.core import Texture +from panda3d.core import TextureStage +from panda3d.core import TexGenAttrib +from panda3d.core import Shader +from panda3d.core import ShaderAttrib +from panda3d.core import loadPrcFileData +from direct.showbase.ShowBase import ShowBase +from direct.gui.OnscreenText import OnscreenText +import sys + +HELP_TEXT = """ +left/right arrow: Rotate teapot +ESC: Quit +""" + +# We need to use GLSL 1.50 for these, and some drivers (notably Mesa) require +# us to explicitly ask for an OpenGL 3.2 context in that case. +config = """ +gl-version 3 2 +""" + +vert = """ +#version 150 +#extension GL_ARB_shader_image_load_store : require + +layout(rgba32f) uniform imageBuffer positions; // current positions +layout(rgba32f) uniform imageBuffer start_vel; // emission velocities +layout(rgba32f) uniform imageBuffer velocities; // current velocities +layout(rgba32f) uniform imageBuffer emission_times; // emission times +uniform mat4 p3d_ModelViewProjectionMatrix; +uniform vec3 emitter_pos; // emitter's position +uniform vec3 accel; // the acceleration of the particles +uniform float osg_FrameTime; // time of the current frame (absolute) +uniform float osg_DeltaFrameTime;// time since last frame +uniform float start_time; // particle system's start time (absolute) +uniform float part_duration; // single particle's duration + +out float from_emission; // time from specific particle's emission +out vec4 color; + +void main() { + float emission_time = imageLoad(emission_times, gl_VertexID).x; + vec4 pos = imageLoad(positions, gl_VertexID); + vec4 vel = imageLoad(velocities, gl_VertexID); + float from_start = osg_FrameTime - start_time; // time from system's start + from_emission = 0; + color = vec4(1); + if (from_start > emission_time) { // we've to show the particle + from_emission = from_start - emission_time; + if (from_emission <= osg_DeltaFrameTime + .01) { + // it's particle's emission frame: let's set its position at the + // emitter's position and set the initial velocity + pos = vec4(emitter_pos, 1); + vel = imageLoad(start_vel, gl_VertexID); + } + pos += vec4((vel * osg_DeltaFrameTime).xyz, 0); + vel += vec4(accel, 0) * osg_DeltaFrameTime; + } else color = vec4(0); + + // update the emission time (for particle recycling) + if (from_start >= emission_time + part_duration) { + imageStore(emission_times, gl_VertexID, vec4(from_start, 0, 0, 1)); + } + gl_PointSize = 10; + gl_Position = p3d_ModelViewProjectionMatrix * pos; + imageStore(positions, gl_VertexID, pos); + imageStore(velocities, gl_VertexID, vel); +} +""" + +frag = """ +#version 150 + +in float from_emission; // time elapsed from particle's emission +in vec4 color; +uniform float part_duration; // single particle's duration +uniform sampler2D image; // particle's texture +out vec4 p3d_FragData[1]; + +void main() { + vec4 col = texture(image, gl_PointCoord) * color; + // fade the particle considering the time from its emission + float alpha = clamp(1 - from_emission / part_duration, 0, 1); + p3d_FragData[0] = vec4(col.rgb, col.a * alpha); +} +""" + + +class Particle: + + def __init__( + self, + emitter, # the node which is emitting + texture, # particle's image + rate=.001, # the emission rate + gravity=-9.81, # z-component of the gravity force + vel=1.0, # length of emission vector + partDuration=1.0 # single particle's duration + ): + self.__emitter = emitter + self.__texture = texture + # let's compute the total number of particles + self.__numPart = int(round(partDuration * 1 / rate)) + self.__rate = rate + self.__gravity = gravity + self.__vel = vel + self.__partDuration = partDuration + self.__nodepath = render.attachNewNode(self.__node()) + self.__nodepath.setTransparency(True) # particles have alpha + self.__nodepath.setBin("fixed", 0) # render it at the end + self.__setTextures() + self.__setShader() + self.__nodepath.setRenderModeThickness(10) # we want sprite particles + self.__nodepath.setTexGen(TextureStage.getDefault(), + TexGenAttrib.MPointSprite) + self.__nodepath.setDepthWrite(False) # don't sort the particles + self.__upd_tsk = taskMgr.add(self.__update, "update") + + def __node(self): + # this function creates and returns particles' GeomNode + points = GeomPoints(GeomEnums.UH_static) + points.addNextVertices(self.__numPart) + format_ = GeomVertexFormat.getEmpty() + geom = Geom(GeomVertexData("abc", format_, GeomEnums.UH_static)) + geom.addPrimitive(points) + geom.setBounds(OmniBoundingVolume()) # always render it + node = GeomNode("node") + node.addGeom(geom) + return node + + def __setTextures(self): + # initial positions are all zeros (each position is denoted by 4 values) + # positions are stored in a texture + positions = [(0, 0, 0, 1) for i in range(self.__numPart)] + posLst = list(chain.from_iterable(positions)) + self.__texPos = self.__buffTex(posLst) + + # define emission times' texture + emissionTimes = [(self.__rate * i, 0, 0, 0) + for i in range(self.__numPart)] + timesLst = list(chain.from_iterable(emissionTimes)) + self.__texTimes = self.__buffTex(timesLst) + + # define a list with emission velocities + velocities = [self.__rndVel() for _ in range(self.__numPart)] + velLst = list(chain.from_iterable(velocities)) + # we need two textures, + # the first one contains the emission velocity (we need to keep it for + # particle recycling)... + self.__texStartVel = self.__buffTex(velLst) + # ... and the second one contains the current velocities + self.__texCurrVel = self.__buffTex(velLst) + + def __buffTex(self, values): + # this function returns a buffer texture with the received values + data = array("f", values) + tex = Texture("tex") + tex.setupBufferTexture(self.__numPart, Texture.T_float, + Texture.F_rgba32, GeomEnums.UH_static) + tex.setRamImage(data) + return tex + + def __rndVel(self): + # this method returns a random vector for emitting the particle + theta = uniform(0, pi / 12) + phi = uniform(0, 2 * pi) + vec = LVector3( + sin(theta) * cos(phi), + sin(theta) * sin(phi), + cos(theta)) + vec *= uniform(self.__vel * .8, self.__vel * 1.2) + return [vec.x, vec.y, vec.z, 1] + + def __setShader(self): + shader = Shader.make(Shader.SL_GLSL, vert, frag) + + # Apply the shader to the node, but set a special flag indicating that + # the point size is controlled bythe shader. + attrib = ShaderAttrib.make(shader) + attrib = attrib.setFlag(ShaderAttrib.F_shader_point_size, True) + self.__nodepath.setAttrib(attrib) + + self.__nodepath.setShaderInputs( + positions=self.__texPos, + emitter_pos=self.__emitter.getPos(render), + start_vel=self.__texStartVel, + velocities=self.__texCurrVel, + accel=(0, 0, self.__gravity), + start_time=globalClock.getFrameTime(), + emission_times=self.__texTimes, + part_duration=self.__partDuration, + image=loader.loadTexture(self.__texture)) + + def __update(self, task): + pos = self.__emitter.getPos(render) + self.__nodepath.setShaderInput("emitter_pos", pos) + return task.again + + +class ParticleDemo(ShowBase): + + def __init__(self): + loadPrcFileData("config", config) + + ShowBase.__init__(self) + + # Standard title and instruction text + self.title = OnscreenText( + text="Panda3D: Tutorial - Shader-based Particles", + parent=base.a2dBottomCenter, + style=1, fg=(1, 1, 1, 1), pos=(0, 0.1), scale=.08) + self.escapeEvent = OnscreenText( + text=HELP_TEXT, parent=base.a2dTopLeft, + style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.06), + align=TextNode.ALeft, scale=.05) + + # More standard initialization + self.accept('escape', sys.exit) + self.accept('arrow_left', self.rotate, ['left']) + self.accept('arrow_right', self.rotate, ['right']) + base.disableMouse() + base.camera.setPos(0, -20, 2) + base.setBackgroundColor(0, 0, 0) + + self.teapot = loader.loadModel("teapot") + self.teapot.setPos(0, 10, 0) + self.teapot.reparentTo(render) + self.setupLights() + + # we define a nodepath as particle's emitter + self.emitter = NodePath("emitter") + self.emitter.reparentTo(self.teapot) + self.emitter.setPos(3.000, 0.000, 2.550) + + # let's create the particle system + Particle(self.emitter, "smoke.png", gravity=.01, vel=1.2, + partDuration=5.0) + + def rotate(self, direction): + direction_factor = (1 if direction == "left" else -1) + self.teapot.setH(self.teapot.getH() + 10 * direction_factor) + + # Set up lighting + def setupLights(self): + ambientLight = AmbientLight("ambientLight") + ambientLight.setColor((.4, .4, .35, 1)) + directionalLight = DirectionalLight("directionalLight") + directionalLight.setDirection(LVector3(0, 8, -2.5)) + directionalLight.setColor((0.9, 0.8, 0.9, 1)) + + # Set lighting on teapot so steam doesn't get affected + self.teapot.setLight(self.teapot.attachNewNode(directionalLight)) + self.teapot.setLight(self.teapot.attachNewNode(ambientLight)) + + +demo = ParticleDemo() +demo.run() diff --git a/samples/roaming-ralph/main.py b/samples/roaming-ralph/main.py index 0290c3e74b..93ed173d5b 100755 --- a/samples/roaming-ralph/main.py +++ b/samples/roaming-ralph/main.py @@ -12,6 +12,7 @@ from direct.showbase.ShowBase import ShowBase from panda3d.core import CollisionTraverser, CollisionNode from panda3d.core import CollisionHandlerQueue, CollisionRay +from panda3d.core import CollisionHandlerPusher, CollisionSphere from panda3d.core import Filename, AmbientLight, DirectionalLight from panda3d.core import PandaNode, NodePath, Camera, TextNode from panda3d.core import CollideMask @@ -40,12 +41,15 @@ class RoamingRalphDemo(ShowBase): # Set up the window, camera, etc. ShowBase.__init__(self) - # Set the background color to black - self.win.setClearColor((0, 0, 0, 1)) - # This is used to store which keys are currently pressed. self.keyMap = { - "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0} + "left": 0, + "right": 0, + "forward": 0, + "backward": 0, + "cam-left": 0, + "cam-right": 0, + } # Post the instructions self.title = addTitle( @@ -54,8 +58,9 @@ class RoamingRalphDemo(ShowBase): self.inst2 = addInstructions(0.12, "[Left Arrow]: Rotate Ralph Left") self.inst3 = addInstructions(0.18, "[Right Arrow]: Rotate Ralph Right") self.inst4 = addInstructions(0.24, "[Up Arrow]: Run Ralph Forward") - self.inst6 = addInstructions(0.30, "[A]: Rotate Camera Left") - self.inst7 = addInstructions(0.36, "[S]: Rotate Camera Right") + self.inst5 = addInstructions(0.30, "[Down Arrow]: Walk Ralph Backward") + self.inst6 = addInstructions(0.36, "[A]: Rotate Camera Left") + self.inst7 = addInstructions(0.42, "[S]: Rotate Camera Right") # Set up the environment # @@ -72,6 +77,9 @@ class RoamingRalphDemo(ShowBase): self.environ = loader.loadModel("models/world") self.environ.reparentTo(render) + # We do not have a skybox, so we will just use a sky blue background color + self.setBackgroundColor(0.53, 0.80, 0.92, 1) + # Create the main character, Ralph ralphStartPos = self.environ.find("**/start_point").getPos() @@ -80,7 +88,7 @@ class RoamingRalphDemo(ShowBase): "walk": "models/ralph-walk"}) self.ralph.reparentTo(render) self.ralph.setScale(.2) - self.ralph.setPos(ralphStartPos + (0, 0, 0.5)) + self.ralph.setPos(ralphStartPos + (0, 0, 1.5)) # Create a floater object, which floats 2 units above ralph. We # use this as a target for the camera to look at. @@ -95,31 +103,51 @@ class RoamingRalphDemo(ShowBase): self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) + self.accept("arrow_down", self.setKey, ["backward", True]) self.accept("a", self.setKey, ["cam-left", True]) self.accept("s", self.setKey, ["cam-right", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) + self.accept("arrow_down-up", self.setKey, ["backward", False]) self.accept("a-up", self.setKey, ["cam-left", False]) self.accept("s-up", self.setKey, ["cam-right", False]) taskMgr.add(self.move, "moveTask") - # Game state variables - self.isMoving = False - # Set up the camera self.disableMouse() self.camera.setPos(self.ralph.getX(), self.ralph.getY() + 10, 2) + self.cTrav = CollisionTraverser() + + # Use a CollisionHandlerPusher to handle collisions between Ralph and + # the environment. Ralph is added as a "from" object which will be + # "pushed" out of the environment if he walks into obstacles. + # + # Ralph is composed of two spheres, one around the torso and one + # around the head. They are slightly oversized since we want Ralph to + # keep some distance from obstacles. + self.ralphCol = CollisionNode('ralph') + self.ralphCol.addSolid(CollisionSphere(center=(0, 0, 2), radius=1.5)) + self.ralphCol.addSolid(CollisionSphere(center=(0, -0.25, 4), radius=1.5)) + self.ralphCol.setFromCollideMask(CollideMask.bit(0)) + self.ralphCol.setIntoCollideMask(CollideMask.allOff()) + self.ralphColNp = self.ralph.attachNewNode(self.ralphCol) + self.ralphPusher = CollisionHandlerPusher() + self.ralphPusher.horizontal = True + + # Note that we need to add ralph both to the pusher and to the + # traverser; the pusher needs to know which node to push back when a + # collision occurs! + self.ralphPusher.addCollider(self.ralphColNp, self.ralph) + self.cTrav.addCollider(self.ralphColNp, self.ralphPusher) + # We will detect the height of the terrain by creating a collision # ray and casting it downward toward the terrain. One ray will # start above ralph's head, and the other will start above the camera. # A ray may hit the terrain, or it may hit a rock or a tree. If it - # hits the terrain, we can detect the height. If it hits anything - # else, we rule that the move is illegal. - self.cTrav = CollisionTraverser() - + # hits the terrain, we can detect the height. self.ralphGroundRay = CollisionRay() self.ralphGroundRay.setOrigin(0, 0, 9) self.ralphGroundRay.setDirection(0, 0, -1) @@ -143,7 +171,7 @@ class RoamingRalphDemo(ShowBase): self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler) # Uncomment this line to see the collision rays - #self.ralphGroundColNp.show() + #self.ralphColNp.show() #self.camGroundColNp.show() # Uncomment this line to show a visual representation of the @@ -181,11 +209,6 @@ class RoamingRalphDemo(ShowBase): if self.keyMap["cam-right"]: self.camera.setX(self.camera, +20 * dt) - # save ralph's initial position so that we can restore it, - # in case he falls off the map or runs into something. - - startpos = self.ralph.getPos() - # If a move-key is pressed, move ralph in the specified direction. if self.keyMap["left"]: @@ -193,17 +216,28 @@ class RoamingRalphDemo(ShowBase): if self.keyMap["right"]: self.ralph.setH(self.ralph.getH() - 300 * dt) if self.keyMap["forward"]: - self.ralph.setY(self.ralph, -25 * dt) + self.ralph.setY(self.ralph, -20 * dt) + if self.keyMap["backward"]: + self.ralph.setY(self.ralph, +10 * dt) # If ralph is moving, loop the run animation. # If he is standing still, stop the animation. + currentAnim = self.ralph.getCurrentAnim() - if self.keyMap["forward"] or self.keyMap["left"] or self.keyMap["right"]: - if self.isMoving is False: + if self.keyMap["forward"]: + if currentAnim != "run": self.ralph.loop("run") - self.isMoving = True + elif self.keyMap["backward"]: + # Play the walk animation backwards. + if currentAnim != "walk": + self.ralph.loop("walk") + self.ralph.setPlayRate(-1.0, "walk") + elif self.keyMap["left"] or self.keyMap["right"]: + if currentAnim != "walk": + self.ralph.loop("walk") + self.ralph.setPlayRate(1.0, "walk") else: - if self.isMoving: + if currentAnim is not None: self.ralph.stop() self.ralph.pose("walk", 5) self.isMoving = False @@ -228,25 +262,24 @@ class RoamingRalphDemo(ShowBase): #self.cTrav.traverse(render) # Adjust ralph's Z coordinate. If ralph's ray hit terrain, - # update his Z. If it hit anything else, or didn't hit anything, put - # him back where he was last frame. + # update his Z - entries = list(self.ralphGroundHandler.getEntries()) + entries = list(self.ralphGroundHandler.entries) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) - if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": - self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()) - else: - self.ralph.setPos(startpos) + for entry in entries: + if entry.getIntoNode().getName() == "terrain": + self.ralph.setZ(entry.getSurfacePoint(render).getZ()) - # Keep the camera at one foot above the terrain, - # or two feet above ralph, whichever is greater. + # Keep the camera at one unit above the terrain, + # or two units above ralph, whichever is greater. - entries = list(self.camGroundHandler.getEntries()) + entries = list(self.camGroundHandler.entries) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) - if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": - self.camera.setZ(entries[0].getSurfacePoint(render).getZ() + 1.0) + for entry in entries: + if entry.getIntoNode().getName() == "terrain": + self.camera.setZ(entry.getSurfacePoint(render).getZ() + 1.5) if self.camera.getZ() < self.ralph.getZ() + 2.0: self.camera.setZ(self.ralph.getZ() + 2.0) diff --git a/samples/roaming-ralph/models/world.egg.pz b/samples/roaming-ralph/models/world.egg.pz index 9ab9439f74..9a1d67ea03 100644 Binary files a/samples/roaming-ralph/models/world.egg.pz and b/samples/roaming-ralph/models/world.egg.pz differ diff --git a/samples/shader-terrain/main.py b/samples/shader-terrain/main.py index 10bffb3767..3d88331ae9 100644 --- a/samples/shader-terrain/main.py +++ b/samples/shader-terrain/main.py @@ -20,6 +20,14 @@ class ShaderTerrainDemo(ShowBase): textures-power-2 none gl-coordinate-system default window-title Panda3D ShaderTerrainMesh Demo + + # As an optimization, set this to the maximum number of cameras + # or lights that will be rendering the terrain at any given time. + stm-max-views 8 + + # Further optimize the performance by reducing this to the max + # number of chunks that will be visible at any given time. + stm-max-chunk-count 2048 """) # Initialize the showbase diff --git a/setup.cfg b/setup.cfg index a2b7a81fa5..ddaadf4f79 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = Panda3D -version = 1.10.0 +version = 1.11.0 url = https://www.panda3d.org/ description = Panda3D is a framework for 3D rendering and game development for Python and C++ programs. license = Modified BSD License @@ -13,12 +13,29 @@ classifiers = Operating System :: OS Independent Programming Language :: C++ Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.4 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: Implementation :: CPython Topic :: Games/Entertainment Topic :: Multimedia Topic :: Multimedia :: Graphics Topic :: Multimedia :: Graphics :: 3D Rendering + Topic :: Software Development :: Libraries + Topic :: Software Development :: Libraries :: Application Frameworks + Topic :: Software Development :: Libraries :: Python Modules author = Panda3D Team author_email = etc-panda3d@lists.andrew.cmu.edu +project_urls = + Source=https://github.com/panda3d/panda3d + Tracker=https://github.com/panda3d/panda3d/issues + Documentation=https://www.panda3d.org/manual/ + Funding=https://opencollective.com/panda3d [tool:pytest] testpaths = tests diff --git a/tests/build_samples.py b/tests/build_samples.py index 222a7df354..4622fd87bc 100644 --- a/tests/build_samples.py +++ b/tests/build_samples.py @@ -1,6 +1,7 @@ import os import subprocess import sys +import tempfile SAMPLES_TO_BUILD = [ 'asteroids', @@ -8,13 +9,24 @@ SAMPLES_TO_BUILD = [ SAMPLES_DIR = os.path.join(os.path.dirname(__file__), '..', 'samples') def main(): + build_base = tempfile.TemporaryDirectory() + dist_dir = tempfile.TemporaryDirectory() + for sample in SAMPLES_TO_BUILD: sampledir = os.path.join(SAMPLES_DIR, sample) os.chdir(sampledir) + args = [ + sys.executable, + 'setup.py', + 'bdist_apps', + '--build-base', build_base.name, + '--dist-dir', dist_dir.name, + ] + # This will raise a CalledProcessError if the build fails, which will cause # this script to fail - subprocess.check_call([sys.executable, 'setup.py', 'bdist_apps']) + subprocess.check_call(args) if __name__ == '__main__': main() diff --git a/tests/bullet/__init__.py b/tests/bullet/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/bullet/conftest.py b/tests/bullet/conftest.py new file mode 100644 index 0000000000..9c2750be38 --- /dev/null +++ b/tests/bullet/conftest.py @@ -0,0 +1,71 @@ +import pytest + +@pytest.fixture +def world(): + bullet = pytest.importorskip("panda3d.bullet") + world = bullet.BulletWorld() + + return world + +def simulate_until(world, cond, dt=1.0/60, limit=600): + for x in range(limit): + world.do_physics(dt) + if cond(): + return True + + return False + +@pytest.fixture +def scene(world): + """ + This test scene contains a "ramp" which slopes down at a 45-degree angle at + locations where X<0, and is flat at X>0. A bowling ball is placed above the + ramp at X=-5, Z=7. A stack of 2 very light boxes is placed at X=5, Z=0. + The world is given gravity. + + When running the Bullet simulation, the ball should fall, travel down the + ramp, and hit the lower box with enough force to displace it and cause the + upper box to topple to the ground. + """ + + core = pytest.importorskip("panda3d.core") + bullet = pytest.importorskip("panda3d.bullet") + + bodies = [] + + ramp = bullet.BulletRigidBodyNode('ramp') + ramp.add_shape(bullet.BulletPlaneShape(core.Vec4(0, 0, 1, 0))) + ramp.add_shape(bullet.BulletPlaneShape(core.Vec4(1, 0, 1, 0).normalized())) + bodies.append(ramp) + + ball = bullet.BulletRigidBodyNode('ball') + ball.add_shape(bullet.BulletSphereShape(1)) + ball.set_mass(100) + ball.set_transform(core.TransformState.make_pos(core.Point3(-5, 0, 7))) + bodies.append(ball) + + lower_box = bullet.BulletRigidBodyNode('lower_box') + lower_box.add_shape(bullet.BulletBoxShape(core.Vec3(2.5))) + lower_box.set_mass(1) + lower_box.set_transform(core.TransformState.make_pos(core.Point3(5, 0, 2.5))) + bodies.append(lower_box) + + upper_box = bullet.BulletRigidBodyNode('upper_box') + upper_box.add_shape(bullet.BulletBoxShape(core.Vec3(2.5))) + upper_box.set_mass(1) + upper_box.set_transform(core.TransformState.make_pos(core.Point3(5, 0, 7.5))) + bodies.append(upper_box) + + world.set_gravity(core.Vec3(0, 0, -9.8)) + + scene = core.NodePath('scene') + for body in bodies: + scene.attach_new_node(body) + world.attach(body) + + yield scene + + scene.remove_node() + + for body in bodies: + world.remove(body) diff --git a/tests/bullet/test_bullet_callbacks.py b/tests/bullet/test_bullet_callbacks.py new file mode 100644 index 0000000000..3efd39a306 --- /dev/null +++ b/tests/bullet/test_bullet_callbacks.py @@ -0,0 +1,79 @@ +import pytest +from .conftest import simulate_until + +# Skip these tests if we can't import bullet. +bullet = pytest.importorskip("panda3d.bullet") +from panda3d import core + +bullet_filter_algorithm = core.ConfigVariableString('bullet-filter-algorithm') + + +def test_tick(world): + fired = [] + + def callback(cd): + fired.append(isinstance(cd, bullet.BulletTickCallbackData)) + + world.set_tick_callback(callback, False) + + assert fired == [] + + world.do_physics(0.1) + + assert fired == [True] + + world.clear_tick_callback() + + world.do_physics(0.1) + + assert fired == [True] + +@pytest.mark.skipif(bullet_filter_algorithm != 'callback', reason='bullet-filter-algorithm not set to callback') +def test_filter(world, scene): + # This is very similar to the basic physics test, but we're using + # a filter callback to prevent collisions between the lower box and ball. + # This should have the effect of the ball rolling to X>+10 without the + # upper box falling at all. + + def callback(cd): + assert isinstance(cd, bullet.BulletFilterCallbackData) + if {cd.node_0.name, cd.node_1.name} == {'ball', 'lower_box'}: + # ball<->lower_box collisions are excluded + cd.collide = False + else: + # Everything else can collide + cd.collide = True + + world.set_filter_callback(callback) + + ball = scene.find('**/ball') + assert simulate_until(world, lambda: ball.get_x() > 10) + + # The upper box shouldn't fall + upper_box = scene.find('**/upper_box') + assert not simulate_until(world, lambda: upper_box.get_z() < 5) + +def test_contact(world, scene): + # This just runs the basic physics test, but detects the toppling of the + # upper box by a contact between upper_box<->ramp + + contacts = [] + + def callback(cd): + assert isinstance(cd, bullet.BulletContactCallbackData) + if {cd.node0.name, cd.node1.name} == {'upper_box', 'ramp'}: + if not contacts: + contacts.append(True) + + world.set_contact_added_callback(callback) + + ball = scene.find('**/ball') + ramp = scene.find('**/ramp') + + ball.node().notify_collisions(True) + ramp.node().notify_collisions(True) + + assert simulate_until(world, lambda: ball.get_x() > 0) + + # Now we wait for the upper box to topple + assert simulate_until(world, lambda: bool(contacts)) diff --git a/tests/bullet/test_bullet_heightfield.py b/tests/bullet/test_bullet_heightfield.py new file mode 100644 index 0000000000..28174b93ec --- /dev/null +++ b/tests/bullet/test_bullet_heightfield.py @@ -0,0 +1,45 @@ +import pytest +# Skip these tests if we can't import bullet. +bullet = pytest.importorskip("panda3d.bullet") + +from panda3d.bullet import BulletWorld, BulletRigidBodyNode, ZUp +from panda3d.bullet import BulletHeightfieldShape, BulletSphereShape +from panda3d.core import NodePath, PNMImage + + +def make_node(name, BulletShape, *args): + # Returns a BulletRigidBodyNode for the given shape + shape = BulletShape(*args) + node = BulletRigidBodyNode(name) + node.add_shape(shape) + return node + + +def test_sphere_into_heightfield(): + root = NodePath("root") + world = BulletWorld() + # Create PNMImage to construct Heightfield with + img = PNMImage(10, 10, 1) + img.fill_val(255) + # Make our nodes + heightfield = make_node("Heightfield", BulletHeightfieldShape, img, 1, ZUp) + sphere = make_node("Sphere", BulletSphereShape, 1) + # Attach to world + np1 = root.attach_new_node(sphere) + np1.set_pos(0, 0, 1) + world.attach(sphere) + + np2 = root.attach_new_node(heightfield) + np2.set_pos(0, 0, 0) + world.attach(heightfield) + + assert world.get_num_rigid_bodies() == 2 + test = world.contact_test_pair(sphere, heightfield) + assert test.get_num_contacts() > 0 + assert test.get_contact(0).get_node0() == sphere + assert test.get_contact(0).get_node1() == heightfield + + # Increment sphere's Z coordinate, no longer colliding + np1.set_pos(0, 0, 2) + test = world.contact_test_pair(sphere, heightfield) + assert test.get_num_contacts() == 0 diff --git a/tests/bullet/test_bullet_simulation.py b/tests/bullet/test_bullet_simulation.py new file mode 100644 index 0000000000..439d3b47e5 --- /dev/null +++ b/tests/bullet/test_bullet_simulation.py @@ -0,0 +1,64 @@ +import pytest +from .conftest import simulate_until + +# Skip these tests if we can't import bullet. +bullet = pytest.importorskip("panda3d.bullet") +from panda3d import core + + +def test_basics(world, scene): + # N.B. see `scene` fixture's docstring in conftest.py to understand what's + # being simulated here + + # Step forward until the ball crosses the threshold + ball = scene.find('**/ball') + assert simulate_until(world, lambda: ball.get_x() >= 0) + + # Continue simulating until upper box falls + upper_box = scene.find('**/upper_box') + assert upper_box.get_z() > 5.0 + assert simulate_until(world, lambda: upper_box.get_z() < 5.0) + +def test_restitution(world, scene): + ball = scene.find('**/ball') + scene.find('**/ramp').node().restitution = 1.0 + + for with_bounce in (False, True): + # Reset ball + ball.node().set_angular_velocity(core.Vec3(0)) + ball.node().set_linear_velocity(core.Vec3(0)) + ball.set_pos(-2, 0, 100) + + ball.node().restitution = 1.0 * with_bounce + + # Simulate until ball rolls/bounces across Y axis + assert simulate_until(world, lambda: ball.get_x() >= 0) + + if with_bounce: + # The ball bounced across, so it should be off the ground a bit + assert ball.get_z() > 1.2 + else: + # The ball rolled, so it should be on the ground + assert ball.get_z() < 1.2 + +def test_friction(world, scene): + ball = scene.find('**/ball') + + for with_friction in (False, True): + # Reset ball, give it a huge negative (CCW) spin about the X axis so + # it'll roll in +Y direction if there's any friction + ball.node().set_angular_velocity(core.Vec3(-1000,0,0)) + ball.node().set_linear_velocity(core.Vec3(0)) + ball.set_pos(-2, 0, 5) + + ball.node().friction = 1.0 * with_friction + + # Simulate until ball crosses Y axis + assert simulate_until(world, lambda: ball.get_x() >= 0) + + if with_friction: + # The ball had friction, so should've gone off in the +Y direction + assert ball.get_y() > 1 + else: + # No friction means the Y axis should be unaffected + assert abs(ball.get_y()) < 0.1 diff --git a/tests/collide/collisions.py b/tests/collide/collisions.py new file mode 100644 index 0000000000..cca2a04215 --- /dev/null +++ b/tests/collide/collisions.py @@ -0,0 +1,30 @@ +from panda3d.core import CollisionNode, NodePath +from panda3d.core import CollisionTraverser, CollisionHandlerQueue +from panda3d.core import CollisionSphere, CollisionInvSphere, CollisionBox, CollisionPolygon, CollisionCapsule +from panda3d.core import CollisionLine, CollisionRay, CollisionSegment, CollisionParabola +from panda3d.core import CollisionPlane +from panda3d.core import Point3, Vec3, Plane, LParabola + + +def make_collision(solid_from, solid_into): + node_from = CollisionNode("from") + node_from.add_solid(solid_from) + node_into = CollisionNode("into") + node_into.add_solid(solid_into) + + root = NodePath("root") + trav = CollisionTraverser() + queue = CollisionHandlerQueue() + + np_from = root.attach_new_node(node_from) + np_into = root.attach_new_node(node_into) + + trav.add_collider(np_from, queue) + trav.traverse(root) + + entry = None + for e in queue.get_entries(): + if e.get_into() == solid_into: + entry = e + + return (entry, np_from, np_into) diff --git a/tests/collide/test_into_box.py b/tests/collide/test_into_box.py new file mode 100644 index 0000000000..0259e1d4a2 --- /dev/null +++ b/tests/collide/test_into_box.py @@ -0,0 +1,116 @@ +from collisions import * + + +def test_sphere_into_box(): + sphere = CollisionSphere(0, 0, 4, 3) + box = CollisionBox((0, 0, 0), 2, 3, 4) + entry = make_collision(sphere, box)[0] + assert entry is not None + assert entry.get_from() == sphere + assert entry.get_into() == box + + # Colliding just on the edge + entry, np_from, np_into = make_collision(CollisionSphere(0, 0, 10, 6), box) + assert entry.get_surface_point(np_from) == Point3(0, 0, 4) + assert entry.get_surface_normal(np_into) == Vec3(0, 0, 1) # Testing surface normal + + # No collision + entry = make_collision(CollisionSphere(100, 100, 100, 100), box)[0] + assert entry is None + + +def test_plane_into_box(): + # CollisionPlane is not a 'from' object + plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, 0))) + box = CollisionBox((0, 0, 0), 2, 3, 4) + + entry = make_collision(plane, box)[0] + assert entry is None + + +def test_ray_into_box(): + ray = CollisionRay(1, 1, 1, 0, 1, 0) + box = CollisionBox((0, 0, 0), 3, 3, 5) + entry = make_collision(ray, box)[0] + assert entry is not None + assert entry.get_from() == ray + assert entry.get_into() == box + + # Colliding just on the edge + entry, np_from, np_into = make_collision(CollisionRay(3, 3, 0, 1, -1, 0), box) + assert entry.get_surface_point(np_from) == Point3(3, 3, 0) + + # No collision + entry = make_collision(CollisionRay(0, 0, 100, 1, 0, 0), box)[0] + assert entry is None + + +def test_parabola_into_box(): + # Set up Parabola and Box + parabola = CollisionParabola() + parabola.set_t1(0) + parabola.set_t2(2) + box = CollisionBox((0,0,0), 3, 3, 3) + + # Parabola is inside the Box and not colliding + parabola.set_parabola( + LParabola((-1, 0, -1), (1, 0, 1), (1, 1, 1))) + entry = make_collision(parabola, box)[0] + assert entry is None + + # Parabola is inside the Box and colliding on its projectile + parabola.set_parabola( + LParabola((0, 0, 1), (0, 0, 1), (1, 1, 1))) + # Parabola collides with Box when t == 1 at point (1, 1, 3) + assert parabola.get_parabola().calc_point(1) == (1, 1, 3) + entry, np_from, into = make_collision(parabola, box) + assert entry.get_surface_point(np_from) == (1, 1, 3) + assert entry.get_from() == parabola + assert entry.get_into() == box + + # Parabola is inside the Box and colliding on one of the endpoints + parabola.set_parabola( + LParabola((0, 0, 0), (0, 0, 1), (-3, 0, -3))) + entry, np_from, np_into = make_collision(parabola, box) + assert entry.get_surface_point(np_from) == (-3, 0, -3) + + # Parabola is outside the Box and not colliding + parabola.set_parabola( + LParabola((0, 0, 0), (0, 0, 1), (-5, 0, 0))) + entry = make_collision(parabola, box)[0] + assert entry is None + + # Parabola is outside the Box and colliding on its projecticle + parabola.set_parabola( + LParabola((-2, -2, -2), (1, 1, 1), (4, 4, 4))) + # Parabola collides with Box when t == 1 at point (3, 3, 3) + assert parabola.get_parabola().calc_point(1) == (3, 3, 3) + entry, np_from, into = make_collision(parabola, box) + assert entry.get_surface_point(np_from) == (3, 3, 3) + + # Parabola is outside the Box and colliding on the first endpoint + parabola.set_parabola( + LParabola((1, 1, 1), (1, 1, 1), (3, 3, 3))) + entry, np_from, np_into = make_collision(parabola, box) + assert entry.get_surface_point(np_from) == (3, 3, 3) + + # Parabola is outside the Box and colliding on the second endpoint + parabola.set_parabola( + LParabola((1, 0, 1), (-1, 0, -1), (-5, -3, -5))) + assert parabola.get_parabola().calc_point(2) == (-3, -3, -3) + entry, np_from, np_into = make_collision(parabola, box) + assert entry.get_surface_point(np_from) == (-3, -3, -3) + + # Parabola intersects the Box at two points, + # t == 0 and t == 2 the earliest one should be chosen. + parabola.set_parabola( + LParabola((-1, -1, -1), (-1, -1, -1), (3, 3, 3))) + entry, np_from, np_into = make_collision(parabola, box) + assert entry.get_surface_point(np_from) == parabola.get_parabola().calc_point(0) + + # First point no longer intersecting + parabola.set_t1(1) + entry, np_from, np_into = make_collision(parabola, box) + assert parabola.get_parabola().calc_point(2) == (-3, -3, -3) + assert entry.get_surface_point(np_from) == parabola.get_parabola().calc_point(2) + assert entry.get_surface_normal(np_from) is not None diff --git a/tests/collide/test_into_capsule.py b/tests/collide/test_into_capsule.py new file mode 100644 index 0000000000..c99953f595 --- /dev/null +++ b/tests/collide/test_into_capsule.py @@ -0,0 +1,39 @@ +from collisions import * + +def test_box_into_capsule(): + capsule = CollisionCapsule((1, 0, 0), (-1, 0, 0), .5) + + # colliding just on the capsule's cylinder + box = CollisionBox((0, 1, 0), .5, .5, .5) + entry = make_collision(box, capsule)[0] + assert entry is not None + + # no longer colliding + box = CollisionBox((0, 1.1, 0), .5, .5, .5) + entry = make_collision(box, capsule)[0] + assert entry is None + + # box is inside the capsule's cylinder + box = CollisionBox((0, .8, 0), .5, .5, .5) + entry = make_collision(box, capsule)[0] + assert entry is not None + + # colliding with the first endcap + box = CollisionBox((2, 0, 0), .5, .5, .5) + entry = make_collision(box, capsule)[0] + assert entry is not None + + # almost colliding with first endcap + box = CollisionBox((2.01, 0, 0), .5, .5, .5) + entry = make_collision(box, capsule)[0] + assert entry is None + + # colliding with the second endcap + box = CollisionBox((-2, 0, 0), .5, .5, .5) + entry = make_collision(box, capsule)[0] + assert entry is not None + + # almost colliding with second endcap + box = CollisionBox((-2.01, 0, 0), .5, .5, .5) + entry = make_collision(box, capsule)[0] + assert entry is None diff --git a/tests/collide/test_into_invsphere.py b/tests/collide/test_into_invsphere.py new file mode 100644 index 0000000000..c728d7caf2 --- /dev/null +++ b/tests/collide/test_into_invsphere.py @@ -0,0 +1,46 @@ +from collisions import * +from pytest import approx + + +def test_parabola_into_invsphere(): + invsphere = CollisionInvSphere(1, 1, 1, 5) + parabola = CollisionParabola() + parabola.set_t1(0) + parabola.set_t2(2) + + # parabola starts from outside the sphere + parabola.set_parabola( + LParabola((1, 1, 1), (0, 0, 0), (7, 7, 7))) + entry, np_from, np_into = make_collision(parabola, invsphere) + assert (entry.get_surface_point(np_from) - + invsphere.get_center()).length() == approx(invsphere.get_radius()) + + # parabola starts on the sphere + parabola.set_parabola( + LParabola((1, 0, 1), (1, 0, 0), (1, 1, 6))) + entry, np_from, np_into = make_collision(parabola, invsphere) + assert entry.get_surface_point(np_from) == (1, 1, 6) + + # parabola starts from inside the sphere but doesn't collide + parabola.set_parabola( + LParabola((-1, -1, -1), (1, 1, 1), (1, 1, 1))) + entry = make_collision(parabola, invsphere)[0] + assert entry is None + + # parabola is inside the sphere and collides on an endpoint + parabola.set_parabola( + LParabola((1, 0, 0), (0, 0, 0), (2, 1, 1))) + entry, np_from, np_into = make_collision(parabola, invsphere) + assert entry.get_surface_point(np_from) == (6, 1, 1) + + # parabola starts from inside the sphere and collides on its projectile + parabola.set_t2(3) + assert parabola.get_parabola().calc_point(2) == (6, 1, 1) + entry, np_from, np_into = make_collision(parabola, invsphere) + assert entry.get_surface_point(np_from) == (6, 1, 1) + + parabola.set_parabola( + LParabola((-1, 0, 0), (-1, 0, 0), (2, 1, 1))) + assert parabola.get_parabola().calc_point(2) == (-4, 1, 1) + entry, np_from, np_into = make_collision(parabola, invsphere) + assert entry.get_surface_point(np_from) == (-4, 1, 1) diff --git a/tests/collide/test_into_lines.py b/tests/collide/test_into_lines.py new file mode 100644 index 0000000000..9787558a3a --- /dev/null +++ b/tests/collide/test_into_lines.py @@ -0,0 +1,24 @@ +# Testing that all variants of CollisionLine +# cannot be used as "into" objects +from collisions import * + + +def test_sphere_into_line(): + entry = make_collision(CollisionSphere(0, 0, 0, 3), CollisionLine(0, 0, 0, 1, 0, 0))[0] + assert entry is None + + +def test_sphere_into_ray(): + entry = make_collision(CollisionSphere(0, 0, 0, 3), CollisionRay(0, 0, 0, 3, 3, 3))[0] + assert entry is None + + +def test_sphere_into_segment(): + entry = make_collision(CollisionSphere(0, 0, 0, 3), CollisionSegment(0, 0, 0, 3, 3, 3))[0] + assert entry is None + + +def test_sphere_into_parabola(): + parabola = LParabola((1, 0, 0), (0, 1, 0), (0, 0, 1)) + entry = make_collision(CollisionSphere(0, 0, 0, 3), CollisionParabola(parabola, 1, 2))[0] + assert entry is None diff --git a/tests/collide/test_into_poly.py b/tests/collide/test_into_poly.py new file mode 100644 index 0000000000..ccfd15fcd1 --- /dev/null +++ b/tests/collide/test_into_poly.py @@ -0,0 +1,48 @@ +from collisions import * + + +def test_box_into_poly(): + box = CollisionBox((0, 0, 0), 2, 3, 4) + poly = CollisionPolygon(Point3(0, 0, 0), Point3(0, 0, 1), Point3(0, 1, 1), Point3(0, 1, 0)) + + entry = make_collision(box, poly)[0] + assert entry is not None + assert entry.get_from() == box + assert entry.get_into() == poly + + # Colliding just on the edge + entry, np_from, np_into = make_collision(CollisionBox((0, 3, 0), 1, 2, 1), poly) + assert entry.get_surface_point(np_from) == Point3(0, 3, 0) + assert entry.get_surface_normal(np_into) == Vec3(-1, 0, 0) # Testing surface normal + + # No collision + entry = make_collision(CollisionBox((10, 10, 10), 8, 9, 10), poly)[0] + assert entry is None + + +def test_sphere_into_poly(): + sphere = CollisionSphere(0, 0, 0, 1) + poly = CollisionPolygon(Point3(0, 0, 0), Point3(0, 0, 1), Point3(0, 1, 1), Point3(0, 1, 0)) + + entry = make_collision(sphere, poly)[0] + assert entry is not None + assert entry.get_from() == sphere + assert entry.get_into() == poly + + # Colliding just on the edge + entry, np_from, np_into = make_collision(CollisionSphere(0, 0, 3, 2), poly) + assert entry.get_surface_point(np_from) == Point3(0, 0, 3) + assert entry.get_surface_normal(np_into) == Vec3(-1, 0, 0) # Testing surface normal + + # No collision + entry = make_collision(CollisionSphere(100, 100, 100, 100), poly)[0] + assert entry is None + + +def test_plane_into_poly(): + # CollisionPlane is not a 'from' object + plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, 0))) + poly = CollisionPolygon(Point3(0, 0, 0), Point3(0, 0, 1), Point3(0, 1, 1), Point3(0, 1, 0)) + + entry = make_collision(plane, poly)[0] + assert entry is None diff --git a/tests/collide/test_into_sphere.py b/tests/collide/test_into_sphere.py new file mode 100644 index 0000000000..b59764d871 --- /dev/null +++ b/tests/collide/test_into_sphere.py @@ -0,0 +1,89 @@ +from collisions import * + + +def test_sphere_into_sphere(): + sphere1 = CollisionSphere(0, 0, 3, 3) + sphere2 = CollisionSphere(0, 0, 0, 3) + + entry = make_collision(sphere1, sphere2)[0] + assert entry is not None + assert entry.get_from() == sphere1 + assert entry.get_into() == sphere2 + + # Colliding just on the edge + entry, np_from, np_into = make_collision(CollisionSphere(0, 0, 10, 7), sphere2) + assert entry.get_surface_point(np_from) == Point3(0, 0, 3) + assert entry.get_surface_normal(np_into) == Vec3(0, 0, 1) # Testing surface normal + + # No collision + entry = make_collision(CollisionSphere(0, 0, 10, 6), sphere2)[0] + assert entry is None + + +def test_box_into_sphere(): + box = CollisionBox((0, 0, 0), 2, 3, 4) + sphere = CollisionSphere(0, 0, 0, 3) + + entry = make_collision(box, sphere)[0] + assert entry is not None + assert entry.get_from() == box + assert entry.get_into() == sphere + + # Colliding just on the edge + entry, np_from, np_into = make_collision(CollisionBox((0, 0, 10), 6, 6, 7), sphere) + assert entry.get_surface_point(np_from) == Point3(0, 0, 3) + assert entry.get_surface_normal(np_into) == Vec3(0, 0, 1) # Testing surface normal + + # No collision + entry = make_collision(CollisionBox((0, 0, 10), 6, 6, 6), sphere)[0] + assert entry is None + + +def test_capsule_into_sphere(): + # First test a sphere that is fully touching the inner line of the capsule + capsule = CollisionCapsule((0, 0, 1.0), (10, 0, 1.0), 1.0) + sphere = CollisionSphere(5, 0, 1.5, 1.0) + + entry = make_collision(capsule, sphere)[0] + assert entry is not None + assert entry.get_from() == capsule + assert entry.get_into() == sphere + + # Now test one that merely grazes. + entry = make_collision(CollisionCapsule((0, 0, 0), (10, 0, 0), 1.0), sphere)[0] + assert entry is not None + + # No collision + entry = make_collision(CollisionCapsule((0, 0, 0), (10, 0, 0), 0.25), sphere)[0] + assert entry is None + + # Degenerate case: capsule is actually a sphere. + entry = make_collision(CollisionCapsule((5, 0, 0), (5, 0, 0), 1.0), sphere)[0] + assert entry is not None + + # Degenerate case, but not colliding. + entry = make_collision(CollisionCapsule((5, 0, 0), (5, 0, 0), 0.25), sphere)[0] + assert entry is None + + +def test_segment_into_sphere(): + segment = CollisionSegment((0, 0, 0), (10, 0, 0)) + sphere = CollisionSphere(5, 0, 0.5, 1.0) + + entry = make_collision(segment, sphere)[0] + assert entry is not None + assert entry.get_from() == segment + assert entry.get_into() == sphere + + # No collision + entry = make_collision(CollisionSegment((0, 0, 0), (3, 0, 0)), sphere)[0] + assert entry is None + + +def test_plane_into_sphere(): + # CollisionPlane is not a 'from' object + plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, 0))) + sphere = CollisionSphere(0, 0, 0, 1) + + entry = make_collision(plane, sphere)[0] + assert entry is None diff --git a/tests/display/cg_bad.sha b/tests/display/cg_bad.sha new file mode 100644 index 0000000000..7f3a77eda4 --- /dev/null +++ b/tests/display/cg_bad.sha @@ -0,0 +1,21 @@ +//Cg +// + +void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord0 : TEXCOORD0, + uniform float4x4 mat_modelproj, + out float4 l_position : POSITION, + out float2 l_texcoord0 : TEXCOORD0) +{ + l_position = mul(mat_modelproj, vtx_position); + l_texcoord0 = vtx_texcoord0; +} + +void fshader(float2 l_texcoord0 : TEXCOORD0, + uniform sampler2D tex_0 : TEXUNIT0, + out float4 o_color : COLOR) +{ + float4 texColor = tex2D(tex_0, l_texcoord0); + does_not_exist = texColor * 2 * (texColor.w - 0.5); +} + diff --git a/tests/display/cg_simple.sha b/tests/display/cg_simple.sha new file mode 100644 index 0000000000..64e419fd0e --- /dev/null +++ b/tests/display/cg_simple.sha @@ -0,0 +1,21 @@ +//Cg +// + +void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord0 : TEXCOORD0, + uniform float4x4 mat_modelproj, + out float4 l_position : POSITION, + out float2 l_texcoord0 : TEXCOORD0) +{ + l_position = mul(mat_modelproj, vtx_position); + l_texcoord0 = vtx_texcoord0; +} + +void fshader(float2 l_texcoord0 : TEXCOORD0, + uniform sampler2D tex_0 : TEXUNIT0, + out float4 o_color : COLOR) +{ + float4 texColor = tex2D(tex_0, l_texcoord0); + o_color = texColor * 2 * (texColor.w - 0.5); +} + diff --git a/tests/display/glsl_bad.vert b/tests/display/glsl_bad.vert new file mode 100644 index 0000000000..8a324916ec --- /dev/null +++ b/tests/display/glsl_bad.vert @@ -0,0 +1,12 @@ +#version 130 + +// Uniform inputs +uniform mat4 p3d_ModelViewProjectionMatrix; + +// Vertex inputs +in vec4 p3d_Vertex; + +void main() { + gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; + does_not_exist = p3d_Vertex; +} diff --git a/tests/display/glsl_include.vert b/tests/display/glsl_include.vert new file mode 100644 index 0000000000..1826fdf677 --- /dev/null +++ b/tests/display/glsl_include.vert @@ -0,0 +1,7 @@ +#version 130 + +#pragma include "glsl_include_inputs.vert" + +void main() { + gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; +} diff --git a/tests/display/glsl_include_inputs.vert b/tests/display/glsl_include_inputs.vert new file mode 100644 index 0000000000..125078ce16 --- /dev/null +++ b/tests/display/glsl_include_inputs.vert @@ -0,0 +1,5 @@ +// Uniform inputs +uniform mat4 p3d_ModelViewProjectionMatrix; + +// Vertex inputs +in vec4 p3d_Vertex; diff --git a/tests/display/glsl_simple.frag b/tests/display/glsl_simple.frag new file mode 100644 index 0000000000..5f190e015c --- /dev/null +++ b/tests/display/glsl_simple.frag @@ -0,0 +1,5 @@ +#version 130 + +void main() { + gl_FragColor = vec4(0, 0, 0, 1); +} diff --git a/tests/display/glsl_simple.vert b/tests/display/glsl_simple.vert new file mode 100644 index 0000000000..ea3afd7498 --- /dev/null +++ b/tests/display/glsl_simple.vert @@ -0,0 +1,11 @@ +#version 130 + +// Uniform inputs +uniform mat4 p3d_ModelViewProjectionMatrix; + +// Vertex inputs +in vec4 p3d_Vertex; + +void main() { + gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; +} diff --git a/tests/display/test_cg_shader.py b/tests/display/test_cg_shader.py new file mode 100644 index 0000000000..8a6c4d7a74 --- /dev/null +++ b/tests/display/test_cg_shader.py @@ -0,0 +1,28 @@ +import os + +from panda3d import core + + +SHADERS_DIR = core.Filename.from_os_specific(os.path.dirname(__file__)) + + +def run_cg_compile_check(gsg, shader_path, expect_fail=False): + """Compile supplied Cg shader path and check for errors""" + shader = core.Shader.load(shader_path, core.Shader.SL_Cg) + # assert shader.is_prepared(gsg.prepared_objects) + if expect_fail: + assert shader is None + else: + assert shader is not None + + +def test_cg_compile_error(gsg): + """Test getting compile errors from bad Cg shaders""" + shader_path = core.Filename(SHADERS_DIR, 'cg_bad.sha') + run_cg_compile_check(gsg, shader_path, expect_fail=True) + + +def test_cg_from_file(gsg): + """Test compiling Cg shaders from files""" + shader_path = core.Filename(SHADERS_DIR, 'cg_simple.sha') + run_cg_compile_check(gsg, shader_path) diff --git a/tests/display/test_color_buffer.py b/tests/display/test_color_buffer.py index d95913b0f7..bb2bf3deb1 100644 --- a/tests/display/test_color_buffer.py +++ b/tests/display/test_color_buffer.py @@ -105,6 +105,12 @@ def render_color_pixel(region, state, vertex_color=None): """Renders a fragment using the specified render settings, and returns the resulting color value.""" + # Skip auto-shader tests if we don't support Cg shaders. + if not region.window.gsg.supports_basic_shaders: + sattr = state.get_attrib(core.ShaderAttrib) + if sattr and sattr.auto_shader(): + pytest.skip("Cannot test auto-shader without Cg shader support") + # Set up the scene with a blank card rendering at specified distance. scene = core.NodePath("root") scene.set_attrib(core.DepthTestAttrib.make(core.RenderAttrib.M_always)) diff --git a/tests/display/test_glsl_shader.py b/tests/display/test_glsl_shader.py index 8deadf40d9..a380f8ccfb 100644 --- a/tests/display/test_glsl_shader.py +++ b/tests/display/test_glsl_shader.py @@ -1,9 +1,13 @@ from panda3d import core +import os import struct import pytest from _pytest.outcomes import Failed +SHADERS_DIR = core.Filename.from_os_specific(os.path.dirname(__file__)) + + # This is the template for the compute shader that is used by run_glsl_test. # It defines an assert() macro that writes failures to a buffer, indexed by # line number. @@ -102,6 +106,19 @@ def run_glsl_test(gsg, body, preamble="", inputs={}, version=150, exts=set()): pytest.fail("{0} GLSL assertions triggered:\n{1}".format(count, formatted)) +def run_glsl_compile_check(gsg, vert_path, frag_path, expect_fail=False): + """Compile supplied GLSL shader paths and check for errors""" + shader = core.Shader.load(core.Shader.SL_GLSL, vert_path, frag_path) + assert shader is not None + + shader.prepare_now(gsg.prepared_objects, gsg) + assert shader.is_prepared(gsg.prepared_objects) + if expect_fail: + assert shader.get_error_flag() + else: + assert not shader.get_error_flag() + + def test_glsl_test(gsg): "Test to make sure that the GLSL tests work correctly." @@ -294,6 +311,36 @@ def test_glsl_pta_mat4(gsg): run_glsl_test(gsg, code, preamble, {'pta': pta}) +def test_glsl_param_vec4(gsg): + param = core.ParamVecBase4((0, 1, 2, 3)) + + preamble = """ + uniform vec4 param; + """ + code = """ + assert(param.x == 0.0); + assert(param.y == 1.0); + assert(param.z == 2.0); + assert(param.w == 3.0); + """ + run_glsl_test(gsg, code, preamble, {'param': param}) + + +def test_glsl_param_ivec4(gsg): + param = core.ParamVecBase4i((0, 1, 2, 3)) + + preamble = """ + uniform ivec4 param; + """ + code = """ + assert(param.x == 0); + assert(param.y == 1); + assert(param.z == 2); + assert(param.w == 3); + """ + run_glsl_test(gsg, code, preamble, {'param': param}) + + def test_glsl_write_extract_image_buffer(gsg): # Tests that we can write to a buffer texture on the GPU, and then extract # the data on the CPU. We test two textures since there was in the past a @@ -329,3 +376,24 @@ def test_glsl_write_extract_image_buffer(gsg): assert struct.unpack('I', tex1.get_ram_image()) == (123,) assert struct.unpack('i', tex2.get_ram_image()) == (-456,) + + +def test_glsl_compile_error(gsg): + """Test getting compile errors from bad shaders""" + vert_path = core.Filename(SHADERS_DIR, 'glsl_bad.vert') + frag_path = core.Filename(SHADERS_DIR, 'glsl_simple.frag') + run_glsl_compile_check(gsg, vert_path, frag_path, expect_fail=True) + + +def test_glsl_from_file(gsg): + """Test compiling GLSL shaders from files""" + vert_path = core.Filename(SHADERS_DIR, 'glsl_simple.vert') + frag_path = core.Filename(SHADERS_DIR, 'glsl_simple.frag') + run_glsl_compile_check(gsg, vert_path, frag_path) + + +def test_glsl_includes(gsg): + """Test preprocessing includes in GLSL shaders""" + vert_path = core.Filename(SHADERS_DIR, 'glsl_include.vert') + frag_path = core.Filename(SHADERS_DIR, 'glsl_simple.frag') + run_glsl_compile_check(gsg, vert_path, frag_path) diff --git a/tests/dtoolutil/test_filename.py b/tests/dtoolutil/test_filename.py new file mode 100644 index 0000000000..d24d9d52d7 --- /dev/null +++ b/tests/dtoolutil/test_filename.py @@ -0,0 +1,24 @@ +from panda3d.core import Filename +import sys, os +import pytest + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="Requires Python 3.6") +def test_filename_fspath(): + fn = Filename.from_os_specific(__file__) + assert os.fspath(fn) == fn.to_os_specific_w() + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="Requires Python 3.6") +def test_filename_open(): + fn = Filename.from_os_specific(__file__) + open(fn, 'rb') + + +@pytest.mark.skipif(sys.version_info < (3, 4), reason="Requires Python 3.4") +def test_filename_ctor_pathlib(): + pathlib = pytest.importorskip('pathlib') + + path = pathlib.Path(__file__) + fn = Filename(path) + assert fn.to_os_specific_w() == str(path) diff --git a/tests/dtoolutil/test_iostream.py b/tests/dtoolutil/test_iostream.py new file mode 100644 index 0000000000..2473ef80d2 --- /dev/null +++ b/tests/dtoolutil/test_iostream.py @@ -0,0 +1,128 @@ +from panda3d.core import StringStream + +import pytest + + +ISTREAM_DATA = b'abcdefghijklmnopqrstuvwxyz' * 500 + +@pytest.fixture +def istream(): + return StringStream(ISTREAM_DATA) + + +def test_istream_readall(istream): + assert istream.readall() == ISTREAM_DATA + assert istream.readall() == b'' + assert istream.readall() == b'' + assert istream.tellg() == len(ISTREAM_DATA) + + +def test_istream_read(istream): + assert istream.read() == ISTREAM_DATA + assert istream.read() == b'' + assert istream.read() == b'' + assert istream.tellg() == len(ISTREAM_DATA) + + +def test_istream_read_size(istream): + assert istream.read(100) == ISTREAM_DATA[:100] + assert istream.read(5000) == ISTREAM_DATA[100:5100] + assert istream.read(5000) == ISTREAM_DATA[5100:10100] + assert istream.read(5000) == ISTREAM_DATA[10100:15100] + assert istream.read() == b'' + assert istream.tellg() == len(ISTREAM_DATA) + + +def test_istream_read1(istream): + accumulated = b'' + data = istream.read1() + while data: + accumulated += data + data = istream.read1() + + assert accumulated == ISTREAM_DATA + assert istream.tellg() == len(ISTREAM_DATA) + + +def test_istream_read1_size(istream): + accumulated = b'' + data = istream.read1(4000) + while data: + accumulated += data + data = istream.read1(4000) + + assert accumulated == ISTREAM_DATA + assert istream.tellg() == len(ISTREAM_DATA) + + +def test_istream_readinto(istream): + ba = bytearray() + assert istream.readinto(ba) == 0 + assert istream.tellg() == 0 + + ba = bytearray(10) + assert istream.readinto(ba) == 10 + assert ba == ISTREAM_DATA[:10] + assert istream.tellg() == 10 + + ba = bytearray(len(ISTREAM_DATA)) + assert istream.readinto(ba) == len(ISTREAM_DATA) - 10 + assert ba[:len(ISTREAM_DATA)-10] == ISTREAM_DATA[10:] + assert istream.tellg() == len(ISTREAM_DATA) + + +def test_istream_readline(): + # Empty stream + stream = StringStream(b'') + assert stream.readline() == b'' + assert stream.readline() == b'' + + # Single line without newline + stream = StringStream(b'A') + assert stream.readline() == b'A' + assert stream.readline() == b'' + + # Single newline + stream = StringStream(b'\n') + assert stream.readline() == b'\n' + assert stream.readline() == b'' + + # Line with text followed by empty line + stream = StringStream(b'A\n\n') + assert stream.readline() == b'A\n' + assert stream.readline() == b'\n' + assert stream.readline() == b'' + + # Preserve null byte + stream = StringStream(b'\x00\x00') + assert stream.readline() == b'\x00\x00' + + +def test_istream_readlines(): + istream = StringStream(b'a') + assert istream.readlines() == [b'a'] + assert istream.readlines() == [] + + istream = StringStream(b'a\nb\nc\n') + assert istream.readlines() == [b'a\n', b'b\n', b'c\n'] + + istream = StringStream(b'\na\nb\nc') + assert istream.readlines() == [b'\n', b'a\n', b'b\n', b'c'] + + istream = StringStream(b'\n\n\n') + assert istream.readlines() == [b'\n', b'\n', b'\n'] + + +def test_istream_iter(): + istream = StringStream(b'a') + assert tuple(istream) == (b'a',) + assert tuple(istream) == () + + istream = StringStream(b'a\nb\nc\n') + assert tuple(istream) == (b'a\n', b'b\n', b'c\n') + + istream = StringStream(b'\na\nb\nc') + assert tuple(istream) == (b'\n', b'a\n', b'b\n', b'c') + + istream = StringStream(b'\n\n\n') + assert tuple(istream) == (b'\n', b'\n', b'\n') diff --git a/tests/egg/test_egg_vertex_uv.py b/tests/egg/test_egg_vertex_uv.py new file mode 100644 index 0000000000..e2f1deeebf --- /dev/null +++ b/tests/egg/test_egg_vertex_uv.py @@ -0,0 +1,84 @@ +import pytest +from panda3d import core + +# Skip these tests if we can't import egg. +egg = pytest.importorskip("panda3d.egg") + + +def read_egg_vertex(string): + """Reads an EggVertex from a string.""" + data = " pool { 1 { %s } }" % (string) + stream = core.StringStream(data.encode('utf-8')) + data = egg.EggData() + assert data.read(stream) + pool, = data.get_children() + return pool.get_vertex(1) + + +def test_egg_vertex_uv_empty(): + vertex = read_egg_vertex(""" + 0 0 0 + { + 0 0 + } + """) + + obj = vertex.get_uv_obj("") + assert not obj.has_tangent() + assert not obj.has_tangent4() + + assert '' not in str(obj) + + +def test_egg_vertex_tangent(): + vertex = read_egg_vertex(""" + 0 0 0 + { + 0 0 + { 2 3 4 } + } + """) + + obj = vertex.get_uv_obj("") + assert obj.has_tangent() + assert not obj.has_tangent4() + assert obj.get_tangent() == (2, 3, 4) + assert obj.get_tangent4() == (2, 3, 4, 1) + + assert '{ 2 3 4 }' in str(obj) + + +def test_egg_vertex_tangent4_pos(): + vertex = read_egg_vertex(""" + 0 0 0 + { + 0 0 + { 2 3 4 1 } + } + """) + + obj = vertex.get_uv_obj("") + assert obj.has_tangent() + assert obj.has_tangent4() + assert obj.get_tangent() == (2, 3, 4) + assert obj.get_tangent4() == (2, 3, 4, 1) + + assert '{ 2 3 4 1 }' in str(obj) + + +def test_egg_vertex_tangent4_neg(): + vertex = read_egg_vertex(""" + 0 0 0 + { + 0 0 + { 2 3 4 -1 } + } + """) + + obj = vertex.get_uv_obj("") + assert obj.has_tangent() + assert obj.has_tangent4() + assert obj.get_tangent() == (2, 3, 4) + assert obj.get_tangent4() == (2, 3, 4, -1) + + assert '{ 2 3 4 -1 }' in str(obj) diff --git a/tests/event/test_pythontask.py b/tests/event/test_pythontask.py new file mode 100644 index 0000000000..d6806b4f72 --- /dev/null +++ b/tests/event/test_pythontask.py @@ -0,0 +1,78 @@ +from panda3d.core import PythonTask +import pytest +import types +import sys + + +def test_pythontask_property_builtin(): + task = PythonTask() + + # Read-write property + assert task.name == "" + task.name = "ABC" + + # Read-only property + assert task.dt == 0.0 + with pytest.raises(AttributeError): + task.dt = 1.0 + assert task.dt == 0.0 + + # Non-existent property + with pytest.raises(AttributeError): + task.abc + + +def test_pythontask_property_custom(): + task = PythonTask() + assert not hasattr(task, 'custom_field') + + task.custom_field = 1.0 + assert hasattr(task, 'custom_field') + assert task.custom_field == 1.0 + + task.custom_field = 2.0 + assert task.custom_field == 2.0 + + del task.custom_field + assert not hasattr(task, 'custom_field') + + +def test_pythontask_property_override(): + task = PythonTask() + assert isinstance(task.gather, types.BuiltinMethodType) + + task.gather = 123 + assert task.gather == 123 + + del task.gather + assert isinstance(task.gather, types.BuiltinMethodType) + + +def test_pythontask_dict_get(): + task = PythonTask() + + d = task.__dict__ + rc1 = sys.getrefcount(d) + + task.__dict__ + task.__dict__ + + rc2 = sys.getrefcount(d) + + assert rc1 == rc2 + + +def test_pythontask_dict_set(): + task = PythonTask() + d = {} + + rc1 = sys.getrefcount(d) + task.__dict__ = d + rc2 = sys.getrefcount(d) + + assert rc1 + 1 == rc2 + + task.__dict__ = {} + rc2 = sys.getrefcount(d) + + assert rc1 == rc2 diff --git a/tests/express/test_encrypt.py b/tests/express/test_encrypt.py new file mode 100644 index 0000000000..5624bcae99 --- /dev/null +++ b/tests/express/test_encrypt.py @@ -0,0 +1,14 @@ +from panda3d import core + + +def test_encrypt_string(): + # Test encrypt and then decrypt cycle + enc = core.encrypt_string('abcdefg', '12345') + assert len(enc) > 0 + + dec = core.decrypt_string(enc, '12345') + assert dec == 'abcdefg' + + # Test pre-encrypted string + enc = b'[\x00\x10\x00d\x00\xb5\x7f\xc44Y\xb7\xd9\x15\xe3\xbd\xcf\xb3yK\xfb\xf6' + assert 'test' == core.decrypt_string(enc, '98765') diff --git a/tests/express/test_hashval.py b/tests/express/test_hashval.py new file mode 100644 index 0000000000..b2334e24d9 --- /dev/null +++ b/tests/express/test_hashval.py @@ -0,0 +1,9 @@ +from panda3d import core +import random + + +def test_hashval_hex(): + hex = '%032x' % random.getrandbits(32 * 4) + val = core.HashVal() + val.input_hex(core.StringStream(hex.encode('ascii'))) + assert str(val) == hex.lower() diff --git a/tests/express/test_multifile.py b/tests/express/test_multifile.py new file mode 100644 index 0000000000..8618688eb2 --- /dev/null +++ b/tests/express/test_multifile.py @@ -0,0 +1,12 @@ +from panda3d.core import Multifile, StringStream, IStreamWrapper + + +def test_multifile_read_empty(): + stream = StringStream(b'pmf\x00\n\r\x01\x00\x01\x00\x01\x00\x00\x00\xdb\x9d7\\\x00\x00\x00\x00') + wrapper = IStreamWrapper(stream) + + m = Multifile() + assert m.open_read(wrapper) + assert m.is_read_valid() + assert m.get_num_subfiles() == 0 + m.close() diff --git a/tests/gobj/test_lenses.py b/tests/gobj/test_lenses.py new file mode 100644 index 0000000000..a51e497c8e --- /dev/null +++ b/tests/gobj/test_lenses.py @@ -0,0 +1,77 @@ +from panda3d.core import PerspectiveLens, Point3, Point2 + + +def test_perspectivelens_extrude(): + lens = PerspectiveLens() + lens.set_fov(90, 90) + lens.set_near_far(0.5, 100) + + near = Point3() + far = Point3() + + assert lens.extrude((0, 0), near, far) + assert near.almost_equal((0, 0.5, 0), 0.001) + assert far.almost_equal((0, 100, 0), 0.1) + + assert lens.extrude((-1, -1), near, far) + assert near.almost_equal((-0.5, 0.5, -0.5), 0.001) + assert far.almost_equal((-100, 100, -100), 0.1) + + assert lens.extrude((1, 0), near, far) + assert near.almost_equal((0.5, 0.5, 0), 0.001) + assert far.almost_equal((100, 100, 0), 0.1) + + +def test_perspectivelens_extrude_depth(): + lens = PerspectiveLens() + lens.set_fov(90, 90) + lens.set_near_far(0.5, 100) + + point = Point3() + + assert lens.extrude_depth((0, 0, -1), point) + assert point.almost_equal((0, 0.5, 0), 0.001) + + assert lens.extrude_depth((0, 0, 1), point) + assert point.almost_equal((0, 100, 0), 0.001) + + assert lens.extrude_depth((-1, -1, -1), point) + assert point.almost_equal((-0.5, 0.5, -0.5), 0.001) + + assert lens.extrude_depth((-1, -1, 1), point) + assert point.almost_equal((-100, 100, -100), 0.1) + + assert lens.extrude_depth((1, 0, -1), point) + assert point.almost_equal((0.5, 0.5, 0), 0.001) + + assert lens.extrude_depth((1, 0, 1), point) + assert point.almost_equal((100, 100, 0), 0.1) + + +def test_perspectivelens_project(): + lens = PerspectiveLens() + lens.set_fov(90, 90) + lens.set_near_far(0.5, 100) + + point = Point2() + + assert not lens.project((0, 0, 0), point) + assert not lens.project((-1, 0.5, 0), point) + + assert lens.project((0, 0.5, 0), point) + assert point.almost_equal((0, 0), 0.001) + + assert lens.project((0, 100, 0), point) + assert point.almost_equal((0, 0), 0.001) + + assert lens.project((-0.5, 0.5, -0.5), point) + assert point.almost_equal((-1, -1), 0.001) + + assert lens.project((-100, 100, -100), point) + assert point.almost_equal((-1, -1), 0.001) + + assert lens.project((0.5, 0.5, 0), point) + assert point.almost_equal((1, 0), 0.001) + + assert lens.project((100, 100, 0), point) + assert point.almost_equal((1, 0), 0.001) diff --git a/tests/gobj/test_shader.py b/tests/gobj/test_shader.py new file mode 100644 index 0000000000..52900de316 --- /dev/null +++ b/tests/gobj/test_shader.py @@ -0,0 +1,83 @@ +from panda3d.core import Shader, VirtualFileSystem, Filename +import time +import pytest + + +@pytest.fixture(scope="session") +def vfs(): + return VirtualFileSystem.get_global_ptr() + + +@pytest.fixture +def ramdir(): + """Fixture yielding a fresh ramdisk directory.""" + from panda3d.core import VirtualFileMountRamdisk, Filename + + vfs = VirtualFileSystem.get_global_ptr() + mount = VirtualFileMountRamdisk() + dir = Filename.temporary("/virtual", "ram.") + assert vfs.mount(mount, dir, 0) + + yield dir + vfs.unmount(mount) + + +def test_shader_load_multi(vfs, ramdir): + # Try non-existent first. + shad0 = Shader.load(Shader.SL_GLSL, + vertex="/nonexistent.glsl", + fragment="/nonexistent.glsl") + assert shad0 is None + + vert_file = Filename(ramdir, "shader.glsl") + frag_file = Filename(ramdir, "shader.glsl") + + # Now write some actual content to the shader files. + vfs.write_file(vert_file, b"#version 100\nvoid main() {}\n", False) + + shad1 = Shader.load(Shader.SL_GLSL, vertex=vert_file, fragment=frag_file) + assert shad1 is not None + assert shad1.this + + # Load the same shader, it should return the cached result. + shad2 = Shader.load(Shader.SL_GLSL, vertex=vert_file, fragment=frag_file) + assert shad2 is not None + assert shad1.this == shad2.this + + # After waiting a second to make the timestamp different, modify the + # shader and load again, it should result in a different object now + time.sleep(1.0) + vfs.write_file(vert_file, b"#version 110\nvoid main() {}\n", False) + + shad2 = Shader.load(Shader.SL_GLSL, vertex=vert_file, fragment=frag_file) + + assert shad2.this != shad1.this + + +def test_shader_load_compute(vfs, ramdir): + # Try non-existent first. + shad0 = Shader.load_compute(Shader.SL_GLSL, "/nonexistent.glsl") + assert shad0 is None + + comp_file = Filename(ramdir, "shader.glsl") + + # Now write some actual content to the shader file. + vfs.write_file(comp_file, b"#version 100\nvoid main() {}\n", False) + + shad1 = Shader.load_compute(Shader.SL_GLSL, comp_file) + assert shad1 is not None + assert shad1.this + + # Load the same shader, it should return the cached result. + shad2 = Shader.load_compute(Shader.SL_GLSL, comp_file) + assert shad2 is not None + assert shad1.this == shad2.this + + # After waiting a second to make the timestamp different, modify the + # shader and load again, it should result in a different object now + time.sleep(1.0) + vfs.write_file(comp_file, b"#version 110\nvoid main() {}\n", False) + + shad2 = Shader.load_compute(Shader.SL_GLSL, comp_file) + + assert shad2.this != shad1.this diff --git a/tests/gui/test_DirectButton.py b/tests/gui/test_DirectButton.py new file mode 100644 index 0000000000..6f85e72f59 --- /dev/null +++ b/tests/gui/test_DirectButton.py @@ -0,0 +1,6 @@ +from direct.gui.DirectButton import DirectButton + + +def test_button_destroy(): + btn = DirectButton(text="Test") + btn.destroy() diff --git a/tests/gui/test_DirectEntry.py b/tests/gui/test_DirectEntry.py new file mode 100644 index 0000000000..3382be3bda --- /dev/null +++ b/tests/gui/test_DirectEntry.py @@ -0,0 +1,43 @@ +# coding=utf-8 +from direct.gui.DirectEntry import DirectEntry +import sys + + +def test_entry_destroy(): + entry = DirectEntry() + entry.destroy() + + +def test_entry_get(): + entry = DirectEntry() + assert isinstance(entry.get(), str) + + +def test_entry_auto_capitalize(): + # Now we can generate the DirectEntry component itself. In normal use, we + # would pass "autoCapitalize=1" to DirectEntry's constructor in order for + # DirectEntry._autoCapitalize() to be called upon typing into the entry + # GUI, however in the case of this unit test where there is no GUI to type + # into, we don't need to bother doing that; we're just calling the function + # ourselves anyway. + entry = DirectEntry() + + # Test DirectEntry._autoCapitalize(). The intended behavior would be that + # the first letter of each word in the entry would be capitalized, so that + # is what we will check for: + entry.set('auto capitalize test') + entry._autoCapitalize() + assert entry.get() == 'Auto Capitalize Test' + + # Test DirectEntry._autoCapitalize() with a unicode object this time. + entry.set(u'àütò çapítalízè ţèsţ') + assert entry.get() == u'àütò çapítalízè ţèsţ' + entry._autoCapitalize() + assert entry.get() == u'Àütò Çapítalízè Ţèsţ' + + # Also test it with a UTF-8 encoded byte string in Python 2. + if sys.version_info < (3, 0): + entry.set(u'àütò çapítalízè ţèsţ'.encode('utf-8')) + assert entry.get() == u'àütò çapítalízè ţèsţ' + entry._autoCapitalize() + assert entry.get() == u'Àütò Çapítalízè Ţèsţ' diff --git a/tests/gui/test_DirectGuiBase.py b/tests/gui/test_DirectGuiBase.py new file mode 100644 index 0000000000..1f9fade613 --- /dev/null +++ b/tests/gui/test_DirectGuiBase.py @@ -0,0 +1,223 @@ +from direct.gui.DirectGuiBase import DirectGuiBase, DirectGuiWidget, toggleGuiGridSnap, setGuiGridSpacing +from direct.gui.OnscreenText import OnscreenText +from direct.gui import DirectGuiGlobals as DGG +from direct.showbase.ShowBase import ShowBase +from direct.showbase import ShowBaseGlobal +from direct.showbase.MessengerGlobal import messenger +from panda3d import core +import pytest + + +def test_create_DirectGuiBase(): + baseitem = DirectGuiBase() + +def test_defineoptions(): + baseitem = DirectGuiBase() + testoptiondefs = (('test',0,None),) + baseitem.defineoptions({}, testoptiondefs) + assert baseitem['test'] == 0 + +def test_addoptions(): + baseitem = DirectGuiBase() + testoptiondefs = (('test',0,None),) + + # Those two will usually be set in the defineoptions function + baseitem._optionInfo = {} + baseitem._constructorKeywords = {} + + baseitem.addoptions(testoptiondefs, {}) + assert baseitem['test'] == 0 + +def test_initialiseoptions(): + baseitem = DirectGuiBase() + + class testWidget(DirectGuiBase): + def __init__(self): + pass + + tw = testWidget() + baseitem.initialiseoptions(tw) + + testoptiondefs = (('test',0,None),) + tw.defineoptions({}, testoptiondefs) + baseitem.initialiseoptions(tw) + + assert tw['test'] == 0 + +def test_postInitialiseFunc(): + baseitem = DirectGuiBase() + + def func_a(): + global test_value_a + test_value_a = 1 + def func_b(): + global test_value_b + test_value_b = 1 + + baseitem.postInitialiseFuncList.append(func_a) + baseitem.postInitialiseFuncList.append(func_b) + + baseitem.postInitialiseFunc() + + global test_value_a + assert test_value_a == 1 + + global test_value_b + assert test_value_b == 1 + +def test_isinitoption(): + baseitem = DirectGuiBase() + testoptiondefs = (('test-true',0,DGG.INITOPT),('test-false',0,None)) + baseitem.defineoptions({}, testoptiondefs) + assert baseitem.isinitoption('test-true') == True + assert baseitem.isinitoption('test-false') == False + +def test_options(): + baseitem = DirectGuiBase() + testoptiondefs = (('test-1',0,DGG.INITOPT),('test-2',0,None),) + baseitem.defineoptions({}, testoptiondefs) + assert len(baseitem.options()) == 2 + +def test_get_options(): + baseitem = DirectGuiBase() + testoptiondefs = (('test-1',0,None),) + baseitem.defineoptions({}, testoptiondefs) + + # Using the configure function + # check if configuration have been set correctly + assert baseitem.configure() == {'test-1': ('test-1', 0, 0),} + # check for specific configurations + assert baseitem.configure('test-1') == ('test-1', 0, 0) + + # using index style references __getItem__ and cget + assert baseitem['test-1'] == 0 + assert baseitem.cget('test-1') == 0 + +def test_set_options(): + baseitem = DirectGuiBase() + + testoptiondefs = (('test-1',0,DGG.INITOPT),('test-2',0,None)) + baseitem.defineoptions({}, testoptiondefs) + + # try to set a value of an initopt option (shouldn't be possible) + baseitem.configure('test-1', **{'test-1': 1}) + # check it's value. It shouldn't have changed + assert baseitem['test-1'] == 0 + + baseitem['test-1'] = 1 + # check it's value. It shouldn't have changed + assert baseitem['test-1'] == 0 + + # try changing using the configure function. As test-2 isn't an + # initopt option, this should work fine + baseitem.configure('test-2', **{'test-2': 2}) + # check it's value. It should be 2 now + assert baseitem['test-2'] == 2 + + # try changing using index style referencing + baseitem['test-2'] = 1 + assert baseitem['test-2'] == 1 + +def test_component_handling(): + baseitem = DirectGuiBase() + testoptiondefs = (('test-1',0,None),) + baseitem.defineoptions({}, testoptiondefs) + + assert len(baseitem.components()) == 0 + + baseitem.createcomponent( + 'componentName', # componentName + (), # componentAliases + 'componentGroup', # componentGroup + OnscreenText, # widgetClass + (), # *widgetArgs + text = 'Test', # **kw + parent = core.NodePath() + ) + + # check if we have exactly one component now + assert len(baseitem.components()) == 1 + # check if we have a component named like the one we just created + assert baseitem.hascomponent('componentName') + + # get our new component + component = baseitem.component('componentName') + # check some of its values + assert component.text == 'Test' + + # destroy our recently created component + baseitem.destroycomponent('componentName') + # check if it really is gone + assert baseitem.hascomponent('componentName') == False + +def test_destroy(): + baseitem = DirectGuiBase() + testoptiondefs = (('test-1',0,None),) + baseitem.defineoptions({}, testoptiondefs) + baseitem.destroy() + # check if things got correctly removed + assert not hasattr(baseitem, '_optionInfo') + assert not hasattr(baseitem, '__componentInfo') + assert not hasattr(baseitem, 'postInitialiseFuncList') + +def test_bindings(): + baseitem = DirectGuiBase() + + # Our test function and variable to check + global commandCalled + commandCalled = False + def command(**kw): + global commandCalled + commandCalled = True + assert True + + # Bind an event to our item + baseitem.bind(DGG.B1CLICK, command) + + # The function should be called now. Note that the guiId will be + # automatically appended by the bind command! + messenger.send(DGG.B1CLICK + baseitem.guiId) + assert commandCalled + + # Try to unbind the event again + baseitem.unbind(DGG.B1CLICK) + + # Now it shouldn't be called anymore + commandCalled = False + messenger.send(DGG.B1CLICK + baseitem.guiId) + assert not commandCalled + +def test_toggle_snap(): + try: + DirectGuiWidget.snapToGrid = 0 + item = DirectGuiWidget() + assert item.snapToGrid == 0 + toggleGuiGridSnap() + assert item.snapToGrid == 1 + finally: + DirectGuiWidget.snapToGrid = 0 + +def test_toggle_spacing(): + try: + DirectGuiWidget.gridSpacing = 0 + item = DirectGuiWidget() + setGuiGridSpacing(5) + assert item.gridSpacing == 5 + finally: + DirectGuiWidget.gridSpacing = 0.05 + +@pytest.mark.skipif(not ShowBaseGlobal.__dev__, reason="requires want-dev") +def test_track_gui_items(): + page = core.load_prc_file_data("", "track-gui-items true") + try: + item = DirectGuiWidget() + id = item.guiId + + assert id in ShowBase.guiItems + assert ShowBase.guiItems[id] == item + + item.destroy() + + assert id not in ShowBase.guiItems + finally: + core.unload_prc_file(page) diff --git a/tests/linmath/test_lmatrix4.py b/tests/linmath/test_lmatrix4.py index 23c1bcc5e9..05c487f807 100644 --- a/tests/linmath/test_lmatrix4.py +++ b/tests/linmath/test_lmatrix4.py @@ -87,3 +87,39 @@ def test_mat4_invert_correct(type): assert (mat * inv).is_identity() assert (inv * mat).is_identity() + + +@pytest.mark.parametrize("type", (core.LMatrix4d, core.LMatrix4f)) +def test_mat4_rows(type): + mat = type((1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16)) + + assert mat.rows[0] == (1, 2, 3, 4) + assert mat.rows[1] == (5, 6, 7, 8) + assert mat.rows[2] == (9, 10, 11, 12) + assert mat.rows[3] == (13, 14, 15, 16) + + assert mat.get_row3(0) == (1, 2, 3) + assert mat.get_row3(1) == (5, 6, 7) + assert mat.get_row3(2) == (9, 10, 11) + assert mat.get_row3(3) == (13, 14, 15) + + +@pytest.mark.parametrize("type", (core.LMatrix4d, core.LMatrix4f)) +def test_mat4_cols(type): + mat = type((1, 5, 9, 13, + 2, 6, 10, 14, + 3, 7, 11, 15, + 4, 8, 12, 16)) + + assert mat.cols[0] == (1, 2, 3, 4) + assert mat.cols[1] == (5, 6, 7, 8) + assert mat.cols[2] == (9, 10, 11, 12) + assert mat.cols[3] == (13, 14, 15, 16) + + assert mat.get_col3(0) == (1, 2, 3) + assert mat.get_col3(1) == (5, 6, 7) + assert mat.get_col3(2) == (9, 10, 11) + assert mat.get_col3(3) == (13, 14, 15) diff --git a/tests/movies/test_user_audio.py b/tests/movies/test_user_audio.py new file mode 100644 index 0000000000..13addd05f0 --- /dev/null +++ b/tests/movies/test_user_audio.py @@ -0,0 +1,15 @@ +import pytest + +from panda3d.core import UserDataAudio + + +@pytest.mark.parametrize("remove_after_read", [True, False]) +def test_userdata_audio(remove_after_read): + audio = UserDataAudio(48000, 2, remove_after_read) + audio.append(b'abcdefgh') + audio.done() + cursor = audio.open() + assert cursor.read_samples(0) == b'' + assert cursor.read_samples(1) == b'abcd' + assert cursor.read_samples(1) == b'efgh' + assert cursor.read_samples(1) == b'' diff --git a/tests/ode/conftest.py b/tests/ode/conftest.py new file mode 100644 index 0000000000..289304cf0f --- /dev/null +++ b/tests/ode/conftest.py @@ -0,0 +1,7 @@ +import pytest + + +@pytest.fixture +def world(): + ode = pytest.importorskip("panda3d.ode") + return ode.OdeWorld() diff --git a/tests/ode/test_ode_joints.py b/tests/ode/test_ode_joints.py new file mode 100644 index 0000000000..d1e202412d --- /dev/null +++ b/tests/ode/test_ode_joints.py @@ -0,0 +1,43 @@ +import pytest + + +def test_odejoint_attach_both(world): + from panda3d import ode + + body1 = ode.OdeBody(world) + body2 = ode.OdeBody(world) + + assert len(body1.joints) == 0 + assert len(body2.joints) == 0 + + joint = ode.OdeBallJoint(world) + joint.attach(body1, body2) + + assert tuple(body1.joints) == (joint,) + assert tuple(body2.joints) == (joint,) + + +def test_odejoint_attach_0(world): + from panda3d import ode + + body = ode.OdeBody(world) + + assert len(body.joints) == 0 + + joint = ode.OdeBallJoint(world) + joint.attach(body, None) + + assert tuple(body.joints) == (joint,) + + +def test_odejoint_attach_1(world): + from panda3d import ode + + body = ode.OdeBody(world) + + assert len(body.joints) == 0 + + joint = ode.OdeBallJoint(world) + joint.attach(None, body) + + assert tuple(body.joints) == (joint,) diff --git a/tests/pgraph/test_loader_types.py b/tests/pgraph/test_loader_types.py new file mode 100644 index 0000000000..8fca1c5849 --- /dev/null +++ b/tests/pgraph/test_loader_types.py @@ -0,0 +1,220 @@ +from panda3d.core import LoaderFileTypeRegistry, ModelRoot, Loader, LoaderOptions, Filename +import pytest +import tempfile +import os +from contextlib import contextmanager + + +@pytest.fixture +def test_filename(): + """Fixture returning a filename to an existent .test file.""" + fp = tempfile.NamedTemporaryFile(suffix='.test', delete=False) + fp.write(b"test") + fp.close() + filename = Filename.from_os_specific(fp.name) + filename.make_true_case() + yield filename + os.unlink(fp.name) + + +@pytest.fixture +def test_pz_filename(): + """Fixture returning a filename to an existent .test.pz file.""" + fp = tempfile.NamedTemporaryFile(suffix='.test.pz', delete=False) + fp.write(b"test") + fp.close() + filename = Filename.from_os_specific(fp.name) + filename.make_true_case() + yield filename + os.unlink(fp.name) + + +@contextmanager +def registered_type(type): + """Convenience method allowing use of register_type in a with block.""" + registry = LoaderFileTypeRegistry.get_global_ptr() + registry.register_type(type) + yield + registry.unregister_type(type) + + +class DummyLoader: + """The simplest possible successful LoaderFileType.""" + + extensions = ["test"] + + @staticmethod + def load_file(path, options, record=None): + return ModelRoot("loaded") + + +def test_loader_invalid(): + """Tests that registering a malformed loader fails.""" + + class MissingExtensionsLoader: + pass + + class InvalidTypeExtensionsLoader: + extensions = "abc" + + class EmptyExtensionsLoader: + extensions = [] + + class InvalidExtensionsLoader: + extensions = [123, None] + + registry = LoaderFileTypeRegistry.get_global_ptr() + + with pytest.raises(Exception): + registry.register_type("invalid") + + with pytest.raises(Exception): + registry.register_type(MissingExtensionsLoader) + + with pytest.raises(TypeError): + registry.register_type(InvalidTypeExtensionsLoader) + + with pytest.raises(ValueError): + registry.register_type(EmptyExtensionsLoader) + + with pytest.raises(TypeError): + registry.register_type(InvalidExtensionsLoader) + + +def test_loader_success(test_filename): + """Tests that a normal dummy loader successfully loads.""" + + with registered_type(DummyLoader): + model = Loader.get_global_ptr().load_sync(test_filename, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model is not None + assert model.name == "loaded" + + +def test_loader_extensions(test_filename): + """Tests multi-extension loaders.""" + + class MultiExtensionLoader: + extensions = ["test1", "teSt2"] + + @staticmethod + def load_file(path, options, record=None): + return ModelRoot("loaded") + + fp1 = tempfile.NamedTemporaryFile(suffix='.test1', delete=False) + fp1.write(b"test1") + fp1.close() + fn1 = Filename.from_os_specific(fp1.name) + fn1.make_true_case() + + fp2 = tempfile.NamedTemporaryFile(suffix='.TEST2', delete=False) + fp2.write(b"test2") + fp2.close() + fn2 = Filename.from_os_specific(fp2.name) + fn2.make_true_case() + + try: + with registered_type(MultiExtensionLoader): + model1 = Loader.get_global_ptr().load_sync(fn1, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model1 is not None + assert model1.name == "loaded" + + model2 = Loader.get_global_ptr().load_sync(fn2, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model2 is not None + assert model2.name == "loaded" + finally: + os.unlink(fp1.name) + os.unlink(fp2.name) + + # Ensure that both were unregistered. + registry = LoaderFileTypeRegistry.get_global_ptr() + assert not registry.get_type_from_extension("test1") + assert not registry.get_type_from_extension("test2") + + +def test_loader_nonexistent(): + """Verifies that non-existent files fail before calling load_file.""" + flag = [False] + + class AssertiveLoader: + extensions = ["test"] + + @staticmethod + def load_file(path, options, record=None): + flag[0] = True + assert False, "should never get here" + + with registered_type(AssertiveLoader): + model = Loader.get_global_ptr().load_sync("/non-existent", LoaderOptions(LoaderOptions.LF_no_cache)) + assert model is None + assert not flag[0] + + +def test_loader_exception(test_filename): + """Tests for a loader that raises an exception.""" + + class FailingLoader: + extensions = ["test"] + + @staticmethod + def load_file(path, options, record=None): + raise Exception("test error") + + with registered_type(FailingLoader): + model = Loader.get_global_ptr().load_sync(test_filename, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model is None + + +def test_loader_compressed(test_pz_filename): + """Tests for loading .pz files and the supports_compressed flag.""" + + class TestLoader: + extensions = ["test"] + + @staticmethod + def load_file(path, options, record=None): + return ModelRoot("loaded") + + # Test with property absent + with registered_type(TestLoader): + model = Loader.get_global_ptr().load_sync(test_pz_filename, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model is None + + # Test with property False, should give same result + TestLoader.supports_compressed = False + with registered_type(TestLoader): + model = Loader.get_global_ptr().load_sync(test_pz_filename, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model is None + + # Test with property True, should work + TestLoader.supports_compressed = True + with registered_type(TestLoader): + model = Loader.get_global_ptr().load_sync(test_pz_filename, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model is not None + assert model.name == "loaded" + + # Test with property invalid type, should not register + TestLoader.supports_compressed = None + with pytest.raises(TypeError): + LoaderFileTypeRegistry.get_global_ptr().register_type(TestLoader) + + +def test_loader_ram_cache(test_filename): + """Tests that the Python loader plug-ins write to the RAM cache.""" + + # Ensure a clean slate. + from panda3d.core import ModelPool + ModelPool.release_all_models() + + with registered_type(DummyLoader): + model1 = Loader.get_global_ptr().load_sync(test_filename, LoaderOptions(LoaderOptions.LF_no_disk_cache | LoaderOptions.LF_allow_instance)) + assert model1 is not None + assert model1.name == "loaded" + + assert ModelPool.has_model(test_filename) + assert ModelPool.get_model(test_filename, True) == model1 + + model2 = Loader.get_global_ptr().load_sync(test_filename, LoaderOptions(LoaderOptions.LF_cache_only | LoaderOptions.LF_allow_instance)) + assert model2 is not None + assert model1 == model2 + + ModelPool.release_model(model2) diff --git a/tests/pgraph/test_nodepath.py b/tests/pgraph/test_nodepath.py index 19a40021ca..7dcbd2662b 100644 --- a/tests/pgraph/test_nodepath.py +++ b/tests/pgraph/test_nodepath.py @@ -149,3 +149,22 @@ def test_nodepath_python_tags(): rc1 = sys.getrefcount(path.python_tags) rc2 = sys.getrefcount(path.python_tags) assert rc1 == rc2 + + +def test_nodepath_replace_texture(): + from panda3d.core import NodePath, Texture + + tex1 = Texture() + tex2 = Texture() + + path1 = NodePath("node1") + path1.set_texture(tex1) + path1.replace_texture(tex1, tex2) + assert path1.get_texture() == tex2 + + path1 = NodePath("node1") + path2 = path1.attach_new_node("node2") + path2.set_texture(tex1) + path1.replace_texture(tex1, tex2) + assert not path1.has_texture() + assert path2.get_texture() == tex2 diff --git a/tests/pipeline/test_condition_var.py b/tests/pipeline/test_condition_var.py new file mode 100644 index 0000000000..4cfc6c226a --- /dev/null +++ b/tests/pipeline/test_condition_var.py @@ -0,0 +1,146 @@ +from panda3d.core import Mutex, ConditionVarFull +from panda3d import core +from direct.stdpy import thread +import pytest + + +def yield_thread(): + # Thread.force_yield() is not enough for true-threading builds, whereas + # time.sleep() does not yield in simple-thread builds. Thread.sleep() + # seems to do the job in all cases, however. + core.Thread.sleep(0.002) + + +def test_cvar_notify(): + # Just tests that notifying without waiting does no harm. + m = Mutex() + cv = ConditionVarFull(m) + + cv.notify() + cv.notify_all() + del cv + + +def test_cvar_notify_locked(): + # Tests the same thing, but with the lock held. + m = Mutex() + cv = ConditionVarFull(m) + + with m: + cv.notify() + + with m: + cv.notify_all() + + del cv + + +@pytest.mark.parametrize("num_threads", [1, 2, 3, 4]) +@pytest.mark.skipif(not core.Thread.is_threading_supported(), + reason="Threading support disabled") +def test_cvar_notify_thread(num_threads): + # Tests notify() with some number of threads waiting. + m = Mutex() + cv = ConditionVarFull(m) + + # We prematurely notify, so that we can test that it's not doing anything. + m.acquire() + cv.notify() + + state = {'waiting': 0} + + def wait_thread(): + m.acquire() + state['waiting'] += 1 + cv.wait() + state['waiting'] -= 1 + m.release() + + # Start the threads, and yield to it, giving it a chance to mess up. + threads = [] + for i in range(num_threads): + thread = core.PythonThread(wait_thread, (), "", "") + thread.start(core.TP_high, True) + + # Yield until all of the threads are waiting for the condition variable. + for i in range(1000): + m.release() + yield_thread() + m.acquire() + if state['waiting'] == num_threads: + break + + assert state['waiting'] == num_threads + + # OK, now signal it, and yield. One thread must be unblocked per notify. + for i in range(num_threads): + cv.notify() + expected_waiters = num_threads - i - 1 + + for j in range(1000): + m.release() + yield_thread() + m.acquire() + if state['waiting'] == expected_waiters: + break + + assert state['waiting'] == expected_waiters + + m.release() + for thread in threads: + thread.join() + cv = None + + +@pytest.mark.parametrize("num_threads", [1, 2, 3, 4]) +@pytest.mark.skipif(not core.Thread.is_threading_supported(), + reason="Threading support disabled") +def test_cvar_notify_all_threads(num_threads): + # Tests notify_all() with some number of threads waiting. + m = Mutex() + cv = ConditionVarFull(m) + + # We prematurely notify, so that we can test that it's not doing anything. + m.acquire() + cv.notify_all() + + state = {'waiting': 0} + + def wait_thread(): + m.acquire() + state['waiting'] += 1 + cv.wait() + state['waiting'] -= 1 + m.release() + + # Start the threads, and yield to it, giving it a chance to mess up. + threads = [] + for i in range(num_threads): + thread = core.PythonThread(wait_thread, (), "", "") + thread.start(core.TP_high, True) + + # Yield until all of the threads are waiting for the condition variable. + for i in range(1000): + m.release() + yield_thread() + m.acquire() + if state['waiting'] == num_threads: + break + + assert state['waiting'] == num_threads + + # OK, now signal it, and yield. All threads must unblock. + cv.notify_all() + for i in range(1000): + m.release() + yield_thread() + m.acquire() + if state['waiting'] == 0: + break + + assert state['waiting'] == 0 + m.release() + + for thread in threads: + thread.join() + cv = None diff --git a/tests/pipeline/test_mutex.py b/tests/pipeline/test_mutex.py index 2073c09efa..15fa435da7 100644 --- a/tests/pipeline/test_mutex.py +++ b/tests/pipeline/test_mutex.py @@ -1,4 +1,8 @@ from panda3d.core import Mutex, ReMutex +from panda3d import core +from random import random +import pytest +import sys def test_mutex_acquire_release(): @@ -31,6 +35,82 @@ def test_mutex_try_acquire(): m.release() +def test_mutex_with(): + m = Mutex() + + rc = sys.getrefcount(m) + with m: + assert m.debug_is_locked() + + with m: + assert m.debug_is_locked() + + assert rc == sys.getrefcount(m) + + +@pytest.mark.skipif(not core.Thread.is_threading_supported(), + reason="Threading support disabled") +def test_mutex_contention(): + # As a smoke test for mutexes, we just spawn a bunch of threads that do a + # lot of mutexing and hope that we can catch any obvious issues with the + # mutex implementation, especially when compiling with DEBUG_THREADS. + m1 = Mutex() + m2 = Mutex() + m3 = Mutex() + m4 = Mutex() + + def thread_acq_rel(m): + for i in range(5000): + m.acquire() + m.release() + + def thread_nested(): + for i in range(5000): + m1.acquire() + m4.acquire() + m4.release() + m1.release() + + def thread_hand_over_hand(): + m1.acquire() + for i in range(5000): + m2.acquire() + m1.release() + m3.acquire() + m2.release() + m1.acquire() + m3.release() + + m1.release() + + def thread_sleep(m): + for i in range(250): + m.acquire() + core.Thread.sleep(random() * 0.003) + m.release() + + threads = [ + core.PythonThread(thread_acq_rel, (m1,), "", ""), + core.PythonThread(thread_acq_rel, (m2,), "", ""), + core.PythonThread(thread_acq_rel, (m3,), "", ""), + core.PythonThread(thread_acq_rel, (m4,), "", ""), + core.PythonThread(thread_nested, (), "", ""), + core.PythonThread(thread_nested, (), "", ""), + core.PythonThread(thread_nested, (), "", ""), + core.PythonThread(thread_hand_over_hand, (), "", ""), + core.PythonThread(thread_hand_over_hand, (), "", ""), + core.PythonThread(thread_sleep, (m1,), "", ""), + core.PythonThread(thread_sleep, (m2,), "", ""), + core.PythonThread(thread_sleep, (m3,), "", ""), + core.PythonThread(thread_sleep, (m4,), "", ""), + ] + for thread in threads: + thread.start(core.TP_normal, True) + + for thread in threads: + thread.join() + + def test_remutex_acquire_release(): m = ReMutex() m.acquire() @@ -58,3 +138,15 @@ def test_remutex_try_acquire(): m.release() m.release() + +def test_remutex_with(): + m = ReMutex() + + rc = sys.getrefcount(m) + with m: + assert m.debug_is_locked() + with m: + assert m.debug_is_locked() + assert m.debug_is_locked() + + assert rc == sys.getrefcount(m) diff --git a/tests/pnmimage/test_pnmimage.py b/tests/pnmimage/test_pnmimage.py index 0826e4f0b8..40d8a88683 100644 --- a/tests/pnmimage/test_pnmimage.py +++ b/tests/pnmimage/test_pnmimage.py @@ -1,4 +1,5 @@ from panda3d.core import PNMImage, PNMImageHeader +from random import randint def test_pixelspec_ctor(): @@ -19,3 +20,53 @@ def test_pixelspec_coerce(): img = PNMImage(1, 1, 4) img.set_pixel(0, 0, (1, 2, 3, 4)) assert img.get_pixel(0, 0) == (1, 2, 3, 4) + + +def test_pnmimage_to_val(): + img = PNMImage(1, 1) + assert img.to_val(-0.5) == 0 + assert img.to_val(0.0) == 0 + assert img.to_val(0.5) == 128 + assert img.to_val(1.0) == 255 + assert img.to_val(2.0) == 255 + + +def test_pnmimage_from_val(): + img = PNMImage(1, 1) + assert img.from_val(0) == 0.0 + assert img.to_val(img.from_val(128)) == 128 + assert img.from_val(255) == 1.0 + + +def test_pnmimage_quantize(): + img = PNMImage(32, 32, 3) + + for x in range(32): + for y in range(32): + img.set_xel_val(x, y, randint(0, 100), randint(50, 100), randint(0, 1)) + + hist = PNMImage.Histogram() + img.make_histogram(hist) + num_colors = hist.get_num_pixels() + assert num_colors > 100 + + img2 = PNMImage(img) + img2.quantize(100) + hist = PNMImage.Histogram() + img2.make_histogram(hist) + assert hist.get_num_pixels() <= 100 + + # Make sure that this is reasonably close + max_dist = 0 + for x in range(32): + for y in range(32): + diff = img.get_xel(x, y) - img2.get_xel(x, y) + max_dist = max(max_dist, diff.length_squared()) + + # Also make sure that they are not out of range of the original + col = img2.get_xel_val(x, y) + assert col.r <= 100 + assert col.g >= 50 and col.g <= 100 + assert col.b in (0, 1) + + assert max_dist < 0.1 ** 2 diff --git a/tests/prc/test_encrypt_stream.py b/tests/prc/test_encrypt_stream.py new file mode 100644 index 0000000000..d87f31a72a --- /dev/null +++ b/tests/prc/test_encrypt_stream.py @@ -0,0 +1,19 @@ +from panda3d import core + +import pytest + + +@pytest.mark.skipif(not hasattr(core, 'IDecryptStream'), reason="Requires OpenSSL") +def test_decrypt_stream(): + encrypted = b'[\x00\x10\x00d\x00\x07K\x08\x03\xabS\x13L\xab\x93\x1b\x15\xe4\xeel\x80u o\xd0\x80aY_]\x10\x8a\xb5\xff\x9d1\xc9\xd3\xac\x95\x04\xd8\xdf\x10\xa1' + decrypted = b'abcdefghijklmnopqrstuvwxyz' + + ss = core.StringStream(encrypted) + ds = core.IDecryptStream(ss, False, '0123456789') + + assert ds.read(len(decrypted)) == decrypted + assert ds.readall() == b'' + + # Allow seeking back to the beginning + ds.seekg(0) + assert ds.readall() == decrypted diff --git a/tests/prc/test_stream_reader.py b/tests/prc/test_stream_reader.py new file mode 100644 index 0000000000..de40e40b80 --- /dev/null +++ b/tests/prc/test_stream_reader.py @@ -0,0 +1,157 @@ +from panda3d.core import StreamReader, StringStream +import pytest + + +def test_streamreader_string(): + # Empty string + stream = StringStream(b'\x00\x00') + reader = StreamReader(stream, False) + assert reader.get_string() == '' + + # String size but no string contents + stream = StringStream(b'\x01\x00') + reader = StreamReader(stream, False) + assert reader.get_string() == '' + + # String of length 1 + stream = StringStream(b'\x01\x00A') + reader = StreamReader(stream, False) + assert reader.get_string() == 'A' + + # String with excess data + stream = StringStream(b'\x01\x00AB') + reader = StreamReader(stream, False) + assert reader.get_string() == 'A' + + # EOF before end of string + stream = StringStream(b'\x03\x00AB') + reader = StreamReader(stream, False) + assert reader.get_string() == 'AB' + + # Preserves null bytes + stream = StringStream(b'\x02\x00\x00\x00') + reader = StreamReader(stream, False) + assert reader.get_string() == '\x00\x00' + + +def test_streamreader_string32(): + # Empty string + stream = StringStream(b'\x00\x00\x00\x00') + reader = StreamReader(stream, False) + assert reader.get_string32() == '' + + # String size but no string contents + stream = StringStream(b'\x01\x00\x00\x00') + reader = StreamReader(stream, False) + assert reader.get_string32() == '' + + # String of length 1 + stream = StringStream(b'\x01\x00\x00\x00A') + reader = StreamReader(stream, False) + assert reader.get_string32() == 'A' + + # String with excess data + stream = StringStream(b'\x01\x00\x00\x00AB') + reader = StreamReader(stream, False) + assert reader.get_string32() == 'A' + + # EOF before end of string + stream = StringStream(b'\x04\x00\x00\x00AB') + reader = StreamReader(stream, False) + assert reader.get_string32() == 'AB' + + # Preserves null bytes + stream = StringStream(b'\x02\x00\x00\x00\x00\x00') + reader = StreamReader(stream, False) + assert reader.get_string32() == '\x00\x00' + + +def test_streamreader_z_string(): + # Empty stream + stream = StringStream(b'') + reader = StreamReader(stream, False) + assert reader.get_z_string() == '' + + # Empty string + stream = StringStream(b'\x00') + reader = StreamReader(stream, False) + assert reader.get_z_string() == '' + + # String of length 1 + stream = StringStream(b'A\x00') + reader = StreamReader(stream, False) + assert reader.get_z_string() == 'A' + + # String with excess data + stream = StringStream(b'ABC\x00AB') + reader = StreamReader(stream, False) + assert reader.get_z_string() == 'ABC' + + # EOF before end of string + stream = StringStream(b'ABC') + reader = StreamReader(stream, False) + assert reader.get_z_string() == 'ABC' + + +def test_streamreader_fixed_string(): + # Zero-length string + stream = StringStream(b'ABC') + reader = StreamReader(stream, False) + assert reader.get_fixed_string(0) == '' + + # Empty stream + stream = StringStream(b'') + reader = StreamReader(stream, False) + assert reader.get_fixed_string(1) == '' + + # Empty string + stream = StringStream(b'\x00') + reader = StreamReader(stream, False) + assert reader.get_fixed_string(1) == '' + + # String of length 1 + stream = StringStream(b'A') + reader = StreamReader(stream, False) + assert reader.get_fixed_string(1) == 'A' + + # String of length 1, excess data + stream = StringStream(b'ABC\x00') + reader = StreamReader(stream, False) + assert reader.get_fixed_string(1) == 'A' + + # EOF before end of string + stream = StringStream(b'AB') + reader = StreamReader(stream, False) + assert reader.get_fixed_string(4) == 'AB' + + +def test_streamreader_readline(): + # Empty stream + stream = StringStream(b'') + reader = StreamReader(stream, False) + assert reader.readline() == b'' + assert reader.readline() == b'' + + # Single line without newline + stream = StringStream(b'A') + reader = StreamReader(stream, False) + assert reader.readline() == b'A' + assert reader.readline() == b'' + + # Single newline + stream = StringStream(b'\n') + reader = StreamReader(stream, False) + assert reader.readline() == b'\n' + assert reader.readline() == b'' + + # Line with text followed by empty line + stream = StringStream(b'A\n\n') + reader = StreamReader(stream, False) + assert reader.readline() == b'A\n' + assert reader.readline() == b'\n' + assert reader.readline() == b'' + + # Preserve null byte + stream = StringStream(b'\x00\x00') + reader = StreamReader(stream, False) + assert reader.readline() == b'\x00\x00' diff --git a/tests/putil/conftest.py b/tests/putil/conftest.py new file mode 100644 index 0000000000..6240f0111c --- /dev/null +++ b/tests/putil/conftest.py @@ -0,0 +1,6 @@ +import pytest +from panda3d.core import ClockObject + +@pytest.fixture +def clockobj(): + return ClockObject() diff --git a/tests/putil/test_clockobject.py b/tests/putil/test_clockobject.py new file mode 100644 index 0000000000..afff7f9b3e --- /dev/null +++ b/tests/putil/test_clockobject.py @@ -0,0 +1,23 @@ +import time + +def test_get_frame_time(clockobj): + current_time = clockobj.get_frame_time() + time.sleep(2) + assert clockobj.get_frame_time() == current_time + +def test_jump_frame_time(clockobj): + current_time = clockobj.get_frame_time() + clockobj.tick() + assert clockobj.get_frame_time() == current_time + clockobj.get_frame_time() + +def test_get_real_time(clockobj): + current_time = clockobj.get_real_time() + time.sleep(2) + assert current_time != clockobj.get_real_time() + +def test_get_dt(clockobj): + clockobj.tick() + first_tick = clockobj.get_frame_time() + clockobj.tick() + second_tick = clockobj.get_frame_time() + assert clockobj.get_dt() == second_tick - first_tick diff --git a/tests/putil/test_modifierbuttons.py b/tests/putil/test_modifierbuttons.py new file mode 100644 index 0000000000..289966d1df --- /dev/null +++ b/tests/putil/test_modifierbuttons.py @@ -0,0 +1,35 @@ +from panda3d.core import ModifierButtons + + +def test_modifierbuttons_empty(): + # Tests the initial state of a ModifierButtons object. + btns = ModifierButtons() + assert btns == ModifierButtons(btns) + assert btns != ModifierButtons() + assert btns.matches(ModifierButtons()) + assert not btns.is_down("alt") + assert not btns.is_any_down() + assert not btns.has_button("alt") + assert btns.get_prefix() == "" + assert btns.get_num_buttons() == 0 + assert len(btns.buttons) == 0 + + +def test_modifierbuttons_cow(): + # Tests the copy-on-write mechanism of the button list. + btns1 = ModifierButtons() + btns1.add_button("space") + + # Modifying original should not affect copy + btns2 = ModifierButtons(btns1) + assert tuple(btns2.buttons) == tuple(btns1.buttons) + btns1.add_button("enter") + assert tuple(btns1.buttons) == ("space", "enter") + assert tuple(btns2.buttons) == ("space",) + + # Modifying copy should not affect original + btns3 = ModifierButtons(btns2) + assert tuple(btns3.buttons) == tuple(btns2.buttons) + btns3.add_button("escape") + assert tuple(btns2.buttons) == ("space",) + assert tuple(btns3.buttons) == ("space", "escape") diff --git a/tests/putil/test_sparsearray.py b/tests/putil/test_sparsearray.py new file mode 100644 index 0000000000..b7767d6e8a --- /dev/null +++ b/tests/putil/test_sparsearray.py @@ -0,0 +1,234 @@ +from panda3d import core + + +def test_sparse_array_set_bit_to(): + """Tests SparseArray behavior for set_bit_to().""" + + s = core.SparseArray() + s.set_bit_to(5, True) + assert s.get_bit(5) + + s = core.SparseArray.all_on() + s.set_bit_to(5, False) + assert not s.get_bit(5) + + +def test_sparse_array_clear(): + """Tests SparseArray behavior for clear().""" + + s = core.SparseArray.all_on() + s.clear() + assert s.is_zero() + assert not s.is_inverse() + assert s.get_num_subranges() == 0 + assert s.get_num_on_bits() == 0 + assert s.get_num_bits() == 0 + + s = core.SparseArray() + s.set_range(5, 10) + s.clear() + assert s.is_zero() + assert not s.is_inverse() + assert s.get_num_subranges() == 0 + assert s.get_num_on_bits() == 0 + assert s.get_num_bits() == 0 + + +def test_sparse_array_clear_range(): + # Not using parametrize because there are too many values for that. + for mask in range(0x7f): + for begin in range(8): + for size in range(8): + b = core.BitArray(mask) + s = core.SparseArray(b) + + s.clear_range(begin, size) + b.clear_range(begin, size) + + assert core.BitArray(s) == b + assert s == core.SparseArray(b) + + +def test_sparse_array_set_clear_ranges(): + """Tests SparseArray behavior for setting and clearing ranges.""" + + # test clear_range with single overlapping on-range + # (clear_range extends beyond highest on-bit) + s = core.SparseArray() + s.set_range(2, 3) + s.clear_range(3, 3) + assert s.get_bit(2) + assert not s.get_bit(3) + + # same as above, using set_range_to + s = core.SparseArray() + s.set_range_to(True, 2, 3) + s.set_range_to(False, 3, 3) + assert s.get_bit(2) + assert not s.get_bit(3) + + # test clear_range using off-range which overlaps two on-ranges + # (lowest off-bit in lowest on-range, highest off-bit in highest on-range) + s = core.SparseArray() + s.set_range(2, 3) + s.set_range(7, 3) + s.clear_range(3, 6) + assert s.get_bit(2) + assert not s.get_bit(3) + assert not s.get_bit(8) + assert s.get_bit(9) + + # same as above, using set_range_to + s = core.SparseArray() + s.set_range_to(True, 2, 3) + s.set_range_to(True, 7, 3) + s.set_range_to(False, 3, 6) + assert s.get_bit(2) + assert not s.get_bit(3) + assert not s.get_bit(8) + assert s.get_bit(9) + + +def test_sparse_array_set_range(): + """Tests SparseArray behavior for set_range().""" + + # test set_range with single overlapping off-range + # (set_range extends beyond highest off-bit) + s = core.SparseArray.all_on() + s.clear_range(2, 3) + s.set_range(3, 3) + assert not s.get_bit(2) + assert s.get_bit(3) + + # same as above, using set_range_to + s = core.SparseArray.all_on() + s.set_range_to(False, 2, 3) + s.set_range_to(True, 3, 3) + assert not s.get_bit(2) + assert s.get_bit(3) + + # test set_range using on-range which overlaps two off-ranges + # (lowest on-bit in lowest off-range, highest on-bit in highest off-range) + s = core.SparseArray.all_on() + s.clear_range(2, 3) + s.clear_range(7, 3) + s.set_range(3, 6) + assert not s.get_bit(2) + assert s.get_bit(3) + assert s.get_bit(8) + assert not s.get_bit(9) + + # same as above, using set_range_to + s = core.SparseArray.all_on() + s.set_range_to(False, 2, 3) + s.set_range_to(False, 7, 3) + s.set_range_to(True, 3, 6) + assert not s.get_bit(2) + assert s.get_bit(3) + assert s.get_bit(8) + assert not s.get_bit(9) + + +def test_sparse_array_bits_in_common(): + """Tests SparseArray behavior for has_bits_in_common().""" + + s = core.SparseArray() + t = core.SparseArray() + s.set_range(2, 4) + t.set_range(5, 4) + assert s.has_bits_in_common(t) + + s = core.SparseArray() + t = core.SparseArray() + s.set_range(2, 4) + t.set_range(6, 4) + assert not s.has_bits_in_common(t) + + +def test_sparse_array_operations(): + """Tests SparseArray behavior for various operations.""" + + # test bitshift to left + s = core.SparseArray() + s.set_bit(2) + t = s << 2 + assert t.get_bit(4) + assert not t.get_bit(2) + + # test bitshift to right + s = core.SparseArray() + s.set_bit(4) + t = s >> 2 + assert t.get_bit(2) + assert not t.get_bit(4) + + # test bitwise AND + s = core.SparseArray() + t = core.SparseArray() + s.set_bit(2) + s.set_bit(3) + t.set_bit(1) + t.set_bit(3) + u = s & t + assert not u.get_bit(0) + assert not u.get_bit(1) + assert not u.get_bit(2) + assert u.get_bit(3) + + # test bitwise OR + s = core.SparseArray() + t = core.SparseArray() + s.set_bit(2) + s.set_bit(3) + t.set_bit(1) + t.set_bit(3) + u = s | t + assert not u.get_bit(0) + assert u.get_bit(1) + assert u.get_bit(2) + assert u.get_bit(3) + + # test bitwise XOR + s = core.SparseArray() + t = core.SparseArray() + s.set_bit(2) + s.set_bit(3) + t.set_bit(1) + t.set_bit(3) + u = s ^ t + assert not u.get_bit(0) + assert u.get_bit(1) + assert u.get_bit(2) + assert not u.get_bit(3) + + +def test_sparse_array_augm_assignment(): + """Tests SparseArray behavior for augmented assignments.""" + + # test in-place bitshift to left + s = t = core.SparseArray() + t <<= 2 + assert s is t + + # test in-place bitshift to right + s = t = core.SparseArray() + t >>= 2 + assert s is t + + # test in-place bitwise AND + s = t = core.SparseArray() + u = core.SparseArray() + t &= u + assert s is t + + # test in-place bitwise OR + s = t = core.SparseArray() + u = core.SparseArray() + t |= u + assert s is t + + # test in-place bitwise XOR + s = t = core.SparseArray() + u = core.SparseArray() + t ^= u + assert s is t diff --git a/tests/requirements.txt b/tests/requirements.txt index 6bb511ebbf..3bc9b3f390 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,2 @@ pytest==3.2.0 ---pre --extra-index-url https://archive.panda3d.org/branches/deploy-ng panda3d diff --git a/tests/showbase/test_Audio3DManager.py b/tests/showbase/test_Audio3DManager.py new file mode 100644 index 0000000000..39b179bc91 --- /dev/null +++ b/tests/showbase/test_Audio3DManager.py @@ -0,0 +1,55 @@ +from direct.showbase.Audio3DManager import Audio3DManager +from panda3d import core +import pytest + + +@pytest.fixture(scope='session') +def manager3d(): + root = core.NodePath("root") + manager = core.AudioManager.create_AudioManager() + manager3d = Audio3DManager(manager, root=root) + yield manager3d + del manager3d + manager.shutdown() + + +def test_audio3dmanager_velocity(manager3d): + sound = manager3d.load_sfx("nonexistent") + + # Generate an object with movement + object = core.NodePath("object") + object.set_pos(0, 0, 0) + object.set_fluid_pos(1, 2, 3) + assert object.get_pos_delta() == (1, 2, 3) + + res = manager3d.attach_sound_to_object(sound, object) + assert res + + clock = core.ClockObject.get_global_clock() + clock.mode = core.ClockObject.M_slave + clock.dt = 0.5 + + # Test auto velocity calculation + manager3d.set_sound_velocity_auto(sound) + assert manager3d.get_sound_velocity(sound) == (2, 4, 6) + + # Test setting to fixed value + manager3d.set_sound_velocity(sound, (5, 5, 5)) + assert manager3d.get_sound_velocity(sound) == (5, 5, 5) + + +def test_audio3dmanager_weak(manager3d): + sound = manager3d.load_sfx("nonexistent") + object = core.NodePath("object") + + res = manager3d.attach_sound_to_object(sound, object) + assert res + assert object in manager3d.sound_dict + + manager3d.update() + assert object in manager3d.sound_dict + + object.remove_node() + manager3d.update() + + assert object not in manager3d.sound_dict diff --git a/tests/showbase/test_Loader.py b/tests/showbase/test_Loader.py new file mode 100644 index 0000000000..0dadecb384 --- /dev/null +++ b/tests/showbase/test_Loader.py @@ -0,0 +1,70 @@ +from panda3d.core import Filename, NodePath +from direct.showbase.Loader import Loader +import pytest + + +@pytest.fixture +def loader(): + return Loader(base=None) + + +@pytest.fixture +def temp_model(): + from panda3d.core import ModelPool, ModelRoot + + root = ModelRoot('model') + root.fullpath = '/test-model.bam' + + ModelPool.add_model(root.fullpath, root) + yield root.fullpath + ModelPool.release_model(root.fullpath) + + +def test_load_model_filename(loader, temp_model): + model = loader.load_model(Filename(temp_model)) + assert model + assert isinstance(model, NodePath) + assert model.name == 'model' + + +def test_load_model_str(loader, temp_model): + model = loader.load_model(str(temp_model)) + assert model + assert isinstance(model, NodePath) + assert model.name == 'model' + + +def test_load_model_list(loader, temp_model): + models = loader.load_model([temp_model, temp_model]) + assert models + assert isinstance(models, list) + assert len(models) == 2 + assert isinstance(models[0], NodePath) + assert isinstance(models[1], NodePath) + + +def test_load_model_tuple(loader, temp_model): + models = loader.load_model((temp_model, temp_model)) + assert models + assert isinstance(models, list) + assert len(models) == 2 + assert isinstance(models[0], NodePath) + assert isinstance(models[1], NodePath) + + +def test_load_model_set(loader, temp_model): + models = loader.load_model({temp_model}) + assert models + assert isinstance(models, list) + assert len(models) == 1 + assert isinstance(models[0], NodePath) + + +def test_load_model_missing(loader): + with pytest.raises(IOError): + loader.load_model('/nonexistent.bam') + + +def test_load_model_okmissing(loader): + model = loader.load_model('/nonexistent.bam', okMissing=True) + assert model is None diff --git a/tests/showbase/test_PythonUtil.py b/tests/showbase/test_PythonUtil.py index faf5da269f..c4c1b8076a 100644 --- a/tests/showbase/test_PythonUtil.py +++ b/tests/showbase/test_PythonUtil.py @@ -1,4 +1,5 @@ from direct.showbase import PythonUtil +import pytest def test_queue(): @@ -103,3 +104,64 @@ def test_priority_callbacks(): pc.clear() pc() assert len(l) == 0 + +def test_weighted_choice(): + # Test PythonUtil.weightedChoice() with no valid list. + with pytest.raises(IndexError): + PythonUtil.weightedChoice([]) + + # Create a sample choice list. + # This contains a few tuples containing only a weight + # and an arbitrary item. + choicelist = [(3, 'item1'), (1, 'item2'), (7, 'item3')] + + # These are the items that we expect. + items = ['item1', 'item2', 'item3'] + + # Test PythonUtil.weightedChoice() with our choice list. + item = PythonUtil.weightedChoice(choicelist) + + # Assert that what we got was at least an available item. + assert item in items + + # Create yet another sample choice list, but with a couple more items. + choicelist = [(2, 'item1'), (25, 'item2'), (14, 'item3'), (5, 'item4'), + (7, 'item5'), (3, 'item6'), (6, 'item7'), (50, 'item8')] + + # Set the items that we expect again. + items = ['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7', 'item8'] + + # The sum of all of the weights is 112. + weightsum = 2 + 25 + 14 + 5 + 7 + 3 + 6 + 50 + + # Test PythonUtil.weightedChoice() with the sum. + item = PythonUtil.weightedChoice(choicelist, sum=weightsum) + + # Assert that we got a valid item (most of the time this should be 'item8'). + assert item in items + + # Test PythonUtil.weightedChoice(), but with an invalid sum. + item = PythonUtil.weightedChoice(choicelist, sum=1) + + # Assert that we got 'item1'. + assert item == items[0] + + # Test PythonUtil.weightedChoice() with an invalid sum. + # This time, we're using 2000 so that regardless of the random + # number, we will still reach the very last item. + item = PythonUtil.weightedChoice(choicelist, sum=100000) + + # Assert that we got 'item8', since we would get the last item. + assert item == items[-1] + + # Create a bogus random function. + rnd = lambda: 0.5 + + # Test PythonUtil.weightedChoice() with the bogus function. + item = PythonUtil.weightedChoice(choicelist, rng=rnd, sum=weightsum) + + # Assert that we got 'item6'. + # We expect 'item6' because 0.5 multiplied by 112 is 56.0. + # When subtracting that number by each weight, it will reach 0 + # by the time it hits 'item6' in the iteration. + assert item == items[5] diff --git a/tests/test_imports.py b/tests/test_imports.py new file mode 100644 index 0000000000..262685f8b7 --- /dev/null +++ b/tests/test_imports.py @@ -0,0 +1,319 @@ +# These tests import Panda3D modules just to make sure that there are no +# missing imports. It is useful for a quick and dirty test to make sure +# that there are no obvious build issues. +import pytest + +# This will print out imports on the command line. +#import direct.showbase.VerboseImport + + + +def test_imports_panda3d(): + import imp, importlib, os + import panda3d + dir = os.path.dirname(panda3d.__file__) + + # Iterate over the things in the panda3d package that look like modules. + extensions = set() + for suffix in imp.get_suffixes(): + extensions.add(suffix[0]) + + for basename in os.listdir(dir): + if basename.startswith('lib'): + # This not a Python module. + continue + + module = basename.split('.', 1)[0] + ext = basename[len(module):] + + if ext in extensions: + importlib.import_module('panda3d.%s' % (module)) + + +def test_imports_direct(): + import direct.actor.Actor + import direct.actor.DistributedActor + import direct.cluster.ClusterClient + import direct.cluster.ClusterConfig + import direct.cluster.ClusterMsgs + import direct.cluster.ClusterServer + import direct.controls.BattleWalker + import direct.controls.ControlManager + import direct.controls.DevWalker + import direct.controls.GhostWalker + import direct.controls.GravityWalker + import direct.controls.InputState + import direct.controls.NonPhysicsWalker + import direct.controls.ObserverWalker + import direct.controls.PhysicsRoller + import direct.controls.PhysicsWalker + import direct.controls.SwimWalker + import direct.controls.TwoDWalker + import direct.directnotify.DirectNotify + import direct.directnotify.DirectNotifyGlobal + import direct.directnotify.Logger + import direct.directnotify.LoggerGlobal + import direct.directnotify.Notifier + import direct.directnotify.RotatingLog + import direct.directtools.DirectCameraControl + import direct.directtools.DirectGeometry + import direct.directtools.DirectGlobals + import direct.directtools.DirectGrid + import direct.directtools.DirectLights + import direct.directtools.DirectManipulation + import direct.directtools.DirectSelection + import direct.directtools.DirectUtil + import direct.directutil.DeltaProfiler + import direct.directutil.DistributedLargeBlobSender + import direct.directutil.DistributedLargeBlobSenderAI + import direct.directutil.LargeBlobSenderConsts + import direct.directutil.Mopath + import direct.directutil.Verify + import direct.directutil.WeightedChoice + import direct.dist.FreezeTool + import direct.dist.icon + import direct.dist.commands + import direct.distributed.AsyncRequest + import direct.distributed.CRCache + import direct.distributed.CRDataCache + import direct.distributed.CachedDOData + import direct.distributed.CartesianGridBase + import direct.distributed.ClientRepository + import direct.distributed.ClientRepositoryBase + import direct.distributed.ClockDelta + import direct.distributed.ConnectionRepository + import direct.distributed.DistributedCamera + import direct.distributed.DistributedCameraAI + import direct.distributed.DistributedCameraOV + import direct.distributed.DistributedCartesianGrid + import direct.distributed.DistributedCartesianGridAI + import direct.distributed.DistributedNode + import direct.distributed.DistributedNodeAI + import direct.distributed.DistributedNodeUD + import direct.distributed.DistributedObject + import direct.distributed.DistributedObjectAI + import direct.distributed.DistributedObjectBase + import direct.distributed.DistributedObjectGlobal + import direct.distributed.DistributedObjectGlobalAI + import direct.distributed.DistributedObjectGlobalUD + import direct.distributed.DistributedObjectOV + import direct.distributed.DistributedObjectUD + import direct.distributed.DistributedSmoothNodeAI + import direct.distributed.DistributedSmoothNodeBase + import direct.distributed.DoCollectionManager + import direct.distributed.DoHierarchy + import direct.distributed.DoInterestManager + import direct.distributed.GridChild + import direct.distributed.GridParent + import direct.distributed.InterestWatcher + import direct.distributed.MsgTypes + import direct.distributed.MsgTypesCMU + import direct.distributed.NetMessenger + import direct.distributed.ParentMgr + import direct.distributed.PyDatagram + import direct.distributed.PyDatagramIterator + import direct.distributed.RelatedObjectMgr + import direct.distributed.SampleObject + import direct.distributed.ServerRepository + import direct.distributed.StagedObject + import direct.distributed.TimeManager + import direct.distributed.TimeManagerAI + import direct.extensions_native.extension_native_helpers + import direct.filter.CommonFilters + import direct.filter.FilterManager + import direct.fsm.ClassicFSM + import direct.fsm.FSM + import direct.fsm.FourState + import direct.fsm.FourStateAI + import direct.fsm.SampleFSM + import direct.fsm.State + import direct.fsm.StateData + import direct.fsm.StatePush + import direct.gui.DirectButton + import direct.gui.DirectCheckBox + import direct.gui.DirectCheckButton + import direct.gui.DirectDialog + import direct.gui.DirectEntry + import direct.gui.DirectEntryScroll + import direct.gui.DirectFrame + import direct.gui.DirectGui + import direct.gui.DirectGuiBase + import direct.gui.DirectGuiGlobals + import direct.gui.DirectGuiTest + import direct.gui.DirectLabel + import direct.gui.DirectOptionMenu + import direct.gui.DirectRadioButton + import direct.gui.DirectScrollBar + import direct.gui.DirectScrolledFrame + import direct.gui.DirectScrolledList + import direct.gui.DirectSlider + import direct.gui.DirectWaitBar + import direct.gui.OnscreenGeom + import direct.gui.OnscreenImage + import direct.gui.OnscreenText + import direct.interval.ActorInterval + import direct.interval.AnimControlInterval + import direct.interval.FunctionInterval + import direct.interval.IndirectInterval + import direct.interval.Interval + import direct.interval.IntervalGlobal + import direct.interval.IntervalManager + import direct.interval.IntervalTest + import direct.interval.LerpBlendHelpers + import direct.interval.LerpInterval + import direct.interval.MetaInterval + import direct.interval.MopathInterval + import direct.interval.ParticleInterval + import direct.interval.ProjectileInterval + import direct.interval.ProjectileIntervalTest + import direct.interval.SoundInterval + import direct.interval.TestInterval + import direct.motiontrail.MotionTrail + import direct.particles.ForceGroup + import direct.particles.GlobalForceGroup + import direct.particles.ParticleEffect + import direct.particles.ParticleFloorTest + import direct.particles.ParticleManagerGlobal + import direct.particles.ParticleTest + import direct.particles.Particles + import direct.particles.SpriteParticleRendererExt + import direct.physics.FallTest + import direct.physics.RotationTest + import direct.showbase.Audio3DManager + import direct.showbase.BufferViewer + import direct.showbase.BulletinBoard + import direct.showbase.BulletinBoardGlobal + import direct.showbase.BulletinBoardWatcher + import direct.showbase.ContainerLeakDetector + import direct.showbase.ContainerReport + import direct.showbase.CountedResource + import direct.showbase.DirectObject + import direct.showbase.DistancePhasedNode + import direct.showbase.EventGroup + import direct.showbase.EventManager + import direct.showbase.EventManagerGlobal + import direct.showbase.ExceptionVarDump + import direct.showbase.Factory + import direct.showbase.Finder + import direct.showbase.GarbageReport + import direct.showbase.GarbageReportScheduler + import direct.showbase.InputStateGlobal + import direct.showbase.Job + import direct.showbase.JobManager + import direct.showbase.JobManagerGlobal + import direct.showbase.LeakDetectors + import direct.showbase.Loader + import direct.showbase.Messenger + import direct.showbase.MessengerGlobal + import direct.showbase.MessengerLeakDetector + import direct.showbase.MirrorDemo + import direct.showbase.ObjectPool + import direct.showbase.ObjectReport + import direct.showbase.OnScreenDebug + import direct.showbase.PhasedObject + import direct.showbase.PhysicsManagerGlobal + import direct.showbase.Pool + import direct.showbase.ProfileSession + import direct.showbase.PythonUtil + import direct.showbase.RandomNumGen + import direct.showbase.ReferrerSearch + import direct.showbase.SfxPlayer + import direct.showbase.ShadowDemo + import direct.showbase.ShadowPlacer + import direct.showbase.ShowBase + import direct.showbase.TaskThreaded + import direct.showbase.ThreeUpShow + import direct.showbase.Transitions + import direct.showbase.VFSImporter + import direct.showbase.WxGlobal + import direct.showutil.BuildGeometry + import direct.showutil.Effects + import direct.showutil.Rope + import direct.showutil.TexMemWatcher + import direct.showutil.TexViewer + import direct.stdpy.file + import direct.stdpy.glob + import direct.stdpy.pickle + import direct.stdpy.thread + import direct.stdpy.threading + import direct.stdpy.threading2 + import direct.task.FrameProfiler + import direct.task.MiniTask + import direct.task.Task + import direct.task.TaskManagerGlobal + import direct.task.TaskProfiler + import direct.task.TaskTester + import direct.task.Timer + + +def test_imports_tk(): + Pmw = pytest.importorskip('Pmw') + + import direct.showbase.TkGlobal + import direct.tkpanels.AnimPanel + import direct.tkpanels.DirectSessionPanel + import direct.tkpanels.FSMInspector + import direct.tkpanels.Inspector + import direct.tkpanels.MopathRecorder + import direct.tkpanels.NotifyPanel + import direct.tkpanels.ParticlePanel + import direct.tkpanels.Placer + import direct.tkpanels.TaskManagerPanel + import direct.tkwidgets.AppShell + import direct.tkwidgets.Dial + import direct.tkwidgets.EntryScale + import direct.tkwidgets.Floater + import direct.tkwidgets.MemoryExplorer + import direct.tkwidgets.ProgressBar + import direct.tkwidgets.SceneGraphExplorer + import direct.tkwidgets.Slider + import direct.tkwidgets.Tree + import direct.tkwidgets.Valuator + import direct.tkwidgets.VectorWidgets + import direct.tkwidgets.WidgetPropertiesDialog + + +def test_imports_wx(): + wx = pytest.importorskip('wx') + + import direct.wxwidgets.ViewPort + import direct.wxwidgets.WxAppShell + import direct.wxwidgets.WxPandaShell + import direct.wxwidgets.WxPandaWindow + import direct.wxwidgets.WxSlider + + import direct.leveleditor.ActionMgr + import direct.leveleditor.AnimControlUI + import direct.leveleditor.AnimGlobals + import direct.leveleditor.AnimMgr + import direct.leveleditor.AnimMgrBase + import direct.leveleditor.CurveAnimUI + import direct.leveleditor.CurveEditor + import direct.leveleditor.FileMgr + import direct.leveleditor.GraphEditorUI + import direct.leveleditor.HotKeyUI + import direct.leveleditor.LayerEditorUI + import direct.leveleditor.LevelEditor + import direct.leveleditor.LevelEditorBase + import direct.leveleditor.LevelEditorStart + import direct.leveleditor.LevelEditorUI + import direct.leveleditor.LevelEditorUIBase + import direct.leveleditor.LevelLoader + import direct.leveleditor.LevelLoaderBase + import direct.leveleditor.MayaConverter + import direct.leveleditor.ObjectGlobals + import direct.leveleditor.ObjectHandler + import direct.leveleditor.ObjectMgr + import direct.leveleditor.ObjectMgrBase + import direct.leveleditor.ObjectPalette + import direct.leveleditor.ObjectPaletteBase + import direct.leveleditor.ObjectPaletteUI + import direct.leveleditor.ObjectPropertyUI + import direct.leveleditor.PaletteTreeCtrl + import direct.leveleditor.ProtoObjs + import direct.leveleditor.ProtoObjsUI + import direct.leveleditor.ProtoPalette + import direct.leveleditor.ProtoPaletteBase + import direct.leveleditor.ProtoPaletteUI + import direct.leveleditor.SceneGraphUI + import direct.leveleditor.SceneGraphUIBase