Fix app icon not being displayed when requesting administrator privleges on Mac.

Previously the application was run from outside a bundle and hence did not
have an app icon - so OS X displayed the generic 'terminal' icon in the elevation
dialog.  The app icon in the dock was set at runtime, but disappeared temporarily
on shutdown when the overridden app icon was replaced with the default icon.

This commit fixes the problem by detecting whether the executable is being
run from within a bundle and if not, creating a minimal app bundle in /tmp
and re-launching the updater from there.
This commit is contained in:
Robert Knight 2011-09-14 15:43:57 +01:00
parent 3fe67e9824
commit 9650a492a9
8 changed files with 185 additions and 13 deletions

View file

@ -38,10 +38,20 @@ set (SOURCES
if (APPLE)
set(MAC_DOCK_ICON_CPP_FILE ${CMAKE_CURRENT_BINARY_DIR}/mac_dock_icon.cpp)
set(MAC_INFO_PLIST_FILE ${CMAKE_CURRENT_BINARY_DIR}/mac_info_plist.cpp)
generate_cpp_resource_file(resource_macdockicon
${CMAKE_CURRENT_SOURCE_DIR}/resources
mac-dock.png ${MAC_DOCK_ICON_CPP_FILE})
set(SOURCES ${SOURCES} StandardDirs.mm UpdateDialogCocoa.mm mac_dock_icon.cpp)
mac.icns ${MAC_DOCK_ICON_CPP_FILE})
generate_cpp_resource_file(resource_macplist
${CMAKE_CURRENT_SOURCE_DIR}/resources
Info.plist ${MAC_INFO_PLIST_FILE})
set(HEADERS ${HEADERS} MacBundle.h)
set(SOURCES ${SOURCES}
MacBundle.cpp
StandardDirs.mm
UpdateDialogCocoa.mm
mac_dock_icon.cpp
mac_info_plist.cpp)
endif()
if (WIN32)

53
src/MacBundle.cpp Normal file
View file

@ -0,0 +1,53 @@
#include "MacBundle.h"
#include "FileUtils.h"
#include "Log.h"
MacBundle::MacBundle(const std::string& path, const std::string& appName)
: m_appName(appName)
{
m_path = path + '/' + appName + ".app";
}
std::string MacBundle::bundlePath() const
{
return m_path;
}
void MacBundle::create(const std::string& infoPlist,
const std::string& icon,
const std::string& exePath)
{
try
{
// create the bundle directories
FileUtils::mkpath(m_path.c_str());
std::string contentDir = m_path + "/Contents";
std::string resourceDir = contentDir + "/Resources";
std::string binDir = contentDir + "/MacOS";
FileUtils::mkpath(resourceDir.c_str());
FileUtils::mkpath(binDir.c_str());
// create the Contents/Info.plist file
FileUtils::writeFile((contentDir + "/Info.plist").c_str(),infoPlist.c_str(),infoPlist.size());
// save the icon to Contents/Resources/<appname>.icns
FileUtils::writeFile((resourceDir + '/' + m_appName + ".icns").c_str(),icon.c_str(),icon.size());
// copy the app binary to Contents/MacOS/<appname>
m_exePath = binDir + '/' + m_appName;
FileUtils::copyFile(exePath.c_str(),m_exePath.c_str());
}
catch (const FileUtils::IOException& exception)
{
LOG(Error,"Unable to create app bundle. " + std::string(exception.what()));
}
}
std::string MacBundle::executablePath() const
{
return m_exePath;
}

35
src/MacBundle.h Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include <string>
/** Class for creating minimal Mac app bundles. */
class MacBundle
{
public:
/** Create a MacBundle instance representing the bundle
* in <path>/<appName>.app
*/
MacBundle(const std::string& path, const std::string& appName);
/** Create a simple Mac bundle.
*
* @param infoPlist The content of the Info.plist file
* @param icon The content of the app icon
* @param exePath The path of the file to use for the main app in the bundle.
*/
void create(const std::string& infoPlist,
const std::string& icon,
const std::string& exePath);
/** Returns the path of the main executable within the Mac bundle. */
std::string executablePath() const;
/** Returns the path of the bundle */
std::string bundlePath() const;
private:
std::string m_path;
std::string m_appName;
std::string m_exePath;
};

View file

@ -90,9 +90,6 @@ UpdateDialogCocoa::~UpdateDialogCocoa()
[d->pool release];
}
extern unsigned char mac_dock_png[];
extern unsigned int mac_dock_png_len;
void UpdateDialogCocoa::enableDockIcon()
{
// convert the application to a foreground application and in
@ -103,13 +100,6 @@ void UpdateDialogCocoa::enableDockIcon()
ProcessSerialNumber psn;
GetCurrentProcess(&psn);
TransformProcessType(&psn,kProcessTransformToForegroundApplication);
// loading the icon for the app has to be done after
// changing the process type
NSData* iconData = [NSData dataWithBytes:mac_dock_png length:mac_dock_png_len];
NSImage* iconImage = [[NSImage alloc] initWithData: iconData];
[NSApp setApplicationIconImage:iconImage];
[iconImage release];
}
void UpdateDialogCocoa::init()

View file

@ -15,6 +15,7 @@
#endif
#if defined(PLATFORM_MAC)
#include "MacBundle.h"
#include "UpdateDialogCocoa.h"
#endif
@ -51,13 +52,58 @@ void runUpdaterThread(void* arg)
#endif
}
#ifdef PLATFORM_MAC
extern unsigned char Info_plist[];
extern unsigned int Info_plist_len;
extern unsigned char mac_icns[];
extern unsigned int mac_icns_len;
bool unpackBundle(int argc, char** argv)
{
MacBundle bundle(FileUtils::tempPath(),AppInfo::name());
std::string currentExePath = ProcessUtils::currentProcessPath();
if (currentExePath.find(bundle.bundlePath()) != std::string::npos)
{
// already running from a bundle
return false;
}
LOG(Info,"Creating bundle " + bundle.bundlePath());
// create a Mac app bundle
std::string plistContent(reinterpret_cast<const char*>(Info_plist),Info_plist_len);
std::string iconContent(reinterpret_cast<const char*>(mac_icns),mac_icns_len);
bundle.create(plistContent,iconContent,ProcessUtils::currentProcessPath());
std::list<std::string> args;
for (int i = 1; i < argc; i++)
{
args.push_back(argv[i]);
}
ProcessUtils::runSync(bundle.executablePath(),args);
return true;
}
#endif
int main(int argc, char** argv)
{
#ifdef PLATFORM_MAC
void* pool = UpdateDialogCocoa::createAutoreleasePool();
#endif
Log::instance()->open(AppInfo::logFilePath());
#ifdef PLATFORM_MAC
// when the updater is run for the first time, create a Mac app bundle
// and re-launch the application from the bundle. This permits
// setting up bundle properties (such as application icon)
if (unpackBundle(argc,argv))
{
return 0;
}
#endif
UpdaterOptions options;
options.parse(argc,argv);
if (options.showVersion)

38
src/resources/Info.plist Normal file
View file

@ -0,0 +1,38 @@
<?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>
<!-- Note - The name of the application specified here must match the value
returned by AppInfo::name()
!-->
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>Mendeley Updater</string>
<key>CFBundleIconFile</key>
<string>Mendeley Updater.icns</string>
<key>CFBundleIdentifier</key>
<string>org.mendeley.MendeleyUpdater</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>LSMinimumSystemVersion</key>
<string>10.5</string>
<key>LSMinimumSystemVersionByArchitecture</key>
<dict>
<key>i386</key>
<string>10.5.0</string>
<key>x86_64</key>
<string>10.5.0</string>
</dict>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

BIN
src/resources/mac.icns Normal file

Binary file not shown.