blob: 7352af3c6d9b573e10a4c31c7aaeaa802ba8ed87 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2010 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
* Kevin Cornell (Rational Software Corporation)
* Tom Tromey (Red Hat, Inc.)
*******************************************************************************/
#include "eclipseMozilla.h"
#include "eclipseCommon.h"
#include "eclipseOS.h"
#include "eclipseUtil.h"
#include "eclipseGtk.h"
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <dlfcn.h>
#ifdef SOLARIS
#include <sys/filio.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <semaphore.h>
#include <fcntl.h>
/* Global Variables */
char* defaultVM = "java";
char* vmLibrary = "libjvm.so";
char* shippedVMDir = "jre/bin/";
/* Define the special arguments for the various Java VMs. */
static char* argVM_JAVA[] = { NULL };
/* Define local variables . */
static long splashHandle = 0;
static GtkWidget* shellHandle = 0;
static GdkPixbuf* pixbuf = 0;
static GtkWidget* image = 0;
static sem_t* mutex;
static Atom appWindowAtom, launcherWindowAtom;
static _TCHAR** openFilePath = NULL; /* the files we want to open */
static int openFileTimeout = 60; /* number of seconds to wait before timeout */
static struct sigaction quitAction;
static struct sigaction intAction;
/* Local functions */
static void catch_signal(int sig) {
//catch signals, free the lock, reinstall the original
//signal handlers and reraise the signal.
sem_post(mutex);
sem_close(mutex);
sigaction(SIGINT, &intAction, NULL);
sigaction(SIGQUIT, &intAction, NULL);
raise(sig);
}
typedef int (*LockFunc)();
int executeWithLock(char *name, LockFunc func) {
int result = -1;
int lock = -1;
struct sigaction action;
mutex = sem_open(name, O_CREAT | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO, 1);
if (mutex == SEM_FAILED) {
//create failed. Probably lock is already created so try opening the existing lock.
mutex = sem_open(name, 0);
}
if (mutex == SEM_FAILED)
return -1; //this is an error.
// install signal handler to free the lock if something bad happens.
// sem_t is not freed automatically when a process ends.
action.sa_handler = catch_signal;
sigaction(SIGINT, &action, &intAction);
sigaction(SIGQUIT, &action, &quitAction);
while ((lock = sem_trywait(mutex)) != 0) {
if (errno == EAGAIN) {
//couldn't acquire lock, sleep a bit and try again
sleep(1);
if (--openFileTimeout > 0)
continue;
}
break;
}
if (lock == 0)
result = func();
sem_post(mutex);
sem_close(mutex);
//reinstall the original signal handlers
sigaction(SIGINT, &intAction, NULL);
sigaction(SIGQUIT, &quitAction, NULL);
return result;
}
static void log_handler(const gchar* domain, GLogLevelFlags flags, const gchar* msg, gpointer data) {
/* nothing */
}
/* Create a "SWT_Window_" + APP_NAME string with optional suffix.
* Caller should free the memory when finished */
static char * createSWTWindowString(char * suffix, int semaphore) {
#ifdef SOLARIS
/* solaris requires semaphore names to start with '/' */
char * prefix = semaphore != 0 ? _T_ECLIPSE("/SWT_Window_") : _T_ECLIPSE("SWT_Window_");
#else
char * prefix = _T_ECLIPSE("SWT_Window_");
#endif
char * result = malloc((_tcslen(prefix) + _tcslen(getOfficialName()) + (suffix != NULL ? _tcslen(suffix) : 0) + 1) * sizeof(char));
if (suffix != NULL)
_stprintf(result, _T_ECLIPSE("%s%s%s"), prefix, getOfficialName(), suffix);
else
_stprintf(result, _T_ECLIPSE("%s%s"), prefix, getOfficialName());
return result;
}
static int setAppWindowPropertyFn() {
Window appWindow;
Atom propAtom;
_TCHAR *propVal;
//Look for the SWT window. If it's there, set a property on it.
appWindow = gtk.XGetSelectionOwner(gtk_GDK_DISPLAY, appWindowAtom);
if (appWindow) {
propAtom = gtk.XInternAtom(gtk_GDK_DISPLAY, "org.eclipse.swt.filePath.message", FALSE);
//append a colon delimiter in case more than one file gets appended to the app windows property.
propVal = concatPaths(openFilePath, _T_ECLIPSE(':'));
gtk.XChangeProperty(gtk_GDK_DISPLAY, appWindow, propAtom, propAtom, 8, PropModeAppend, (unsigned char *)propVal, _tcslen(propVal));
free(propVal);
return 1;
}
return 0;
}
/* set the Application window property by executing _setWindowPropertyFn within a semaphore */
int setAppWindowProperty() {
int result;
char * mutexName = createSWTWindowString(NULL, 1);
result = executeWithLock(mutexName, setAppWindowPropertyFn);
gtk.XSync(gtk_GDK_DISPLAY, False);
free(mutexName);
return result;
}
/* timer callback function to call setAppWindowProperty */
static gboolean setAppWindowTimerProc(gpointer data) {
//try to set the app window property. If unsuccessful return true to reschedule the timer.
openFileTimeout--;
return !setAppWindowProperty() && openFileTimeout > 0;
}
int createLauncherWindow() {
Window window, launcherWindow;
//check if a launcher window exists. If none exists, we know we are the first and we should be launching the app.
window = gtk.XGetSelectionOwner(gtk_GDK_DISPLAY, launcherWindowAtom);
if (window == 0) {
//create a launcher window that other processes can find.
launcherWindow = gtk.XCreateWindow(gtk_GDK_DISPLAY, gtk.XRootWindow(gtk_GDK_DISPLAY, gtk.XDefaultScreen(gtk_GDK_DISPLAY)), -10, -10, 1,
1, 0, 0, InputOnly, CopyFromParent, (unsigned long) 0, (XSetWindowAttributes *) NULL);
//for some reason Set and Get are both necessary. Set alone does nothing.
gtk.XSetSelectionOwner(gtk_GDK_DISPLAY, launcherWindowAtom, launcherWindow, CurrentTime);
gtk.XGetSelectionOwner(gtk_GDK_DISPLAY, launcherWindowAtom);
//add a timeout to set the property on the apps window once the app is launched.
gtk.g_timeout_add(1000, setAppWindowTimerProc, 0);
return 0;
}
return 1;
}
int reuseWorkbench(_TCHAR** filePath, int timeout) {
char *appName, *launcherName;
int result = 0;
if (initWindowSystem(&initialArgc, initialArgv, 1) != 0)
return -1;
openFileTimeout = timeout;
openFilePath = filePath;
//App name is defined in SWT as well. Values must be consistent.
appName = createSWTWindowString(NULL, 0);
appWindowAtom = gtk.XInternAtom(gtk_GDK_DISPLAY, appName, FALSE);
free(appName);
//check if app is already running. Just set property if it is.
if (setAppWindowProperty() > 0)
return 1;
/* app is not running, create a launcher window to act as a mutex so we don't need to keep the semaphore locked */
launcherName = createSWTWindowString(_T_ECLIPSE("_Launcher"), 1);
launcherWindowAtom = gtk.XInternAtom(gtk_GDK_DISPLAY, launcherName, FALSE);
result = executeWithLock(launcherName, createLauncherWindow);
free(launcherName);
if (result == 1) {
//The app is already being launched in another process. Set the property on that app window and exit
while (openFileTimeout > 0) {
if (setAppWindowProperty() > 0)
return 1; //success
else {
openFileTimeout--;
sleep(1);
}
}
//timed out trying to set the app property
result = 0;
}
return result;
}
/* Create and Display the Splash Window */
int showSplash( const char* featureImage )
{
GtkAdjustment* vadj, *hadj;
int width, height;
guint handlerId;
GtkWidget* vboxHandle, * scrolledHandle, * handle;
if (splashHandle != 0)
return 0; /* already showing splash */
if (featureImage == NULL)
return -1;
if (initialArgv == NULL)
initialArgc = 0;
if( initWindowSystem(&initialArgc, initialArgv, 1) != 0)
return -1;
shellHandle = gtk.gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk.gtk_window_set_decorated((GtkWindow*)(shellHandle), FALSE);
gtk.gtk_signal_connect((GtkObject*)shellHandle, "destroy", (GtkSignalFunc)(gtk.gtk_widget_destroyed), &shellHandle);
vboxHandle = gtk.gtk_vbox_new(FALSE, 0);
if(vboxHandle == 0)
return -1;
vadj = (GtkAdjustment*)gtk.gtk_adjustment_new(0, 0, 100, 1, 10, 10);
hadj = (GtkAdjustment*)gtk.gtk_adjustment_new(0, 0, 100, 1, 10, 10);
if (vadj == 0 || hadj == 0)
return -1;
scrolledHandle = gtk.gtk_scrolled_window_new(hadj, vadj);
gtk.gtk_container_add((GtkContainer*)(vboxHandle), scrolledHandle);
gtk.gtk_box_set_child_packing((GtkBox*)(vboxHandle), scrolledHandle, TRUE, TRUE, 0, GTK_PACK_END);
gtk.gtk_scrolled_window_set_policy((GtkScrolledWindow*)(scrolledHandle), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
handle = gtk.gtk_fixed_new();
gtk.gtk_fixed_set_has_window((GtkFixed*)(handle), TRUE);
((GtkObject*)handle)->flags |= GTK_CAN_FOCUS; /*GTK_WIDGET_SET_FLAGS(handle, GTK_CAN_FOCUS);*/
/* avoid gtk_scrolled_window_add warning */
handlerId = gtk.g_log_set_handler("Gtk", G_LOG_LEVEL_WARNING, &log_handler, NULL);
gtk.gtk_container_add((GtkContainer*)(scrolledHandle), handle);
gtk.g_log_remove_handler("Gtk", handlerId);
gtk.gtk_container_add((GtkContainer*)(shellHandle), vboxHandle);
pixbuf = gtk.gdk_pixbuf_new_from_file(featureImage, NULL);
image = gtk.gtk_image_new_from_pixbuf(pixbuf);
gtk.gtk_signal_connect((GtkObject*)(image), "destroy", (GtkSignalFunc)(gtk.gtk_widget_destroyed), &image);
gtk.gtk_container_add((GtkContainer*)(handle), image);
width = gtk.gdk_pixbuf_get_width(pixbuf);
height = gtk.gdk_pixbuf_get_height(pixbuf);
gtk.gtk_window_set_position((GtkWindow*)(shellHandle), GTK_WIN_POS_CENTER);
if (getOfficialName() != NULL)
gtk.gtk_window_set_title((GtkWindow*)(shellHandle), getOfficialName());
gtk.gtk_window_resize((GtkWindow*)(shellHandle), width, height);
gtk.gtk_widget_show_all((GtkWidget*)(shellHandle));
splashHandle = (long)shellHandle;
dispatchMessages();
return 0;
}
void dispatchMessages() {
if (gtk.g_main_context_iteration != 0)
while(gtk.g_main_context_iteration(0,0) != 0) {}
}
jlong getSplashHandle() {
return splashHandle;
}
void takeDownSplash() {
if(shellHandle != 0) {
gtk.gtk_widget_destroy(shellHandle);
if (image != NULL) {
gtk.gtk_widget_destroy(image);
gtk.g_object_unref(pixbuf);
}
dispatchMessages();
splashHandle = 0;
shellHandle = NULL;
}
}
/* Get the window system specific VM arguments */
char** getArgVM( char* vm )
{
char** result;
/* if (isJ9VM( vm ))
return argVM_J9;*/
/* Use the default arguments for a standard Java VM */
result = argVM_JAVA;
return result;
}
JavaResults* launchJavaVM( char* args[] )
{
JavaResults* jvmResults = NULL;
pid_t jvmProcess;
int exitCode;
#ifdef MOZILLA_FIX
fixEnvForMozilla();
#endif /* MOZILLA_FIX */
jvmProcess = fork();
if (jvmProcess == 0)
{
/* Child process ... start the JVM */
execv(args[0], args);
/* The JVM would not start ... return error code to parent process. */
/* TODO, how to distinguish this as a launch problem to the other process? */
_exit(errno);
}
jvmResults = malloc(sizeof(JavaResults));
memset(jvmResults, 0, sizeof(JavaResults));
/* If the JVM is still running, wait for it to terminate. */
if (jvmProcess != 0)
{
waitpid(jvmProcess, &exitCode, 0);
if (WIFEXITED(exitCode))
/* TODO, this should really be a runResult if we could distinguish the launch problem above */
jvmResults->launchResult = WEXITSTATUS(exitCode);
}
return jvmResults;
}