blob: 27fcf4b23ce6e0f076a23de659f040c28837ac77 [file] [log] [blame]
/*
* Copyright (c) 2000, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Andre Weinand (OTI Labs)
*/
/* MacOS X Carbon specific logic for displaying the splash screen. */
#include "eclipseOS.h"
#include "eclipseCommon.h"
#include "eclipseJNI.h"
#include <unistd.h>
#include <CoreServices/CoreServices.h>
#include <Carbon/Carbon.h>
#include <mach-o/dyld.h>
#include <pthread.h>
#include "NgCommon.h"
#include "NgImageData.h"
#include "NgWinBMPFileFormat.h"
#define startupJarName "startup.jar"
#define LAUNCHER "-launcher"
#define SPLASH_LAUNCHER "/Resources/Splash.app/Contents/"
#define DEBUG 0
char *findCommand(char *command);
/* Global Variables */
char* defaultVM = "java";
char* vmLibrary = "JavaVM";
char* shippedVMDir = "jre/bin/";
/* Define the window system arguments for the various Java VMs. */
static char* argVM_JAVA[] = { "-XstartOnFirstThread", NULL };
static WindowRef window;
static ControlRef pane = NULL;
static CGImageRef image = NULL;
static CGImageRef loadBMPImage(const char *image);
/* thread stuff */
typedef struct {
_TCHAR * libPath;
_TCHAR ** vmArgs;
_TCHAR ** progArgs;
int result;
} StartVMArgs;
static CFRunLoopRef loopRef = NULL;
static void * startThread(void * init);
static void runEventLoop(CFRunLoopRef ref);
static void dummyCallback(void * info) {}
typedef CGImageSourceRef (*CGImageSourceCreateWithURL_FUNC) (CFURLRef, CFDictionaryRef);
typedef CGImageRef (*CGImageSourceCreateImageAtIndex_FUNC)(CGImageSourceRef, size_t, CFDictionaryRef);
static CGImageSourceCreateWithURL_FUNC createWithURL = NULL;
static CGImageSourceCreateImageAtIndex_FUNC createAtIndex = NULL;
int main() {
return -1;
}
static OSStatus drawProc (EventHandlerCallRef eventHandlerCallRef, EventRef eventRef, void * data) {
int result = CallNextEventHandler(eventHandlerCallRef, eventRef);
if (image) {
ControlRef control;
CGContextRef context;
GetEventParameter(eventRef, kEventParamDirectObject, typeControlRef, NULL, 4, NULL, &control);
GetEventParameter(eventRef, kEventParamCGContextRef, typeCGContextRef, NULL, 4, NULL, &context);
HIRect rect;
HIViewGetBounds(control, &rect);
HIViewDrawCGImage(context, &rect, image);
}
return result;
}
static OSStatus disposeProc (EventHandlerCallRef eventHandlerCallRef, EventRef eventRef, void * data) {
window = NULL;
return eventNotHandledErr;
}
void loadImageFns()
{
static int initialized = 0;
static CFBundleRef bundle = NULL;
if (!initialized) {
if (!bundle) bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Carbon"));
if (bundle) createAtIndex = (CGImageSourceCreateImageAtIndex_FUNC)CFBundleGetFunctionPointerForName(bundle, CFSTR("CGImageSourceCreateImageAtIndex"));
if (bundle) createWithURL = (CGImageSourceCreateWithURL_FUNC)CFBundleGetFunctionPointerForName(bundle, CFSTR("CGImageSourceCreateWithURL"));
initialized = 1;
}
}
/* Show the Splash Window
*
* Create the splash window, load the bitmap and display the splash window.
*/
int showSplash( const _TCHAR* featureImage )
{
Rect wRect;
int w, h, deviceWidth, deviceHeight;
int attributes;
EventTypeSpec draw = {kEventClassControl, kEventControlDraw};
EventTypeSpec dispose = {kEventClassWindow, kEventWindowDispose};
ControlRef root;
if(window != NULL)
return 0; /*already showing */
if (featureImage == NULL)
return ENOENT;
loadImageFns();
if (createWithURL && createAtIndex) {
CFStringRef imageString = CFStringCreateWithCString(kCFAllocatorDefault, featureImage, kCFStringEncodingASCII);
if(imageString != NULL) {
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, imageString, kCFURLPOSIXPathStyle, false);
if(url != NULL) {
CGImageSourceRef imageSource = createWithURL(url, NULL);
if(imageSource != NULL) {
image = createAtIndex(imageSource, 0, NULL);
}
CFRelease(url);
}
}
CFRelease(imageString);
} else {
image = loadBMPImage(featureImage);
}
/*If the splash image data could not be loaded, return an error.*/
if (image == NULL)
return ENOENT;
w = CGImageGetWidth(image);
h = CGImageGetHeight(image);
GetAvailableWindowPositioningBounds(GetMainDevice(), &wRect);
deviceWidth= wRect.right - wRect.left;
deviceHeight= wRect.bottom - wRect.top;
wRect.left+= (deviceWidth-w)/2;
wRect.top+= (deviceHeight-h)/3;
wRect.right= wRect.left + w;
wRect.bottom= wRect.top + h;
attributes = kWindowStandardHandlerAttribute | kWindowCompositingAttribute;
attributes &= GetAvailableWindowAttributes(kSheetWindowClass);
CreateNewWindow(kSheetWindowClass, attributes, &wRect, &window);
if (window != NULL) {
GetRootControl(window, &root);
wRect.left = wRect.top = 0;
wRect.right = w;
wRect.bottom = h;
CreateUserPaneControl(window, &wRect, kControlSupportsEmbedding | kControlSupportsFocus | kControlGetsFocusOnClick, &pane);
HIViewAddSubview(root, pane);
InstallEventHandler(GetControlEventTarget(pane), (EventHandlerUPP)drawProc, 1, &draw, NULL, NULL);
InstallEventHandler(GetWindowEventTarget(window), (EventHandlerUPP)disposeProc, 1, &dispose, NULL, NULL);
ShowWindow(window);
dispatchMessages();
}
return 0;
}
long getSplashHandle() {
return (long)window;
}
void takeDownSplash() {
if( window != 0) {
DisposeWindow(window);
window = NULL;
}
if(image){
CGImageRelease(image);
image = NULL;
}
}
void dispatchMessages() {
EventRef event;
EventTargetRef target;
target = GetEventDispatcherTarget();
while( ReceiveNextEvent(0, NULL, kEventDurationNoWait, true, &event) == noErr ) {
SendEventToEventTarget(event, target);
ReleaseEvent(event);
}
}
/* Get the window system specific VM arguments */
char** getArgVM( char* vm )
{
char** result;
/* Use the default arguments for a standard Java VM */
result = argVM_JAVA;
return result;
}
char * findVMLibrary( char* command ) {
char *start, *end;
char *version;
int length;
/*check first to see if command already points to the library */
if (strcmp(command, JAVA_FRAMEWORK) == 0) {
return JAVA_FRAMEWORK;
}
/* select a version to use based on the command */
start = strstr(command, "/Versions/");
if (start != NULL){
start += 10;
end = strchr( start, dirSeparator);
if (end != NULL && end > start) {
length = end - start;
version = malloc(length + 1);
strncpy(version, start, length);
version[length] = 0;
/*only set a version if it starts with a number */
if(strtol(version, NULL, 10) != 0 || version[0] == '0') {
setenv("JAVA_JVM_VERSION", version, 1);
}
free(version);
}
}
return JAVA_FRAMEWORK;
}
void restartLauncher( char* program, char* args[] )
{
pid_t pid= fork();
if (pid == 0) {
/* Child process ... start the JVM */
execv(program != NULL ? program : args[0], args);
/* The JVM would not start ... return error code to parent process. */
_exit(errno);
}
}
int launchJavaVM( _TCHAR* args[] )
{
/*for now always do JNI on Mac, should not come in here */
return -1;
}
int startJavaVM( _TCHAR* libPath, _TCHAR* vmArgs[], _TCHAR* progArgs[] )
{
if (secondThread == 0)
return startJavaJNI(libPath, vmArgs, progArgs);
/* else, --launcher.secondThread was specified, create a new thread and run the
* vm on it. This main thread will run the CFRunLoop
*/
pthread_t thread;
struct rlimit limit = {0, 0};
int stackSize = 0;
if (getrlimit(RLIMIT_STACK, &limit) == 0) {
if (limit.rlim_cur != 0) {
stackSize = limit.rlim_cur;
}
}
/* initialize thread attributes */
pthread_attr_t attributes;
pthread_attr_init(&attributes);
pthread_attr_setscope(&attributes, PTHREAD_SCOPE_SYSTEM);
pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
if (stackSize != 0)
pthread_attr_setstacksize(&attributes, stackSize);
/* arguments to start the vm */
StartVMArgs args;
args.libPath = libPath;
args.vmArgs = vmArgs;
args.progArgs = progArgs;
loopRef = CFRunLoopGetCurrent();
/* create the thread */
pthread_create( &thread, &attributes, &startThread, &args);
pthread_attr_destroy(&attributes);
runEventLoop(loopRef);
return args.result;
}
void * startThread(void * init) {
StartVMArgs *args = (StartVMArgs *) init;
args->result = startJavaJNI(args->libPath, args->vmArgs, args->progArgs);
return NULL;
}
void runEventLoop(CFRunLoopRef ref) {
CFRunLoopSourceContext sourceContext = { .version = 0, .info = NULL, .retain = NULL, .release = NULL,
.copyDescription = NULL, .equal = NULL, .hash = NULL,
.schedule = NULL, .cancel = NULL, .perform = &dummyCallback };
CFRunLoopSourceRef sourceRef = CFRunLoopSourceCreate(NULL, 0, &sourceContext);
CFRunLoopAddSource(ref, sourceRef, kCFRunLoopCommonModes);
CFRunLoopRun();
CFRelease(sourceRef);
}
void disposeData(void *info, void *data, size_t size)
{
DisposePtr(data);
}
/**
* loadBMPImage
* Create a QuickDraw PixMap representing the given BMP file.
*
* bmpPathname: absolute path and name to the bmp file
*
* returned value: the PixMapHandle newly created if successful. 0 otherwise.
*/
static CGImageRef loadBMPImage (const char *bmpPathname) {
ng_stream_t in;
ng_bitmap_image_t image;
ng_err_t err= ERR_OK;
CGImageRef ref;
UBYTE1* data = NULL;
NgInit();
if (NgStreamInit(&in, (char*) bmpPathname) != ERR_OK) {
NgError(ERR_NG, "Error can't open BMP file");
return 0;
}
NgBitmapImageInit(&image);
err= NgBmpDecoderReadImage (&in, &image);
NgStreamClose(&in);
if (err != ERR_OK) {
NgBitmapImageFree(&image);
return 0;
}
UBYTE4 srcDepth= NgBitmapImageBitCount(&image);
if (srcDepth != 24) { /* We only support image depth of 24 bits */
NgBitmapImageFree(&image);
NgError (ERR_NG, "Error unsupported depth - only support 24 bit");
return 0;
}
int width= (int)NgBitmapImageWidth(&image);
int height= (int)NgBitmapImageHeight(&image);
int rowBytes= width * 4;
int alphainfo = kCGImageAlphaNoneSkipFirst | (NgIsMSB() ? 0 : kCGBitmapByteOrder32Little);
data = (UBYTE1*)NewPtr(rowBytes * height);
CGDataProviderRef provider = CGDataProviderCreateWithData(0, data, rowBytes * height, (CGDataProviderReleaseDataCallback)disposeData);
ref = CGImageCreate(width, height, 8, 32, width * 4, CGColorSpaceCreateDeviceRGB(), alphainfo, provider, NULL, 1, 0);
CGDataProviderRelease(provider);
/* 24 bit source to direct screen destination */
NgBitmapImageBlitDirectToDirect(NgBitmapImageImageData(&image), NgBitmapImageBytesPerRow(&image), width, height,
data, 32, rowBytes, NgIsMSB(), 0xff0000, 0x00ff00, 0x0000ff);
NgBitmapImageFree(&image);
return ref;
}
#define DOCK_ICON_PREFIX "-Xdock:icon="
#define DOCK_NAME_PREFIX "-Xdock:name="
#define APP_ICON_PATTERN "APP_ICON_%d"
#define APP_NAME_PATTERN "APP_NAME_%d"
void processVMArgs(char **vmargs[] )
{
int i = -1;
int pid = 0, pidLength = 1, temp = 0;
char * name = NULL, *icon = NULL;
char * c;
if( *vmargs == NULL)
return;
while( (*vmargs)[++i] != NULL ) {
/*-Xdock:icon -> APP_ICON_<pid>*/
if(_tcsncmp((*vmargs)[i], DOCK_ICON_PREFIX, _tcslen(DOCK_ICON_PREFIX)) == 0) {
icon = (*vmargs)[i] + _tcslen(DOCK_ICON_PREFIX);
}
/*-Xdock:name -> APP_NAME_<pid>*/
else if(_tcsncmp((*vmargs)[i], DOCK_NAME_PREFIX, _tcslen(DOCK_NAME_PREFIX)) == 0) {
name = (*vmargs)[i] + _tcslen(DOCK_NAME_PREFIX);
}
if (name != NULL && icon != NULL)
break;
}
if (name == NULL && icon == NULL)
return; /* don't need to do anything */
temp = pid = getpid();
/* how many digits in pid? */
while (temp > 9) {
pidLength++;
temp /= 10;
}
if (name != NULL) {
c = malloc( (_tcslen(APP_NAME_PATTERN) + pidLength + 1) * sizeof(char*));
_stprintf( c, APP_NAME_PATTERN, pid );
setenv(c, name, 1);
}
if (icon != NULL) {
c = malloc( (_tcslen(icon) + _tcslen(APP_ICON_PATTERN) + pidLength + 1) * sizeof(char*));
_stprintf( c, APP_ICON_PATTERN, pid );
setenv(c, icon, 1);
}
}
int isSunVM( _TCHAR * vm ) {
return (strncmp(vm, JAVA_FRAMEWORK, strlen(JAVA_FRAMEWORK)) == 0);
}