1 /******************************************************************************* |
|
2 * Copyright (c) 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 package org.eclipse.swt.internal.qt; |
|
13 |
|
14 import org.eclipse.swt.internal.qt.ApplicationUIListener; |
|
15 import org.eclipse.swt.internal.qt.midp.UIThreadLauncher; |
|
16 import org.eclipse.swt.widgets.Display; |
|
17 import org.eclipse.swt.widgets.Internal_PackageSupport; |
|
18 import org.eclipse.swt.widgets.Shell; |
|
19 |
|
20 /** |
|
21 * A class that manages the UI thread hand-over from the internal UI event loop |
|
22 * to the application. |
|
23 */ |
|
24 public final class UIThreadManager { |
|
25 private static Object lock = new Object(); |
|
26 private static boolean threadWaitingForApplication; |
|
27 private static Runnable applicationRunnable; |
|
28 private static ApplicationUIListener applicationUIListener; |
|
29 |
|
30 /** |
|
31 * Pass the control of the thread to the application by calling a Runnable |
|
32 * provided by the application. If the Runnable has not yet been provided |
|
33 * then the thread waits until it becomes available, or the wait is aborted |
|
34 * by calling <code>abortWait()</code>. |
|
35 */ |
|
36 public static void runApplicationUI() { |
|
37 synchronized(lock) { |
|
38 if(!applicationWaiting()) { |
|
39 waitForApplication(); |
|
40 } |
|
41 callback(); |
|
42 } |
|
43 } |
|
44 |
|
45 /** |
|
46 * If the UI thread is waiting for the application to request the callback |
|
47 * in the UI thread then calling this will abort the wait and make the UI |
|
48 * thread to return back from the <code>runApplicationUI()</code>. |
|
49 */ |
|
50 public static void abortWait() { |
|
51 synchronized(lock) { |
|
52 lock.notify(); |
|
53 } |
|
54 } |
|
55 |
|
56 /** |
|
57 * Called indirectly by the application when it's requesting the UI thread. |
|
58 * Application will pass a Runnable as a parameter and expects it to get called |
|
59 * in the UI thread. |
|
60 * |
|
61 * @param runnable |
|
62 * The Runnable to call in the UI thread. |
|
63 * @return True if obtaining the UI thread was successful and the runnable |
|
64 * will be called. |
|
65 */ |
|
66 public static boolean startInUIThread(Runnable runnable) { |
|
67 synchronized(lock) { |
|
68 applicationRunnable = runnable; |
|
69 if(threadWaitingForApplication) { |
|
70 lock.notify(); |
|
71 } else { |
|
72 if(!waitForUIThread()) { |
|
73 // If the internal UI has not been created then it can't be |
|
74 // signaled and we can't wait for it. This must mean there's |
|
75 // no UI thread yet in the process and we can create it here. |
|
76 if(!UIThreadLauncher.startInUIThread(runnable)) { |
|
77 return false; |
|
78 } |
|
79 } |
|
80 } |
|
81 } |
|
82 return true; |
|
83 } |
|
84 |
|
85 /** |
|
86 * Sets the listener that will be notified of the state of the UI during its |
|
87 * life-time. |
|
88 */ |
|
89 public static void setApplicationUIListener(ApplicationUIListener listener) { |
|
90 synchronized(lock) { |
|
91 applicationUIListener = listener; |
|
92 } |
|
93 } |
|
94 |
|
95 /* |
|
96 * Signal the internal UI loop and wait until it passes the control of the |
|
97 * UI thread into runApplicationUI(). Return false if the internal UI can't |
|
98 * be signaled. |
|
99 */ |
|
100 private static boolean waitForUIThread() { |
|
101 if(signalUIThreadRequest()) { |
|
102 try { |
|
103 lock.wait(); |
|
104 } catch(InterruptedException e) { |
|
105 // Nothing to do |
|
106 } |
|
107 return true; |
|
108 } |
|
109 return false; |
|
110 } |
|
111 |
|
112 /* |
|
113 * Wait until signaled by the application requesting the callback to its |
|
114 * Runnable, or by abortWait(). |
|
115 */ |
|
116 private static void waitForApplication() { |
|
117 threadWaitingForApplication = true; |
|
118 try { |
|
119 lock.wait(); |
|
120 } catch(InterruptedException e) { |
|
121 // Nothing to do |
|
122 } finally { |
|
123 threadWaitingForApplication = false; |
|
124 } |
|
125 } |
|
126 |
|
127 /* |
|
128 * Returns true if application has provided the Runnable and is thus waiting |
|
129 * for the callback. |
|
130 */ |
|
131 private static boolean applicationWaiting() { |
|
132 return (applicationRunnable != null); |
|
133 } |
|
134 |
|
135 /* |
|
136 * Post an event to the internal Display to request the control of the UI |
|
137 * thread. After this runApplication() should get called in the UI thread. |
|
138 */ |
|
139 private static boolean signalUIThreadRequest() { |
|
140 Display internalDisplay = Internal_PackageSupport.getInternalDisplayInstance(); |
|
141 if(internalDisplay != null) { |
|
142 if(applicationUIListener != null) { |
|
143 applicationUIListener.applicationUIThreadRequest(); |
|
144 } |
|
145 return true; |
|
146 } |
|
147 return false; |
|
148 } |
|
149 |
|
150 /* |
|
151 * Call back the application's runnable |
|
152 */ |
|
153 private static void callback() { |
|
154 try { |
|
155 if(applicationRunnable != null) { |
|
156 flushInternals(); |
|
157 applicationRunnable.run(); |
|
158 } |
|
159 } finally { |
|
160 applicationRunnable = null; |
|
161 } |
|
162 } |
|
163 |
|
164 /* |
|
165 * Clear any events or other items possibly left over by any previous usage |
|
166 * of the UI resources. There must not be anything that can interfere with |
|
167 * the application. It must appear as the application starts with an |
|
168 * uninitialized UI. |
|
169 */ |
|
170 private static void flushInternals() { |
|
171 Display internalDisplay = Internal_PackageSupport.getInternalDisplayInstance(); |
|
172 |
|
173 // Dispose all widgets |
|
174 Shell[] shells = internalDisplay.getShells(); |
|
175 for(int i = 0; i < shells.length; ++i) { |
|
176 if(shells[i] != null && !shells[i].isDisposed()) { |
|
177 shells[i].dispose(); |
|
178 } |
|
179 } |
|
180 |
|
181 // Flush the event queue |
|
182 while(internalDisplay.readAndDispatch()) {} |
|
183 } |
|
184 } |
|