/** * MicroEmulator * Copyright (C) 2002 Bartek Teodorczyk <barteo@barteo.net> * * It is licensed under the following two licenses as alternatives: * 1. GNU Lesser General Public License (the "LGPL") version 2.1 or any newer version * 2. Apache License (the "AL") Version 2.0 * * You may not use this file except in compliance with at least one of * the above two licenses. * * You may obtain a copy of the LGPL at * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt * * You may obtain a copy of the AL at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the LGPL or the AL for the specific language governing permissions and * limitations. */ package org.microemu.device.ui; import org.microemu.device.DeviceFactory; public class EventDispatcher implements Runnable { public static final String EVENT_DISPATCHER_NAME = "event-thread"; public static int maxFps = -1; private volatile boolean cancelled = false; private Event head = null; private Event tail = null; private PaintEvent scheduledPaintEvent = null; private PointerEvent scheduledPointerDraggedEvent = null; private Object serviceRepaintsLock = new Object(); private long lastPaintEventTime = 0; public EventDispatcher() { } public void run() { while (!cancelled) { Event event = null; synchronized (this) { if (head != null) { event = head; if (maxFps > 0 && event instanceof PaintEvent) { long difference = System.currentTimeMillis() - lastPaintEventTime; if (difference < (1000 / maxFps)) { event = null; try { wait((1000 / maxFps) - difference); } catch (InterruptedException e) { } } } if (event != null) { head = event.next; if (head == null) { tail = null; } if (event instanceof PointerEvent && ((PointerEvent) event).type == PointerEvent.POINTER_DRAGGED) { scheduledPointerDraggedEvent = null; } } } else { try { wait(); } catch (InterruptedException e) { } } } if (event != null) { if (event instanceof PaintEvent) { synchronized (serviceRepaintsLock) { synchronized (this) { scheduledPaintEvent = null; } lastPaintEventTime = System.currentTimeMillis(); post(event); serviceRepaintsLock.notifyAll(); } } else { post(event); } } } } /** * Do not service any more events */ public final void cancel() { cancelled = true; synchronized (this) { notify(); } } public void put(Event event) { synchronized (this) { if (event instanceof PaintEvent && scheduledPaintEvent != null) { scheduledPaintEvent.merge((PaintEvent) event); } else if (event instanceof PointerEvent && scheduledPointerDraggedEvent != null && ((PointerEvent) event).type == PointerEvent.POINTER_DRAGGED) { scheduledPointerDraggedEvent.x = ((PointerEvent) event).x; scheduledPointerDraggedEvent.y = ((PointerEvent) event).y; } else { if (event instanceof PaintEvent) { scheduledPaintEvent = (PaintEvent) event; } if (event instanceof PointerEvent && ((PointerEvent) event).type == PointerEvent.POINTER_DRAGGED) { scheduledPointerDraggedEvent = (PointerEvent) event; } if (tail != null) { tail.next = event; } tail = event; if (head == null) { head = event; } notify(); } } } public void put(Runnable runnable) { put(new RunnableEvent(runnable)); } public void serviceRepaints() { synchronized (serviceRepaintsLock) { synchronized (this) { if (scheduledPaintEvent == null) { return; } // TODO move scheduledPaintEvent to head } try { serviceRepaintsLock.wait(); } catch (InterruptedException e) { } } } protected void post(Event event) { event.run(); } public abstract class Event implements Runnable { Event next = null; } public final class PaintEvent extends Event { private int x = -1, y = -1, width = -1, height = -1; public PaintEvent(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; } public void run() { DeviceFactory.getDevice().getDeviceDisplay().repaint(x, y, width, height); } /** * Do a 2-D merge of the paint areas * * @param event */ public final void merge(PaintEvent event) { int xMax = x + width; int yMax = y + height; this.x = Math.min(this.x, event.x); xMax = Math.max(xMax, event.x + event.width); this.y = Math.min(this.y, event.y); yMax = Math.max(yMax, event.y + event.height); this.width = xMax - x; this.height = yMax - y; } } public final class PointerEvent extends EventDispatcher.Event { public static final short POINTER_PRESSED = 0; public static final short POINTER_RELEASED = 1; public static final short POINTER_DRAGGED = 2; public static final short ANDROID_POINTER_PRESSED = 3; public static final short ANDROID_POINTER_TAP = 4; public static final short ANDROID_POINTER_MOVING = 5; public static final short ANDROID_POINTER_MOVED = 6; private Runnable runnable; private short type; private int x; private int y; public PointerEvent(Runnable runnable, short type, int x, int y) { this.runnable = runnable; this.type = type; this.x = x; this.y = y; } public void run() { runnable.run(); } } public class RunnableEvent extends Event { private Runnable runnable; public RunnableEvent(Runnable runnable) { this.runnable = runnable; } public void run() { try { runnable.run(); } catch (Exception e) { jimm.modules.DebugLog.panic("EventDispatcher", e); } } } }