javauis/eswt_qt/org.eclipse.swt/Eclipse_SWT_PI/qt/library/swts60.cpp
branchRCL_3
changeset 65 ae942d28ec0e
equal deleted inserted replaced
60:6c158198356e 65:ae942d28ec0e
       
     1 /*******************************************************************************
       
     2  * Copyright (c) 2009, 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     3  * All rights reserved. This program and the accompanying materials
       
     4  * are made available under the terms of the Eclipse Public License v1.0
       
     5  * which accompanies this distribution, and is available at
       
     6  * http://www.eclipse.org/legal/epl-v10.html
       
     7  *
       
     8  * Contributors:
       
     9  *     Nokia Corporation - initial implementation
       
    10  *******************************************************************************/
       
    11 
       
    12 #include <eikenv.h>
       
    13 #include <apgwgnam.h> // For CApaWindowGroupName
       
    14 #include <coemain.h>
       
    15 #include <eikappui.h>
       
    16 #include <w32std.h>
       
    17 #include <avkon.hrh>
       
    18 #include <QVariant>
       
    19 #include <QWidget>
       
    20 #include <AknDef.h>
       
    21 #include <apgtask.h>
       
    22 #include <hal.h>
       
    23 #include <org_eclipse_swt_internal_qt_OS.h>
       
    24 #include "eventcallback.h"
       
    25 #include "swts60.h"
       
    26 #include "autorelease.h"
       
    27 
       
    28 using namespace Java::eSWT;
       
    29 
       
    30 // Name of the UI thread.
       
    31 _LIT(KSwtUiThreadName, "SwtQtUiThread");
       
    32 
       
    33 // Name of the support thread.
       
    34 _LIT(KSwtSupportThreadName, "SwtQtSupportThread");
       
    35 
       
    36 // Name of the shared library
       
    37 _LIT(KSwtDllName, "eswtqt.dll");
       
    38 
       
    39 // Stack size for the UI thread, 0x14000 = 80kB
       
    40 const TInt KSwtUiThreadStackSize = 0x14000;
       
    41 
       
    42 // Stack size for the support thread
       
    43 const TInt KSwtSupportThreadStackSize = KSwtUiThreadStackSize;
       
    44 
       
    45 static const char* const EVENT_FILTER = "swt_event_filter";
       
    46 
       
    47 // Data stored to thread/dll specific Symbian thread local storage of the UI
       
    48 // thread.
       
    49 typedef struct
       
    50     {
       
    51     JavaVM* vm;
       
    52     jint uid;
       
    53     jobject runner;
       
    54     TRequestStatus initStatus;
       
    55     TThreadId initThreadId;
       
    56     TThreadId uiThreadId;
       
    57     TBool doFreeTLSInCleanupUIThread;
       
    58     }
       
    59 SwtTlsData;
       
    60 
       
    61 inline static void freeTLSData(SwtTlsData*& aData)
       
    62     {
       
    63     Dll::SetTls(NULL);
       
    64     delete aData;
       
    65     aData = NULL;
       
    66     }
       
    67 
       
    68 TUid SwtQtS60MainApplication::AppDllUid() const
       
    69     {
       
    70     SwtTlsData* data = reinterpret_cast<SwtTlsData*>(Dll::Tls());
       
    71     TUid uid;
       
    72     uid.iUid = static_cast<TInt>(data ? data->uid : 0);
       
    73     return uid;
       
    74     }
       
    75 
       
    76 int SymbianUtils::startUI(JNIEnv* aJniEnv, jobject aRunner, jint aUid)
       
    77     {
       
    78     // Get a VM pointer using the current thread context
       
    79     JavaVM* javaVM = NULL;
       
    80     jint getVMStatus = aJniEnv->GetJavaVM(&javaVM);
       
    81     if(getVMStatus < 0) return KErrNoMemory;
       
    82 
       
    83     // Add a global reference to callback object so that in can be used from
       
    84     // the UI thread.
       
    85     jobject globalRef = aJniEnv->NewGlobalRef(aRunner);
       
    86     if(!globalRef) return KErrNoMemory;
       
    87 
       
    88     // Put needed data to a stucture for TLS storing that will be done later
       
    89     // in the UI thread
       
    90     SwtTlsData* data = new (std::nothrow) SwtTlsData();
       
    91     if(!data) return KErrNoMemory;
       
    92     data->vm = javaVM;
       
    93     data->uid = aUid;
       
    94     data->runner = globalRef;
       
    95     data->initStatus = KRequestPending;
       
    96     RThread thread;
       
    97     data->initThreadId = thread.Id();
       
    98 
       
    99     // Create a new thread that will be the UI thread
       
   100     TName uiThreadName(KSwtUiThreadName);
       
   101     RThread uiThread;
       
   102     TInt createStatus = uiThread.Create( uiThreadName, SymbianUtils::uiThreadEntryPoint,
       
   103             KSwtUiThreadStackSize, NULL, reinterpret_cast<TAny*>(data) );
       
   104     data->uiThreadId = uiThread.Id();
       
   105     if(createStatus != KErrNone)
       
   106         {
       
   107         delete data;
       
   108         return createStatus;
       
   109         }
       
   110 
       
   111     // Resume the UI thread and wait until it reports back the initialization
       
   112     // status
       
   113     uiThread.Resume();
       
   114     User::WaitForRequest(data->initStatus);
       
   115 
       
   116     // Launch the support thread
       
   117     if(data->initStatus == KErrNone)
       
   118         {
       
   119         startSupportThread(reinterpret_cast<TAny*>(data));
       
   120         }
       
   121 
       
   122     // Return the thread initialization status
       
   123     return data->initStatus.Int();
       
   124     }
       
   125 
       
   126 int SymbianUtils::initUiThread(JNIEnv* aJniEnv, const TInt& aUid)
       
   127     {
       
   128     // This thread may or may not be a UI thread started by calling startUI.
       
   129     // It's concluded that this thread was started by startUI if TLS is set by
       
   130     // this DLL. If this is already the UI thread then not much initialization
       
   131     // is left to do. If this isn't a UI thread then everything has to be
       
   132     // initialized. In the latter case the stack size has already been fixed
       
   133     // and that might cause problems.
       
   134     TBool isUiThread = ETrue;
       
   135     SwtTlsData* data = reinterpret_cast<SwtTlsData*>(Dll::Tls());
       
   136     if(!data) isUiThread = EFalse;
       
   137 
       
   138     // If this is already initialized as a UI thread by startUI the do nothing
       
   139     // more. Otherwise continue with the initialization.
       
   140     if(isUiThread)
       
   141         {
       
   142         return KErrNone;
       
   143         }
       
   144 
       
   145     // Create and set the TLS data structure.
       
   146     data = new (std::nothrow) SwtTlsData();
       
   147     if(!data) return KErrNoMemory;
       
   148     data->doFreeTLSInCleanupUIThread = ETrue;
       
   149     Dll::SetTls(data);
       
   150 
       
   151     // This is the UI thread now, store its id
       
   152     RThread uiThread;
       
   153     data->uiThreadId = uiThread.Id();
       
   154 
       
   155     // Get a VM pointer using the current thread context
       
   156     JavaVM* javaVM = NULL;
       
   157     jint getVMStatus = aJniEnv->GetJavaVM(&javaVM);
       
   158     if(getVMStatus < 0) return KErrNoMemory;
       
   159     data->vm = javaVM;
       
   160 
       
   161     // Store the MIDlet uid we got as a parameter
       
   162     data->uid = aUid;
       
   163 
       
   164     // Launch the support thread
       
   165     startSupportThread(reinterpret_cast<TAny*>(data));
       
   166 
       
   167     // Return the support thread initialization status
       
   168     return data->initStatus.Int();
       
   169     }
       
   170 
       
   171 void SymbianUtils::cleanupUiThread()
       
   172     {
       
   173     SwtTlsData* data = reinterpret_cast<SwtTlsData*>(Dll::Tls());
       
   174     if(!data) return;
       
   175     if(data->doFreeTLSInCleanupUIThread)
       
   176         {
       
   177         freeTLSData(data);
       
   178         }
       
   179     }
       
   180 
       
   181 void SymbianUtils::setAppName(JNIEnv* aJniEnv, jstring aName)
       
   182     {
       
   183     // Convert name from jstring to Symbian descriptor
       
   184     HBufC16* buffer = NULL;
       
   185     if(aName != NULL)
       
   186         {
       
   187         jboolean isCopy;
       
   188         const jchar* javaChars = aJniEnv->GetStringChars(aName, &isCopy);
       
   189         if (javaChars)
       
   190             {
       
   191             AutoReleaseStringChars cleaner(aJniEnv, aName, javaChars);
       
   192             jsize length = aJniEnv->GetStringLength(aName);
       
   193 
       
   194             TRAPD(err, buffer = HBufC16::NewL(length));
       
   195             if (err == KErrNone)
       
   196                 {
       
   197                  TText16* ptr =const_cast<TText16*> (buffer->Des().Ptr());
       
   198                  memcpy(ptr, javaChars, length * sizeof(jchar));
       
   199                  buffer->Des().SetLength(length);
       
   200                 }
       
   201             else
       
   202                 {
       
   203                 throw std::bad_alloc();
       
   204                 }
       
   205             }
       
   206         else
       
   207             {
       
   208             throw std::bad_alloc();
       
   209             }
       
   210         }
       
   211 
       
   212     // Set the name to the window group
       
   213     CCoeEnv* coe = CCoeEnv::Static();
       
   214     CApaWindowGroupName* wgn = NULL;
       
   215     TRAPD( err, wgn = CApaWindowGroupName::NewL(coe->WsSession()));
       
   216     if(err == KErrNone)
       
   217         {
       
   218         SwtTlsData* data = reinterpret_cast<SwtTlsData*>(Dll::Tls());
       
   219         wgn->SetAppUid(TUid::Uid(static_cast<TInt>(data->uid)));
       
   220         TRAP_IGNORE(wgn->SetCaptionL(*buffer));
       
   221         wgn->SetWindowGroupName(coe->RootWin());
       
   222         delete wgn;
       
   223         }
       
   224 
       
   225     delete buffer;
       
   226     }
       
   227 
       
   228 bool SymbianUtils::eventFilter(QObject* object, const TWsEvent* aEvent)
       
   229 {
       
   230     int swtEventType = -1;
       
   231     switch (aEvent->Type())
       
   232     {
       
   233     case KAknShutOrHideApp: //The event is received when exit from task list,
       
   234         //which terminates application straight away
       
   235         swtEventType = org_eclipse_swt_internal_qt_OS_QSWTEVENT_SYSTEMSHUTDOWN;
       
   236         break;
       
   237     case EEventUser:
       
   238         if ((*reinterpret_cast<TApaSystemEvent*> (aEvent->EventData()))
       
   239             == EApaSystemEventShutdown)
       
   240         {
       
   241             // other system exit (e.g. when out of memory).
       
   242             if (!CEikonEnv::Static()->IsSystem())
       
   243             {
       
   244                 swtEventType
       
   245                     = org_eclipse_swt_internal_qt_OS_QSWTEVENT_SYSTEMSHUTDOWN;
       
   246             }
       
   247         }
       
   248         break;
       
   249     case EEventWindowVisibilityChanged:
       
   250     {
       
   251         CCoeControl* control =
       
   252             reinterpret_cast<CCoeControl*> (aEvent->Handle());
       
   253         QWidget* widget = QWidget::find(control);
       
   254         if (widget)
       
   255         {
       
   256             const TWsVisibilityChangedEvent* ev = aEvent->VisibilityChanged();
       
   257             if (ev)
       
   258             {
       
   259                 if (ev->iFlags & TWsVisibilityChangedEvent::ENotVisible)
       
   260                 {
       
   261                     swtEventType
       
   262                         = org_eclipse_swt_internal_qt_OS_QSWTEVENT_SYMBIAN_WINDOW_NOT_VISIBLE;
       
   263                 }
       
   264                 else if (ev->iFlags
       
   265                     & TWsVisibilityChangedEvent::EPartiallyVisible)
       
   266                 {
       
   267                     swtEventType
       
   268                         = org_eclipse_swt_internal_qt_OS_QSWTEVENT_SYMBIAN_WINDOW_PARTIALLY_VISIBLE;
       
   269                 }
       
   270                 else if (ev->iFlags & TWsVisibilityChangedEvent::EFullyVisible)
       
   271                 {
       
   272                     swtEventType
       
   273                         = org_eclipse_swt_internal_qt_OS_QSWTEVENT_SYMBIAN_WINDOW_FULLY_VISIBLE;
       
   274                 }
       
   275             }
       
   276             if (swtEventType > -1)
       
   277             {
       
   278                 object = widget;
       
   279             }
       
   280         }
       
   281 
       
   282         bool res = eventFilter(object, -1, swtEventType);
       
   283         return res;
       
   284     }
       
   285     default:
       
   286         break;
       
   287     }
       
   288     return eventFilter(object, -1, swtEventType);
       
   289 }
       
   290 
       
   291 bool SymbianUtils::eventFilter(QObject* object, const TInt aSymbianType, TInt aSwtType)
       
   292     {
       
   293     if(aSymbianType > -1)
       
   294         {
       
   295         switch (aSymbianType)
       
   296            {
       
   297            case KEikDynamicLayoutVariantSwitch:
       
   298                aSwtType = org_eclipse_swt_internal_qt_OS_QSWTEVENT_RESOURCECHANGE;
       
   299                break;
       
   300            default:
       
   301                break;
       
   302            }
       
   303         }
       
   304 
       
   305     if (aSwtType > -1)
       
   306         {
       
   307         QVariant data = qApp->property(EVENT_FILTER);
       
   308         if (data.isValid())
       
   309             {
       
   310             EventCallback* filter = reinterpret_cast<EventCallback*> (data.toInt());
       
   311             if (filter)
       
   312                 {
       
   313                 return filter->eventFilter(object, 0, aSwtType);
       
   314                 }
       
   315             }
       
   316         }
       
   317     return false;
       
   318     }
       
   319 
       
   320 TInt SymbianUtils::GetScreenDeviceNumber()
       
   321     {
       
   322     return CCoeEnv::Static()->ScreenDevice()->GetScreenNumber();
       
   323     }
       
   324 
       
   325 TInt SymbianUtils::GetColorDepth()
       
   326     {
       
   327     return TDisplayModeUtils::NumDisplayModeBitsPerPixel(
       
   328             CCoeEnv::Static()->ScreenDevice()->DisplayMode());
       
   329     }
       
   330 
       
   331 TInt SymbianUtils::GetHwInputs()
       
   332     {
       
   333     TInt mask;
       
   334     HAL::Get(HALData::EKeyboard, mask);
       
   335     return mask;
       
   336     }
       
   337 
       
   338 CApaApplication* SymbianUtils::NewApplication()
       
   339     {
       
   340     return new SwtQtS60MainApplication;
       
   341     }
       
   342 
       
   343 void SymbianUtils::notifyThreadInitStatus(const TInt& aStatus,
       
   344         TThreadId aInitThreadId, TRequestStatus* aStatusPtr)
       
   345     {
       
   346     RThread initThread;
       
   347     TInt openStatus = initThread.Open(aInitThreadId);
       
   348     __ASSERT_DEBUG(openStatus == KErrNone, User::Panic(KSwtDllName, 0));
       
   349     if(openStatus == KErrNone)
       
   350         {
       
   351         initThread.RequestComplete(aStatusPtr, aStatus);
       
   352         initThread.Close();
       
   353         }
       
   354     }
       
   355 
       
   356 TInt SymbianUtils::uiThreadEntryPoint(TAny* aTlsData)
       
   357     {
       
   358     // Take ownership the the TLS data
       
   359     SwtTlsData* data = reinterpret_cast<SwtTlsData*>(aTlsData);
       
   360     Dll::SetTls(aTlsData);
       
   361     data->doFreeTLSInCleanupUIThread = EFalse;
       
   362 
       
   363     // Set the thread as process critical so that the entire process will die
       
   364     // if the UI thread panics.
       
   365     User::SetCritical(User::EProcessCritical);
       
   366 
       
   367     // Create a CleanupStack
       
   368     CTrapCleanup* cleanup = CTrapCleanup::New();
       
   369     if(!cleanup)
       
   370         {
       
   371         notifyThreadInitStatus(KErrNoMemory, data->initThreadId, &data->initStatus);
       
   372         freeTLSData(data);
       
   373         return KErrNoMemory;
       
   374         }
       
   375 
       
   376     // Attach this thread to the VM to get the JNIEnv pointer.
       
   377     JNIEnv* env = NULL;
       
   378     void* args = NULL;
       
   379     jint attachStatus = data->vm->AttachCurrentThread((void**)&env, args);
       
   380     if(attachStatus < 0)
       
   381         {
       
   382         notifyThreadInitStatus(KErrNoMemory, data->initThreadId, &data->initStatus);
       
   383         freeTLSData(data);
       
   384         delete cleanup;
       
   385         cleanup = NULL;
       
   386         return KErrNoMemory;
       
   387         }
       
   388 
       
   389     // Obtain methodID of run() of the Java callback object
       
   390     jclass runnerClass = env->GetObjectClass(data->runner);
       
   391     jmethodID mid = NULL;
       
   392     if(runnerClass) mid = env->GetMethodID(runnerClass, "run", "()V");
       
   393 
       
   394     // Check if something failed
       
   395     if(!mid)
       
   396         {
       
   397         notifyThreadInitStatus(KErrNoMemory, data->initThreadId, &data->initStatus);
       
   398 
       
   399         TRAP_IGNORE(data->vm->DetachCurrentThread());
       
   400         freeTLSData(data);
       
   401         delete cleanup;
       
   402         cleanup = NULL;
       
   403 
       
   404         return KErrNoMemory;
       
   405         }
       
   406 
       
   407     // Notify the waiting thread that initialization has completed successfully
       
   408     notifyThreadInitStatus(KErrNone, data->initThreadId, &data->initStatus);
       
   409 
       
   410     // Call run() of the Java callback object. Inside this call the UI event
       
   411     // loop will be executed.
       
   412     TRAPD(err, env->CallVoidMethod(data->runner, mid));
       
   413     if(err != KErrNone)
       
   414         {
       
   415         // Something did leave. All Qt APIs are trapped so it might be a Java
       
   416         // class library that has failed. This is a fatal error and the process
       
   417         // should die.
       
   418         User::Panic(KSwtUiThreadName, 0);
       
   419         }
       
   420 
       
   421     // The application allowed the UI thread to exit. Clean-up and die.
       
   422 
       
   423     // Remove the reference to the runner Java object
       
   424     TRAP_IGNORE(env->DeleteGlobalRef(data->runner));
       
   425     data->runner = NULL;
       
   426 
       
   427     // Detach the UI thread from the VM
       
   428     TRAP_IGNORE(data->vm->DetachCurrentThread());
       
   429     freeTLSData(data);
       
   430     delete cleanup;
       
   431     cleanup = NULL;
       
   432 
       
   433     return KErrNone;
       
   434     }
       
   435 
       
   436 /*
       
   437  * Don't trust the JNI implementation to trap everything properly but let's
       
   438  * always have a top-level trap also in this thread to avoid panics such as
       
   439  * EUSER-CBase 66/69.
       
   440  */
       
   441 TInt SymbianUtils::supportThreadEntryPoint(TAny* aParams)
       
   442     {
       
   443     CTrapCleanup* cleanup = CTrapCleanup::New();
       
   444     int retVal = 0;
       
   445     TRAP_IGNORE(retVal = trappedSupportThreadEntryPoint(aParams));
       
   446     delete cleanup;
       
   447     cleanup = NULL;
       
   448     return retVal;
       
   449     }
       
   450 
       
   451 TInt SymbianUtils::trappedSupportThreadEntryPoint(TAny* aParams)
       
   452     {
       
   453     // Prevent the library from getting detached when the VM closes its handle.
       
   454     // That would lead in the destruction of the Qt's global statics in a different
       
   455     // thread than they were created in causing problems. Keep a handle to the
       
   456     // library in a thread until the process terminates.
       
   457     RLibrary libRef;
       
   458 #ifdef _DEBUG
       
   459     TInt addLibRef =
       
   460 #endif
       
   461     libRef.Load(KSwtDllName);
       
   462     __ASSERT_DEBUG(addLibRef == KErrNone, User::Panic(KSwtDllName, 0));
       
   463 
       
   464     // Store JavaVM pointer and UI thread id from the thread params.
       
   465     SwtTlsData* data = reinterpret_cast<SwtTlsData*>(aParams);
       
   466     TThreadId uiThreadId = data->uiThreadId;
       
   467     JavaVM* vm = data->vm;
       
   468 
       
   469     // Try attach using the JavaVM pointer. At this point there's a Java thread
       
   470     // waiting ensuring that the VM has not closed down and the JavaVM pointer
       
   471     // must be valid.
       
   472     JNIEnv* env = NULL;
       
   473     void* args = NULL;
       
   474     jint attachStatus = vm->AttachCurrentThread((void**)&env, args);
       
   475     // Continue even if attach failed ->
       
   476 
       
   477     // Notify the waiting Java thread that we have attached and it can continue
       
   478     notifyThreadInitStatus(attachStatus, data->initThreadId, &data->initStatus);
       
   479 
       
   480     // Create a rendezvous request to detect if the UI thread dies.
       
   481     RThread uiThread;
       
   482     TInt openStatus = uiThread.Open(uiThreadId);
       
   483     __ASSERT_DEBUG(openStatus == KErrNone, User::Panic(KSwtDllName, 0));
       
   484     TRequestStatus uiThreadRendezvous;
       
   485     if(openStatus == KErrNone)
       
   486         {
       
   487         uiThread.Rendezvous(uiThreadRendezvous);
       
   488         }
       
   489 
       
   490     // Wait until the process dies. If the UI thread dies notify MIDP
       
   491     // application management software that the MIDlet should die.
       
   492     // This is a workaround until QTBUG-5284 is resolved.
       
   493     while(ETrue)
       
   494         {
       
   495         User::WaitForAnyRequest();
       
   496         if(vm && openStatus == KErrNone) {
       
   497             if(uiThread.ExitType() != EExitPending)
       
   498                 {
       
   499                 if(attachStatus == 0) {
       
   500                     // Notify once and detach the thread.
       
   501                     notifyUIThreadExit(env);
       
   502                     vm->DetachCurrentThread();
       
   503                 }
       
   504                 env = NULL;
       
   505                 vm = NULL;
       
   506                 }
       
   507             }
       
   508         }
       
   509 
       
   510     // Because the thread is waiting until the process terminates,
       
   511     // execution will never reach here.
       
   512     }
       
   513 
       
   514 void SymbianUtils::startSupportThread(TAny* aParams)
       
   515     {
       
   516     // Set the thread id that the support thread will notify when it has
       
   517     // attached to the VM.
       
   518     SwtTlsData* data = reinterpret_cast<SwtTlsData*>(aParams);
       
   519     RThread initThread;
       
   520     data->initThreadId = initThread.Id();
       
   521     data->initStatus = KRequestPending;
       
   522 
       
   523     // Launch the support thread
       
   524     TName supportThreadName(KSwtSupportThreadName);
       
   525     RThread supportThread;
       
   526     TInt createStatus = supportThread.Create(supportThreadName, SymbianUtils::supportThreadEntryPoint,
       
   527             KSwtSupportThreadStackSize, NULL, aParams);
       
   528 
       
   529     // If the application disposed the Display and recreated it in the same
       
   530     // thread then support thread already exists.
       
   531     if(createStatus == KErrAlreadyExists) return;
       
   532 
       
   533     if(createStatus == KErrNone) supportThread.Resume();
       
   534     __ASSERT_DEBUG(createStatus == KErrNone, User::Panic(KSwtSupportThreadName, 0));
       
   535 
       
   536     // Wait until the support thread is attached to the VM to ensure that
       
   537     // the VM doesn't have a chance to unload and invalidate the VM pointer.
       
   538     User::WaitForRequest(data->initStatus);
       
   539     }
       
   540 
       
   541 void SymbianUtils::notifyUIThreadExit(JNIEnv* aEnv)
       
   542     {
       
   543     if(!aEnv) return;
       
   544     if(aEnv->ExceptionCheck() == JNI_TRUE) return;
       
   545 
       
   546     jclass clazz = aEnv->FindClass("org/eclipse/swt/internal/ExitNotificationWrapper");
       
   547     __ASSERT_DEBUG(clazz, User::Panic(KSwtUiThreadName, 0));
       
   548     if(clazz)
       
   549         {
       
   550         jmethodID id = aEnv->GetStaticMethodID(clazz, "uiDisposed", "()V");
       
   551         __ASSERT_DEBUG(id, User::Panic(KSwtUiThreadName, 0));
       
   552         if(id)
       
   553             {
       
   554             aEnv->CallStaticVoidMethod(clazz, id);
       
   555             }
       
   556         aEnv->DeleteLocalRef(clazz);
       
   557         }
       
   558     }