Explorar el Código

using bundles (and LSUIElement=1) to better manage OSX dock icons

David Rose hace 16 años
padre
commit
884c389bad

+ 70 - 14
direct/src/p3d/Packager.py

@@ -39,7 +39,8 @@ class Packager:
                      newName = None, deleteTemp = False,
                      newName = None, deleteTemp = False,
                      explicit = False, compress = None, extract = None,
                      explicit = False, compress = None, extract = None,
                      text = None, unprocessed = None,
                      text = None, unprocessed = None,
-                     executable = None, platformSpecific = None):
+                     executable = None, dependencyDir = None,
+                     platformSpecific = None):
             assert isinstance(filename, Filename)
             assert isinstance(filename, Filename)
             self.filename = Filename(filename)
             self.filename = Filename(filename)
             self.newName = newName
             self.newName = newName
@@ -50,6 +51,7 @@ class Packager:
             self.text = text
             self.text = text
             self.unprocessed = unprocessed
             self.unprocessed = unprocessed
             self.executable = executable
             self.executable = executable
+            self.dependencyDir = dependencyDir
             self.platformSpecific = platformSpecific
             self.platformSpecific = platformSpecific
 
 
             if not self.newName:
             if not self.newName:
@@ -73,6 +75,11 @@ class Packager:
             if self.executable is None:
             if self.executable is None:
                 self.executable = (ext in packager.executableExtensions)
                 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:
             if self.extract is None:
                 self.extract = self.executable or (ext in packager.extractExtensions)
                 self.extract = self.executable or (ext in packager.extractExtensions)
             if self.platformSpecific is None:
             if self.platformSpecific is None:
@@ -779,7 +786,9 @@ class Packager:
                 for filename in filenames:
                 for filename in filenames:
                     filename = Filename.fromOsSpecific(filename)
                     filename = Filename.fromOsSpecific(filename)
                     filename.resolveFilename(path)
                     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)
                                  explicit = False, executable = True)
                     
                     
         def __parseDependenciesWindows(self, tempFile):
         def __parseDependenciesWindows(self, tempFile):
@@ -851,13 +860,19 @@ class Packager:
             """ Copies the given library file to a temporary directory,
             """ Copies the given library file to a temporary directory,
             and alters the dependencies so that it doesn't contain absolute
             and alters the dependencies so that it doesn't contain absolute
             framework dependencies. """
             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.copyTo(tmpfile)
+                file.filename = tmpfile
+                file.deleteTemp = True
             
             
             # Alter the dependencies to have a relative path rather than absolute
             # Alter the dependencies to have a relative path rather than absolute
             for filename in framework_deps:
             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()))
                     os.system('install_name_tool -id "%s" "%s"' % (os.path.basename(filename), tmpfile.toOsSpecific()))
                 else:
                 else:
                     os.system('install_name_tool -change "%s" "%s" "%s"' % (filename, os.path.basename(filename), tmpfile.toOsSpecific()))
                     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):
         def __addImplicitDependenciesOSX(self):
             """ Walks through the list of files, looking for dylib's
             """ Walks through the list of files, looking for dylib's
@@ -915,7 +929,7 @@ class Packager:
                 if len(framework_deps) > 0:
                 if len(framework_deps) > 0:
                     # Fixes dependencies like @executable_path/../Library/Frameworks/Cg.framework/Cg
                     # Fixes dependencies like @executable_path/../Library/Frameworks/Cg.framework/Cg
                     self.__alterFrameworkDependencies(file, framework_deps)
                     self.__alterFrameworkDependencies(file, framework_deps)
-
+                
                 for filename in filenames:
                 for filename in filenames:
                     if '.framework/' in filename:
                     if '.framework/' in filename:
                         # It references a framework, and besides the fact
                         # 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.
                         # It's just a normal library - find it on the path.
                         filename = Filename.fromOsSpecific(filename)
                         filename = Filename.fromOsSpecific(filename)
                         filename.resolveFilename(path)
                         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)
                                  explicit = False, executable = True)
                     
                     
         def __parseDependenciesOSX(self, tempFile):
         def __parseDependenciesOSX(self, tempFile):
@@ -998,7 +1014,9 @@ class Packager:
                 for filename in filenames:
                 for filename in filenames:
                     filename = Filename.fromOsSpecific(filename)
                     filename = Filename.fromOsSpecific(filename)
                     filename.resolveFilename(path)
                     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)
                                  explicit = False, executable = True)
                     
                     
         def __parseDependenciesPosix(self, tempFile):
         def __parseDependenciesPosix(self, tempFile):
@@ -2517,6 +2535,22 @@ class Packager:
         # an associated dynamic library.  Note that the .exe and .dll
         # an associated dynamic library.  Note that the .exe and .dll
         # extensions are automatically replaced with the appropriate
         # extensions are automatically replaced with the appropriate
         # platform-specific extensions.
         # 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')
         self.do_file('p3dpython.exe')
         if PandaSystem.getPlatform().startswith('win'):
         if PandaSystem.getPlatform().startswith('win'):
             self.do_file('p3dpythonw.exe')
             self.do_file('p3dpythonw.exe')
@@ -2574,6 +2608,27 @@ class Packager:
         freezer.reset()
         freezer.reset()
         package.mainModule = None
         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):
     def do_file(self, *args, **kw):
         """ Adds the indicated file or files to the current package.
         """ Adds the indicated file or files to the current package.
         See addFiles(). """
         See addFiles(). """
@@ -2582,7 +2637,7 @@ class Packager:
 
 
     def addFiles(self, filenames, text = None, newName = None,
     def addFiles(self, filenames, text = None, newName = None,
                  newDir = None, extract = None, executable = 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.
         """ Adds the indicated arbitrary files to the current package.
 
 
@@ -2705,7 +2760,8 @@ class Packager:
             self.currentPackage.addFile(
             self.currentPackage.addFile(
                 filename, newName = name, extract = extract,
                 filename, newName = name, extract = extract,
                 explicit = explicit, executable = executable,
                 explicit = explicit, executable = executable,
-                text = text, deleteTemp = deleteTemp)
+                text = text, deleteTemp = deleteTemp,
+                dependencyDir = dependencyDir)
 
 
     def do_exclude(self, filename):
     def do_exclude(self, filename):
         """ Marks the indicated filename as not to be included.  The
         """ Marks the indicated filename as not to be included.  The

+ 13 - 1
direct/src/p3d/coreapi.pdef

@@ -69,4 +69,16 @@ class p3dcert(package):
     # user to accept or deny unknown applications, is its own package.
     # user to accept or deny unknown applications, is its own package.
     config(display_name = "Authorization Dialog")
     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')

+ 4 - 0
direct/src/plugin/p3dAuthSession.cxx

@@ -155,6 +155,10 @@ start_p3dcert() {
 #ifdef _WIN32
 #ifdef _WIN32
   _p3dcert_exe += ".exe";
   _p3dcert_exe += ".exe";
 #endif
 #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.
   // Populate the new process' environment.
   _env = string();
   _env = string();

+ 7 - 16
direct/src/plugin/p3dCert.cxx

@@ -17,11 +17,6 @@
 #include "wx/filename.h"
 #include "wx/filename.h"
 
 
 #include "ca_bundle_data_src.c"
 #include "ca_bundle_data_src.c"
-         
-#ifdef __WXMAC__
-#include <Carbon/Carbon.h>
-extern "C" { void CPSEnableForegroundOperation(ProcessSerialNumber* psn); }
-#endif
 
 
 static const wxString
 static const wxString
 self_signed_cert_text =
 self_signed_cert_text =
@@ -96,19 +91,11 @@ OnInit() {
 
 
   OpenSSL_add_all_algorithms();
   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);
   AuthDialog *dialog = new AuthDialog(_cert_filename, _cert_dir);
   SetTopWindow(dialog);
   SetTopWindow(dialog);
   dialog->Show(true);
   dialog->Show(true);
+  dialog->SetFocus();
+  dialog->Raise();
 
 
   // Return true to enter the main loop and wait for user input.
   // Return true to enter the main loop and wait for user input.
   return true;
   return true;
@@ -155,7 +142,11 @@ END_EVENT_TABLE()
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 AuthDialog::
 AuthDialog::
 AuthDialog(const wxString &cert_filename, const wxString &cert_dir) : 
 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)
   _cert_dir(cert_dir)
 {
 {
   _view_cert_dialog = NULL;
   _view_cert_dialog = NULL;

+ 4 - 0
direct/src/plugin/p3dSession.cxx

@@ -786,6 +786,10 @@ start_p3dpython(P3DInstance *inst) {
   _p3dpython_exe = P3D_PLUGIN_P3DPYTHON;
   _p3dpython_exe = P3D_PLUGIN_P3DPYTHON;
   if (_p3dpython_exe.empty()) {
   if (_p3dpython_exe.empty()) {
     _p3dpython_exe = _python_root_dir + "/p3dpython";
     _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
 #ifdef _WIN32
   if (!inst_mgr->get_console_environment()) {
   if (!inst_mgr->get_console_environment()) {

+ 34 - 0
direct/src/plugin/p3dcert.plist

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleDisplayName</key>
+	<string>P3DCert</string>
+	<key>CFBundleExecutable</key>
+	<string>p3dcert</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.panda3d.runtime.p3dcert</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>P3DCert</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>0.9.3</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>0.9.3</string>
+	<key>LSUIElement</key>
+	<string>1</string>
+	<key>LSHasLocalizedDisplayName</key>
+	<false/>
+	<key>NSAppleScriptEnabled</key>
+	<false/>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>

+ 34 - 0
direct/src/plugin/p3dpython.plist

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleDisplayName</key>
+	<string>P3DPython</string>
+	<key>CFBundleExecutable</key>
+	<string>p3dpython</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.panda3d.runtime.p3dpython</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>P3DPython</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>0.9.3</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>0.9.3</string>
+	<key>LSUIElement</key>
+	<string>1</string>
+	<key>LSHasLocalizedDisplayName</key>
+	<false/>
+	<key>NSAppleScriptEnabled</key>
+	<false/>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>

+ 1 - 1
direct/src/plugin_standalone/panda3d_mac.plist

@@ -32,7 +32,7 @@
 	<key>CFBundleIconFile</key>
 	<key>CFBundleIconFile</key>
 	<string>panda3d.icns</string>
 	<string>panda3d.icns</string>
 	<key>CFBundleIdentifier</key>
 	<key>CFBundleIdentifier</key>
-	<string>org.panda3d.runtime</string>
+	<string>org.panda3d.runtime.panda3d_mac</string>
 	<key>CFBundleInfoDictionaryVersion</key>
 	<key>CFBundleInfoDictionaryVersion</key>
 	<string>6.0</string>
 	<string>6.0</string>
 	<key>CFBundleName</key>
 	<key>CFBundleName</key>

+ 4 - 9
direct/src/showbase/showBase.cxx

@@ -11,15 +11,6 @@
 // with this source code in a file named "LICENSE."
 // 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 <Carbon/Carbon.h>
-extern "C" { void CPSEnableForegroundOperation(ProcessSerialNumber* psn); }
-#endif
 
 
 #include "showBase.h"
 #include "showBase.h"
 
 
@@ -68,6 +59,9 @@ get_config_showbase() {
 // At the moment, this is a no-op except on Mac.
 // At the moment, this is a no-op except on Mac.
 void
 void
 init_app_for_gui() {
 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;
   static bool initted_for_gui = false;
   if (!initted_for_gui) {
   if (!initted_for_gui) {
     initted_for_gui = true;
     initted_for_gui = true;
@@ -79,6 +73,7 @@ init_app_for_gui() {
     SetFrontProcess(&psn);
     SetFrontProcess(&psn);
 #endif  // IS_OSX
 #endif  // IS_OSX
   }
   }
+  */
 }
 }
 
 
 // klunky interface since we cant pass array from python->C++ to use verify_window_sizes directly
 // klunky interface since we cant pass array from python->C++ to use verify_window_sizes directly

+ 11 - 1
panda/src/osxdisplay/osxGraphicsWindow.mm

@@ -1077,7 +1077,17 @@ os_open_window(WindowProperties &req_properties) {
     GlobalInits = true;
     GlobalInits = true;
     
     
     ProcessSerialNumber psn = { 0, kCurrentProcess };
     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);
     SetFrontProcess(&psn);
   }
   }
   
   

+ 12 - 2
panda/src/tinydisplay/tinyOsxGraphicsWindow.mm

@@ -880,8 +880,18 @@ bool TinyOsxGraphicsWindow::OSOpenWindow(WindowProperties &req_properties)
 		GlobalInits = true;
 		GlobalInits = true;
 
 
 		ProcessSerialNumber psn = { 0, kCurrentProcess };
 		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())
 	if (req_properties.has_fullscreen() && req_properties.get_fullscreen())