diff --git a/direct/src/p3d/Packager.py b/direct/src/p3d/Packager.py index bb9211f03b..13f0fd3126 100644 --- a/direct/src/p3d/Packager.py +++ b/direct/src/p3d/Packager.py @@ -39,7 +39,8 @@ class Packager: newName = None, deleteTemp = False, explicit = False, compress = None, extract = None, text = None, unprocessed = None, - executable = None, platformSpecific = None): + executable = None, dependencyDir = None, + platformSpecific = None): assert isinstance(filename, Filename) self.filename = Filename(filename) self.newName = newName @@ -50,6 +51,7 @@ class Packager: self.text = text self.unprocessed = unprocessed self.executable = executable + self.dependencyDir = dependencyDir self.platformSpecific = platformSpecific if not self.newName: @@ -73,6 +75,11 @@ class Packager: 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 + # same directory with the executable itself. + self.dependencyDir = Filename(self.newName).getDirname() + if self.extract is None: self.extract = self.executable or (ext in packager.extractExtensions) if self.platformSpecific is None: @@ -779,7 +786,9 @@ class Packager: for filename in filenames: filename = Filename.fromOsSpecific(filename) filename.resolveFilename(path) - self.addFile(filename, newName = filename.getBasename(), + + newName = Filename(file.dependencyDir, filename.getBasename()) + self.addFile(filename, newName = newName.cStr(), explicit = False, executable = True) def __parseDependenciesWindows(self, tempFile): @@ -851,13 +860,19 @@ class Packager: """ Copies the given library file to a temporary directory, and alters the dependencies so that it doesn't contain absolute framework dependencies. """ - - # 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) - assert file.filename.exists(), "File doesn't exist: %s" % ffilename - tmpfile = Filename.fromOsSpecific("/tmp/p3d_" + hashlib.md5(file.filename.toOsSpecific()).hexdigest()) - if not tmpfile.exists(): + + 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" % ffilename + tmpfile = Filename.temporary('', "p3d_" + file.filename.getBasename()) 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: @@ -865,7 +880,6 @@ class Packager: os.system('install_name_tool -id "%s" "%s"' % (os.path.basename(filename), tmpfile.toOsSpecific())) else: os.system('install_name_tool -change "%s" "%s" "%s"' % (filename, os.path.basename(filename), tmpfile.toOsSpecific())) - self.sourceFilenames[file.filename].filename = tmpfile def __addImplicitDependenciesOSX(self): """ Walks through the list of files, looking for dylib's @@ -915,7 +929,7 @@ class Packager: 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 '.framework/' in filename: # It references a framework, and besides the fact @@ -926,7 +940,9 @@ class Packager: # It's just a normal library - find it on the path. filename = Filename.fromOsSpecific(filename) filename.resolveFilename(path) - self.addFile(filename, newName = filename.getBasename(), + + newName = Filename(file.dependencyDir, filename.getBasename()) + self.addFile(filename, newName = newName.cStr(), explicit = False, executable = True) def __parseDependenciesOSX(self, tempFile): @@ -998,7 +1014,9 @@ class Packager: for filename in filenames: filename = Filename.fromOsSpecific(filename) filename.resolveFilename(path) - self.addFile(filename, newName = filename.getBasename(), + + newName = Filename(file.dependencyDir, filename.getBasename()) + self.addFile(filename, newName = newName.cStr(), explicit = False, executable = True) def __parseDependenciesPosix(self, tempFile): @@ -2517,6 +2535,22 @@ class Packager: # 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. + + # Find p3dpython.plist in the direct source tree. + import direct + plist = Filename(direct.__path__[0], 'plugin/p3dpython.plist') + self.do_makeBundle('P3DPython.app', plist, executable = 'p3dpython', + dependencyDir = '') + + else: + # Anywhere else, we just ship the executable file p3dcert.exe. + self.do_file('p3dcert.exe') + self.do_file('p3dpython.exe') if PandaSystem.getPlatform().startswith('win'): self.do_file('p3dpythonw.exe') @@ -2574,6 +2608,27 @@ class Packager: 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(). """ @@ -2582,7 +2637,7 @@ class Packager: def addFiles(self, filenames, text = None, newName = None, newDir = None, extract = None, executable = None, - deleteTemp = False, literal = False): + deleteTemp = False, literal = False, dependencyDir = None): """ Adds the indicated arbitrary files to the current package. @@ -2705,7 +2760,8 @@ class Packager: self.currentPackage.addFile( filename, newName = name, extract = extract, explicit = explicit, executable = executable, - text = text, deleteTemp = deleteTemp) + text = text, deleteTemp = deleteTemp, + dependencyDir = dependencyDir) def do_exclude(self, filename): """ Marks the indicated filename as not to be included. The diff --git a/direct/src/p3d/coreapi.pdef b/direct/src/p3d/coreapi.pdef index d9ced83ed8..0a631b5743 100644 --- a/direct/src/p3d/coreapi.pdef +++ b/direct/src/p3d/coreapi.pdef @@ -69,4 +69,16 @@ class p3dcert(package): # user to accept or deny unknown applications, is its own package. config(display_name = "Authorization Dialog") - file('p3dcert.exe') + 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') diff --git a/direct/src/plugin/p3dAuthSession.cxx b/direct/src/plugin/p3dAuthSession.cxx index c0294c35f9..4831ca5e2e 100644 --- a/direct/src/plugin/p3dAuthSession.cxx +++ b/direct/src/plugin/p3dAuthSession.cxx @@ -155,6 +155,10 @@ start_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. _env = string(); diff --git a/direct/src/plugin/p3dCert.cxx b/direct/src/plugin/p3dCert.cxx index a942f502b5..b10756e5d9 100644 --- a/direct/src/plugin/p3dCert.cxx +++ b/direct/src/plugin/p3dCert.cxx @@ -17,11 +17,6 @@ #include "wx/filename.h" #include "ca_bundle_data_src.c" - -#ifdef __WXMAC__ -#include -extern "C" { void CPSEnableForegroundOperation(ProcessSerialNumber* psn); } -#endif static const wxString self_signed_cert_text = @@ -96,19 +91,11 @@ OnInit() { OpenSSL_add_all_algorithms(); -#ifdef __WXMAC__ - // Enable the dialog to go to the foreground on Mac, even without - // having to wrap it up in a bundle. - ProcessSerialNumber psn; - - GetCurrentProcess(&psn); - CPSEnableForegroundOperation(&psn); - SetFrontProcess(&psn); -#endif - 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; @@ -155,7 +142,11 @@ END_EVENT_TABLE() //////////////////////////////////////////////////////////////////// AuthDialog:: AuthDialog(const wxString &cert_filename, const wxString &cert_dir) : - wxDialog(NULL, wxID_ANY, _T("New Panda3D Application"), wxDefaultPosition), + // 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(NULL, wxID_ANY, _T("New Panda3D Application"), wxDefaultPosition, + wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxSTAY_ON_TOP), _cert_dir(cert_dir) { _view_cert_dialog = NULL; diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index d497ab630b..efc5945544 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -786,6 +786,10 @@ start_p3dpython(P3DInstance *inst) { _p3dpython_exe = P3D_PLUGIN_P3DPYTHON; if (_p3dpython_exe.empty()) { _p3dpython_exe = _python_root_dir + "/p3dpython"; +#ifdef __APPLE__ + // On OSX, run from the packaged bundle. + _p3dpython_exe = _python_root_dir + "/P3DPython.app/Contents/MacOS/p3dpython"; +#endif } #ifdef _WIN32 if (!inst_mgr->get_console_environment()) { diff --git a/direct/src/plugin/p3dcert.plist b/direct/src/plugin/p3dcert.plist new file mode 100644 index 0000000000..d536f638df --- /dev/null +++ b/direct/src/plugin/p3dcert.plist @@ -0,0 +1,34 @@ + + + + + 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 new file mode 100644 index 0000000000..d86cc2d0ca --- /dev/null +++ b/direct/src/plugin/p3dpython.plist @@ -0,0 +1,34 @@ + + + + + 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_standalone/panda3d_mac.plist b/direct/src/plugin_standalone/panda3d_mac.plist index ab32a790ee..eb122e23d8 100644 --- a/direct/src/plugin_standalone/panda3d_mac.plist +++ b/direct/src/plugin_standalone/panda3d_mac.plist @@ -32,7 +32,7 @@ CFBundleIconFile panda3d.icns CFBundleIdentifier - org.panda3d.runtime + org.panda3d.runtime.panda3d_mac CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/direct/src/showbase/showBase.cxx b/direct/src/showbase/showBase.cxx index 2eb0bc10f3..4ea6a510bc 100644 --- a/direct/src/showbase/showBase.cxx +++ b/direct/src/showbase/showBase.cxx @@ -11,15 +11,6 @@ // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// - -#ifdef __APPLE__ -// We have to include this before we include any Panda libraries, -// because one of the things we pick up in Panda defines a macro for -// TCP_NODELAY and friends, causing heartaches for the header files -// picked up here. -#include -extern "C" { void CPSEnableForegroundOperation(ProcessSerialNumber* psn); } -#endif #include "showBase.h" @@ -68,6 +59,9 @@ get_config_showbase() { // At the moment, this is a no-op except on Mac. void init_app_for_gui() { + // Actually, this may not be necessary after all. Let's assume the + // user will always be running from a bundle or from pythonw. + /* static bool initted_for_gui = false; if (!initted_for_gui) { initted_for_gui = true; @@ -79,6 +73,7 @@ init_app_for_gui() { SetFrontProcess(&psn); #endif // IS_OSX } + */ } // klunky interface since we cant pass array from python->C++ to use verify_window_sizes directly diff --git a/panda/src/osxdisplay/osxGraphicsWindow.mm b/panda/src/osxdisplay/osxGraphicsWindow.mm index 70685cf90c..e801ad79d6 100644 --- a/panda/src/osxdisplay/osxGraphicsWindow.mm +++ b/panda/src/osxdisplay/osxGraphicsWindow.mm @@ -1077,7 +1077,17 @@ os_open_window(WindowProperties &req_properties) { GlobalInits = true; ProcessSerialNumber psn = { 0, kCurrentProcess }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); + + // Determine if we're running from a bundle. + CFDictionaryRef dref = + ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask); + // If the dictionary doesn't have "BundlePath", 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. + if (!CFDictionaryContainsKey(dref, CFSTR("BundlePath"))) { + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + } SetFrontProcess(&psn); } diff --git a/panda/src/tinydisplay/tinyOsxGraphicsWindow.mm b/panda/src/tinydisplay/tinyOsxGraphicsWindow.mm index 7cba5a009d..311af99a7f 100644 --- a/panda/src/tinydisplay/tinyOsxGraphicsWindow.mm +++ b/panda/src/tinydisplay/tinyOsxGraphicsWindow.mm @@ -880,8 +880,18 @@ bool TinyOsxGraphicsWindow::OSOpenWindow(WindowProperties &req_properties) GlobalInits = true; ProcessSerialNumber psn = { 0, kCurrentProcess }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - SetFrontProcess(&psn); + + // Determine if we're running from a bundle. + CFDictionaryRef dref = + ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask); + // If the dictionary doesn't have "BundlePath", 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. + if (!CFDictionaryContainsKey(dref, CFSTR("BundlePath"))) { + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + } + SetFrontProcess(&psn); } if (req_properties.has_fullscreen() && req_properties.get_fullscreen())