/*********************************************************************************
* TotalCross Software Development Kit *
* Copyright (C) 2000 Dave Slaughter *
* Copyright (C) 2000-2010 SuperWaba Ltda. *
* All Rights Reserved *
* *
* This library and virtual machine 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. *
* *
* This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 *
* A copy of this license is located in file license.txt at the root of this *
* SDK or can be downloaded here: *
* http://www.gnu.org/licenses/lgpl-3.0.txt *
* *
*********************************************************************************/
package totalcross;
class TCEventThread extends Thread
{
Queue eventQueue;
static private final int INVOKE_IN_EVENT_THREAD = -99998;
MainClass win;
public boolean running = true;
public int popTime = 100;
public TCEventThread(MainClass win)
{
super("TC Event Thread");
this.win = win;
eventQueue = new Queue();
nativeCreate();
}
void nativeCreate()
{
setPriority(Thread.MAX_PRIORITY); // event thread should have maximum priority
setDaemon(true);
start();
}
public void run()
{
while (running)
{
try
{
privatePumpEvents();
}
catch(java.lang.Throwable t)
{
// There's no vm.debug in Android!
System.out.println("---------------------------");
System.out.println(">>>>>>> CAUGHT UNHANDLED EXCEPTION IN EVENT THREAD:");
t.printStackTrace();
}
}
}
boolean eventAvailable()
{
return eventQueue.getSize() > 0;
}
void pumpEvents()
{
if (Thread.currentThread() == this) // only continue if pumpEvents is being called from the event thread
privatePumpEvents();
}
void privatePumpEvents()
{
// This gives the system CPU some breathing room when in a tight event
// loop and no events are posted.
final TCEvent event = popTime <= 0 ? (TCEvent)eventQueue.pop() : (TCEvent)eventQueue.popWait(popTime);
if (event != null)
{
if (event.type == INVOKE_IN_EVENT_THREAD)
{
event.r.run();
// If they are waiting for this, then notify.
if (event.synch != null)
synchronized(event.synch)
{
event.synch.notify();
}
}
else
win._postEvent(event.type, event.key, event.x, event.y, event.modifiers, event.timestamp);
}
}
void pushEvent(int type, int key, int x, int y, int modifiers, int timestamp)
{
eventQueue.push(new TCEvent(type, key, x, y, modifiers, timestamp));
}
boolean hasEvent(int type)
{
Node n = eventQueue.queue;
while (n != null)
{
TCEvent ev = (TCEvent)n.o;
if (ev.type == type)
return true;
n = n.next;
}
return false;
}
void invokeInEventThread(boolean wait, Runnable r)
{
if (!wait)
eventQueue.push(new TCEvent(INVOKE_IN_EVENT_THREAD, r));
else
{
// 9/16/02 - Andy - I have modified the code below so that it now checks
// to see if we are currently in the event thread. If we are in the
// event thread, then we will simply call r.run() directly, thus
// avoiding a deadlock if invokeInEventThread() was called from an event handler.
// Are we currently in the event thread?
java.lang.Thread current = java.lang.Thread.currentThread();
if (current.equals(this))
r.run(); // Execute directly.
else
{
// We are not in the event thread, so push to event thread.
// We have to create a new Object, in case there is more than one
// thread waiting on an invoke.
Object synch = new Object();
synchronized(synch)
{
eventQueue.push(new TCEvent(INVOKE_IN_EVENT_THREAD, r, synch));
try { synch.wait(); } catch(Exception ie) { }
}
}
}
}
private class Node
{
Object o;
Node next;
Node(Object obj) { o = obj; }
}
/** Implements a simple FIFO queue of unlimited size. This queue is thread safe, including
* functionality that allows one thread to wait for another to push an object onto the queue. */
private class Queue
{
private Node queue;
private Node end;
private int size;
/** Push an object on the queue and notify any waiting threads. */
synchronized void push(Object o)
{
Node node = new Node(o);
if(queue == null)
queue = end = node;
else
{
end.next = node;
end = node;
}
size++;
notify();
}
/** Returns the oldest object in the queue or null if no objects in the queue */
synchronized Object pop()
{
if(queue == null || size == 0)
return null;
Object ret = queue.o;
queue = queue.next;
size--;
return ret;
}
/** Returns the oldest object in the queue. If there are no object in the queue, then this method will block until an object is placed in the queue from another
* thread, or the specified time expires. If the time expires and no object has been placed in the queue, this method will return null.
*/
synchronized Object popWait(int maxTime)
{
long begin;
try
{
begin = System.currentTimeMillis();
long timeLeft;
while(queue == null && (timeLeft = (maxTime - (System.currentTimeMillis() - begin))) > 0)
wait(timeLeft);
}
catch(Exception ie)
{
}
return pop();
}
synchronized int getSize()
{
return size;
}
}
private class TCEvent
{
int type;
int key;
int x;
int y;
int modifiers;
int timestamp;
Runnable r;
Object synch;
TCEvent(int type, int key, int x, int y, int modifiers, int timestamp)
{
this.type = type;
this.key = key;
this.x = x;
this.y = y;
this.modifiers = modifiers;
this.timestamp = timestamp;
}
TCEvent(int type, Runnable r)
{
this.type = type;
this.r = r;
}
TCEvent(int type, Runnable r, Object synch)
{
this(type, r);
this.synch = synch;
}
}
}