diff --git a/README.md b/README.md index a4bd9f39d5..061497dd6d 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ are included as part of the Windows 7.1 SDK. You will also need to have the third-party dependency libraries available for the build scripts to use. These are available from one of these two URLs, depending on whether you are on a 32-bit or 64-bit system: -https://www.panda3d.org/download/panda3d-1.9.0/panda3d-1.9.0-tools-win32.zip -https://www.panda3d.org/download/panda3d-1.9.0/panda3d-1.9.0-tools-win64.zip +https://www.panda3d.org/download/panda3d-1.9.1/panda3d-1.9.1-tools-win32.zip +https://www.panda3d.org/download/panda3d-1.9.1/panda3d-1.9.1-tools-win64.zip After acquiring these dependencies, you may simply build Panda3D from the command prompt using the following command: @@ -97,7 +97,7 @@ Mac OS X -------- On Mac OS X, 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.0/panda3d-1.9.0-tools-mac.tar.gz). +compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.9.1/panda3d-1.9.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: diff --git a/direct/src/distributed/ClockDelta.py b/direct/src/distributed/ClockDelta.py index 20f3274003..21c282cb85 100644 --- a/direct/src/distributed/ClockDelta.py +++ b/direct/src/distributed/ClockDelta.py @@ -1,7 +1,7 @@ # ClockDelta provides the ability to use clock synchronization for # distributed objects -from pandac.PandaModules import * +from panda3d.core import ClockObject from direct.directnotify import DirectNotifyGlobal from direct.showbase import DirectObject import math diff --git a/direct/src/gui/DirectDialog.py b/direct/src/gui/DirectDialog.py index 1aa75332d0..9c705f7ef4 100644 --- a/direct/src/gui/DirectDialog.py +++ b/direct/src/gui/DirectDialog.py @@ -94,8 +94,8 @@ class DirectDialog(DirectFrame): ('text', '', None), ('text_align', TextNode.ALeft, None), ('text_scale', 0.06, None), - ('image', None, None), - ('relief', DGG.RAISED, None), + ('image', DGG.getDefaultDialogGeom(), None), + ('relief', DGG.getDefaultDialogRelief(), None), ('borderWidth', (0.01, 0.01), None), ('buttonTextList', [], DGG.INITOPT), ('buttonGeomList', [], DGG.INITOPT), @@ -316,7 +316,8 @@ class DirectDialog(DirectFrame): # reduce bottom by pad, button height and 2*button pad b = min(b - self['midPad'] - bpad[1] - bHeight - bpad[1], b) - pad[1] t = t + self['topPad'] + pad[1] - self['frameSize'] = (l, r, b, t) + if self['frameSize'] is None: + self['frameSize'] = (l, r, b, t) self['image_scale'] = (r - l, 1, t - b) # Center frame about text and buttons self['image_pos'] = ((l+r)*0.5, 0.0, (b+t)*0.5) diff --git a/direct/src/gui/DirectGuiGlobals.py b/direct/src/gui/DirectGuiGlobals.py index 0602f9cfc0..a84442da87 100644 --- a/direct/src/gui/DirectGuiGlobals.py +++ b/direct/src/gui/DirectGuiGlobals.py @@ -14,6 +14,7 @@ defaultFontFunc = TextNode.getDefaultFont defaultClickSound = None defaultRolloverSound = None defaultDialogGeom = None +defaultDialogRelief = PGFrameStyle.TBevelOut drawOrder = 100 panel = None @@ -132,13 +133,16 @@ def setDefaultFontFunc(newFontFunc): def getDefaultDialogGeom(): global defaultDialogGeom - if defaultDialogGeom == None: - defaultDialogGeom = loader.loadModel('models/gui/dialog_box_gui', okMissing = True) return defaultDialogGeom -def setDefaultDialogGeom(newDialogGeom): - global defaultDialogGeom +def getDefaultDialogRelief(): + global defaultDialogRelief + return defaultDialogRelief + +def setDefaultDialogGeom(newDialogGeom, relief=None): + global defaultDialogGeom, defaultDialogRelief defaultDialogGeom = newDialogGeom + defaultDialogRelief = relief def getDefaultDrawOrder(): return drawOrder diff --git a/direct/src/p3d/DeploymentTools.py b/direct/src/p3d/DeploymentTools.py index 7f18bd10e7..8d03fd94bc 100644 --- a/direct/src/p3d/DeploymentTools.py +++ b/direct/src/p3d/DeploymentTools.py @@ -5,6 +5,7 @@ to build for as many platforms as possible. """ __all__ = ["Standalone", "Installer"] import os, sys, subprocess, tarfile, shutil, time, zipfile, socket, getpass, struct +import gzip from io import BytesIO, TextIOWrapper from direct.directnotify.DirectNotifyGlobal import * from direct.showbase.AppRunnerGlobal import appRunner @@ -994,7 +995,7 @@ class Installer: 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("pkmkrpkg1") + pkginfo.write("pmkrpkg1") pkginfo.close() pkginfo = open(Filename(output, "Contents/Resources/package_version").toOsSpecific(), "w") pkginfo.write("major: 1\nminor: 9") @@ -1079,18 +1080,18 @@ class Installer: plist.write('\n') plist.close() - if hasattr(tarfile, "PAX_FORMAT"): - archive = tarfile.open(Filename(output, "Contents/Archive.pax.gz").toOsSpecific(), "w:gz", format = tarfile.PAX_FORMAT, tarinfo = TarInfoRootOSX) - else: - archive = tarfile.open(Filename(output, "Contents/Archive.pax.gz").toOsSpecific(), "w:gz", tarinfo = TarInfoRootOSX) - archive.add(appfn.toOsSpecific(), appname) + # 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 - archive = Filename(output.getDirname(), "%s %s.zip" % (self.fullname, self.version)) + zip_fn = Filename(output.getDirname(), "%s %s.pkg.zip" % (self.fullname, self.version)) dir = Filename(output.getDirname()) dir.makeAbsolute() - zip = zipfile.ZipFile(archive.toOsSpecific(), 'w') + 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)) @@ -1101,6 +1102,61 @@ class Installer: 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 diff --git a/direct/src/p3d/Packager.py b/direct/src/p3d/Packager.py index 14e8a9fa3c..e097e8da3c 100644 --- a/direct/src/p3d/Packager.py +++ b/direct/src/p3d/Packager.py @@ -2427,7 +2427,7 @@ class Packager: # Binary files that are considered uncompressible, and are # copied without compression. - self.uncompressibleExtensions = [ 'mp3', 'ogg', 'wav', 'rml', 'rcss', 'otf' ] + 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 @@ -3744,8 +3744,7 @@ class Packager: self.__recurseDir(dirname, newDir, unprocessed = unprocessed) def __recurseDir(self, filename, newName, unprocessed = None, packageTree = None): - dirList = vfs.scanDirectory(filename) - if dirList: + if filename.isDirectory(): # It's a directory name. Recurse. prefix = newName if prefix and prefix[-1] != '/': @@ -3753,6 +3752,7 @@ class Packager: # 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': @@ -3764,6 +3764,9 @@ class Packager: 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() diff --git a/direct/src/p3d/panda3d.pdef b/direct/src/p3d/panda3d.pdef index e85d7f290e..8317d0442b 100644 --- a/direct/src/p3d/panda3d.pdef +++ b/direct/src/p3d/panda3d.pdef @@ -103,8 +103,6 @@ class panda3d(package): excludeModule('MySQLdb', '_mysql') - excludeModule('xml', 'xml.parsers.expat', 'xml.sax') - # 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, diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index 3bb5d7f829..da1575fcdd 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -3,31 +3,107 @@ This minor release fixes some important regressions and bugs found in 1.9.0, but also introduces a few minor features. -It also reintroduces the deployment pipeline that was absent from +It also reintroduces the deployment tools that were absent from the previous release. -* Textures were not being scaled to power-of-2 in some cases -* Fix various issues with shader inputs -* Bullet step function accidentally defaulted to step size of 0 -* Use model-path for finding libRocket assets -* Fix inconsistent behavior with non-power-of-2 textures in rocket -* Fix regression with memoryviews -* Fix symbol error when loading libp3ffmpeg on Mac OS X +The following issues were fixed: +* SDK now properly installs in Mac OS X 10.11 "El Capitan" +* Windows 8.1+ no longer applies DPI virtualization to Panda window +* Fix ffmpeg library load issue on Mac OS X * Fix issues running maya2egg on Mac OS X -* PStats now tracks memory residency of graphics buffers -* Support wireframe and point rendering modes in OpenGL ES -* Add missing keys to libRocket keymap +* Fix compiler errors on different platforms +* Fix various rare crashes +* Fix crashes on shutdown in threaded pipeline +* Fix low-level threading crash on ARM machines +* More reliably and robustly handle failures opening OpenAL device +* Textures were not being scaled to power-of-2 in some cases +* Correct scaling of normal vectors with flatten operation +* Correct positioning of viewing axis when showing lens frustum +* Add dpi-window-resize option to auto-resize window on DPI change +* Fix assertions when alpha-file-channel references unknown channel +* Use OpenGL-style vertex colors by default on non-Windows systems +* Default vertex column alignment is now 4 bytes +* Add PNMImage premultiply/unpremultiply methods. * Fix incorrect parsing of numbers with exponents in Config.prc -* Various performance optimizations * Fix for reading URLs mounted via the virtual file system -* Improve GLSL error reporting -* Fix issue with model disappearing in rare cases with GLSL * Fix shader generator memory leaks and runtime performance -* Add M_confined mouse mode that keeps cursor in window +* Fix shader generator scaling of binormals and tangents * Expose _NET_WM_PID to window managers in X11 -* bam2egg supports collision sphere and plane solids -* Add sample program demonstrating mouse modes +* Fix a range of bugs in tinydisplay renderer. +* Don't error when setting lens far distance to infinity +* Allow passing custom lens to saveCubeMap/saveSphereMap +* Fix errors in saveCubeMap/saveSphereMap in threaded pipeline +* Fix DynamicTextFont.makeCopy() +* Make Texture memory size estimation more accurate +* Fix various window resizing issues +* Fix PandaSystem.getCompiler() value for clang (it reported gcc) +* x2egg no longer replaces face normals with vertex normals +* Include Eigen headers in Mac and Windows SDK +* Added geomipterrain-incorrect-normals setting, default=true +* DisplayInformation resolution list was missing on Windows +* Upgrade FMOD and Bullet versions on Windows and Mac OS X +* Various performance optimizations +* Fixed various other bugs not listed here. + +Fixes and improvements for the runtime: +* Fix splash screen freezing in the X11 web plug-in +* pdeploy will now handle extracted files (eg. .ico and .cur) +* Added more options for customizing splash screen +* Fix missing xml and ast modules from morepy package +* Certificate dialog is now localized to various languages +* Fix packp3d error when Python file is not in a package +* Pass on failing exit status from packaged application +* Remove annoying ":Packager(warning): No such file" warning +* Fix issue installing pdeploy-generated .pkg on OS X 10.11 + +Fixes for the Python API: +* Fix mysterious and rare crash in tp_traverse +* Bullet step function accidentally defaulted to step size of 0 +* Fix overflow of file offsets (eg. when seeking in huge files) +* Fix regression with memoryviews +* Fix hasattr/getattr of vector classes for invalid attributes +* Allow passing a long to methods accepting an int +* Fix crash when passing None to Filename constructor +* MouseWatcherGroup was erroneously not exposed in 1.9.0 +* ShowBase no longer unmounts VFS when shutting down +* No longer requires setting PATH to import panda3d.* +* DirectDialog default geom is once again respected +* DirectDialog no longer overrides custom frameSize +* Fix WebcamVideo/MicrophoneAudio.getOptions() methods + +Changes relating to the OpenGL renderer: +* Various performance improvements +* Fix point/line thickness setting +* Improve GLSL error reporting +* Fix Intel driver issues, particularly with geometry shaders +* Add more error checking for parameter types +* Integer shader inputs were not being converted to float properly +* Fix crash passing an undersized array to a GLSL shader input +* p3d_ColorScale et al may now be declared as vec3 +* Fix flickering when using trans_model_to_apiview in Cg +* Support wireframe and point rendering modes in OpenGL ES +* Fix issue with model disappearing in rare cases with GLSL +* Fix ColorWriteAttrib not working as it should +* Allow deactivating PStats collectors for GPU timers +* Memory residency of graphics buffers now tracked by PStats +* Allow changing OpenGL coordinate system with gl-coordinate-system + +Fixes for libRocket integration: +* libRocket did not work on Mac OS X in 1.9.0 +* Fix inconsistent behavior with non-power-of-2 textures in rocket +* Use model-path for finding libRocket assets +* Add missing keys to libRocket keymap +* libRocket elements showed up white in tinydisplay + +New features: * Add -L (lighting) and -P (graphics pipe) pview options +* Add M_confined mouse mode that keeps cursor in window +* Add sample program demonstrating mouse modes +* bam2egg supports collision sphere and plane solids +* p3d_TransformTable GLSL input backported from 1.10 branch +* Add openal-device setting for selecting OpenAL audio output +* Add limited modification timestamp tracking for Ramdisk mounts +* Support for Autodesk Maya 2016 ------------------------ RELEASE 1.9.0 ------------------------ diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 0a74c31349..d3ac6dcf4e 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -327,7 +327,7 @@ def parseopts(args): else: PkgDisable("TOUCHINPUT") - if clean_build: + if clean_build and os.path.isdir(GetOutputDir()): print("Deleting %s" % (GetOutputDir())) shutil.rmtree(GetOutputDir()) @@ -783,9 +783,7 @@ if (COMPILER=="GCC"): rocket_libs = ("RocketCore", "RocketControls") if (GetOptimize() <= 3): rocket_libs += ("RocketDebugger",) - if (GetHost() != "darwin"): - # We use a statically linked libboost_python on OSX - rocket_libs += ("boost_python",) + rocket_libs += ("boost_python",) SmartPkgEnable("ROCKET", "", rocket_libs, "Rocket/Core.h") if not PkgSkip("PYTHON"): @@ -2762,9 +2760,15 @@ if tp_dir is not None: dylibs = set() if GetTarget() == 'darwin': + # Make a list of all the dylibs we ship, to figure out whether we should use + # install_name_tool to correct the library reference to point to our copy. for lib in glob.glob(tp_dir + "/*/lib/*.dylib"): dylibs.add(os.path.basename(lib)) + if not PkgSkip("PYTHON"): + for lib in glob.glob(tp_dir + "/*/lib/" + SDK["PYTHONVERSION"] + "/*.dylib"): + dylibs.add(os.path.basename(lib)) + for pkg in PkgListGet(): if PkgSkip(pkg): continue @@ -2775,48 +2779,68 @@ if tp_dir is not None: CopyAllFiles(GetOutputDir() + "/bin/", tp_pkg + "/bin/") if (PkgSkip("PYTHON")==0 and os.path.exists(tp_pkg + "/bin/" + SDK["PYTHONVERSION"])): CopyAllFiles(GetOutputDir() + "/bin/", tp_pkg + "/bin/" + SDK["PYTHONVERSION"] + "/") - else: + + elif GetTarget() == 'darwin': + tp_libs = glob.glob(tp_pkg + "/lib/*.dylib") + + if not PkgSkip("PYTHON"): + tp_libs += glob.glob(os.path.join(tp_pkg, "lib", SDK["PYTHONVERSION"], "*.dylib")) + tp_libs += glob.glob(os.path.join(tp_pkg, "lib", SDK["PYTHONVERSION"], "*.so")) + if pkg != 'PYTHON': + tp_libs += glob.glob(os.path.join(tp_pkg, "lib", SDK["PYTHONVERSION"], "*.py")) + + for tp_lib in tp_libs: + basename = os.path.basename(tp_lib) + if basename.endswith('.dylib'): + # It's a dynamic link library. Put it in the lib directory. + target = GetOutputDir() + "/lib/" + basename + dep_prefix = "@loader_path/../lib/" + lib_id = dep_prefix + basename + else: + # It's a Python module, like _rocketcore.so. Copy it to the root, because + # nowadays the 'lib' directory may no longer be on the PYTHONPATH. + target = GetOutputDir() + "/" + basename + dep_prefix = "@loader_path/lib/" + lib_id = basename + + if not NeedsBuild([target], [tp_lib]): + continue + + CopyFile(target, tp_lib) + if os.path.islink(target) or target.endswith('.py'): + continue + + # Correct the inter-library dependencies so that the build is relocatable. + oscmd('install_name_tool -id %s %s' % (lib_id, target)) + oscmd("otool -L %s | grep .dylib > %s/tmp/otool-libs.txt" % (target, GetOutputDir()), True) + + for line in open(GetOutputDir() + "/tmp/otool-libs.txt", "r"): + line = line.strip() + if not line or line.startswith(dep_prefix) or line.endswith(":"): + continue + + libdep = line.split(" ", 1)[0] + dep_basename = os.path.basename(libdep) + if dep_basename in dylibs: + oscmd("install_name_tool -change %s %s%s %s" % (libdep, dep_prefix, dep_basename, target), True) + + JustBuilt([target], [tp_lib]) + + for fwx in glob.glob(tp_pkg + "/*.framework"): + CopyTree(GetOutputDir() + "/Frameworks/" + os.path.basename(fwx), fwx) + + else: # Linux / FreeBSD case. for tp_lib in glob.glob(tp_pkg + "/lib/*.so*"): CopyFile(GetOutputDir() + "/lib/" + os.path.basename(tp_lib), tp_lib) if not PkgSkip("PYTHON"): for tp_lib in glob.glob(os.path.join(tp_pkg, "lib", SDK["PYTHONVERSION"], "*.so*")): - CopyFile(GetOutputDir() + "/lib/" + os.path.basename(tp_lib), tp_lib) - - if GetTarget() == 'darwin': - tp_libs = glob.glob(tp_pkg + "/lib/*.dylib") - - if not PkgSkip("PYTHON"): - tp_libs += glob.glob(os.path.join(tp_pkg, "lib", SDK["PYTHONVERSION"], "*.dylib")) - - for tp_lib in tp_libs: - basename = os.path.basename(tp_lib) - target = GetOutputDir() + "/lib/" + basename - if not NeedsBuild([target], [tp_lib]): - continue - - CopyFile(target, tp_lib) - if os.path.islink(target): - continue - - # Correct the inter-library dependencies so that the build is relocatable. - oscmd('install_name_tool -id @loader_path/../lib/%s %s' % (basename, target)) - oscmd("otool -L %s | grep .dylib > %s/tmp/otool-libs.txt" % (target, GetOutputDir()), True) - - for line in open(GetOutputDir() + "/tmp/otool-libs.txt", "r"): - line = line.strip() - if not line or line.startswith('@loader_path/../lib/') or line.endswith(":"): - continue - - libdep = line.split(" ", 1)[0] - dep_basename = os.path.basename(libdep) - if dep_basename in dylibs: - oscmd("install_name_tool -change %s @loader_path/../lib/%s %s" % (libdep, dep_basename, target), True) - - JustBuilt([target], [tp_lib]) - - for fwx in glob.glob(tp_pkg + "/*.framework"): - CopyTree(GetOutputDir() + "/Frameworks/" + os.path.basename(fwx), fwx) + base = os.path.basename(tp_lib) + if base.startswith('lib'): + CopyFile(GetOutputDir() + "/lib/" + base, tp_lib) + else: + # It's a Python module, like _rocketcore.so. + CopyFile(GetOutputDir() + "/" + base, tp_lib) if GetTarget() == 'windows': CopyAllFiles(GetOutputDir() + "/bin/", tp_dir + "extras/bin/") @@ -2853,8 +2877,8 @@ if (PkgSkip("PYTHON")==0 and os.path.isdir(GetThirdpartyBase()+"/Pmw")): ConditionalWriteFile(GetOutputDir()+'/include/ctl3d.h', '/* dummy file to make MAX happy */') # Since Eigen is included by all sorts of core headers, as a convenience -# to C++ users on Windows, we include it in the Panda include directory. -if not PkgSkip("EIGEN") and GetTarget() == "windows" and GetThirdpartyDir(): +# to C++ users on Win and Mac, we include it in the Panda include directory. +if not PkgSkip("EIGEN") and GetTarget() in ("windows", "darwin") and GetThirdpartyDir(): CopyTree(GetOutputDir()+'/include/Eigen', GetThirdpartyDir()+'eigen/include/Eigen') ######################################################################## @@ -6799,7 +6823,6 @@ def MakeInstallerOSX(): if (os.path.exists("Panda3D-rw.dmg")): oscmd('rm -f Panda3D-rw.dmg') oscmd("mkdir -p dstroot/base/Developer/Panda3D/lib") - oscmd("mkdir -p dstroot/base/Developer/Panda3D/panda3d") oscmd("mkdir -p dstroot/base/Developer/Panda3D/etc") oscmd("cp %s/etc/Config.prc dstroot/base/Developer/Panda3D/etc/Config.prc" % GetOutputDir()) oscmd("cp %s/etc/Confauto.prc dstroot/base/Developer/Panda3D/etc/Confauto.prc" % GetOutputDir()) @@ -6810,43 +6833,42 @@ def MakeInstallerOSX(): if os.path.isdir(GetOutputDir()+"/plugins"): oscmd("cp -R %s/plugins dstroot/base/Developer/Panda3D/plugins" % GetOutputDir()) - install_libs = [] + # Libraries that shouldn't be in base, but are instead in other modules. + no_base_libs = ['libp3ffmpeg', 'libp3fmod_audio', 'libfmodex', 'libfmodexL'] + for base in os.listdir(GetOutputDir()+"/lib"): - if (not base.endswith(".a")): - install_libs.append("lib/"+base) - for base in os.listdir(GetOutputDir()+"/panda3d"): - if (not base.endswith(".a")): - install_libs.append("panda3d/"+base) + if not base.endswith(".a") and base.split('.')[0] not in no_base_libs: + libname = "dstroot/base/Developer/Panda3D/lib/" + base + # We really need to specify -R in order not to follow symlinks + # On OSX, just specifying -P is not enough to do that. + oscmd("cp -R -P " + GetOutputDir() + "/lib/" + base + " " + libname) - for base in install_libs: - libname = "dstroot/base/Developer/Panda3D/" + base - # We really need to specify -R in order not to follow symlinks - # On OSX, just specifying -P is not enough to do that. - oscmd("cp -R -P " + GetOutputDir() + "/" + base + " " + libname) - - oscmd("mkdir -p dstroot/tools/Developer/Tools/Panda3D") - oscmd("mkdir -p dstroot/tools/Developer/Panda3D") + oscmd("mkdir -p dstroot/tools/Developer/Panda3D/bin") + oscmd("mkdir -p dstroot/tools/Developer/Tools") + oscmd("ln -s ../Panda3D/bin dstroot/tools/Developer/Tools/Panda3D") oscmd("mkdir -p dstroot/tools/etc/paths.d") # Trailing newline is important, works around a bug in OSX - WriteFile("dstroot/tools/etc/paths.d/Panda3D", "/Developer/Tools/Panda3D\n") + WriteFile("dstroot/tools/etc/paths.d/Panda3D", "/Developer/Panda3D/bin\n") oscmd("mkdir -p dstroot/tools/usr/local/share/man/man1") oscmd("cp doc/man/*.1 dstroot/tools/usr/local/share/man/man1/") for base in os.listdir(GetOutputDir()+"/bin"): - binname = "dstroot/tools/Developer/Tools/Panda3D/" + base + binname = "dstroot/tools/Developer/Panda3D/bin/" + base # OSX needs the -R argument to copy symbolic links correctly, it doesn't have -d. How weird. oscmd("cp -R " + GetOutputDir() + "/bin/" + base + " " + binname) if PkgSkip("PYTHON")==0: PV = SDK["PYTHONVERSION"].replace("python", "") oscmd("mkdir -p dstroot/pythoncode/usr/local/bin") - oscmd("mkdir -p dstroot/pythoncode/Developer/Panda3D") + oscmd("mkdir -p dstroot/pythoncode/Developer/Panda3D/panda3d") oscmd("mkdir -p dstroot/pythoncode/Library/Python/%s/site-packages" % PV) WriteFile("dstroot/pythoncode/Library/Python/%s/site-packages/Panda3D.pth" % PV, "/Developer/Panda3D") oscmd("cp -R %s/pandac dstroot/pythoncode/Developer/Panda3D/pandac" % GetOutputDir()) oscmd("cp -R %s/direct dstroot/pythoncode/Developer/Panda3D/direct" % GetOutputDir()) oscmd("ln -s %s dstroot/pythoncode/usr/local/bin/ppython" % SDK["PYTHONEXEC"]) + oscmd("cp -R %s/*.so dstroot/pythoncode/Developer/Panda3D/" % GetOutputDir()) + oscmd("cp -R %s/*.py dstroot/pythoncode/Developer/Panda3D/" % GetOutputDir()) if os.path.isdir(GetOutputDir()+"/Pmw"): oscmd("cp -R %s/Pmw dstroot/pythoncode/Developer/Panda3D/Pmw" % GetOutputDir()) compileall.compile_dir("dstroot/pythoncode/Developer/Panda3D/Pmw") @@ -6855,6 +6877,26 @@ def MakeInstallerOSX(): if ((base != "extensions") and (base != "extensions_native")): compileall.compile_dir("dstroot/pythoncode/Developer/Panda3D/direct/"+base) + for base in os.listdir(GetOutputDir()+"/panda3d"): + if base.endswith('.py') or base.endswith('.so'): + libname = "dstroot/pythoncode/Developer/Panda3D/panda3d/" + base + # We really need to specify -R in order not to follow symlinks + # On OSX, just specifying -P is not enough to do that. + oscmd("cp -R -P " + GetOutputDir() + "/panda3d/" + base + " " + libname) + + if not PkgSkip("FFMPEG"): + oscmd("mkdir -p dstroot/ffmpeg/Developer/Panda3D/lib") + oscmd("cp -R %s/lib/libp3ffmpeg.* dstroot/ffmpeg/Developer/Panda3D/lib/" % GetOutputDir()) + + #if not PkgSkip("OPENAL"): + # oscmd("mkdir -p dstroot/openal/Developer/Panda3D/lib") + # oscmd("cp -R %s/lib/libp3openal_audio.* dstroot/openal/Developer/Panda3D/lib/" % GetOutputDir()) + + if not PkgSkip("FMODEX"): + oscmd("mkdir -p dstroot/fmodex/Developer/Panda3D/lib") + oscmd("cp -R %s/lib/libp3fmod_audio.* dstroot/fmodex/Developer/Panda3D/lib/" % GetOutputDir()) + oscmd("cp -R %s/lib/libfmodex* dstroot/fmodex/Developer/Panda3D/lib/" % GetOutputDir()) + oscmd("mkdir -p dstroot/headers/Developer/Panda3D") oscmd("cp -R %s/include dstroot/headers/Developer/Panda3D/include" % GetOutputDir()) @@ -6873,7 +6915,10 @@ def MakeInstallerOSX(): oscmd("mkdir -p dstroot/Panda3D/Panda3D.mpkg/Contents/Resources/en.lproj/") pkgs = ["base", "tools", "headers"] - if PkgSkip("PYTHON")==0: pkgs.append("pythoncode") + if not PkgSkip("PYTHON"): pkgs.append("pythoncode") + if not PkgSkip("FFMPEG"): pkgs.append("ffmpeg") + #if not PkgSkip("OPENAL"): pkgs.append("openal") + if not PkgSkip("FMODEX"): pkgs.append("fmodex") if os.path.isdir("samples"): pkgs.append("samples") for pkg in pkgs: identifier = "org.panda3d.panda3d.%s.pkg" % pkg @@ -6918,38 +6963,51 @@ def MakeInstallerOSX(): dist.write(' \n') dist.write(' %s\n' % ReadFile("doc/LICENSE")) dist.write(' \n') - dist.write(' \n') - dist.write(' \n') - if PkgSkip("PYTHON")==0: - dist.write(' \n') - if os.path.isdir("samples"): - dist.write(' \n') - dist.write(' \n') + for pkg in pkgs: + dist.write(' \n' % (pkg)) dist.write(' \n') dist.write(' \n') dist.write(' \n') dist.write(' \n') - dist.write(' \n') + dist.write(' \n') dist.write(' \n') dist.write(' \n') - if PkgSkip("PYTHON")==0: - dist.write(' \n') + + if not PkgSkip("PYTHON"): + dist.write(' \n') dist.write(' \n') dist.write(' \n') + + if not PkgSkip("FFMPEG"): + dist.write(' \n') + else: + dist.write(' It is not required for loading .wav or .ogg files, which Panda3D can read out of the box.">\n') + dist.write(' \n') + dist.write(' \n') + + #if not PkgSkip("OPENAL"): + # dist.write(' \n') + # dist.write(' \n') + # dist.write(' \n') + + if not PkgSkip("FMODEX"): + dist.write(' \n') + dist.write(' \n') + dist.write(' \n') + if os.path.isdir("samples"): dist.write(' \n') dist.write(' \n') dist.write(' \n') + dist.write(' \n') dist.write(' \n') dist.write(' \n') - dist.write(' file:./Contents/Packages/base.pkg\n' % (GetDirectorySize("dstroot/base") // 1024)) - dist.write(' file:./Contents/Packages/tools.pkg\n' % (GetDirectorySize("dstroot/tools") // 1024)) - if PkgSkip("PYTHON")==0: - dist.write(' file:./Contents/Packages/pythoncode.pkg\n' % (GetDirectorySize("dstroot/pythoncode") // 1024)) - if os.path.isdir("samples"): - dist.write(' file:./Contents/Packages/samples.pkg\n' % (GetDirectorySize("dstroot/samples") // 1024)) - dist.write(' file:./Contents/Packages/headers.pkg\n' % (GetDirectorySize("dstroot/headers") // 1024)) + for pkg in pkgs: + size = GetDirectorySize("dstroot/" + pkg) // 1024 + dist.write(' file:./Contents/Packages/%s.pkg\n' % (pkg, size, pkg)) dist.write('\n') dist.close() diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index e587cf26ef..bcd6394626 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -2546,6 +2546,10 @@ def SetupBuildEnvironment(compiler): dyldpath.insert(0, os.path.join(builtdir, 'lib')) os.environ["DYLD_LIBRARY_PATH"] = os.pathsep.join(dyldpath) + # OS X 10.11 removed DYLD_LIBRARY_PATH, but we still need to pass + # on our lib directory to ppackage, so add it to PATH instead. + os.environ["PATH"] = os.path.join(builtdir, 'lib') + ':' + os.environ.get("PATH", "") + # Workaround around compile issue on PCBSD if (os.path.exists("/usr/PCBSD")): os.environ["LD_LIBRARY_PATH"] += os.pathsep + "/usr/PCBSD/local/lib" @@ -2563,8 +2567,12 @@ def CopyFile(dstfile, srcfile): if NeedsBuild([dstfile], [srcfile]): if os.path.islink(srcfile): # Preserve symlinks - if os.path.exists(dstfile): + if os.path.isfile(dstfile) or os.path.islink(dstfile): + print("Removing file %s" % (dstfile)) os.unlink(dstfile) + elif os.path.isdir(dstfile): + print("Removing directory %s" % (dstfile)) + shutil.rmtree(dstfile) os.symlink(os.readlink(srcfile), dstfile) else: WriteBinaryFile(dstfile, ReadBinaryFile(srcfile)) @@ -2595,24 +2603,37 @@ def CopyAllJavaSources(dir, skip=[]): JustBuilt([dstfile], [srcfile]) def CopyTree(dstdir, srcdir, omitVCS=True): - if (os.path.isdir(dstdir)): - for entry in os.listdir(srcdir): + if os.path.isdir(dstdir): + source_entries = os.listdir(srcdir) + for entry in source_entries: srcpth = os.path.join(srcdir, entry) dstpth = os.path.join(dstdir, entry) - if (os.path.isfile(srcpth)): + + if os.path.islink(srcpth) or os.path.isfile(srcpth): if not omitVCS or entry not in VCS_FILES: CopyFile(dstpth, srcpth) else: if not omitVCS or entry not in VCS_DIRS: CopyTree(dstpth, srcpth) + + # Delete files in dstdir that are not in srcdir. + for entry in os.listdir(dstdir): + if entry not in source_entries: + path = os.path.join(dstdir, entry) + if os.path.islink(path) or os.path.isfile(path): + os.remove(path) + elif os.path.isdir(path): + shutil.rmtree(path) else: if GetHost() == 'windows': srcdir = srcdir.replace('/', '\\') dstdir = dstdir.replace('/', '\\') cmd = 'xcopy /I/Y/E/Q "' + srcdir + '" "' + dstdir + '"' + oscmd(cmd) else: - cmd = 'cp -R -f ' + srcdir + ' ' + dstdir - oscmd(cmd) + if subprocess.call(['cp', '-R', '-f', srcdir, dstdir]) != 0: + exit("Copy failed.") + if omitVCS: DeleteVCS(dstdir) diff --git a/panda/src/audiotraits/config_openalAudio.cxx b/panda/src/audiotraits/config_openalAudio.cxx index ab89ad775f..fe8cc6e128 100644 --- a/panda/src/audiotraits/config_openalAudio.cxx +++ b/panda/src/audiotraits/config_openalAudio.cxx @@ -27,6 +27,12 @@ ConfigureFn(config_openalAudio) { init_libOpenALAudio(); } +ConfigVariableString openal_device +("openal-device", "", + PRC_DESC("Specify the OpenAL device string for audio playback (no quotes). If this " + "is not specified, the OpenAL default device is used.")); + + //////////////////////////////////////////////////////////////////// // Function: init_libOpenALAudio // Description: Initializes the library. This must be called at diff --git a/panda/src/audiotraits/config_openalAudio.h b/panda/src/audiotraits/config_openalAudio.h index 9a122e2d90..646e13bdc8 100644 --- a/panda/src/audiotraits/config_openalAudio.h +++ b/panda/src/audiotraits/config_openalAudio.h @@ -27,4 +27,6 @@ NotifyCategoryDecl(openalAudio, EXPCL_OPENAL_AUDIO, EXPTP_OPENAL_AUDIO); extern EXPCL_OPENAL_AUDIO void init_libOpenALAudio(); extern "C" EXPCL_OPENAL_AUDIO Create_AudioManager_proc *get_audio_manager_func_openal_audio(); +extern ConfigVariableString openal_device; + #endif // CONFIG_OPENALAUDIO_H diff --git a/panda/src/audiotraits/openalAudioManager.cxx b/panda/src/audiotraits/openalAudioManager.cxx index 0a8faa896d..b5138a345b 100644 --- a/panda/src/audiotraits/openalAudioManager.cxx +++ b/panda/src/audiotraits/openalAudioManager.cxx @@ -19,6 +19,7 @@ #include "config_audio.h" #include "config_util.h" #include "config_express.h" +#include "config_openalAudio.h" #include "openalAudioManager.h" #include "openalAudioSound.h" #include "virtualFileSystem.h" @@ -27,6 +28,14 @@ #include +#ifndef ALC_DEFAULT_ALL_DEVICES_SPECIFIER +#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012 +#endif + +#ifndef ALC_ALL_DEVICES_SPECIFIER +#define ALC_ALL_DEVICES_SPECIFIER 0x1013 +#endif + TypeHandle OpenALAudioManager::_type_handle; ReMutex OpenALAudioManager::_lock; @@ -90,7 +99,7 @@ OpenALAudioManager() { _active = audio_active; _volume = audio_volume; _play_rate = 1.0f; - + _cache_limit = audio_cache_limit; _concurrent_sound_limit = 0; @@ -116,20 +125,50 @@ OpenALAudioManager() { _forward_up[5] = 0; // Initialization + audio_cat.init(); if (_active_managers == 0 || !_openal_active) { - _device = alcOpenDevice(NULL); // select the "preferred device" - if (!_device) { - // this is a unique kind of error - audio_error("OpenALAudioManager: alcOpenDevice(NULL): ALC couldn't open device"); + _device = NULL; + string dev_name = select_audio_device(); + + if (!dev_name.empty()) { + // Open a specific device by name. + audio_cat.info() << "Using OpenAL device " << dev_name << "\n"; + _device = alcOpenDevice(dev_name.c_str()); + + if (_device == NULL) { + audio_cat.error() + << "Couldn't open OpenAL device \"" << dev_name << "\", falling back to default device\n"; + } } else { + audio_cat.info() << "Using default OpenAL device\n"; + } + + if (_device == NULL) { + // Open the default device. + _device = alcOpenDevice(NULL); + + if (_device == NULL && dev_name != "OpenAL Soft") { + // Try the OpenAL Soft driver instead, which is fairly reliable. + _device = alcOpenDevice("OpenAL Soft"); + + if (_device == NULL) { + audio_cat.error() + << "Couldn't open default OpenAL device\n"; + } + } + } + + if (_device != NULL) { + // We managed to get a device open. alcGetError(_device); // clear errors _context = alcCreateContext(_device, NULL); - alc_audio_errcheck("alcCreateContext(_device, NULL)",_device); + alc_audio_errcheck("alcCreateContext(_device, NULL)", _device); if (_context != NULL) { _openal_active = true; } } } + // We increment _active_managers regardless of possible errors above. // The shutdown call will do the right thing when it's called, // either way. @@ -150,15 +189,15 @@ OpenALAudioManager() { audio_3d_set_drop_off_factor(audio_drop_off_factor); if (audio_cat.is_debug()) { - audio_cat->debug() + audio_cat.debug() << "ALC_DEVICE_SPECIFIER:" << alcGetString(_device, ALC_DEVICE_SPECIFIER) << endl; } } if (audio_cat.is_debug()) { - audio_cat->debug() << "AL_RENDERER:" << alGetString(AL_RENDERER) << endl; - audio_cat->debug() << "AL_VENDOR:" << alGetString(AL_VENDOR) << endl; - audio_cat->debug() << "AL_VERSION:" << alGetString(AL_VERSION) << endl; + audio_cat.debug() << "AL_RENDERER:" << alGetString(AL_RENDERER) << endl; + audio_cat.debug() << "AL_VENDOR:" << alGetString(AL_VENDOR) << endl; + audio_cat.debug() << "AL_VERSION:" << alGetString(AL_VERSION) << endl; } } @@ -212,6 +251,82 @@ is_valid() { return _is_valid; } +//////////////////////////////////////////////////////////////////// +// Function: OpenALAudioManager::select_audio_device +// Access: Private +// Description: Enumerate the audio devices, selecting the one that +// is most appropriate or has been selected by the user. +//////////////////////////////////////////////////////////////////// +string OpenALAudioManager:: +select_audio_device() { + string selected_device = openal_device; + + const char *devices = NULL; + + // This extension gives us all audio paths on all drivers. + if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") == AL_TRUE) { + string default_device = alcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER); + devices = (const char *)alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); + + if (devices) { + audio_cat.debug() << "All OpenAL devices:\n"; + + while (*devices) { + string device(devices); + devices += device.size() + 1; + + if (audio_cat.is_debug()) { + if (device == selected_device) { + audio_cat.debug() << " " << device << " [selected]\n"; + } else if (device == default_device) { + audio_cat.debug() << " " << device << " [default]\n"; + } else { + audio_cat.debug() << " " << device << "\n"; + } + } + } + } + } else { + audio_cat.debug() << "ALC_ENUMERATE_ALL_EXT not supported\n"; + } + + // This extension just gives us generic driver names, like "OpenAL Soft" + // and "Generic Software", rather than individual outputs. + if (alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT") == AL_TRUE) { + string default_device = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); + devices = (const char *)alcGetString(NULL, ALC_DEVICE_SPECIFIER); + + if (devices) { + audio_cat.debug() << "OpenAL drivers:\n"; + + while (*devices) { + string device(devices); + devices += device.size() + 1; + + if (selected_device.empty() && device == "OpenAL Soft" && + default_device == "Generic Software") { + // Prefer OpenAL Soft over the buggy Generic Software driver. + selected_device = "OpenAL Soft"; + } + + if (audio_cat.is_debug()) { + if (device == selected_device) { + audio_cat.debug() << " " << device << " [selected]\n"; + } else if (device == default_device) { + audio_cat.debug() << " " << device << " [default]\n"; + } else { + audio_cat.debug() << " " << device << "\n"; + } + } + } + } + } else { + audio_cat.debug() << "ALC_ENUMERATION_EXT not supported\n"; + } + + return selected_device; +} + //////////////////////////////////////////////////////////////////// // Function: OpenALAudioManager::make_current // Access: Private diff --git a/panda/src/audiotraits/openalAudioManager.h b/panda/src/audiotraits/openalAudioManager.h index 0dcc0e07e2..3923f43a2e 100644 --- a/panda/src/audiotraits/openalAudioManager.h +++ b/panda/src/audiotraits/openalAudioManager.h @@ -116,6 +116,8 @@ class EXPCL_OPENAL_AUDIO OpenALAudioManager : public AudioManager { virtual void update(); private: + string select_audio_device(); + void make_current() const; bool can_use_audio(MovieAudioCursor *source); diff --git a/panda/src/bullet/bulletTranslationalLimitMotor.I b/panda/src/bullet/bulletTranslationalLimitMotor.I index 2089dd9780..6f492dac63 100644 --- a/panda/src/bullet/bulletTranslationalLimitMotor.I +++ b/panda/src/bullet/bulletTranslationalLimitMotor.I @@ -177,7 +177,7 @@ set_stop_erp(const LVecBase3 &erp) { INLINE int BulletTranslationalLimitMotor:: get_current_limit(int axis) const { - nassertr((0 <- axis) && (axis <=2), false); + nassertr((0 <= axis) && (axis <= 2), false); return _motor.m_currentLimit[axis]; } diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index efe9849ff5..7ac27c723d 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -798,16 +798,20 @@ set_properties_now(WindowProperties &properties) { int height = properties.get_y_size(); if (!_properties.get_fullscreen()) { - _properties.set_size(width, height); if (_window != nil) { [_window setContentSize:NSMakeSize(width, height)]; } [_view setFrameSize:NSMakeSize(width, height)]; - cocoadisplay_cat.debug() - << "Setting size to " << width << ", " << height << "\n"; + if (cocoadisplay_cat.is_debug()) { + cocoadisplay_cat.debug() + << "Setting size to " << width << ", " << height << "\n"; + } - _context_needs_update = true; + // Cocoa doesn't send an event, and the other + // resize-window handlers will do nothing once the properties + // have been changed, so do this now + handle_resize_event(); properties.clear_size(); } else { @@ -1022,6 +1026,23 @@ set_properties_now(WindowProperties &properties) { } properties.clear_z_order(); } + + if (properties.has_mouse_mode()) { + switch (properties.get_mouse_mode()) { + case WindowProperties::M_absolute: + case WindowProperties::M_confined: // confined is maintained in mouse move event + CGAssociateMouseAndMouseCursorPosition(true); + _properties.set_mouse_mode(properties.get_mouse_mode()); + properties.clear_mouse_mode(); + break; + + case WindowProperties::M_relative: + CGAssociateMouseAndMouseCursorPosition(false); + _properties.set_mouse_mode(properties.get_mouse_mode()); + properties.clear_mouse_mode(); + break; + } + } } //////////////////////////////////////////////////////////////////// @@ -1670,6 +1691,8 @@ handle_mouse_button_event(int button, bool down) { //////////////////////////////////////////////////////////////////// void CocoaGraphicsWindow:: handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) { + double nx, ny; + if (absolute) { if (cocoadisplay_cat.is_spam()) { if (in_window != _input_devices[0].get_pointer().get_in_window()) { @@ -1682,15 +1705,40 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) { } // Strangely enough, in Cocoa, mouse Y coordinates are 1-based. - _input_devices[0].set_pointer(in_window, x, y - 1, - ClockObject::get_global_clock()->get_frame_time()); + nx = x; + ny = y - 1; } else { // We received deltas, so add it to the current mouse position. MouseData md = _input_devices[0].get_pointer(); - _input_devices[0].set_pointer_in_window(md.get_x() + x, md.get_y() + y); + nx = md.get_x() + x; + ny = md.get_y() + y; } + if (_properties.get_mouse_mode() == WindowProperties::M_confined + && !in_window) { + CGPoint point; + + nx = std::max(0., std::min((double) get_x_size() - 1, nx)); + ny = std::max(0., std::min((double) get_y_size() - 1, ny)); + + if (_properties.get_fullscreen()) { + point = CGPointMake(nx, ny + 1); + } else { + point = CGPointMake(nx + _properties.get_x_origin(), + ny + _properties.get_y_origin() + 1); + } + + if (CGWarpMouseCursorPosition(point) == kCGErrorSuccess) { + in_window = true; + } else { + cocoadisplay_cat.warning() << "Failed to return mouse pointer to window\n"; + } + } + + _input_devices[0].set_pointer(in_window, nx, ny, + ClockObject::get_global_clock()->get_frame_time()); + if (in_window != _mouse_hidden && _properties.get_cursor_hidden()) { // Hide the cursor if the mouse enters the window, // and unhide it when the mouse leaves the window. diff --git a/panda/src/cocoadisplay/cocoaPandaView.mm b/panda/src/cocoadisplay/cocoaPandaView.mm index bb7d03c832..3f0fa550fd 100644 --- a/panda/src/cocoadisplay/cocoaPandaView.mm +++ b/panda/src/cocoadisplay/cocoaPandaView.mm @@ -45,8 +45,10 @@ - (void) drawRect:(NSRect)dirtyRect { // Do nothing. We draw from another thread. - cocoadisplay_cat.spam() - << "drawRect was called.\n"; + if (cocoadisplay_cat.is_spam()) { + cocoadisplay_cat.spam() + << "drawRect was called.\n"; + } } - (void) finalize { @@ -116,7 +118,10 @@ NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; BOOL inside = [self mouse:loc inRect:[self bounds]]; - if (_graphicsWindow->get_properties().get_mouse_mode() == WindowProperties::M_relative) { + // the correlation between mouse deltas and location + // are "debounced" apparently, so send deltas for both + // relative and confined modes + if (_graphicsWindow->get_properties().get_mouse_mode() != WindowProperties::M_absolute) { _graphicsWindow->handle_mouse_moved_event(inside, [event deltaX], [event deltaY], false); } else { _graphicsWindow->handle_mouse_moved_event(inside, loc.x, loc.y, true); diff --git a/panda/src/osxdisplay/osxGraphicsWindow.mm b/panda/src/osxdisplay/osxGraphicsWindow.mm index 3b18fd3914..36db5a55ce 100644 --- a/panda/src/osxdisplay/osxGraphicsWindow.mm +++ b/panda/src/osxdisplay/osxGraphicsWindow.mm @@ -2064,6 +2064,25 @@ set_properties_now(WindowProperties &properties) { 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"; diff --git a/panda/src/rocket/config_rocket.cxx b/panda/src/rocket/config_rocket.cxx index 26e4a83b5c..0bb8dd1c0a 100644 --- a/panda/src/rocket/config_rocket.cxx +++ b/panda/src/rocket/config_rocket.cxx @@ -53,6 +53,10 @@ init_librocket() { RocketInputHandler::init_type(); RocketRegion::init_type(); + if (rocket_cat->is_debug()) { + rocket_cat->debug() << "Initializing libRocket library.\n"; + } + RocketFileInterface* fi = new RocketFileInterface; Rocket::Core::SetFileInterface(fi); @@ -61,6 +65,10 @@ init_librocket() { Rocket::Core::Initialise(); + // Register that we have the libRocket system. + PandaSystem *ps = PandaSystem::get_global_ptr(); + ps->add_system("libRocket"); + #ifdef COMPILE_IN_DEFAULT_FONT #ifdef HAVE_FREETYPE // Load Panda's default compiled-in freetype font (Perspective Sans). diff --git a/samples/mouse-modes/main.py b/samples/mouse-modes/main.py old mode 100644 new mode 100755 index 50e81f550e..c0b8ecbdef --- a/samples/mouse-modes/main.py +++ b/samples/mouse-modes/main.py @@ -34,7 +34,8 @@ class App(ShowBase): # Disable the camera trackball controls. self.disableMouse() - self.mouseMagnitude = 144 + # control mapping of mouse movement to box movement + self.mouseMagnitude = 1 self.rotateX, self.rotateY = 0, 0 @@ -66,7 +67,7 @@ class App(ShowBase): self.manualRecenterMouse = True # make a box to move with the mouse - self.model = self.loader.loadModel("box.egg") + self.model = self.loader.loadModel("box") self.model.reparentTo(self.render) self.cam.setPos(0, -5, 0) @@ -75,7 +76,7 @@ class App(ShowBase): self.mouseTask = taskMgr.add(self.mouseTask, "Mouse Task") def setMouseMode(self, mode): - print "Changing mode to",mode + print("Changing mode to %s" % mode) self.mouseMode = mode @@ -91,7 +92,7 @@ class App(ShowBase): actualMode = wp.getMouseMode() if self.mouseMode != actualMode: - print "ACTUAL MOUSE MODE:", actualMode + print("ACTUAL MOUSE MODE: %s" % actualMode) self.mouseMode = actualMode @@ -106,11 +107,11 @@ class App(ShowBase): def toggleRecenter(self): - print "Toggling re-center behavior" + print("Toggling re-center behavior") self.manualRecenterMouse = not self.manualRecenterMouse def toggleMouse(self): - print "Toggling mouse visibility" + print("Toggling mouse visibility") self.hideMouse = not self.hideMouse @@ -146,7 +147,8 @@ class App(ShowBase): if self.manualRecenterMouse: # move mouse back to center self.recenterMouse() - + self.lastMouseX, self.lastMouseY = 0, 0 + # scale position and delta to pixels for user w, h = self.win.getSize() @@ -158,8 +160,8 @@ class App(ShowBase): int(dx*w), int(dy*h))) # rotate box by delta - self.rotateX += dx * 10 - self.rotateY += dy * 10 + self.rotateX += dx * 10 * self.mouseMagnitude + self.rotateY += dy * 10 * self.mouseMagnitude self.positionText.setText("Model rotation: {0}, {1}".format( int(self.rotateX*1000)/1000., int(self.rotateY*1000)/1000.))