With modifications by Aaron Voisine for gimp.app
With modifications by Marianne gagnon for Wilber-loves-apple
With modifications by Michael Wybrow for Inkscape.app
+ With modifications by ~suv for Inkscape.app
With modifications by Gerald Combs for Wireshark.app
This program is free software; you can redistribute it and/or modify
/*
* This app laucher basically takes care of:
- * - launching Wireshark and X11 when double-clicked
+ * - launching Wireshark (and, on OS X prior to Leopard, X11) when
+ * double-clicked (it's auto-launched by launchd on Leopard and later)
* - bringing X11 to the top when its icon is clicked in the dock (via a small applescript)
* - catch file dropped on icon events (and double-clicked gimp documents) and notify gimp.
* - catch quit events performed outside gimp, e.g. on the dock icon.
#pragma mark Includes
// Apple stuff
+
+// Note: including Carbon prevents building the launcher app in x86_64
+// used for StandardAlert in RequestUserAttention(),
+// RedFatalAlert()
#include <Carbon/Carbon.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Authorization.h>
#include <sys/wait.h>
#include <pthread.h>
#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
///////////////////////////////////////
// Definitions
#define kScriptFileName "script"
#define kOpenDocFileName "openDoc"
-// custom carbon events
+// custom carbon event class
#define kEventClassRedFatalAlert 911
+
+// custom carbon event types
#define kEventKindX11Failed 911
#define kEventKindFCCacheFailed 912
static OSErr ExecuteScript(char *script, pid_t *pid);
static void GetParameters(void);
-static char* GetScript(void);
-static char* GetOpenDoc(void);
+static unsigned char* GetScript(void);
+static unsigned char* GetOpenDoc(void);
OSErr LoadMenuBar(char *appName);
-static OSStatus FSMakePath(FSSpec file, char *path, long maxPathSize);
+static OSStatus FSMakePath(FSRef fileRef, unsigned char *path, long maxPathSize);
static void RedFatalAlert(Str255 errorString, Str255 expStr);
-static short DoesFileExist(char *path);
+static short DoesFileExist(unsigned char *path);
static OSErr AppQuitAEHandler(const AppleEvent *theAppleEvent,
AppleEvent *reply, long refCon);
extern char **environ;
+//
+// 0 if we should expect a bundled X11, 1 if we should expect XQuartz
+//
+static int expect_xquartz;
+
+static enum {
+ BUNDLED_X11, // bundled X11, in /usr/X11
+ XQUARTZ, // unbundled XQuartz, in /opt/X11, with /usr/X11 linking to it
+ XQUARTZ_STUB, // stub libraries in /usr/X11 that will tell user to install XQuartz
+ BROKEN_XQUARTZ, // unbundled XQuartz, in /opt/X11, with no /usr/X11
+ NO_X11 // no X11
+} x11_type;
+
#pragma mark -
///////////////////////////////////////
OSErr err = noErr;
EventTypeSpec X11events = { kEventClassRedFatalAlert, kEventKindX11Failed };
EventTypeSpec FCCacheEvents = { kEventClassRedFatalAlert, kEventKindFCCacheFailed };
+ struct utsname os_info;
+ struct stat statb_usr_x11, statb_opt_x11;
+ char symlink_target[MAXPATHLEN+1];
+ ssize_t link_length;
+
+ // Which version of OS X is this? Use that to determine what sort
+ // of X11 we should expect to have installed
+ if (uname(&os_info) == -1) {
+ // Couldn't find out; this "should not happen",
+ // assume bundled X11 for the lulz
+ expect_xquartz = 0;
+ } else {
+ if (os_info.release[0] <= '9' && os_info.release[1] == '.') {
+ // Darwin 9.0 (Leopard) or earlier; expect bundled X11
+ expect_xquartz = 0;
+ } else if (os_info.release[0] == '1' &&
+ (os_info.release[1] == '0' || os_info.release[1] == '1') &&
+ os_info.release[2] == '.') {
+ // Darwin 10.0 (Snow Leopard) or 11.0 (Lion); expect bundled X11
+ expect_xquartz = 0;
+ } else {
+ // Mountain Lion or later; expect XQuartz
+ expect_xquartz = 1;
+ }
+ }
+
+ if (expect_xquartz) {
+ // Do we have /opt/X11?
+ if (lstat("/opt/X11", &statb_opt_x11) == -1) {
+ // No. Is /usr/X11 a directory?
+ if (lstat("/usr/X11", &statb_usr_x11) != -1 &&
+ S_ISDIR(statb_opt_x11.st_mode)) {
+ // It's a directory; assume it contains the stub libraries.
+ x11_type = XQUARTZ_STUB;
+ } else {
+ // It's not a directory; assume we need X11 installed.
+ x11_type = NO_X11;
+ }
+ } else {
+ // Yes. Is /usr/X11 a symbolic link to /opt/X11?
+ if (lstat("/usr/X11", &statb_usr_x11) != -1 &&
+ S_ISLNK(statb_opt_x11.st_mode)) {
+ // OK, it's a symlink; does it point to /opt/X11?
+ link_length = readlink("/usr/X11", symlink_target, MAXPATHLEN);
+ if (link_length == -1) {
+ // Couldn't read it; broken X11
+ x11_type = BROKEN_XQUARTZ;
+ } else {
+ // Read it; nul-terminate the string
+ symlink_target[link_length] = '\0';
+ if (strcmp(symlink_target, "/opt/X11") == 0) {
+ // Yes, it points to /opt/X11, so that's good
+ x11_type = XQUARTZ;
+ } else {
+ // No, it doesn't - broken
+ x11_type = BROKEN_XQUARTZ;
+ }
+ }
+ } else {
+ // Non-existent or not a symlink
+ x11_type = BROKEN_XQUARTZ;
+ }
+ }
+ } else {
+ // Look for /usr/X11
+ if (lstat("/usr/X11", &statb_usr_x11) == -1) {
+ // No /usr/X11; tell the user to install X11
+ x11_type = NO_X11;
+ } else {
+ // Assume it's OK
+ x11_type = BUNDLED_X11;
+ }
+ }
InitCursor();
GetParameters(); //load data from files containing exec settings
// compile "icon clicked" script so it's ready to execute
- SimpleCompileAppleScript("tell application \"X11\" to activate");
+ // Don't tell it to activate if it's not installed;
+ // that will pop up the annoying "where is XQuartz?"/"where is X11?"
+ // dialog, and if somebody selects the wrong app, you end up with
+ // a messed-up system that fails to start X correctly.
+ switch (x11_type) {
+ case XQUARTZ:
+ SimpleCompileAppleScript("tell application \"XQuartz\" to activate");
+ break;
+
+ case BUNDLED_X11:
+ SimpleCompileAppleScript("tell application \"X11\" to activate");
+ break;
+
+ default:
+ break;
+ }
RunApplicationEventLoop(); //Run the event loop
return 0;
///////////////////////////////////////
// Get path to the script in Resources folder
///////////////////////////////////////
-static char* GetScript (void)
+static unsigned char* GetScript (void)
{
CFStringRef fileName;
CFBundleRef appBundle;
CFURLRef scriptFileURL;
FSRef fileRef;
- FSSpec fileSpec;
- char *path;
+ unsigned char *path;
//get CF URL for script
if (! (appBundle = CFBundleGetMainBundle())) return NULL;
CFRelease(scriptFileURL);
CFRelease(fileName);
- //convert FSRef to FSSpec
- if (FSGetCatalogInfo(&fileRef, kFSCatInfoNone, NULL, NULL, &fileSpec,
- NULL)) return NULL;
-
//create path string
if (! (path = malloc(kMaxPathLength))) return NULL;
- if (FSMakePath(fileSpec, path, kMaxPathLength)) return NULL;
+ if (FSMakePath(fileRef, path, kMaxPathLength)) return NULL;
if (! DoesFileExist(path)) return NULL;
return path;
///////////////////////////////////////
// Gets the path to openDoc in Resources folder
///////////////////////////////////////
-static char* GetOpenDoc (void)
+static unsigned char* GetOpenDoc (void)
{
CFStringRef fileName;
CFBundleRef appBundle;
CFURLRef openDocFileURL;
FSRef fileRef;
- FSSpec fileSpec;
- char *path;
+ unsigned char *path;
//get CF URL for openDoc
if (! (appBundle = CFBundleGetMainBundle())) return NULL;
CFRelease(openDocFileURL);
CFRelease(fileName);
- //convert FSRef to FSSpec
- if (FSGetCatalogInfo(&fileRef, kFSCatInfoNone, NULL, NULL, &fileSpec,
- NULL)) return NULL;
-
//create path string
if (! (path = malloc(kMaxPathLength))) return NULL;
- if (FSMakePath(fileSpec, path, kMaxPathLength)) return NULL;
+ if (FSMakePath(fileRef, path, kMaxPathLength)) return NULL;
if (! DoesFileExist(path)) return NULL;
return path;
///////////////////////////////////////
// Generate path string from FSSpec record
///////////////////////////////////////
-static OSStatus FSMakePath(FSSpec file, char *path, long maxPathSize)
+static OSStatus FSMakePath(FSRef fileRef, unsigned char *path, long maxPathSize)
{
- OSErr err = noErr;
- FSRef fileRef;
-
- //create file reference from file spec
- if (err = FSpMakeFSRef(&file, &fileRef)) return err;
-
// and then convert the FSRef to a path
return FSRefMakePath(&fileRef, path, maxPathSize);
}
///////////////////////////////////////
// Determines whether file exists at path or not
///////////////////////////////////////
-static short DoesFileExist (char *path)
+static short DoesFileExist (unsigned char *path)
{
- if (access(path, F_OK) == -1) return false;
+ if (access((char *)path, F_OK) == -1) return false;
return true;
}
if (! taskDone && pid) { //kill the script process brutally
kill(pid, 9);
- printf("Platypus App: PID %d killed brutally\n", pid);
+ printf("Wireshark.app: PID %d killed brutally\n", pid);
}
pthread_cancel(tid);
#pragma unused (reply, refCon)
OSErr err = noErr;
- AEDescList fileSpecList;
+ AEDescList fileRefList;
AEKeyword keyword;
DescType type;
short i;
long count, actualSize;
- FSSpec fileSpec;
- char path[kMaxPathLength];
+ FSRef fileRef;
+ unsigned char path[kMaxPathLength];
while (numArgs > 0) free(fileArgs[numArgs--]);
//Read the AppleEvent
err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList,
- &fileSpecList);
+ &fileRefList);
- err = AECountItems(&fileSpecList, &count); //Count number of files
+ err = AECountItems(&fileRefList, &count); //Count number of files
for (i = 1; i <= count; i++) { //iteratively process each file
- //get fsspec from apple event
- if (! (err = AEGetNthPtr(&fileSpecList, i, typeFSS, &keyword, &type,
- (Ptr)&fileSpec, sizeof(FSSpec), &actualSize)))
+ //get fsref from apple event
+ if (! (err = AEGetNthPtr(&fileRefList, i, typeFSRef, &keyword, &type,
+ (Ptr)&fileRef, sizeof(FSRef), &actualSize)))
{
- //get path from file spec
- if ((err = FSMakePath(fileSpec, (unsigned char *)&path,
+ //get path from file ref
+ if ((err = FSMakePath(fileRef, (unsigned char *)&path,
kMaxPathLength))) return err;
if (numArgs == kMaxArgumentsToScript) break;
//////////////////////////////////
// Handler for when X11 fails to start
+// Applies only to pre-Leopard OS X, as it's auto-started
+// through launchd in Leopard and later
//////////////////////////////////
static OSStatus X11FailedHandler(EventHandlerCallRef theHandlerCall,
EventRef theEvent, void *userData)