/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.midp.lcdui;
import com.sun.midp.events.EventTypes;
import com.sun.midp.events.Event;
import com.sun.midp.events.EventListener;
import com.sun.midp.events.EventQueue;
import com.sun.midp.security.Permissions;
import com.sun.midp.security.SecurityToken;
import javax.microedition.lcdui.Display;
/**
* This class performs all of the repaint event handling.
*/
public class RepaintEventProducer implements EventListener {
/** Cached reference to the MIDP event queue. */
private EventQueue eventQueue;
/** Repaint event free for reuse. */
private RepaintEvent pooledEvent1;
/** Repaint event free for reuse. */
private RepaintEvent pooledEvent2;
/** Repaint event free for reuse. */
private RepaintEvent pooledEvent3;
/**
* Repaint event in the queue or waiting to be moved to the processed
* slot. Events are rotated to different slots in
* <code>preprocessRepaints</code>.
*/
private RepaintEvent queuedEvent;
/**
* Repaint event being processed (queue has event also) or
* has been processed (no event in the queue).
*/
private RepaintEvent eventInProcess;
/**
* The constructor LCDUI repaint events handler.
*
* @param theEventQueue the event queue
*/
public RepaintEventProducer(EventQueue theEventQueue) {
eventQueue = theEventQueue;
pooledEvent1 = RepaintEvent.createRepaintEvent(null, 0, 0, 0, 0, null);
pooledEvent2 = RepaintEvent.createRepaintEvent(null, 0, 0, 0, 0, null);
pooledEvent3 = RepaintEvent.createRepaintEvent(null, 0, 0, 0, 0, null);
eventQueue.registerEventListener(EventTypes.REPAINT_EVENT, this);
}
/**
* Called to schedule a repaint of the current Displayable
* as soon as possible.
*
* @param d The Display
* @param x The x coordinate of the origin of the repaint rectangle
* @param y The y coordinate of the origin of the repaint rectangle
* @param w The width of the repaint rectangle
* @param h The height of the repaint rectangle
* @param target An optional target Object, which may have been the
* original requestor for the repaint
*/
public void scheduleRepaint(DisplayEventConsumer d,
int x, int y, int w, int h, Object target) {
RepaintEvent freeEvent;
synchronized (this) {
freeEvent = pooledEvent1;
freeEvent.setRepaintFields(d, x, y, w, h, target);
if (queuedEvent == null) {
/*
* Since the queue does not lock posting during event
* processing, we need to rotate 3 repaint events for reuse.
*
* 1 for the queued event, 1 for the event begin processed
* and 1 for temporary use when merging a new event with
* a queued event.
*
* Event pooling is done because game can generate upto 15
* repaints a second and cause a lot garbage collection if
* we created new repaint every time.
*/
pooledEvent1 = pooledEvent2;
pooledEvent2 = pooledEvent3;
pooledEvent3 = freeEvent;
queuedEvent = freeEvent;
eventQueue.post(queuedEvent);
} else if (queuedEvent.display != d) {
/*
* A new display has come to the foreground so don't
* bother paint the display in the queued event.
*
* Overwrite this fields in queue event with new values.
*/
queuedEvent.setRepaintFields(d, x, y, w, h, target);
} else {
/*
* When there is a pending repaint
* union the dirty regions into one event
*/
if (queuedEvent.paintX1 > freeEvent.paintX1) {
queuedEvent.paintX1 = freeEvent.paintX1;
}
if (queuedEvent.paintY1 > freeEvent.paintY1) {
queuedEvent.paintY1 = freeEvent.paintY1;
}
if (queuedEvent.paintX2 < freeEvent.paintX2) {
queuedEvent.paintX2 = freeEvent.paintX2;
}
if (queuedEvent.paintY2 < freeEvent.paintY2) {
queuedEvent.paintY2 = freeEvent.paintY2;
}
queuedEvent.paintTarget = null;
}
}
}
/**
* Preprocess an event that is being posted to the event queue.
*
* @param event event being posted
*
* @param waitingEvent previous event of this type waiting in the
* queue to be processed
*
* @return true to allow the post to continue, false to not post the
* event to the queue
*/
public boolean preprocess(Event event, Event waitingEvent) {
/*
* Because of the needs of serviceRepaints the preprocessing
* is done in scheduleRepaint.
*/
return true;
}
/**
* Process an event.
*
* @param genericEvent event to process
*/
public void process(Event genericEvent) {
RepaintEvent event = (RepaintEvent)genericEvent;
synchronized (this) {
queuedEvent = null;
eventInProcess = event;
}
/*
* Target DisplayEventConsumer is obtained directly from event field.
* Assumed that target consumer is not null.
*/
event.display.handleRepaintEvent(
event.paintX1, event.paintY1,
event.paintX2, event.paintY2,
event.paintTarget);
synchronized (this) {
/* Change the ID here to signal waitForRepaint. */
eventInProcess.perUseID++;
eventInProcess = null;
// ServiceRepaints may be blocking.
notifyAll();
}
}
/**
* Called to block the calling MIDlet until the the pending repaint
* operation is performed.
* <p>
* Does not return until the pending repaint (if any)
* has been processed.</p>
*
*/
public void serviceRepaints() {
if (EventQueue.isDispatchThread()) {
Event event;
if (eventInProcess != null) {
/*
* We are in the midst of a calling the application's
* paint method, avoid recursion, simply return
*/
return;
}
/*
* Since we are in the dispatch thread, at this point
* there can only one repaint in the queue so remove it
* from the queue and process it.
*/
event = eventQueue.remove(EventTypes.REPAINT_EVENT);
/* Do not hold a lock when calling out to the application */
if (event != null) {
process(event);
}
} else {
/*
* We only want paint events to be process in the dispatch
* to avoid deadlocks. So wait for the repaint event to occur.
*/
waitForCurrentRepaintEvents();
}
}
/**
* Wait for the event queue to process all of the events currently
* in the queue but not any new ones after the start of this
* method.
*/
private void waitForCurrentRepaintEvents() {
RepaintEvent eventToWaitFor = null;
int currentEventUseID;
synchronized (this) {
if (queuedEvent != null) {
eventToWaitFor = queuedEvent;
} else if (eventInProcess != null) {
eventToWaitFor = eventInProcess;
} else {
/* Nothing to wait for, done. */
return;
}
currentEventUseID = eventToWaitFor.perUseID;
while (eventToWaitFor.perUseID == currentEventUseID) {
try {
wait();
} catch (InterruptedException ie) {
/* The application wants this thread to unblock */
break;
}
}
}
}
}
|