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