X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fibex%2FSurface.java;fp=src%2Forg%2Fibex%2FSurface.java;h=383e7a86b6387d1b7d17a5f0b9b127f476898df3;hb=3591b88b94a6bb378af3d4abe6eb5233ce583104;hp=0000000000000000000000000000000000000000;hpb=de378041d5ca2aca1a2b5a31ef15ae90a86c977f;p=org.ibex.core.git diff --git a/src/org/ibex/Surface.java b/src/org/ibex/Surface.java new file mode 100644 index 0000000..383e7a8 --- /dev/null +++ b/src/org/ibex/Surface.java @@ -0,0 +1,374 @@ +// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL] +package org.ibex; + +import org.ibex.js.*; +import org.ibex.util.*; + +/** + * A Surface, as described in the Ibex Reference. + * + * Platform subclasses should include an inner class subclass of + * Surface to return from the Platform._createSurface() method + */ +public abstract class Surface extends PixelBuffer implements Scheduler.Task { + + // Static Data //////////////////////////////////////////////////////////////////////////////// + + private static Boolean T = Boolean.TRUE; + private static Boolean F = Boolean.FALSE; + + /** all instances of Surface which need to be refreshed by the Scheduler */ + public static Vec allSurfaces = new Vec(); + + /** When set to true, render() should abort as soon as possible and restart the rendering process */ + volatile boolean abort = false; + + // these three variables are used to ensure that user resizes trump programmatic resizes + volatile boolean syncRootBoxToSurface = false; + volatile int pendingWidth = 0; + volatile int pendingHeight = 0; + + public static boolean alt = false; ///< true iff the alt button is pressed down + public static boolean control = false; ///< true iff the control button is pressed down + public static boolean shift = false; ///< true iff the shift button is pressed down + public static boolean button1 = false; ///< true iff button 1 is depressed + public static boolean button2 = false; ///< true iff button 2 is depressed + public static boolean button3 = false; ///< true iff button 3 is depressed + + + // Instance Data /////////////////////////////////////////////////////////////////////// + + public Box root; ///< The Box at the root of this surface + public String cursor = "default"; ///< The active cursor to switch to when syncCursor() is called + public int mousex; ///< x position of the mouse + public int mousey; ///< y position of the mouse + public int _mousex; ///< x position of the mouse FIXME + public int _mousey; ///< y position of the mouse FIXME + public int newmousex = -1; ///< x position of the mouse, in real time; this lets us collapse Move's + public int newmousey = -1; ///< y position of the mouse, in real time; this lets us collapse Move's + public boolean minimized = false; ///< True iff this surface is minimized, in real time + public boolean maximized = false; ///< True iff this surface is maximized, in real time + DirtyList dirtyRegions = new DirtyList(); ///< Dirty regions on the surface + + // Used For Simulating Clicks and DoubleClicks ///////////////////////////////////////////////// + + int last_press_x = Integer.MAX_VALUE; ///< the x-position of the mouse the last time a Press message was enqueued + int last_press_y = Integer.MAX_VALUE; ///< the y-position of the mouse the last time a Press message was enqueued + static int lastClickButton = 0; ///< the last button to recieve a Click message; used for simulating DoubleClick's + static long lastClickTime = 0; ///< the last time a Click message was processed; used for simulating DoubleClick's + + + // Methods to be overridden by subclasses /////////////////////////////////////////////////////// + + public abstract void toBack(); ///< should push surface to the back of the stacking order + public abstract void toFront(); ///< should pull surface to the front of the stacking order + public abstract void syncCursor(); ///< set the actual cursor to this.cursor if they do not match + public abstract void setInvisible(boolean b); ///< If b, make window invisible; otherwise, make it non-invisible. + protected abstract void _setMaximized(boolean b); ///< If b, maximize the surface; otherwise, un-maximize it. + protected abstract void _setMinimized(boolean b); ///< If b, minimize the surface; otherwise, un-minimize it. + public abstract void setLocation(); ///< Set the surface's x/y position to that of the root box + protected abstract void _setSize(int w, int h); ///< set the actual size of the surface + public abstract void setTitleBarText(String s); ///< Sets the surface's title bar text, if applicable + public abstract void setIcon(Picture i); ///< Sets the surface's title bar text, if applicable + public abstract void _dispose(); ///< Destroy the surface + public void setMinimumSize(int minx, int miny, boolean resizable) { } + protected void setSize(int w, int h) { _setSize(w, h); } + + + // Helper methods for subclasses //////////////////////////////////////////////////////////// + + protected final void Press(final int button) { + last_press_x = mousex; + last_press_y = mousey; + + if (button == 1) button1 = true; + else if (button == 2) button2 = true; + else if (button == 3) button3 = true; + + if (button == 1) new Message("_Press1", T, root); + else if (button == 2) new Message("_Press2", T, root); + else if (button == 3) { + final Box who = root; + Scheduler.add(new Scheduler.Task() { public void perform() throws JSExn { + Platform.clipboardReadEnabled = true; + try { + root.putAndTriggerTraps("_Press3", T); + } finally { + Platform.clipboardReadEnabled = false; + } + }}); + } + } + + protected final void Release(int button) { + if (button == 1) button1 = false; + else if (button == 2) button2 = false; + else if (button == 3) button3 = false; + + if (button == 1) new Message("_Release1", T, root); + else if (button == 2) new Message("_Release2", T, root); + else if (button == 3) new Message("_Release3", T, root); + + if (Platform.needsAutoClick() && Math.abs(last_press_x - mousex) < 5 && Math.abs(last_press_y - mousey) < 5) Click(button); + last_press_x = Integer.MAX_VALUE; + last_press_y = Integer.MAX_VALUE; + } + + protected final void Click(int button) { + if (button == 1) new Message("_Click1", T, root); + else if (button == 2) new Message("_Click2", T, root); + else if (button == 3) new Message("_Click3", T, root); + if (Platform.needsAutoDoubleClick()) { + long now = System.currentTimeMillis(); + if (lastClickButton == button && now - lastClickTime < 350) DoubleClick(button); + lastClickButton = button; + lastClickTime = now; + } + } + + /** we enqueue ourselves in the Scheduler when we have a Move message to deal with */ + public void perform() { + if (mousex == newmousex && mousey == newmousey) return; + int oldmousex = mousex; mousex = newmousex; + int oldmousey = mousey; mousey = newmousey; + String oldcursor = cursor; cursor = "default"; + // Root gets motion events outside itself (if trapped) + if (!root.inside(oldmousex, oldmousey) && !root.inside(mousex, mousey) && (button1 || button2 || button3)) + root.putAndTriggerTrapsAndCatchExceptions("_Move", T); + if (!cursor.equals(oldcursor)) syncCursor(); + } + + /** + * Notify Ibex that the mouse has moved. If the mouse leaves the + * surface, but the host windowing system does not provide its new + * position (for example, a Java MouseListener.mouseExited() + * message), the subclass should use (-1,-1). + */ + protected final void Move(final int newmousex, final int newmousey) { + this.newmousex = newmousex; + this.newmousey = newmousey; + Scheduler.add(this); + } + + /** subclasses should invoke this method when the user resizes the window */ + protected final void SizeChange(final int width, final int height) { + if (pendingWidth == width && pendingHeight == height) return; + pendingWidth = width; + pendingHeight = height; + syncRootBoxToSurface = true; + abort = true; + Scheduler.renderAll(); + } + + // FEATURE: can we avoid creating objects here? + protected final void PosChange(final int x, final int y) { + Scheduler.add(new Scheduler.Task() { public void perform() throws JSExn { + root.x = x; + root.y = y; + root.putAndTriggerTrapsAndCatchExceptions("PosChange", T); + }}); + } + + private final String[] doubleClick = new String[] { null, "_DoubleClick1", "_DoubleClick2", "_DoubleClick3" }; + protected final void DoubleClick(int button) { new Message(doubleClick[button], T, root); } + protected final void KeyPressed(String key) { new Message("_KeyPressed", key, root); } + protected final void KeyReleased(String key) { new Message("_KeyReleased", key, root); } + protected final void Close() { new Message("Close", T, root); } + protected final void Minimized(boolean b) { minimized = b; new Message("Minimized", b ? T : F, root); } + protected final void Maximized(boolean b) { maximized = b; new Message("Maximized", b ? T : F, root); } + protected final void Focused(boolean b) { new Message("Focused", b ? T : F, root); } + public void Refresh() { Scheduler.add(new Scheduler.Task() { public void perform() { } }); } + + public final void setMaximized(boolean b) { if (b != maximized) _setMaximized(maximized = b); } + public final void setMinimized(boolean b) { if (b != minimized) _setMinimized(minimized = b); } + + + // Other Methods /////////////////////////////////////////////////////////////////////////////// + + /** Indicates that the Surface is no longer needed */ + public final void dispose(boolean quitIfAllSurfacesGone) { + if (Log.on) Log.info(this, "disposing " + this); + allSurfaces.removeElement(this); + _dispose(); + if (allSurfaces.size() == 0) { + if (Log.on) Log.info(this, "exiting because last surface was destroyed"); + System.exit(0); + } + } + + public void dirty(int x, int y, int w, int h) { + dirtyRegions.dirty(x, y, w, h); + Refresh(); + } + + public static Surface fromBox(Box b) { + // FIXME use a hash table here + for(int i=0; i root.width) w = root.width - x; + if (y+h > root.height) h = root.height - y; + if (w <= 0 || h <= 0) continue; + + root.render(0, 0, x, y, x + w, y + h, this, identity); + drawPicture(Main.scarImage, 0, root.height - Main.scarImage.height, x, y, x+w, y+h); + + if (abort) { + // x,y,w,h is only partially reconstructed, so we must be careful not to re-blit it + dirtyRegions.dirty(x, y, w, h); + // put back all the dirty regions we haven't yet processed (including the current one) + for(int j=i; j root.width) w = root.width - x; + if (y+h > root.height) h = root.height - y; + if (w <= 0 || h <= 0) continue; + if (abort) return; + blit(backbuffer, x, y, x, y, w + x, h + y); + } + } + + /** This is how subclasses signal a 'shallow dirty', indicating that although the backbuffer is valid, the screen is not */ + public final void Dirty(int x, int y, int w, int h) { + screenDirtyRegions.dirty(x, y, w, h); + Scheduler.renderAll(); + } + + public void dirty(int x, int y, int w, int h) { + screenDirtyRegions.dirty(x, y, w, h); + super.dirty(x, y, w, h); + } + + /** copies a region from the doublebuffer to this surface */ + public abstract void blit(PixelBuffer source, int sx, int sy, int dx, int dy, int dx2, int dy2); + + } + +}