From 681ea02fb9e3df819f812e359d3fe76c77423e32 Mon Sep 17 00:00:00 2001 From: Per Johansson Date: Tue, 17 Apr 2012 21:29:13 +0200 Subject: start: Support getting the file to open by AppleEvent. To: wine-patches Reply-To: wine-devel Uses a private API. --- configure.ac | 10 ++ programs/start/Makefile.in | 1 + programs/start/resources.h | 1 + programs/start/start.c | 276 +++++++++++++++++++++++++++++++++++++++++++- programs/start/start.rc | 3 + 5 filer ändrade, 285 tillägg(+), 6 borttagningar(-) diff --git a/configure.ac b/configure.ac index 67a0af6..543ea12 100644 --- a/configure.ac +++ b/configure.ac @@ -815,6 +815,16 @@ case $host_os in AC_MSG_ERROR([Xcode 3.x cannot build 16-bit code correctly. Use --disable-win16 if you don't need 16-bit support.])], AC_MSG_RESULT([[cross-compiling, assuming yes]])) fi + if test "$ac_cv_header_CoreServices_CoreServices_h" = "yes" + then + ac_save_LIBS="$LIBS" + LIBS="$LIBS $CORESERVICESLIB" + AC_CHECK_FUNCS(\ + _LSGetCurrentApplicationASN \ + _LSCopyLaunchModifiers \ + ) + LIBS="$ac_save_LIBS" + fi ;; *) DLLFLAGS="$DLLFLAGS -fPIC" diff --git a/programs/start/Makefile.in b/programs/start/Makefile.in index 2271109..fbdf784 100644 --- a/programs/start/Makefile.in +++ b/programs/start/Makefile.in @@ -1,6 +1,7 @@ MODULE = start.exe APPMODE = -mconsole -municode IMPORTS = shell32 user32 +EXTRALIBS = @CORESERVICESLIB@ C_SRCS = start.c diff --git a/programs/start/resources.h b/programs/start/resources.h index d965de6..2e434a7 100644 --- a/programs/start/resources.h +++ b/programs/start/resources.h @@ -23,3 +23,4 @@ #define STRING_USAGE 101 #define STRING_EXECFAIL 103 #define STRING_UNIXFAIL 104 +#define STRING_APPLEEVENTFAIL 105 diff --git a/programs/start/start.c b/programs/start/start.c index 7019abc..1d48ded 100644 --- a/programs/start/start.c +++ b/programs/start/start.c @@ -20,6 +20,48 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include "config.h" + +#ifdef __APPLE__ +#include +#include +#ifdef HAVE_CORESERVICES_CORESERVICES_H +#define GetCurrentProcess GetCurrentProcess_Mac +#define GetCurrentThread GetCurrentThread_Mac +#define LoadResource LoadResource_Mac +#define EqualRect EqualRect_Mac +#define FillRect FillRect_Mac +#define FrameRect FrameRect_Mac +#define GetCursor GetCursor_Mac +#define InvertRect InvertRect_Mac +#define OffsetRect OffsetRect_Mac +#define PtInRect PtInRect_Mac +#define SetCursor SetCursor_Mac +#define SetRect SetRect_Mac +#define ShowCursor ShowCursor_Mac +#define UnionRect UnionRect_Mac +#define Polygon Polygon_Mac +#include +#include +#undef GetCurrentProcess +#undef GetCurrentThread +#undef LoadResource +#undef EqualRect +#undef FillRect +#undef FrameRect +#undef GetCursor +#undef InvertRect +#undef OffsetRect +#undef PtInRect +#undef SetCursor +#undef SetRect +#undef ShowCursor +#undef UnionRect +#undef Polygon +#undef DPRINTF +#endif +#endif + #include #include #include @@ -169,6 +211,210 @@ static WCHAR *get_parent_dir(WCHAR* path) return result; } +#ifdef __APPLE__ +void *_LSGetCurrentApplicationASN(void); +CFArrayRef _LSCopyLaunchModifiers(uint32_t, void *); + +static AEDesc get_launchd_appleevent(void) +{ + AEDesc desc; + CFIndex i; + CFArrayRef arr; + + memset(&desc, 0, sizeof(desc)); + desc.descriptorType = typeNull; + +#if defined(HAVE__LSCOPYLAUNCHMODIFIERS) && defined(HAVE__LSGETCURRENTAPPLICATIONASN) + arr = _LSCopyLaunchModifiers(0xfffffffe, _LSGetCurrentApplicationASN()); +#else + arr = NULL; +#endif + + if (!arr) + return desc; + + if (CFGetTypeID(arr) != CFArrayGetTypeID()) + goto out; + + for (i = 0 ; i < CFArrayGetCount(arr) ; i++) { + CFDictionaryRef dict = CFArrayGetValueAtIndex(arr, i); + CFDataRef data; + + if (!dict || CFGetTypeID(dict) != CFDictionaryGetTypeID()) + continue; + + data = CFDictionaryGetValue(dict, CFSTR("LSLaunchData")); + + if (!data) + continue; + + if (!AEUnflattenDesc(CFDataGetBytePtr(data), &desc)) + break; + } + +out: + CFRelease(arr); + return desc; +} + +AEDesc get_mach_appleevent(void) +{ + AppleEvent event; + mach_port_t aeport = AEGetRegisteredMachPort(); + + /* Wait up to 10 seconds. */ + struct { + mach_msg_header_t header; + char data[10240]; /* Difficult to choose a size. How long is a URL? */ + } msg; + mach_msg_return_t macherr = mach_msg(&msg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, + 0, sizeof(msg), aeport, 10000, MACH_PORT_NULL); + + memset(&event, 0, sizeof(event)); + event.descriptorType = typeNull; + + if (macherr) + return event; + + AEDecodeMessage(&msg.header, &event, NULL); + return event; +} + +static WCHAR *get_file_from_odoc_appleevent(AEDesc *event) +{ + OSStatus oserr; + AEDesc doclist; + long count; + WCHAR *file = NULL; + long i; + unsigned char bmarkbuf[4096]; + Size bmarklen; + + oserr = AEGetParamDesc(event, keyDirectObject, typeAEList, &doclist); + if (oserr) + return NULL; + + oserr = AECountItems(&doclist, &count); + if (oserr) + goto out; + + for (i = 1 ; !file && i <= count ; i++) + { + CFURLRef url; + CFDataRef urldata; + Boolean isStale; + + oserr = AEGetNthPtr(&doclist, i, typeBookmarkData, NULL, NULL, bmarkbuf, sizeof(bmarkbuf), &bmarklen); + if (oserr) + continue; + if (bmarklen > sizeof(bmarkbuf)) + continue; + + urldata = CFDataCreateWithBytesNoCopy(NULL, bmarkbuf, bmarklen, kCFAllocatorNull); + url = CFURLCreateByResolvingBookmarkData(NULL, urldata, 0, NULL, NULL, &isStale, NULL); + + if (url && !isStale) + { + LPWSTR (*CDECL wine_get_dos_file_name_ptr)(LPCSTR); + char pathbuf[PATH_MAX]; + + wine_get_dos_file_name_ptr = (void*)GetProcAddress(GetModuleHandleA("KERNEL32"), "wine_get_dos_file_name"); + if (!wine_get_dos_file_name_ptr) + fatal_string(STRING_UNIXFAIL); + + CFURLGetFileSystemRepresentation(url, FALSE, (UInt8*)pathbuf, sizeof(pathbuf)); + file = wine_get_dos_file_name_ptr(pathbuf); + + if (!file) + fatal_string(STRING_UNIXFAIL); + } + CFRelease(url); + CFRelease(urldata); + } +out: + + AEDisposeDesc(&doclist); + return file; +} + +static WCHAR *get_file_from_gurl_appleevent(AEDesc *event) +{ + OSStatus oserr; + char urlbuf[4096]; + Size urllen; + WCHAR *file = NULL; + int file_len; + + oserr = AEGetParamPtr(event, keyDirectObject, typeUTF8Text, NULL, urlbuf, sizeof(urlbuf), &urllen); + if (oserr) + return NULL; + if (urllen > sizeof(urlbuf)) + return NULL; + + file_len = MultiByteToWideChar(CP_UTF8, 0, urlbuf, urllen, NULL, 0); + file = HeapAlloc(GetProcessHeap(), 0, (file_len + 1) * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, urlbuf, urllen, file, file_len); + file[file_len] = '\0'; + + return file; +} +#endif + +static WCHAR *get_file_from_appleevent(void) +{ +#ifdef __APPLE__ + AEDesc desc = get_launchd_appleevent(); + AEEventClass evcl; + AEEventID evid; + OSStatus oserr; + WCHAR *file = NULL; + + if (desc.descriptorType == typeNull) + desc = get_mach_appleevent(); + + if (desc.descriptorType == typeNull) + return NULL; + + oserr = AEGetAttributePtr(&desc, keyEventClassAttr, typeWildCard, NULL, &evcl, sizeof(evcl), NULL); + if (oserr) + goto out; + oserr = AEGetAttributePtr(&desc, keyEventIDAttr, typeWildCard, NULL, &evid, sizeof(evid), NULL); + if (oserr) + goto out; + + switch (evcl) { + case kCoreEventClass: + switch (evid) { +#if 0 + case kAEOpenApplication: + break; +#endif + case kAEOpenDocuments: + file = get_file_from_odoc_appleevent(&desc); + break; +#if 0 + case kAEOpenContents: + break; +#endif + } + break; + case kInternetEventClass: + switch (evid) { + case kAEGetURL: + file = get_file_from_gurl_appleevent(&desc); + break; + } + break; + } + +out: + AEDisposeDesc(&desc); + return file; +#else + return NULL; +#endif +} + static BOOL is_option(const WCHAR* arg, const WCHAR* opt) { return CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, @@ -183,6 +429,7 @@ int wmain (int argc, WCHAR *argv[]) int i; int unix_mode = 0; int progid_open = 0; + int appleevent_mode = 0; WCHAR *title = NULL; WCHAR *dos_filename = NULL; WCHAR *parent_directory = NULL; @@ -205,6 +452,7 @@ int wmain (int argc, WCHAR *argv[]) static const WCHAR waitW[] = { '/', 'w', 'a', 'i', 't', 0 }; static const WCHAR helpW[] = { '/', '?', 0 }; static const WCHAR unixW[] = { '/', 'u', 'n', 'i', 'x', 0 }; + static const WCHAR appleEventW[] = { '/', 'a', 'p', 'p', 'l', 'e', 'E', 'v', 'e', 'n', 't', 0 }; static const WCHAR progIDOpenW[] = { '/', 'p', 'r', 'o', 'g', 'I', 'D', 'O', 'p', 'e', 'n', 0}; static const WCHAR openW[] = { 'o', 'p', 'e', 'n', 0 }; @@ -315,9 +563,13 @@ int wmain (int argc, WCHAR *argv[]) else if (is_option(argv[i], unixW)) { unix_mode = 1; } + else if (is_option(argv[i], appleEventW)) { + appleevent_mode = 1; + } else if (is_option(argv[i], progIDOpenW)) { progid_open = 1; - unix_mode = 1; + if (!appleevent_mode) + unix_mode = 1; } else { @@ -333,13 +585,22 @@ int wmain (int argc, WCHAR *argv[]) sei.fMask |= SEE_MASK_CLASSNAME; } - if (i == argc) { - if (unix_mode) + if (appleevent_mode) { + if (i != argc) usage(); - sei.lpFile = cmdW; + + sei.lpFile = get_file_from_appleevent(); + if (!sei.lpFile) + fatal_string(STRING_APPLEEVENTFAIL); + } else { + if (i == argc) { + if (unix_mode) + usage(); + sei.lpFile = cmdW; + } + else + sei.lpFile = argv[i++]; } - else - sei.lpFile = argv[i++]; args = build_args( argc - i, &argv[i] ); sei.lpParameters = args; @@ -420,5 +681,8 @@ done: ExitProcess(exitcode); } + if (appleevent_mode) + HeapFree( GetProcessHeap(), 0, (void*)sei.lpFile ); + ExitProcess(0); } diff --git a/programs/start/start.rc b/programs/start/start.rc index a1ac57c..dc2f87c 100644 --- a/programs/start/start.rc +++ b/programs/start/start.rc @@ -47,9 +47,12 @@ Options:\n\ /wait Wait for the started program to finish, then exit with its exit code.\n\ /unix Use a Unix filename and start the file like windows explorer.\n\ /ProgIDOpen Open a document using the specified progID.\n\ +/AppleEvent Wait for an apple event to receive the filename. No filename should be given.\n\ /? Display this help and exit.\n" STRING_EXECFAIL, "Application could not be started, or no application associated with the specified file.\nShellExecuteEx failed" STRING_UNIXFAIL "Could not translate the specified Unix filename to a DOS filename." + +STRING_APPLEEVENTFAIL "Did not receive an appropriate AppleEvent.\n" } -- 1.7.10.2