javauis/lcdui_qt/src/javax/microedition/lcdui/EventDispatcher.java
changeset 21 2a9601315dfc
child 23 98ccebc37403
equal deleted inserted replaced
18:e8e63152f320 21:2a9601315dfc
       
     1 /*
       
     2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: 
       
    15 *
       
    16 */
       
    17 
       
    18 package javax.microedition.lcdui;
       
    19 
       
    20 import org.eclipse.swt.widgets.Widget;
       
    21 
       
    22 
       
    23 /*
       
    24  * Event dispatcher singleton class. Takes care of dispatching the UI callbacks
       
    25  * to the MIDlet in a dedicated dispatcher thread. Owns and encapsulates an 
       
    26  * event queue of callback events. 
       
    27  */
       
    28 final class EventDispatcher {
       
    29 
       
    30 	private static EventDispatcher singleton;
       
    31 	private static EventQueue eventQueue;
       
    32 	private static Thread dispatcherThread;
       
    33 	private static boolean terminated;
       
    34 	private static Object wakeLock;
       
    35 	private static Object callbackLock;
       
    36 	private static boolean pendingWake;
       
    37 	private static Runnable terminatedNotification;
       
    38 	private final static String logName = "LCDUI event dispatcher: ";
       
    39 
       
    40 	/*
       
    41 	 * LCDUI-internal event class for callback events. 
       
    42 	 */
       
    43 	static final class LCDUIEvent {
       
    44 		/*
       
    45 		 * High bits of the event event type identifier are used to group the
       
    46 		 * events that are handled the same way in the Event Dispatcher.
       
    47 		 */
       
    48 		static final int CANVASBIT = 0x80000000;
       
    49 		static final int CUSTOMITEMBIT = 0x40000000;
       
    50 		static final int DISPLAYABLEBIT = 0x20000000;
       
    51 		static final int EVENTTYPEMASK = CANVASBIT|CUSTOMITEMBIT|DISPLAYABLEBIT;
       
    52 		
       
    53 		/*
       
    54 		 * Event type identifiers. 
       
    55 		 */
       
    56 		static final int CANVAS_HIDENOTIFY = 1|CANVASBIT;
       
    57 		static final int CANVAS_KEYPRESSED = 2|CANVASBIT;
       
    58 		static final int CANVAS_KEYREPEATED = 3|CANVASBIT;
       
    59 		static final int CANVAS_KEYRELEASED = 4|CANVASBIT;
       
    60 		static final int CANVAS_PAINT_NATIVE_REQUEST = 5|CANVASBIT;
       
    61 		static final int CANVAS_PAINT_MIDLET_REQUEST = 6|CANVASBIT;
       
    62 		static final int CANVAS_POINTERDRAGGED = 7|CANVASBIT;
       
    63 		static final int CANVAS_POINTERPRESSED = 8|CANVASBIT;
       
    64 		static final int CANVAS_POINTERRELEASED = 9|CANVASBIT;
       
    65 		static final int CANVAS_SHOWNOTIFY = 10|CANVASBIT;
       
    66 		static final int DISPLAYABLE_SIZECHANGED = 11|DISPLAYABLEBIT;
       
    67 		static final int DISPLAYABLE_COMMANDACTION = 12|DISPLAYABLEBIT;
       
    68 		static final int CUSTOMITEM_HIDENOTIFY = 13|CUSTOMITEMBIT;
       
    69 		static final int CUSTOMITEM_KEYPRESSED = 14|CUSTOMITEMBIT;
       
    70 		static final int CUSTOMITEM_KEYREPEATED = 15|CUSTOMITEMBIT;
       
    71 		static final int CUSTOMITEM_KEYRELEASED = 16|CUSTOMITEMBIT;
       
    72 		static final int CUSTOMITEM_PAINT_NATIVE_REQUEST = 17|CUSTOMITEMBIT;
       
    73 		static final int CUSTOMITEM_PAINT_MIDLET_REQUEST = 18|CUSTOMITEMBIT;
       
    74 		static final int CUSTOMITEM_POINTERDRAGGED = 19|CUSTOMITEMBIT;
       
    75 		static final int CUSTOMITEM_POINTERPRESSED = 20|CUSTOMITEMBIT;
       
    76 		static final int CUSTOMITEM_POINTERRELEASED = 21|CUSTOMITEMBIT;
       
    77 		static final int CUSTOMITEM_SHOWNOTIFY = 22|CUSTOMITEMBIT;
       
    78 		static final int CUSTOMITEM_SIZECHANGED = 23|CUSTOMITEMBIT;
       
    79 		static final int CUSTOMITEM_TRAVERSE = 24|CUSTOMITEMBIT;
       
    80 		static final int CUSTOMITEM_TRAVERSEOUT = 25|CUSTOMITEMBIT;
       
    81 		static final int ITEM_COMMANDACTION = 26;
       
    82 		static final int ITEMSTATELISTENER_ITEMSTATECHANGED = 27;
       
    83 		static final int RUNNABLE_CALLSERIALLY = 28;
       
    84 		
       
    85 		/*
       
    86 		 * Event parameters. 
       
    87 		 */
       
    88 		int type;
       
    89 		int x;
       
    90 		int y;
       
    91 		int width;
       
    92 		int height;
       
    93 		int keyCode;
       
    94 		Runnable runnable;
       
    95 		Command command;
       
    96 		CommandListener commandListener;
       
    97 		ItemCommandListener itemCommandListener;
       
    98 		Item item;
       
    99 
       
   100 		/*
       
   101 		 * Handler object. The object that the LCDUIEvent will be passed to 
       
   102 		 * when the event is dispatched. Based on the event type it's known 
       
   103 		 * what type of an object it must be. The handler object will 
       
   104 		 * implement how to do the actual callback. 
       
   105 		 */
       
   106 		Object handlerObject;
       
   107 		
       
   108 		/*
       
   109 		 * The associated eSWT widget. Used to store the eSWT widget which 
       
   110 		 * the original eSWT event was for. E.g. in Form the CustomItem's 
       
   111 		 * widget may change while the event is in the queue. 
       
   112 		 */
       
   113 		Widget widget;
       
   114 		
       
   115 		/*
       
   116 		 * Used by EventQueue. 
       
   117 		 */
       
   118 		LCDUIEvent next;
       
   119 
       
   120 		private LCDUIEvent() {	
       
   121 		}
       
   122 	}
       
   123 	
       
   124 	/*
       
   125 	 * The event loop executed in a dedicated dispatcher thread. Events are 
       
   126 	 * popped from the queue and dispatched oldest first until there are no 
       
   127 	 * more events. Then the thread sleeps until new events are posted or 
       
   128 	 * termination is requested. Any exceptions thrown from the event handlers
       
   129 	 * are let through to the runtime which should lead into termination of
       
   130 	 * all threads in the process. 
       
   131 	 */
       
   132 	private static class EventLoop implements Runnable {
       
   133 		private boolean noException;
       
   134 		public void run() {
       
   135 			try {
       
   136 				Logger.info(logName + "Dispatcher thread started");
       
   137 				while(true) {
       
   138 					LCDUIEvent event;
       
   139 					do {
       
   140 						synchronized(wakeLock) {
       
   141 							if(terminated) {
       
   142 								cleanup();
       
   143 								noException = true;
       
   144 								return;
       
   145 							}
       
   146 							event = eventQueue.pop();
       
   147 							pendingWake = false;
       
   148 						}
       
   149 						
       
   150 						if(event != null) {
       
   151 							synchronized(callbackLock) {
       
   152 								// No callbacks are sent if already terminating
       
   153 								if(!terminated) {
       
   154 									handleEvent(event);
       
   155 								}
       
   156 							}
       
   157 						}
       
   158 					} while(event != null);
       
   159 					
       
   160 					// No more events 
       
   161 					waitForWake();
       
   162 				}
       
   163 			} finally {
       
   164 				if(noException) {
       
   165 					Logger.info(logName + "Dispatcher thread exiting normally");
       
   166 				} else {
       
   167 					Logger.error(logName + "Dispatcher thread exiting abnormally");					
       
   168 				}
       
   169 				if(terminatedNotification != null) {
       
   170 					terminatedNotification.run();
       
   171 					terminatedNotification = null;
       
   172 				}
       
   173 			}
       
   174 		}
       
   175 	}
       
   176 
       
   177 	private EventDispatcher() {
       
   178 		wakeLock = new Object();
       
   179 		callbackLock = new Object();
       
   180 		eventQueue = new EventQueue();
       
   181 		dispatcherThread = new Thread(new EventLoop(), this.toString());
       
   182 		dispatcherThread.start();
       
   183 	}
       
   184 
       
   185 	/*
       
   186 	 * Cleanup that is done when closing down in a controlled way. 
       
   187 	 */
       
   188 	private static void cleanup() {
       
   189 		eventQueue = null;
       
   190 		dispatcherThread = null;
       
   191 	}
       
   192 
       
   193 	/*
       
   194 	 * Synchronized to the EventDispatcher class to synchronize with multiple
       
   195 	 * clients requesting the instance concurrently.
       
   196 	 */
       
   197 	static synchronized EventDispatcher instance() {
       
   198 		if(singleton == null) {
       
   199 			singleton = new EventDispatcher();
       
   200 		}
       
   201 		return singleton;
       
   202 	}
       
   203 	
       
   204 	/*
       
   205 	 * LCDUIEvent factory. 
       
   206 	 */
       
   207 	LCDUIEvent newEvent(int type, Object handlerObject) {
       
   208 		LCDUIEvent event = new LCDUIEvent();
       
   209 		event.type = type;
       
   210 		event.handlerObject = handlerObject;
       
   211 		return event;
       
   212 	}
       
   213 
       
   214 	/*
       
   215 	 * UI thread and the application threads call to add an event to the queue.
       
   216 	 * Synchronized to the EventDispatcher instance to synchronize with queries about
       
   217 	 * the queue content.
       
   218 	 */
       
   219 	synchronized void postEvent(LCDUIEvent event) {
       
   220 		// New events ignored after terminate() has been called. 
       
   221 		if(terminated) {
       
   222 			return;
       
   223 		}
       
   224 		eventQueue.push(event);
       
   225 		wake();
       
   226 	}
       
   227 	
       
   228 	/*
       
   229 	 * Asynchronously terminates the event dispatcher. If it's currently 
       
   230 	 * executing a callback it will finish it but not execute any callbacks 
       
   231 	 * after it. Events possibly remaining in the queue are discarded. The 
       
   232 	 * method is synchronized to the EventDispatcher instance to synchronize 
       
   233 	 * with queue content manipulation and query operations.
       
   234 	 * 
       
   235 	 * @param runnable Called when dispatcher is out of the final callback and 
       
   236 	 * is going to die. 
       
   237 	 */
       
   238 	synchronized void terminate(Runnable runnable) {
       
   239 		synchronized(wakeLock) {
       
   240 			wake();
       
   241 			terminatedNotification = runnable;
       
   242 			terminated = true;
       
   243 		}
       
   244 	}
       
   245 		
       
   246 	/*
       
   247 	 * Canvas.serviceRepaints support is implemented in EventDispatcher so that
       
   248 	 * it's possible to synchronize properly with termination and other 
       
   249 	 * callbacks without exposing the event dispatcher's internal 
       
   250 	 * implementation details to other classes. This method must be called 
       
   251 	 * directly in the application thread calling Canvas.serviceRepaints(). It 
       
   252 	 * can be either the dispatcher thread or any application thread. Never 
       
   253 	 * the UI thread. 
       
   254 	 */
       
   255 	void serviceRepaints(Canvas canvas) {
       
   256 		// Synchronizing to callbackLock guarantees that dispatcher thread is
       
   257 		// not in a callback and is not going to make a callback until the lock
       
   258 		// is freed from here. If this is the dispatcher thread then it already 
       
   259 		// holds the lock. 
       
   260 		synchronized(callbackLock) {
       
   261 			// Synchronized to the EventDispatcher instance to synchronize with
       
   262 			// terminate() calls. Lock is freed before the callback so that
       
   263 			// it's possible to complete a terminate() call during the 
       
   264 			// callback. Event loop is going to check after acquiring the
       
   265 			// callbackLock if terminate() was called during this callback. 
       
   266 			synchronized(this) {
       
   267 				if(terminated) {
       
   268 					return;
       
   269 				}
       
   270 			}
       
   271 			// Canvas.paint() is called back directly in the thread that called
       
   272 			// serviceRepaints(). Event is not really needed but is created so
       
   273 			// that the same APIs can be used. 
       
   274             LCDUIEvent event = newEvent(LCDUIEvent.CANVAS_PAINT_MIDLET_REQUEST, canvas);
       
   275             event.widget = canvas.getContentComp();
       
   276     		canvas.doCallback(event);
       
   277 		}
       
   278 	}
       
   279 	
       
   280 	private static void wake() {
       
   281 		synchronized(wakeLock) {
       
   282 			wakeLock.notify();
       
   283 			pendingWake = true;
       
   284 		}
       
   285 	}
       
   286 	
       
   287 	private static void waitForWake() {
       
   288 		try {
       
   289 			synchronized(wakeLock) {
       
   290 				// If there has been a call to wake() then one more iteration
       
   291 				// must be executed before entering wait(). 
       
   292 				if(!pendingWake) {
       
   293 					wakeLock.wait();
       
   294 				}
       
   295 			}
       
   296 		} catch(InterruptedException interruptedException) {
       
   297 		}
       
   298 	}
       
   299 	
       
   300 	private static void handleEvent(LCDUIEvent event) {
       
   301 		switch(event.type & LCDUIEvent.EVENTTYPEMASK) {
       
   302 		case LCDUIEvent.CANVASBIT:
       
   303 			handleCanvasEvent(event);
       
   304 			break;
       
   305 		case LCDUIEvent.CUSTOMITEMBIT:
       
   306 			handleCustomItemEvent(event);
       
   307 			break;
       
   308 		case LCDUIEvent.DISPLAYABLEBIT:
       
   309 			handleDisplayableEvent(event);
       
   310 			break;
       
   311 		default:
       
   312 			handleOtherEvent(event);
       
   313 			break;
       
   314 		}
       
   315 		// When returning from here all the references to the event have been 
       
   316 		// lost and the objects referenced by the event can be gc'd. No need 
       
   317 		// to set the fields to null. 
       
   318 	}
       
   319 	
       
   320 	private static void handleCanvasEvent(LCDUIEvent event) {
       
   321 		Canvas canvas = (Canvas)event.handlerObject;
       
   322 		canvas.doCallback(event);
       
   323 	}	
       
   324 	
       
   325 	private static void handleCustomItemEvent(LCDUIEvent event) {
       
   326 		Form form = (Form)event.handlerObject;
       
   327 		form.doCallback(event);
       
   328 	}
       
   329 	
       
   330 	private static void handleDisplayableEvent(LCDUIEvent event) {
       
   331 		Displayable displayable = (Displayable)event.handlerObject;
       
   332 		displayable.doCallback(event);
       
   333 	}
       
   334 	
       
   335 	private static void handleOtherEvent(LCDUIEvent event) {
       
   336 		switch(event.type) {
       
   337 		case LCDUIEvent.RUNNABLE_CALLSERIALLY:
       
   338 			handleCallSerially(event);
       
   339 			break;
       
   340 		case LCDUIEvent.ITEM_COMMANDACTION:
       
   341 			handleItemCommandListenerCommandAction(event);
       
   342 			break;
       
   343 		default:
       
   344 			Logger.error(logName + "Unknown event type: " + event.type);
       
   345 			break;
       
   346 		}		
       
   347 	}
       
   348 	
       
   349 	private static void handleCallSerially(LCDUIEvent event) {
       
   350 		Display display = (Display)event.handlerObject;
       
   351 		display.doCallback(event);
       
   352 	}
       
   353 		
       
   354 	private static void handleItemCommandListenerCommandAction(LCDUIEvent event) {
       
   355 		Item item = (Item)event.handlerObject;
       
   356 		item.doCallback(event);
       
   357 	}
       
   358 }