diff -r e8e63152f320 -r 2a9601315dfc javauis/eswt_qt/org.eclipse.swt/Eclipse_SWT_PI/qt/library/swts60.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javauis/eswt_qt/org.eclipse.swt/Eclipse_SWT_PI/qt/library/swts60.cpp Mon May 03 12:27:20 2010 +0300 @@ -0,0 +1,515 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 Nokia Corporation and/or its subsidiary(-ies). + * 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: + * Nokia Corporation - initial implementation + *******************************************************************************/ + +#include +#include // For CApaWindowGroupName +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "eventcallback.h" +#include "swts60.h" +#include "autorelease.h" + +using namespace Java::eSWT; + +// Name of the UI thread. +_LIT(KSwtUiThreadName, "SwtQtUiThread"); + +// Name of the support thread. +_LIT(KSwtSupportThreadName, "SwtQtSupportThread"); + +// Name of the shared library +_LIT(KSwtDllName, "eswtqt.dll"); + +// Stack size for the UI thread, 0x14000 = 80kB +const TInt KSwtUiThreadStackSize = 0x14000; + +// Stack size for the support thread, 0x200 = 0.5kB +const TInt KSwtSupportThreadStackSize = 0x200; + +static const char* const EVENT_FILTER = "swt_event_filter"; + +// Data stored to thread/dll specific Symbian thread local storage of the UI +// thread. +typedef struct + { + JavaVM* vm; + jint uid; + jobject runner; + TRequestStatus initStatus; + TThreadId initThreadId; + TThreadId uiThreadId; + TBool doFreeTLSInCleanupUIThread; + } +SwtTlsData; + +inline static void freeTLSData(SwtTlsData*& aData) + { + Dll::SetTls(NULL); + delete aData; + aData = NULL; + } + +TUid SwtQtS60MainApplication::AppDllUid() const + { + SwtTlsData* data = reinterpret_cast(Dll::Tls()); + TUid uid; + uid.iUid = static_cast(data ? data->uid : 0); + return uid; + } + +int SymbianUtils::startUI(JNIEnv* aJniEnv, jobject aRunner, jint aUid) + { + // Get a VM pointer using the current thread context + JavaVM* javaVM = NULL; + jint getVMStatus = aJniEnv->GetJavaVM(&javaVM); + if(getVMStatus < 0) return KErrNoMemory; + + // Add a global reference to callback object so that in can be used from + // the UI thread. + jobject globalRef = aJniEnv->NewGlobalRef(aRunner); + if(!globalRef) return KErrNoMemory; + + // Put needed data to a stucture for TLS storing that will be done later + // in the UI thread + SwtTlsData* data = new (std::nothrow) SwtTlsData(); + if(!data) return KErrNoMemory; + data->vm = javaVM; + data->uid = aUid; + data->runner = globalRef; + data->initStatus = KRequestPending; + RThread thread; + data->initThreadId = thread.Id(); + + // Create a new thread that will be the UI thread + TName uiThreadName(KSwtUiThreadName); + RThread uiThread; + TInt createStatus = uiThread.Create( uiThreadName, SymbianUtils::uiThreadEntryPoint, + KSwtUiThreadStackSize, NULL, reinterpret_cast(data) ); + data->uiThreadId = uiThread.Id(); + if(createStatus != KErrNone) + { + delete data; + return createStatus; + } + + // Resume the UI thread and wait until it reports back the initialization + // status + uiThread.Resume(); + User::WaitForRequest(data->initStatus); + + // Launch the support thread + if(data->initStatus == KErrNone) + { + startSupportThread(reinterpret_cast(data)); + } + + // Return the thread initialization status + return data->initStatus.Int(); + } + +int SymbianUtils::initUiThread(JNIEnv* aJniEnv, const TInt& aUid) + { + // This thread may or may not be a UI thread started by calling startUI. + // It's concluded that this thread was started by startUI if TLS is set by + // this DLL. If this is already the UI thread then not much initialization + // is left to do. If this isn't a UI thread then everything has to be + // initialized. In the latter case the stack size has already been fixed + // and that might cause problems. + TBool isUiThread = ETrue; + SwtTlsData* data = reinterpret_cast(Dll::Tls()); + if(!data) isUiThread = EFalse; + + // If this is already initialized as a UI thread by startUI the do nothing + // more. Otherwise continue with the initialization. + if(isUiThread) + { + return KErrNone; + } + + // Create and set the TLS data structure. + data = new (std::nothrow) SwtTlsData(); + if(!data) return KErrNoMemory; + data->doFreeTLSInCleanupUIThread = ETrue; + Dll::SetTls(data); + + // This is the UI thread now, store its id + RThread uiThread; + data->uiThreadId = uiThread.Id(); + + // Get a VM pointer using the current thread context + JavaVM* javaVM = NULL; + jint getVMStatus = aJniEnv->GetJavaVM(&javaVM); + if(getVMStatus < 0) return KErrNoMemory; + data->vm = javaVM; + + // Store the MIDlet uid we got as a parameter + data->uid = aUid; + + // Launch the support thread + startSupportThread(reinterpret_cast(data)); + + return KErrNone; + } + +void SymbianUtils::cleanupUiThread() + { + SwtTlsData* data = reinterpret_cast(Dll::Tls()); + if(!data) return; + if(data->doFreeTLSInCleanupUIThread) + { + freeTLSData(data); + } + } + +void SymbianUtils::setAppName(JNIEnv* aJniEnv, jstring aName) + { + // Convert name from jstring to Symbian descriptor + HBufC16* buffer = NULL; + if(aName != NULL) + { + jboolean isCopy; + const jchar* javaChars = aJniEnv->GetStringChars(aName, &isCopy); + if (javaChars) + { + AutoReleaseStringChars cleaner(aJniEnv, aName, javaChars); + jsize length = aJniEnv->GetStringLength(aName); + + TRAPD(err, buffer = HBufC16::NewL(length)); + if (err == KErrNone) + { + TText16* ptr =const_cast (buffer->Des().Ptr()); + memcpy(ptr, javaChars, length * sizeof(jchar)); + buffer->Des().SetLength(length); + } + else + { + throw std::bad_alloc(); + } + } + else + { + throw std::bad_alloc(); + } + } + + // Set the name to the window group + CCoeEnv* coe = CCoeEnv::Static(); + CApaWindowGroupName* wgn = NULL; + TRAPD( err, wgn = CApaWindowGroupName::NewL(coe->WsSession())); + if(err == KErrNone) + { + SwtTlsData* data = reinterpret_cast(Dll::Tls()); + wgn->SetAppUid(TUid::Uid(static_cast(data->uid))); + TRAP_IGNORE(wgn->SetCaptionL(*buffer)); + wgn->SetWindowGroupName(coe->RootWin()); + delete wgn; + } + + delete buffer; + } + +bool SymbianUtils::eventFilter(QObject* object, const TWsEvent* aEvent) +{ + int swtEventType = -1; + switch (aEvent->Type()) + { + case KAknUidValueEndKeyCloseEvent: + swtEventType = org_eclipse_swt_internal_qt_OS_QSWTEVENT_ENDKEYCLOSE; + break; + case KAknShutOrHideApp: //The event is received when exit from task list, + //which terminates application straight away + swtEventType = org_eclipse_swt_internal_qt_OS_QSWTEVENT_SYSTEMSHUTDOWN; + break; + case EEventUser: + if ( ( *reinterpret_cast( aEvent->EventData() ) ) == EApaSystemEventShutdown ) + { + // other system exit (e.g. when out of memory). + if( !CEikonEnv::Static()->IsSystem() ) + { + swtEventType = org_eclipse_swt_internal_qt_OS_QSWTEVENT_SYSTEMSHUTDOWN; + } + } + break; + default: + break; + } + return eventFilter( object, -1, swtEventType); +} + +bool SymbianUtils::eventFilter(QObject* object, const TInt aSymbianType, TInt aSwtType) + { + if(aSymbianType > -1) + { + switch (aSymbianType) + { + case KEikDynamicLayoutVariantSwitch: + aSwtType = org_eclipse_swt_internal_qt_OS_QSWTEVENT_RESOURCECHANGE; + break; + default: + break; + } + } + + if (aSwtType > -1) + { + QVariant data = object->property(EVENT_FILTER); + if (data.isValid()) + { + EventCallback* filter = reinterpret_cast (data.toInt()); + if (filter) + { + return filter->eventFilter(object, 0, aSwtType); + } + } + } + return false; + } + +TInt SymbianUtils::GetScreenDeviceNumber() + { + return CCoeEnv::Static()->ScreenDevice()->GetScreenNumber(); + } + +TInt SymbianUtils::GetColorDepth() + { + return TDisplayModeUtils::NumDisplayModeBitsPerPixel( + CCoeEnv::Static()->ScreenDevice()->DisplayMode()); + } + +TInt SymbianUtils::GetHwInputs() + { + TInt mask; + HAL::Get(HALData::EKeyboard, mask); + return mask; + } + +CApaApplication* SymbianUtils::NewApplication() + { + return new SwtQtS60MainApplication; + } + +void SymbianUtils::notifyThreadInitStatus(const TInt& aStatus, + TThreadId aInitThreadId, TRequestStatus* aStatusPtr) + { + RThread initThread; + TInt openStatus = initThread.Open(aInitThreadId); + __ASSERT_DEBUG(openStatus == KErrNone, User::Panic(KSwtDllName, 0)); + if(openStatus == KErrNone) + { + initThread.RequestComplete(aStatusPtr, aStatus); + initThread.Close(); + } + } + +TInt SymbianUtils::uiThreadEntryPoint(TAny* aTlsData) + { + // Take ownership the the TLS data + SwtTlsData* data = reinterpret_cast(aTlsData); + Dll::SetTls(aTlsData); + data->doFreeTLSInCleanupUIThread = EFalse; + + // Set the thread as process critical so that the entire process will die + // if the UI thread panics. + User::SetCritical(User::EProcessCritical); + + // Create a CleanupStack + CTrapCleanup* cleanup = CTrapCleanup::New(); + if(!cleanup) + { + notifyThreadInitStatus(KErrNoMemory, data->initThreadId, &data->initStatus); + freeTLSData(data); + return KErrNoMemory; + } + + // Attach this thread to the VM to get the JNIEnv pointer. + JNIEnv* env = NULL; + void* args = NULL; + jint attachStatus = data->vm->AttachCurrentThread((void**)&env, args); + if(attachStatus < 0) + { + notifyThreadInitStatus(KErrNoMemory, data->initThreadId, &data->initStatus); + freeTLSData(data); + delete cleanup; + cleanup = NULL; + return KErrNoMemory; + } + + // Obtain methodID of run() of the Java callback object + jclass runnerClass = env->GetObjectClass(data->runner); + jmethodID mid = NULL; + if(runnerClass) mid = env->GetMethodID(runnerClass, "run", "()V"); + + // Check if something failed + if(!mid) + { + notifyThreadInitStatus(KErrNoMemory, data->initThreadId, &data->initStatus); + + TRAP_IGNORE(data->vm->DetachCurrentThread()); + freeTLSData(data); + delete cleanup; + cleanup = NULL; + + return KErrNoMemory; + } + + // Notify the waiting thread that initialization has completed successfully + notifyThreadInitStatus(KErrNone, data->initThreadId, &data->initStatus); + + // Call run() of the Java callback object. Inside this call the UI event + // loop will be executed. + TRAPD(err, env->CallVoidMethod(data->runner, mid)); + if(err != KErrNone) + { + // Something did leave. All Qt APIs are trapped so it might be a Java + // class library that has failed. This is a fatal error and the process + // should die. + User::Panic(KSwtUiThreadName, 0); + } + + // The application allowed the UI thread to exit. Clean-up and die. + + // Remove the reference to the runner Java object + TRAP_IGNORE(env->DeleteGlobalRef(data->runner)); + data->runner = NULL; + + // Detach the UI thread from the VM + TRAP_IGNORE(data->vm->DetachCurrentThread()); + freeTLSData(data); + delete cleanup; + cleanup = NULL; + + return KErrNone; + } + +/* + * Don't trust the JNI implementation to trap everything properly but let's + * always have a top-level trap also in this thread to avoid panics such as + * EUSER-CBase 66/69. + */ +TInt SymbianUtils::supportThreadEntryPoint(TAny* aParams) + { + CTrapCleanup* cleanup = CTrapCleanup::New(); + int retVal = 0; + TRAP_IGNORE(retVal = trappedSupportThreadEntryPoint(aParams)); + delete cleanup; + cleanup = NULL; + return retVal; + } + +TInt SymbianUtils::trappedSupportThreadEntryPoint(TAny* aParams) + { + // Prevent the library from getting detached when the VM closes its handle. + // That would lead in the destruction of the Qt's global statics in a different + // thread than they were created in causing problems. Keep a handle to the + // library in a thread until the process terminates. + RLibrary libRef; +#ifdef _DEBUG + TInt addLibRef = +#endif + libRef.Load(KSwtDllName); + __ASSERT_DEBUG(addLibRef == KErrNone, User::Panic(KSwtDllName, 0)); + + // Store JavaVM pointer and UI thread id from the thread params. + SwtTlsData* data = reinterpret_cast(aParams); + TThreadId uiThreadId = data->uiThreadId; + JavaVM* vm = data->vm; + + // Try attach using the JavaVM pointer. At this point there's a Java thread + // waiting ensuring that the VM has not closed down and the JavaVM pointer + // must be valid. + JNIEnv* env = NULL; + void* args = NULL; + jint attachStatus = vm->AttachCurrentThread((void**)&env, args); + __ASSERT_DEBUG(attachStatus == 0, User::Panic(KSwtDllName, 0)); + + // Notify the waiting Java thread that we have attached and it can continue + notifyThreadInitStatus(attachStatus, data->initThreadId, &data->initStatus); + + // Create a rendezvous request to detect if the UI thread dies. + RThread uiThread; + TInt openStatus = uiThread.Open(uiThreadId); + __ASSERT_DEBUG(openStatus == KErrNone, User::Panic(KSwtDllName, 0)); + TRequestStatus uiThreadRendezvous; + if(openStatus == KErrNone) + { + uiThread.Rendezvous(uiThreadRendezvous); + } + + // Wait until the process dies. If the UI thread dies notify MIDP + // application management software that the MIDlet should die. + while(ETrue) + { + User::WaitForAnyRequest(); + if(vm && openStatus == KErrNone) { + if(uiThread.ExitType() != EExitPending) + { + // Notify once and detach the thread. + notifyExitCmd(env); + vm->DetachCurrentThread(); + env = NULL; + vm = NULL; + } + } + } + return KErrNone; + } + +void SymbianUtils::startSupportThread(TAny* aParams) + { + // Set the thread id that the support thread will notify when it has + // attached to the VM. + SwtTlsData* data = reinterpret_cast(aParams); + RThread initThread; + data->initThreadId = initThread.Id(); + data->initStatus = KRequestPending; + + // Launch the support thread + TName supportThreadName(KSwtSupportThreadName); + RThread supportThread; + TInt createStatus = supportThread.Create(supportThreadName, SymbianUtils::supportThreadEntryPoint, + KSwtSupportThreadStackSize, NULL, aParams); + + // If the application disposed the Display and recreated it in the same + // thread then support thread already exists. + if(createStatus == KErrAlreadyExists) return; + + if(createStatus == KErrNone) supportThread.Resume(); + __ASSERT_DEBUG(createStatus == KErrNone, User::Panic(KSwtSupportThreadName, 0)); + + // Wait until the support thread is attached to the VM to ensure that + // the VM doesn't have a chance to unload and invalidate the VM pointer. + User::WaitForRequest(data->initStatus); + } + +void SymbianUtils::notifyExitCmd(JNIEnv* aEnv) + { + if(!aEnv) return; + + jclass clazz = aEnv->FindClass("org/eclipse/swt/internal/ExitNotificationWrapper"); + __ASSERT_DEBUG(clazz, User::Panic(KSwtUiThreadName, 0)); + if(clazz) + { + jmethodID id = aEnv->GetStaticMethodID(clazz, "notifyExit", "()V"); + __ASSERT_DEBUG(id, User::Panic(KSwtUiThreadName, 0)); + if(id) + { + aEnv->CallStaticVoidMethod(clazz, id); + } + aEnv->DeleteLocalRef(clazz); + } + }