javauis/eswt_qt/org.eclipse.swt/Eclipse_SWT_PI/qt/library/swts60.cpp
branchRCL_3
changeset 65 ae942d28ec0e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javauis/eswt_qt/org.eclipse.swt/Eclipse_SWT_PI/qt/library/swts60.cpp	Tue Aug 31 15:09:22 2010 +0300
@@ -0,0 +1,558 @@
+/*******************************************************************************
+ * 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 <eikenv.h>
+#include <apgwgnam.h> // For CApaWindowGroupName
+#include <coemain.h>
+#include <eikappui.h>
+#include <w32std.h>
+#include <avkon.hrh>
+#include <QVariant>
+#include <QWidget>
+#include <AknDef.h>
+#include <apgtask.h>
+#include <hal.h>
+#include <org_eclipse_swt_internal_qt_OS.h>
+#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
+const TInt KSwtSupportThreadStackSize = KSwtUiThreadStackSize;
+
+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<SwtTlsData*>(Dll::Tls());
+    TUid uid;
+    uid.iUid = static_cast<TInt>(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<TAny*>(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<TAny*>(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<SwtTlsData*>(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<TAny*>(data));
+
+    // Return the support thread initialization status
+    return data->initStatus.Int();
+    }
+
+void SymbianUtils::cleanupUiThread()
+    {
+    SwtTlsData* data = reinterpret_cast<SwtTlsData*>(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<TText16*> (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<SwtTlsData*>(Dll::Tls());
+        wgn->SetAppUid(TUid::Uid(static_cast<TInt>(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 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<TApaSystemEvent*> (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;
+    case EEventWindowVisibilityChanged:
+    {
+        CCoeControl* control =
+            reinterpret_cast<CCoeControl*> (aEvent->Handle());
+        QWidget* widget = QWidget::find(control);
+        if (widget)
+        {
+            const TWsVisibilityChangedEvent* ev = aEvent->VisibilityChanged();
+            if (ev)
+            {
+                if (ev->iFlags & TWsVisibilityChangedEvent::ENotVisible)
+                {
+                    swtEventType
+                        = org_eclipse_swt_internal_qt_OS_QSWTEVENT_SYMBIAN_WINDOW_NOT_VISIBLE;
+                }
+                else if (ev->iFlags
+                    & TWsVisibilityChangedEvent::EPartiallyVisible)
+                {
+                    swtEventType
+                        = org_eclipse_swt_internal_qt_OS_QSWTEVENT_SYMBIAN_WINDOW_PARTIALLY_VISIBLE;
+                }
+                else if (ev->iFlags & TWsVisibilityChangedEvent::EFullyVisible)
+                {
+                    swtEventType
+                        = org_eclipse_swt_internal_qt_OS_QSWTEVENT_SYMBIAN_WINDOW_FULLY_VISIBLE;
+                }
+            }
+            if (swtEventType > -1)
+            {
+                object = widget;
+            }
+        }
+
+        bool res = eventFilter(object, -1, swtEventType);
+        return res;
+    }
+    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 = qApp->property(EVENT_FILTER);
+        if (data.isValid())
+            {
+            EventCallback* filter = reinterpret_cast<EventCallback*> (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<SwtTlsData*>(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<SwtTlsData*>(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);
+    // Continue even if attach failed ->
+
+    // 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.
+    // This is a workaround until QTBUG-5284 is resolved.
+    while(ETrue)
+        {
+        User::WaitForAnyRequest();
+        if(vm && openStatus == KErrNone) {
+            if(uiThread.ExitType() != EExitPending)
+                {
+                if(attachStatus == 0) {
+                    // Notify once and detach the thread.
+                    notifyUIThreadExit(env);
+                    vm->DetachCurrentThread();
+                }
+                env = NULL;
+                vm = NULL;
+                }
+            }
+        }
+
+    // Because the thread is waiting until the process terminates,
+    // execution will never reach here.
+    }
+
+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<SwtTlsData*>(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::notifyUIThreadExit(JNIEnv* aEnv)
+    {
+    if(!aEnv) return;
+    if(aEnv->ExceptionCheck() == JNI_TRUE) return;
+
+    jclass clazz = aEnv->FindClass("org/eclipse/swt/internal/ExitNotificationWrapper");
+    __ASSERT_DEBUG(clazz, User::Panic(KSwtUiThreadName, 0));
+    if(clazz)
+        {
+        jmethodID id = aEnv->GetStaticMethodID(clazz, "uiDisposed", "()V");
+        __ASSERT_DEBUG(id, User::Panic(KSwtUiThreadName, 0));
+        if(id)
+            {
+            aEnv->CallStaticVoidMethod(clazz, id);
+            }
+        aEnv->DeleteLocalRef(clazz);
+        }
+    }