gecko-dev/java/webclient/src_moz/NativeEventThread.cpp
edburns%acm.org 0eca4c3b0f I now have webclient mostly working on win32. The following features have been tested and work well.
File->New Window

File->Close

View->View Page Source as String

Search->Find

Search->Find Next

Edit->Select All

Edit->Copy

History->Back

History->Forward

History-> <Navigation Number>

Bookmarks->Manage Bookmarks

The bookmarks window pops up and you can double click on bookmarks to
cause webclient to go to that page.

Stream->Load Stream From File...

Stream->Load Random HTML InputStream

Mouse over events work

The DOMViewer works

Navigation buttons work.

This leaves Bookmarks->Add Current Page, Bookmarks->Add Current Page in New Folder,
Profile->Create Profile, and Profile->Delete Profile. The first two should be easy,
the second two, I'll probably just take out, since their implementation was based in
BlackConnect, which is currently quite dead.

When I'm happy with the state of Webclient on win32, I'll get it working on linux.
2002-05-08 21:55:14 +00:00

756 lines
24 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is RaptorCanvas.
*
* The Initial Developer of the Original Code is Kirk Baker and
* Ian Wilkinson. Portions created by Kirk Baker and Ian Wilkinson are
* Copyright (C) 1999 Kirk Baker and Ian Wilkinson. All
* Rights Reserved.
*
* Contributor(s): Kirk Baker <kbaker@eb.com>
* Ian Wilkinson <iw@ennoble.com>
* Mark Lin <mark.lin@eng.sun.com>
* Mark Goddard
* Ed Burns <edburns@acm.org>
* Ashutosh Kulkarni <ashuk@eng.sun.com>
* Ann Sunhachawee
*/
#include "NativeEventThread.h"
#include "CBrowserContainer.h"
#include "ns_util.h"
#include "ns_globals.h"
#include "nsEmbedAPI.h" // for NS_InitEmbedding
#include "nsIProfile.h" // for the profile manager
#include "nsIProfileInternal.h" // for the profile manager
#include "nsICmdLineService.h" // for the cmdline service to give to the
// profile manager.
#include "nsCRT.h" // for nsCRT::strcmp
#include "prenv.h"
#include "nsILocalFile.h"
// #include "WrapperFactoryImpl.cpp"
#ifdef XP_PC
#include <windows.h>
#endif
#include "nsAppShellCIDs.h" // for NS_SESSIONHISTORY_CID
#include "nsCOMPtr.h" // to get nsIBaseWindow from webshell
//nsIDocShell is included in ns_util.h
#include "nsIEventQueueService.h" // for PLEventQueue
#include "nsIServiceManager.h" // for do_GetService
#include "nsISHistory.h" // for sHistory
#include "nsIThread.h" // for PRThread
#include "nsIDocShell.h"
#include "nsIBaseWindow.h"
#include "nsIDocShellTreeItem.h"
#include "nsCWebBrowser.h"
#include "nsIEventQueueService.h"
#include "nsIThread.h"
//nsIWebShell is included in ns_util.h
#include "NativeEventThreadActionEvents.h"
#include "prlog.h" // for PR_ASSERT
#ifdef XP_UNIX
#include <unistd.h>
#include "gdksuperwin.h"
#include "gtkmozarea.h"
extern "C" {
static int wc_x_error (Display *display,
XErrorEvent *error);
}
#endif
static NS_DEFINE_IID(kWebShellCID, NS_WEB_SHELL_CID);
static NS_DEFINE_IID(kIWebShellIID, NS_IWEB_SHELL_IID);
static NS_DEFINE_IID(kISHistoryIID, NS_ISHISTORY_IID);
static NS_DEFINE_CID(kSHistoryCID, NS_SHISTORY_CID);
static NS_DEFINE_CID(kCmdLineServiceCID, NS_COMMANDLINE_SERVICE_CID);
static const char *NS_DOCSHELL_PROGID = "component://netscape/docshell/html";
//static const char *NS_WEBBROWSER_PROGID = "component://netscape/embedding/browser/nsWebBrowser";
extern const char * gBinDir; // defined in WrapperFactoryImpl.cpp
#ifdef XP_PC
// All this stuff is needed to initialize the history
#define APPSHELL_DLL "appshell.dll"
#define BROWSER_DLL "nsbrowser.dll"
#define EDITOR_DLL "ender.dll"
#else
#ifdef XP_MAC
#define APPSHELL_DLL "APPSHELL_DLL"
#define EDITOR_DLL "ENDER_DLL"
#else
// XP_UNIX || XP_BEOS
#define APPSHELL_DLL "libnsappshell"MOZ_DLL_SUFFIX
#define APPCORES_DLL "libappcores"MOZ_DLL_SUFFIX
#define EDITOR_DLL "libender"MOZ_DLL_SUFFIX
#endif // XP_MAC
#endif // XP_PC
//
// Local functions
//
#ifdef XP_UNIX
static int
wc_x_error (Display *display,
XErrorEvent *error)
{
if (error->error_code)
{
char buf[64];
XGetErrorText (display, error->error_code, buf, 63);
fprintf (stderr, "Webclient-Gdk-ERROR **: %s\n serial %ld error_code %d request_code %d minor_code %d\n",
buf,
error->serial,
error->error_code,
error->request_code,
error->minor_code);
}
return 0;
}
#endif
/**
* Called from InitMozillaStuff().
*/
int processEventLoop(WebShellInitContext * initContext);
/**
* Called from Java nativeInitialize to create the webshell
* and other mozilla things, then start the event loop.
*/
nsresult InitMozillaStuff (WebShellInitContext * arg);
//
// Local data
//
nsIComponentManager *gComponentManager = nsnull;
static PRBool gFirstTime = PR_TRUE;
PLEventQueue * gActionQueue = nsnull;
PRThread * gEmbeddedThread = nsnull;
nsISHistory *gHistory = nsnull;
char * errorMessages[] = {
"No Error",
"Could not obtain the event queue service.",
"Unable to create the WebShell instance.",
"Unable to initialize the WebShell instance.",
"Unable to show the WebShell."
};
//
// JNI methods
//
JNIEXPORT void JNICALL Java_org_mozilla_webclient_wrapper_1native_NativeEventThread_nativeInitialize
(JNIEnv *env, jobject obj, jint webShellPtr)
{
nsresult rv = NS_ERROR_FAILURE;
WebShellInitContext * initContext = (WebShellInitContext *) webShellPtr;
if (nsnull == initContext) {
::util_ThrowExceptionToJava(env,
"NULL webShellPtr passed to nativeInitialize.");
return;
}
if (nsnull == gVm) { // declared in ../src_share/jni_util.h
::util_GetJavaVM(env, &gVm); // save this vm reference
}
rv = InitMozillaStuff(initContext);
if (NS_FAILED(rv)) {
::util_ThrowExceptionToJava(env,
errorMessages[initContext->initFailCode]);
return;
}
while (initContext->initComplete == FALSE) {
::PR_Sleep(PR_INTERVAL_NO_WAIT);
if (initContext->initFailCode != 0) {
::util_ThrowExceptionToJava(env, errorMessages[initContext->initFailCode]);
return;
}
}
}
JNIEXPORT void JNICALL Java_org_mozilla_webclient_wrapper_1native_NativeEventThread_nativeProcessEvents
(JNIEnv *env, jobject obj, jint webShellPtr)
{
WebShellInitContext * initContext = (WebShellInitContext *) webShellPtr;
if (nsnull == initContext) {
::util_ThrowExceptionToJava(env,
"NULL webShellPtr passed to nativeProcessEvents.");
return;
}
void* threadId = PR_GetCurrentThread();
if (threadId == (void *) gEmbeddedThread) {
// printf("--------- Thread ID ---- %p\n",gEmbeddedThread);
processEventLoop(initContext);
}
}
/**
* <P> This method takes the typedListener, which is a
* WebclientEventListener java subclass, figures out what type of
* subclass it is, using the gSupportedListenerInterfaces array, and
* calls the appropriate add*Listener local function. </P>
*<P> PENDING(): we could do away with the switch statement using
* function pointers, or some other mechanism. </P>
* <P>the NewGlobalRef call is very important, since the argument
* typedListener is used to call back into java, at another time, as a
* result of the a mozilla event being fired.</P>
* PENDING(): implement nativeRemoveListener, which must call
* RemoveGlobalRef.
*/
JNIEXPORT void JNICALL Java_org_mozilla_webclient_wrapper_1native_NativeEventThread_nativeAddListener
(JNIEnv *env, jobject obj, jint webShellPtr, jobject typedListener,
jstring listenerString)
{
WebShellInitContext *initContext = (WebShellInitContext *)webShellPtr;
if (initContext == nsnull) {
::util_ThrowExceptionToJava(env, "Exception: null initContext passed tonativeAddListener");
return;
}
if (nsnull == initContext->nativeEventThread) {
// store the java EventRegistrationImpl class in the initContext
initContext->nativeEventThread =
::util_NewGlobalRef(env, obj); // VERY IMPORTANT!!
// This enables the listener to call back into java
}
jclass clazz = nsnull;
int listenerType = 0;
const char *listenerStringChars = ::util_GetStringUTFChars(env,
listenerString);
if (listenerStringChars == nsnull) {
::util_ThrowExceptionToJava(env, "Exception: nativeAddListener: Can't get className for listener.");
return;
}
while (nsnull != gSupportedListenerInterfaces[listenerType]) {
if (0 == nsCRT::strcmp(gSupportedListenerInterfaces[listenerType],
listenerStringChars)) {
// We've got a winner!
break;
}
listenerType++;
}
::util_ReleaseStringUTFChars(env, listenerString, listenerStringChars);
listenerStringChars = nsnull;
if (LISTENER_NOT_FOUND == (LISTENER_CLASSES) listenerType) {
::util_ThrowExceptionToJava(env, "Exception: NativeEventThread.nativeAddListener(): can't find listener \n\tclass for argument");
return;
}
jobject globalRef = nsnull;
// PENDING(edburns): make sure do DeleteGlobalRef on the removeListener
if (nsnull == (globalRef = ::util_NewGlobalRef(env, typedListener))) {
::util_ThrowExceptionToJava(env, "Exception: NativeEventThread.nativeAddListener(): can't create NewGlobalRef\n\tfor argument");
return;
}
PR_ASSERT(initContext->browserContainer);
switch(listenerType) {
case DOCUMENT_LOAD_LISTENER:
initContext->browserContainer->AddDocumentLoadListener(globalRef);
break;
case MOUSE_LISTENER:
initContext->browserContainer->AddMouseListener(globalRef);
break;
}
return;
}
JNIEXPORT void JNICALL
Java_org_mozilla_webclient_wrapper_1native_NativeEventThread_nativeRemoveListener
(JNIEnv *env, jobject obj, jint webShellPtr, jobject typedListener,
jstring listenerString)
{
WebShellInitContext *initContext = (WebShellInitContext *)webShellPtr;
if (initContext == nsnull) {
::util_ThrowExceptionToJava(env, "Exception: null initContext passed to nativeRemoveListener");
return;
}
jclass clazz = nsnull;
int listenerType = 0;
const char *listenerStringChars = ::util_GetStringUTFChars(env,
listenerString);
if (listenerStringChars == nsnull) {
::util_ThrowExceptionToJava(env, "Exception: nativeRemoveListener: Can't get className for listener.");
return;
}
while (nsnull != gSupportedListenerInterfaces[listenerType]) {
if (0 == nsCRT::strcmp(gSupportedListenerInterfaces[listenerType],
listenerStringChars)) {
// We've got a winner!
break;
}
listenerType++;
}
::util_ReleaseStringUTFChars(env, listenerString, listenerStringChars);
listenerStringChars = nsnull;
if (LISTENER_NOT_FOUND == (LISTENER_CLASSES) listenerType) {
::util_ThrowExceptionToJava(env, "Exception: NativeEventThread.nativeRemoveListener(): can't find listener \n\tclass for argument");
return;
}
PR_ASSERT(initContext->browserContainer);
switch(listenerType) {
case DOCUMENT_LOAD_LISTENER:
initContext->browserContainer->RemoveDocumentLoadListener();
break;
case MOUSE_LISTENER:
initContext->browserContainer->RemoveMouseListener();
break;
}
}
JNIEXPORT void JNICALL
Java_org_mozilla_webclient_wrapper_1native_NativeEventThread_nativeRemoveAllListeners(JNIEnv *env, jobject obj, jint webShellPtr)
{
WebShellInitContext *initContext = (WebShellInitContext *)webShellPtr;
if (initContext == nsnull) {
::util_ThrowExceptionToJava(env, "Exception: null initContext passed to nativeRemoveAllListeners");
return;
}
initContext->browserContainer->RemoveAllListeners();
}
/**
* This function processes methods inserted into WebShellInitContext's
* actionQueue. It is called once during the initialization of the
* NativeEventThread java thread, and infinitely in
* NativeEventThread.run()'s event loop. The call to PL_HandleEvent
* below, ends up calling the nsActionEvent subclass's handleEvent()
* method. After which it calls nsActionEvent::destroyEvent().
*/
int processEventLoop(WebShellInitContext * initContext)
{
#ifdef XP_UNIX
while(gtk_events_pending()) {
gtk_main_iteration();
}
#else
// PENDING(mark): Does this work on the Mac?
MSG msg;
PRBool wasHandled;
if (::PeekMessage(&msg, nsnull, 0, 0, PM_NOREMOVE)) {
if (::GetMessage(&msg, nsnull, 0, 0)) {
wasHandled = PR_FALSE;
::NS_HandleEmbeddingEvent(msg, wasHandled);
if (!wasHandled) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
}
#endif
::PR_Sleep(PR_INTERVAL_NO_WAIT);
if ((initContext->initComplete) && (gActionQueue)) {
PLEvent * event = nsnull;
PL_ENTER_EVENT_QUEUE_MONITOR(gActionQueue);
if (::PL_EventAvailable(gActionQueue)) {
event = ::PL_GetEvent(gActionQueue);
}
PL_EXIT_EVENT_QUEUE_MONITOR(gActionQueue);
if (event != nsnull) {
::PL_HandleEvent(event);
}
}
if (initContext->stopThread) {
initContext->stopThread++;
return 0;
}
// PENDING(edburns): revisit this. Not sure why this is necessary, but
// this fixes bug 44327
// printf("%c", 8); // 8 is ASCII for backspace
return 1;
}
// Ashu
#ifdef XP_UNIX
static void event_processor_callback(gpointer data,
gint source,
GdkInputCondition condition) {
#if DEBUG_RAPTOR_CANVAS
if (prLogModuleInfo) {
PR_LOG(prLogModuleInfo, 3,
"EventHandler: event_processor_callback()\n");
}
#endif
nsIEventQueue *eventQueue = (nsIEventQueue*)data;
eventQueue->ProcessPendingEvents();
#if DEBUG_RAPTOR_CANVAS
if (prLogModuleInfo) {
PR_LOG(prLogModuleInfo, 3, "EventHandler: Done processing pending events\n");
}
#endif
}
#endif
//
void DoMozInitialization(WebShellInitContext * initContext)
{
if (gFirstTime) {
// PENDING(edburns): We need this for rdf_getChildCount
PR_SetEnv("XPCOM_CHECK_THREADSAFE=0");
nsILocalFile * pathFile = nsnull;
nsresult rv = nsnull;
JNIEnv * env = initContext->env;
const char * BinDir = gBinDir;
rv = NS_NewLocalFile(BinDir, PR_TRUE, &pathFile);
if (NS_FAILED(rv)) {
::util_ThrowExceptionToJava(env, "call to NS_NewLocalFile failed.");
return;
}
// It is vitally important to call NS_InitEmbedding before calling
// anything else.
NS_InitEmbedding(pathFile, nsnull);
rv = NS_GetGlobalComponentManager(&gComponentManager);
if (NS_FAILED(rv)) {
::util_ThrowExceptionToJava(env, "NS_GetGlobalComponentManager() failed.");
return;
}
prLogModuleInfo = PR_NewLogModule("webclient");
const char *webclientLogFile = PR_GetEnv("WEBCLIENT_LOG_FILE");
if (nsnull != webclientLogFile) {
PR_SetLogFile(webclientLogFile);
// If this fails, it just goes to stdout/stderr
}
// handle the profile manager nonsense
nsCOMPtr<nsICmdLineService> cmdLine =do_GetService(kCmdLineServiceCID);
nsCOMPtr<nsIProfile> profile = do_GetService(NS_PROFILE_CONTRACTID);
if (!cmdLine || !profile) {
::util_ThrowExceptionToJava(env, "Can't get the profile manager.");
return;
}
PRInt32 numProfiles=0;
rv = profile->GetProfileCount(&numProfiles);
char *argv[3];
int i, argc = 0;
argv[0] = strdup(gBinDir);
if (numProfiles > 1) {
PRUnichar **Names;
PRUint32 NamesLen = 0;
rv = profile->GetProfileList(&NamesLen, &Names);
argv[1] = strdup("-p");
if (NS_SUCCEEDED(rv)) {
PR_ASSERT(NamesLen >= 1);
// PENDING(edburns): fix for 70656. Really we should have a way
// for the embedding app to specify which profile to use.
// For now we just get the name of the first profile.
char * temp = new char[100]; // de-allocated in following for loop
for (i = 0; Names[0][i] != '\0'; i++) {
temp[i] = (char) Names[0][i];
}
nsMemory::Free(Names);
temp[i] = '\0';
argv[2] = temp;
argc = 3;
}
else {
argv[2] = strdup("default");
}
printf("debug: edburns: argv[1]: %s argv[2]: %s\n", argv[1],
argv[2]);
}
else {
argc = 1;
}
rv = cmdLine->Initialize(argc, argv);
for (i = 0; i < argc; i++) {
nsCRT::free(argv[i]);
}
if (NS_FAILED(rv)) {
::util_ThrowExceptionToJava(env, "Can't initialize nsICmdLineService.");
return;
}
nsCOMPtr<nsIProfileInternal> profileInt = do_QueryInterface(profile);
if (profileInt) {
rv = profileInt->StartupWithArgs(cmdLine, PR_FALSE);
if (NS_FAILED(rv)) {
::util_ThrowExceptionToJava(env, "Can't statrup nsIProfile service.");
return;
}
}
else {
::util_ThrowExceptionToJava(env, "Can't statrup nsIProfile service.");
return;
}
}
}
nsresult InitMozillaStuff (WebShellInitContext * initContext)
{
nsresult rv = nsnull;
DoMozInitialization(initContext);
PR_ASSERT(gComponentManager);
if (gFirstTime) {
printf ("\n\nCreating Event Queue \n\n");
nsCOMPtr<nsIEventQueueService>
aEventQService = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID);
// if we get here, we know that aEventQService is not null.
if (!aEventQService) {
rv = NS_ERROR_FAILURE;
return rv;
}
//TODO Add tracing from nspr.
#if DEBUG_RAPTOR_CANVAS
if (prLogModuleInfo) {
PR_LOG(prLogModuleInfo, 3, ("InitMozillaStuff(%lx): Create the Event Queue for the UI thread...\n", initContext));
}
#endif
// Create the Event Queue for the UI thread...
if (!aEventQService) {
initContext->initFailCode = kEventQueueError;
return rv;
}
// Create the event queue.
rv = aEventQService->CreateThreadEventQueue();
gEmbeddedThread = PR_GetCurrentThread();
// Create the action queue
if (gEmbeddedThread) {
if (gActionQueue == nsnull) {
printf("InitMozillaStuff(%lx): Create the action queue\n", initContext);
// We need to do something different for Unix
nsIEventQueue * EQueue = nsnull;
rv = aEventQService->GetThreadEventQueue(gEmbeddedThread,
&EQueue);
if (NS_FAILED(rv)) {
initContext->initFailCode = kCreateWebShellError;
return rv;
}
#ifdef XP_UNIX
gdk_input_add(EQueue->GetEventQueueSelectFD(),
GDK_INPUT_READ,
event_processor_callback,
EQueue);
#endif
PLEventQueue * plEventQueue = nsnull;
EQueue->GetPLEventQueue(&plEventQueue);
gActionQueue = plEventQueue;
}
}
else {
initContext->initFailCode = kCreateWebShellError;
return NS_ERROR_UNEXPECTED;
}
}
if (gFirstTime) {
gFirstTime = PR_FALSE;
}
PRBool allowPlugins = PR_TRUE;
/*
// Create the WebBrowser.
nsCOMPtr<nsIWebBrowser> webBrowser = nsnull;
webBrowser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID);
initContext->webBrowser = webBrowser;
// Get the BaseWindow from the DocShell - upcast
// nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(webBrowser));
nsCOMPtr<nsIBaseWindow> docShellAsWin;
rv = webBrowser->QueryInterface(NS_GET_IID(nsIBaseWindow), getter_AddRefs(docShellAsWin));
initContext->baseWindow = docShellAsWin;
printf ("Init the baseWindow\n");
#ifdef XP_UNIX
GtkWidget * bin;
bin = (GtkWidget *) initContext->gtkWinPtr;
if (prLogModuleInfo) {
PR_LOG(prLogModuleInfo, 3, ("Ashu Debugs - Inside InitMozillaStuff(%lx): - before Init Call...\n", initContext));
}
rv = initContext->baseWindow->InitWindow((nativeWindow) bin, nsnull, initContext->x, initContext->y, initContext->w, initContext->h);
if (prLogModuleInfo) {
PR_LOG(prLogModuleInfo, 3, ("Ashu Debugs - Inside InitMozillaStuff(%lx): - after Init Call...\n", initContext));
}
#else
rv = initContext->baseWindow->InitWindow((nativeWindow) initContext->parentHWnd, nsnull,
initContext->x, initContext->y, initContext->w, initContext->h);
#endif
printf("Create the BaseWindow...\n");
rv = initContext->baseWindow->Create();
if (NS_FAILED(rv)) {
initContext->initFailCode = kInitWebShellError;
return rv;
}
// Create the DocShell
initContext->docShell = do_GetInterface(initContext->webBrowser);
if (!initContext->docShell) {
initContext->initFailCode = kCreateDocShellError;
return rv;
}
// create our BrowserContainer, which implements many many things.
initContext->browserContainer =
new CBrowserContainer(initContext->webBrowser, initContext->env,
initContext);
// set the WebShellContainer. This is a pain. It's necessary
// because nsWebShell.cpp still checks for mContainer all over the
// place.
nsCOMPtr<nsIWebShellContainer> wsContainer(do_QueryInterface(initContext->browserContainer));
nsCOMPtr<nsIWebShell> webShell(do_QueryInterface(initContext->docShell));
webShell->SetContainer(wsContainer);
// set the TreeOwner
nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(initContext->docShell));
nsCOMPtr<nsIDocShellTreeOwner> treeOwner(do_QueryInterface(initContext->browserContainer));
docShellAsItem->SetTreeOwner(treeOwner);
// set the docloaderobserver
nsCOMPtr<nsIDocumentLoaderObserver> observer(do_QueryInterface(initContext->browserContainer));
initContext->docShell->SetDocLoaderObserver(observer);
printf("Creation Done.....\n");
// Get the WebNavigation Object from the DocShell
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(initContext->docShell));
initContext->webNavigation = webNav;
printf("Show the webBrowser\n");
// Show the webBrowser
rv = initContext->baseWindow->SetVisibility(PR_TRUE);
if (NS_FAILED(rv)) {
initContext->initFailCode = kShowWebShellError;
return rv;
}
initContext->initComplete = TRUE;
*/
wsRealizeBrowserEvent * actionEvent = new wsRealizeBrowserEvent(initContext);
PLEvent * event = (PLEvent*) *actionEvent;
::util_PostSynchronousEvent(initContext, event);
#if DEBUG_RAPTOR_CANVAS
if (prLogModuleInfo) {
PR_LOG(prLogModuleInfo, 3,
("InitMozillaStuff(%lx): enter event loop\n", initContext));
}
#endif
#ifdef XP_UNIX
// The gdk_x_error function exits in some cases, we don't
// want that.
XSetErrorHandler(wc_x_error);
#endif
// Just need to loop once to clear out events before returning
processEventLoop(initContext);
return rv;
}