|
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 } |