2003/10/31 09:50:08
authormegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:40:39 +0000 (07:40 +0000)
committermegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:40:39 +0000 (07:40 +0000)
darcs-hash:20040130074039-2ba56-175e5323ee263057ec5dd652ea85c488c66e3cf7.gz

16 files changed:
src/org/xwt/Box.java.pp
src/org/xwt/Glyph.java
src/org/xwt/HTTP.java
src/org/xwt/Main.java
src/org/xwt/Message.java [deleted file]
src/org/xwt/Picture.java
src/org/xwt/Platform.java
src/org/xwt/Res.java
src/org/xwt/Scheduler.java [new file with mode: 0644]
src/org/xwt/Surface.java
src/org/xwt/Template.java
src/org/xwt/ThreadMessage.java [deleted file]
src/org/xwt/Trap.java
src/org/xwt/XMLRPC.java
src/org/xwt/XWT.java
src/org/xwt/builtin/splash.xwt

index 21cc5bf..c58eebc 100644 (file)
@@ -89,7 +89,6 @@ public final class Box extends JS.Scope {
     static final int NOTPACKED_FLAG    = 0x00000004;
     static final int HSHRINK_FLAG      = 0x00000008;
     static final int VSHRINK_FLAG      = 0x00000010;
-    static final int TILE_FLAG         = 0x00000020;
     static final int FONT_CHANGED_FLAG = 0x00000040;  // set when font changes, cleared during repack
     static final int ISROOT_FLAG       = 0x00000080;
     static final int NOCLIP_FLAG       = 0x00000100;
@@ -436,6 +435,7 @@ public final class Box extends JS.Scope {
         int globaly = parenty + (parent == null ? 0 : y);
 
         // intersect the x,y,w,h rendering window with ourselves; quit if it's empty
+
         if ((flags & NOCLIP_FLAG) == 0) {
             clipw = min(max(clipx, parent == null ? 0 : globalx) + clipw, (parent == null ? 0 : globalx) + width) - globalx;
             cliph = min(max(clipy, parent == null ? 0 : globaly) + cliph, (parent == null ? 0 : globaly) + height) - globaly;
@@ -450,11 +450,19 @@ public final class Box extends JS.Scope {
         }
 
         if (image != null)
-            if ((flags & TILE_FLAG) != 0) renderTiledImage(globalx, globaly, clipx, clipy, clipw, cliph, buf);
-            else buf.drawPicture(image, globalx, globaly, clipx, clipy, clipx + clipw, clipy + cliph);
+            for(int x = globalx; x < clipx + clipw; x += image.getWidth())
+                for(int y = globaly; y < clipy + cliph; y += image.getHeight())
+                    buf.drawPicture(image, x, y, clipx, clipy, clipx + clipw, clipy + cliph);
 
        if (text != null && !text.equals(""))
-            renderText(globalx, globaly, clipx, clipy, clipw, cliph, buf);
+            Glyph.rasterizeGlyphs(font, fontsize, text, buf, textcolor, globalx, globaly, clipx, clipy, clipw + clipx, clipy + cliph,
+                                  new Callback() { public Object call(Object arg) {
+                                      Box.this.dirty();
+                                      Box b = Box.this;
+                                      MARK_FOR_REFLOW_b;
+                                      b.dirty();
+                                      return null;
+                                  }});
 
         if (path != null) {
             if (rtransform == null) rpath = null;
@@ -480,57 +488,6 @@ public final class Box extends JS.Scope {
         }
     }
 
-    void renderTiledImage(int globalx, int globaly, int x, int y, int w, int h, PixelBuffer buf) {
-        /*
-          FIXME
-        int iw = image.getWidth();
-        int ih = image.getHeight();
-        for(int i=(x - x)/iw; i <= (x + w - x)/iw; i++) {
-            for(int j=(y - y)/ih; j<= (y + h - y)/ih; j++) {
-                
-                int dx1 = max(i * iw + x, x);
-                int dy1 = max(j * ih + y, y);
-                int dx2 = min((i+1) * iw + x, x + w);
-                int dy2 = min((j+1) * ih + y, y + h);
-                
-                int sx1 = dx1 - (i*iw) - x;
-                int sy1 = dy1 - (j*ih) - y;
-                int sx2 = dx2 - (i*iw) - x;
-                int sy2 = dy2 - (j*ih) - y;
-
-                if (dx2 - dx1 > 0 && dy2 - dy1 > 0 && sx2 - sx1 > 0 && sy2 - sy1 > 0)
-                    buf.drawPicture(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2);
-            }
-        }
-        */
-    }
-
-    void renderText(int x, int y, int clipx, int clipy, int clipw, int cliph, PixelBuffer buf) {
-        for(int i=0; i<text.length(); i++) {
-            final char c = text.charAt(i);
-           Glyph g = Glyph.getCachedGlyph(font, fontsize, c);
-           if (g != null) {
-               int top = y + g.max_ascent - g.baseline + g.max_descent;
-               if (g.p != null)
-                   buf.drawPictureAlphaOnly(g.p, x, top,
-                                            clipx, clipy, clipx + clipw, clipy + cliph, textcolor);
-               x += g.advance;
-           } else {
-               final int fontsize_final = fontsize;
-               final Res font_final = font;
-                ThreadMessage.newthread(new JS.Callable() {
-                        public Object call(JS.Array args) {
-                           Glyph.renderGlyph(font_final, fontsize_final, c);   
-                           recompute_font();
-                           Box b = Box.this; MARK_FOR_REFLOW_b;
-                           dirty();
-                           return null;
-                       } });
-               return;
-           }
-        }
-    }
-
 
     // Methods to implement org.xwt.js.JS //////////////////////////////////////
 
@@ -850,37 +807,24 @@ public final class Box extends JS.Scope {
     }
 
     public void recompute_font() {
-        try {
-            MARK_FOR_REFLOW_this;
-            textwidth = 0;
-            textheight = 0;
-            if (text == null) return;
-            for(int i=0; i<text.length(); i++) {
-                Glyph g = Glyph.getCachedGlyph(font, fontsize, text.charAt(i));
-               if (g == null) {
-                   final int fontsize_final = fontsize;
-                   final Res font_final = font;
-                   final char c = text.charAt(i);
-                   ThreadMessage.newthread(new JS.Callable() {
-                           public Object call(JS.Array args) {
-                               Glyph.renderGlyph(font_final, fontsize_final, c);       
-                               recompute_font();
-                               Box b = Box.this; MARK_FOR_REFLOW_b;
-                               dirty();
-                               return null;
-                           } });
-               } else {
-                   textwidth += g.advance;
-                   textheight = max(textheight, g.max_ascent + g.max_descent);
-               }
-            }
-        } catch (Exception e) {
-            Log.log(this, e);
-        }
+        if (text == null) { textwidth = textheight = 0; return; }
+        if (font == null) { /* FIXME */ }
+        long widthheight = Glyph.rasterizeGlyphs(font, fontsize, text, null, textcolor, 0, 0, 0, 0, 0, 0,
+                                                 new Callback() { public Object call(Object arg) {
+                                                     Box b = Box.this;
+                                                     recompute_font();
+                                                     MARK_FOR_REFLOW_b;
+                                                     b.dirty();
+                                                     return null;
+                                                 } });
+        if (widthheight == -1) return;
+        textwidth = (int)((widthheight & 0xffff0000) >> 16);
+        textheight = (int)(widthheight & 0x0000ffff);
+        MARK_FOR_REFLOW_this;
     }
 
 
-    // Trivial Helper Methods (should be inlined) /////////////////////////////////////////
+    // Trivial Helper Method s(should be inlined) /////////////////////////////////////////
 
     static final int min(int a, int b) { if (a<b) return a; else return b; }
     static final float min(float a, float b) { if (a<b) return a; else return b; }
@@ -951,15 +895,17 @@ public final class Box extends JS.Scope {
 
         static {
             specialBoxProperties.put("fill", new ColorBoxProperty() {
-                    public void put(Box b, Object value) {
-                        if (value != null && value instanceof Res) {
-                            b.image = Picture.fromRes((Res)value);
+                    public void put(final Box b, final Object value) {
+                        if (value == null || !(value instanceof Res)) super.put(b, value);
+                        else Picture.fromRes((Res)value, new Callback() { public Object call(Object pic) {
+                            if (pic == b.image) return null;
+                            b.image = (Picture)pic;
                             b.minwidth = b.image.getWidth();
                             b.minheight = b.image.getHeight();
+                            MARK_FOR_REFLOW_b;
                             b.dirty();
-                        } else {
-                            super.put(b, value);
-                        }
+                            return null;
+                        } });
                     }
                     public int getColor(Box b) { return b.fillcolor; }
                     public void putColor(Box b, int argb) { b.fillcolor = argb; }
@@ -979,13 +925,8 @@ public final class Box extends JS.Scope {
                         String t = value == null ? "null" : value.toString();
                         if (t.equals(b.text)) return;
                        b.text = t;
-                       if (t == null) {
-                            if (b.textwidth != 0 || b.textheight != 0) MARK_FOR_REFLOW_b;
-                           b.textwidth = b.textheight = 0;
-                       } else {
-                            b.recompute_font();
-                       }
                        b.dirty();
+                        b.recompute_font();
                     } });
 
             specialBoxProperties.put("path", new SpecialBoxProperty() {
@@ -1239,14 +1180,6 @@ public final class Box extends JS.Scope {
                 });
             //#end
         
-            specialBoxProperties.put("tile", new SpecialBoxProperty() {
-                    public Object get(Box b) { return ((b.flags & TILE_FLAG) != 0) ? Boolean.TRUE : Boolean.FALSE; }
-                    public void put(Box b, Object value) {
-                        if (((b.flags & TILE_FLAG) != 0) == stob(value)) return;
-                        if (stob(value)) b.flags |= TILE_FLAG; else b.flags &= ~TILE_FLAG;
-                        b.dirty();
-                    } });
-        
             specialBoxProperties.put("noclip", new SpecialBoxProperty() {
                     public Object get(Box b) { return ((b.flags & NOCLIP_FLAG) != 0) ? Boolean.TRUE : Boolean.FALSE; }
                     public void put(Box b, Object value) {
@@ -1283,23 +1216,6 @@ public final class Box extends JS.Scope {
                         if (b.parent != null) MARK_FOR_REFLOW_b_parent;
                     } });
         
-            specialBoxProperties.put("image", new SpecialBoxProperty() {
-                    public Object get(Box b) { return b.image == null ? null : b.image.res; }
-                    public void put(Box b, Object value) {
-                        if (value == null) {
-                            b.image = null;
-                        } else if (value instanceof Res) {
-                            b.image = Picture.fromRes((Res)value);
-                        } else {
-                            // FIXME
-                        }
-                        b.minwidth = min(b.maxwidth, max(b.minwidth, b.image == null ? 0 : b.image.getWidth()));
-                        b.minheight = min(b.maxheight, max(b.minheight, b.image == null ? 0 : b.image.getHeight()));
-                        MARK_FOR_REFLOW_b;
-                        b.dirty();
-                    }
-                });
-
             //#repeat globalx/globaly x/y
             specialBoxProperties.put("globalx", new SpecialBoxProperty() {
                     public Object get(Box b) { return new Integer(b.parent == null || b.surface == null ? 0 : b.x); }
index efe2ad0..71cf19e 100644 (file)
@@ -3,6 +3,7 @@ package org.xwt;
 import org.xwt.translators.*;
 import org.xwt.util.*;
 import org.xwt.js.*;
+import java.util.*;
 
 public class Glyph {
     public char c;
@@ -10,27 +11,93 @@ public class Glyph {
     public int max_descent;    // same value for every glyph in font; the max descent of all chars
     public int baseline;       // within the picture, this is the y-coordinate of the baseline
     public int advance;        // amount to increment the x-coordinate
+    public int pointsize;
     public Picture p;
+    public Res res;
+    public boolean rendered = false;
 
     // k1=font.res k2=(c << 16 | pointsize)
     private static Cache glyphCache = new Cache();
 
-    public static Glyph getCachedGlyph(Res res, int pointsize, char c) {
-        return (Glyph)glyphCache.get(res, new Integer((((int)c) << 16) | pointsize));
-    }
-    public static void renderGlyph(Res res, int pointsize, char c) {
-       if (!ThreadMessage.suspendThread())
-           throw new RuntimeException("attempt to perform background-only operation in a foreground thread");
-       synchronized(res) {
-           // FEATURE: be smarter here
-           if ((Glyph)glyphCache.get(res, new Integer((((int)c) << 16) | pointsize)) != null) return;
-           Log.log(Freetype.class, "rendering glyphs for font " + res.getDescriptiveName());
-           if (c >= 32 && c < 127) Freetype.renderGlyphs(res, pointsize, 32, 126, glyphCache);
-           else Freetype.renderGlyphs(res, pointsize, (int)c, (int)c, glyphCache);
-           if ((Glyph)glyphCache.get(res, new Integer((((int)c) << 16) | pointsize)) == null)
-               throw new JS.Exn("error rendering glyph " + c + "; glyph is null");
-           Log.log(Freetype.class, "    done rendering glyphs for font " + res.getDescriptiveName());
-       }
-       ThreadMessage.resumeThread();
+    private static Queue glyphsToBeRendered = new Queue(255);
+    private static Freetype freetype = new Freetype();
+
+    private static Scheduler.Task glyphRenderingTask = new Scheduler.Task() {
+            public Object call(Object arg) {
+                Glyph g = (Glyph)glyphsToBeRendered.remove(false);
+                if (g == null) return null;
+                if (g.p != null) return null;
+                Log.log(Glyph.class, "rendering glyph " + g.c);
+                freetype.renderGlyph(g.res, g);
+                g.rendered = true;
+                Scheduler.add(this);
+                return null;
+            }
+        };
+
+    /**
+
+     *  If the glyphs of <code>text</code> are not yet loaded, spawn a
+     *  Task to load them and invoke callback.
+     *
+
+     *  returns the width (in the high-order int) and height (in the
+     *  low-order int) of the string's rasterization, or -1 if some
+     *  glyphs are not loaded.
+
+     */
+    public static long rasterizeGlyphs(final Res res, final int pointsize, final String text, PixelBuffer pb, int textcolor,
+                                  int x, int y, int cx1, int cy1, int cx2, int cy2, final Callback callback) {
+        boolean ret = true;
+        int width = 0;
+        int height = 0;
+        for(int i=0; i<text.length(); i++) {
+            final char c = text.charAt(i);
+            Glyph g = (Glyph)glyphCache.get(res, new Integer((((int)c) << 16) | pointsize));
+            if (g == null) {
+                g = new Glyph();
+                g.c = c;
+                g.pointsize = pointsize;
+                g.res = res; 
+                glyphCache.put(res, new Integer((((int)c) << 16) | pointsize), g);
+                glyphsToBeRendered.prepend(g);
+            }
+            if (g.rendered && ret) {
+                if (pb != null && g.p != null)
+                    pb.drawPictureAlphaOnly(g.p, x, y + g.max_ascent - g.baseline, cx1, cy1, cx2, cy2, textcolor);
+                x += g.advance;
+                width += g.advance;
+                height = java.lang.Math.max(height, g.max_ascent + g.max_descent);
+            } else {
+                glyphsToBeRendered.prepend(g);
+                Scheduler.add(glyphRenderingTask);
+                ret = false;
+            }
+        }
+        if (!ret) Scheduler.add(new Scheduler.Task() { public Object call(Object arg) {
+            for(int i=0; i<text.length(); i++) {
+                Glyph g = (Glyph)glyphCache.get(res, new Integer((((int)text.charAt(i)) << 16) | pointsize));
+                if (g == null || !g.rendered) {
+                    Scheduler.add(this);
+                    return null;
+                }
+            }
+            callback.call(null);
+            return null;
+        }});
+
+        if (ret) return ((long)width << 16) | (long)height;
+        for(int i=32; i<128; i++) {
+            Glyph g = (Glyph)glyphCache.get(res, new Integer((i << 16) | pointsize));
+            if (g == null) {
+                g = new Glyph();
+                g.c = (char)i;
+                g.pointsize = pointsize;
+                g.res = res;
+                glyphCache.put(res, new Integer((i << 16) | pointsize), g);
+                glyphsToBeRendered.append(g);
+            }
+        }
+        return -1;
     }
 }
index 3bd0a6b..7295f1f 100644 (file)
@@ -181,18 +181,8 @@ public class HTTP {
             return;
         } catch (UnknownHostException uhe) { }
 
-        if (Platform.detectProxy() == null) throw new HTTPException("could not resolve hostname \"" + host + "\" and no proxy configured");
-        if (Log.on) Log.log(this, "  could not resolve host " + host + "; using xmlrpc.xwt.org to ensure security");
-        try {
-            JS.Array args = new JS.Array();
-            args.addElement(host);
-            Object ret = new XMLRPC("http://xmlrpc.xwt.org/RPC2/", "dns.resolve").call(args);
-            if (ret == null || !(ret instanceof String)) throw new Exception("    xmlrpc.xwt.org returned non-String: " + ret);
-            resolvedHosts.put(host, ret);
-            return;
-        } catch (Throwable e) {
-            throw new HTTPException("exception while attempting to use xmlrpc.xwt.org to resolve " + host + ": " + e);
-        }
+        if (Platform.detectProxy() == null)
+            throw new HTTPException("could not resolve hostname \"" + host + "\" and no proxy configured");
     }
 
 
@@ -293,9 +283,11 @@ public class HTTP {
             org.xwt.js.JS.Array args = new org.xwt.js.JS.Array();
             args.addElement(url.toString());
             args.addElement(url.getHost());
+            /* FIXME
             Object obj = pacFunc.call(args);
             if (Log.verbose) Log.log(this, "  PAC script returned \"" + obj + "\"");
             pac = obj.toString();
+            */
         } catch (Throwable e) {
             if (Log.on) Log.log(this, "PAC script threw exception " + e);
             return null;
@@ -789,7 +781,10 @@ public class HTTP {
                 }
 
                 JS.CompiledFunction scr = JS.parse("PAC script at " + url, 0, new StringReader(script));
+                // FIXME
+                /*
                 scr.call(new JS.Array(), proxyAutoConfigRootScope);
+                */
                 return (JS.Callable)proxyAutoConfigRootScope.get("FindProxyForURL");
             } catch (Exception e) {
                 if (Log.on) {
@@ -825,6 +820,8 @@ public class HTTP {
 
                 if (authorization != oldAuth) return;
                 if (Log.on) Log.log(Authorization.class, "displaying proxy authorization dialog");
+                /*
+                  FIXME
                 Message.Q.add(new Message() {
                         public void perform() {
                             Box b = new Box();
@@ -834,7 +831,7 @@ public class HTTP {
                             b.put("proxyIP", proxyIP);
                         }
                     });
-
+                */
                 waitingForUser.block();
                 if (Log.on) Log.log(Authorization.class, "got proxy authorization info; re-attempting connection");
             
index f413578..3c79fd8 100644 (file)
@@ -88,19 +88,19 @@ public class Main {
         final XWT xwt = new XWT(rr);
         final Res final_rr = rr;
 
-        new Thread(new Runnable() { 
-            public void run() {
-                Message.Q.startQ();
-                ThreadMessage.newthread(new JS.Callable() {
-                        public Object call(JS.Array args) {
-                            scarImage = Picture.fromRes((Res)Main.builtin.get("org/xwt/builtin/scar.png"));
-                            Template.getTemplate(((Res)final_rr.get(initialTemplate))).apply(new Box(), null, xwt);
-                            return null;
-                        }
-                    });
-            }
-        }).start();
-        
+        Picture.fromRes((Res)Main.builtin.get("org/xwt/builtin/scar.png"), new Callback() {
+                public Object call(Object arg) {
+                    scarImage = (Picture)arg;
+                    Scheduler.add(new Scheduler.Task() {
+                            public Object call(Object args) {
+                                Template.getTemplate(((Res)final_rr.get(initialTemplate))).apply(new Box(), null, xwt);
+                                return null;
+                            }
+                        });
+                    return null;
+                } });
+
+        new Thread() { public void run() { Scheduler.run(); } }.start();
         Platform.running();
     }
 
diff --git a/src/org/xwt/Message.java b/src/org/xwt/Message.java
deleted file mode 100644 (file)
index c8d60cc..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
-package org.xwt;
-
-import java.util.*;
-import org.xwt.util.*;
-
-
-/** A simple interface that must be supported by any object inserted into the MessageQueue. */
-public interface Message {
-
-    /** Invoked when the Message is dequeued. */
-    public void perform();
-
-
-    /** 
-     *  A singleton class (one instance per JVM) that implements a queue
-     *  for XWT events and threads; this is the main action-scheduling
-     *  loop for the engine.
-     *
-     *  This <i>is</i> the foreground thread -- it
-     *  dequeues Messages when they arrive in the queue. Using this
-     *  thread ensures that the messages are executed in a synchronous,
-     *  in-order fashion.
-     */
-    public static class Q extends Thread {
-
-        /** a do-nothing message enqueued to trigger Surfaces to refresh themselves */
-        private static Message refreshMessage = new Message() { public void perform() { } };
-
-        /** enqueues a do-nothing message to get the Surfaces to refresh themselves */
-        public static void refresh() { add(refreshMessage); }
-
-        /** true iff latency-sensitive UI work is being done; signals the networking code to yield */
-        public static volatile boolean working = false;
-
-        private Q() { }
-
-        /** invoked by Main */
-        public static void startQ() { singleton.start(); }
-
-        /** pending events */
-        private static Queue events = new Queue(50);
-
-        /** the number of objects in the queue that are not subclasses of ThreadMessage */
-        public static volatile int nonThreadEventsInQueue = 0;
-
-        /** the message currently being performed */    
-        static Message currentlyPerforming = null;
-
-        private static Q singleton = new Q();
-        private static Watcher watcher = new Watcher();
-
-        /**
-         *  The message loop. Note that non-ThreadMessage Messages get
-         *  priority, and that the queue is emptied in passes -- first we
-         *  look at how many events are in the queue, then perform that
-         *  many events, then render. This has the effect of throttling
-         *  render requests to the appropriate frequency -- when many
-         *  messages are in the queue, refreshes happen less frequently.
-         */
-        public void run() {
-            Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
-            while(true) {
-                try {
-                    int size = events.size();
-                    for(int i=0; i<Math.max(1, size); i++) {
-                        Message e = (Message)events.remove();
-
-                        // if there are non-ThreadMessage events, perform them first
-                        // we check against Thread instead of ThreadMessage so we don't get link failures in the Shoehorn
-                        if (!(e instanceof Thread)) nonThreadEventsInQueue--;
-                        else if (nonThreadEventsInQueue > 0) {
-                            add(e);
-                            i--;
-                            continue;
-                        }
-                        if (!(e instanceof Thread)) working = true;
-                        currentlyPerforming = e;
-                        e.perform();
-                        currentlyPerforming = null;
-                        working = false;
-                    }
-                    working = true;
-                    for(int i=0; i<Surface.allSurfaces.size(); i++)
-                        ((Surface)Surface.allSurfaces.elementAt(i)).render();
-                    working = false;
-
-                } catch (Throwable t) {
-                    if (Log.on) Log.log(this, "caught throwable in Q.run(); this should never happen");
-                    if (Log.on) Log.log(this, "    currentlyPerforming == " + currentlyPerforming);
-                    if (Log.on) Log.log(this, "    working             == " + working);
-                    if (Log.on) Log.log(this, t);
-                    if (Log.on) Log.log(this, "resuming Q loop");
-                }
-            }
-        }
-
-        /** Adds an event to the queue */
-        public static void add(Message e) {
-            // Even though we don't synchronize around these two statements, it is not a race condition. In the worst case,
-            // nonThreadEventsInQueue undercounts -- it never overcounts.
-            events.append(e);
-            if (!(e instanceof Thread)) nonThreadEventsInQueue++;
-        }
-
-        /** a simple thread that logs a warning if too much time is spent in any given message -- helpful for debugging infinite loops */
-        private static class Watcher extends Thread {
-            long t = System.currentTimeMillis();
-            Message m = null;
-            public Watcher() { start(); }
-            public void run() {
-                while(true) {
-                    if ((m != null && m == Q.currentlyPerforming) || Q.working) {
-                        String what, where;
-                        if (m != null && m instanceof ThreadMessage) {
-                            where = org.xwt.js.JS.Thread.fromJavaThread((ThreadMessage)m).getSourceName();
-                            what = "background thread";
-                        } else if (m != null) {
-                            where = org.xwt.js.JS.Thread.fromJavaThread(Q.singleton).getSourceName();
-                            what = "event trap";
-                        } else {
-                            where = org.xwt.js.JS.Thread.fromJavaThread(Q.singleton).getSourceName();
-                            what = "script";
-                        }
-                        long howlong = (System.currentTimeMillis() - t) / 1000;
-                        if (howlong >= 5)
-                            if (Log.on) Log.log(this, "note: executing same " + what + " for " + howlong + "s" + " at " + where);
-                    } else {
-                        m = Q.currentlyPerforming;
-                        t = System.currentTimeMillis();
-                    }
-                    try { Thread.sleep(1000); } catch (Exception e) { }
-                }
-            }
-        }
-    
-    }
-
-}
-
index da65368..a4cc602 100644 (file)
@@ -20,33 +20,6 @@ import org.xwt.translators.*;
  */
 public abstract class Picture {
 
-    /** Pictures, cached by Res */
-    private static Cache cache = new Cache();
-
-    private static GIF gif = new GIF();
-    
-    /** turns a resource into a Picture.Source */
-    public static Picture fromRes(Res r) {
-        Picture ret = (Picture)cache.get(r);
-        if (ret == null) {
-            try {
-                PushbackInputStream pbis = new PushbackInputStream(r.getInputStream());
-                int c = pbis.read();
-                pbis.unread(c);
-                if (c == 'G') ret = gif.fromInputStream(pbis, r.getDescriptiveName());
-                else if (c == 137) ret = new PNG().fromInputStream(pbis, r.getDescriptiveName());
-                else if (c == 0xff) ret = Platform.decodeJPEG(pbis, r.getDescriptiveName());
-                else throw new JS.Exn("couldn't figure out image type from first byte");
-                cache.put(r, ret);
-                ret.res = r;
-            } catch (IOException e) {
-                Log.logJS(Picture.class, e);
-                return null;
-            }
-        }
-        return ret;
-    }
-
     /** the resource that created this Picture */
     public Res res = null;
 
@@ -56,4 +29,32 @@ public abstract class Picture {
     /** the width of the picture */
     public abstract int getWidth();
 
+    /** Pictures, cache keyed by Res instance */
+    private static Cache cache = new Cache();
+    private static GIF gif = new GIF();
+    
+    /** turns a resource into a Picture.Source and passes it to the callback */
+    public static void fromRes(final Res r, final Callback callback) {
+        Picture ret = (Picture)cache.get(r);
+        if (ret != null) {
+            callback.call(ret);
+            return;
+        }
+
+        try {
+            // FIXME: put self in background
+            PushbackInputStream pbis = new PushbackInputStream(r.getInputStream());
+            int c = pbis.read();
+            pbis.unread(c);
+            if (c == 'G') ret = gif.fromInputStream(pbis, r.getDescriptiveName());
+            else if (c == 137) ret = new PNG().fromInputStream(pbis, r.getDescriptiveName());
+            else if (c == 0xff) ret = Platform.decodeJPEG(pbis, r.getDescriptiveName());
+            else throw new JS.Exn("couldn't figure out image type from first byte");
+            cache.put(r, ret);
+            ret.res = r;
+            callback.call(ret);
+        } catch (Exception e) {
+            Log.log(Picture.class, e);
+        }
+    }
 }
index 8e77b87..5b1f0ac 100644 (file)
@@ -227,12 +227,8 @@ public class Platform {
     /** displays a platform-specific "open file" dialog and returns the chosen filename, or null if the user hit cancel */
     protected String _fileDialog(String suggestedFileName, boolean write) { return null; }
     public static String fileDialog(String suggestedFileName, boolean write) {
-        if (!ThreadMessage.suspendThread()) return null;
-        try {
-            return platform._fileDialog(suggestedFileName, write);
-        } finally {
-            ThreadMessage.resumeThread();
-        }
+        // FIXME: put self in background
+        return platform._fileDialog(suggestedFileName, write);
     }
 
     /** default implementation is Eric Albert's BrowserLauncher.java */
@@ -285,9 +281,12 @@ public class Platform {
 
         Object icon = b.get("icon", true);
         if (icon != null && icon instanceof Res) {
+            /*
+              FIXME
             Picture pic = Picture.fromRes((Res)icon);
             if (pic != null) ret.setIcon(pic);
             else if (Log.on) Log.log(Platform.class, "unable to load icon " + icon);
+            */
         }
 
         ret.setLimits(b.minwidth, b.minheight, b.maxwidth, b.maxheight);
index 6bf79a3..196bb2c 100644 (file)
@@ -22,8 +22,10 @@ public abstract class Res extends JS {
     public Res getParent() { return null; }
 
     /** an InputStream that makes sure it is not in the MessageQueue when blocked on a read */
+    // FIXME
     private static class BackgroundInputStream extends FilterInputStream {
         BackgroundInputStream(InputStream i) { super(i); }
+    /*
         private void suspend() throws IOException {
             if (!ThreadMessage.suspendThread())
                 throw new IOException("attempt to perform background-only operation in a foreground thread");
@@ -41,6 +43,7 @@ public abstract class Res extends JS {
             try { return super.read(b, off, len); }
             finally { resume(); }
         }
+    */
     }
 
     /** returns an InputStream containing the Resource's contents */
@@ -201,8 +204,8 @@ public abstract class Res extends JS {
     /** shadow resource which replaces the graft */
     public static class ProgressWatcher extends Res {
         final Res watchee;
-        JS.Callable callback;
-        ProgressWatcher(Res watchee, JS.Callable callback) { this.watchee = watchee; this.callback = callback; }
+        JS.CompiledFunction callback;
+        ProgressWatcher(Res watchee, JS.CompiledFunction callback) { this.watchee = watchee; this.callback = callback; }
         public String getDescriptiveName() { return watchee.getDescriptiveName(); }
         public InputStream getInputStream(String s) throws IOException {
             final InputStream is = watchee.getInputStream(s);
@@ -216,11 +219,12 @@ public abstract class Res extends JS {
                     public int read(byte[] b, int off, int len) throws IOException {
                         int ret = super.read(b, off, len);
                         if (ret != 1) bytesDownloaded += ret;
-                        ThreadMessage.newthread(new JS.Callable() { public Object call(JS.Array a) {
+                        Scheduler.add(new Scheduler.Task() { public Object call(Object arg) {
                             JS.Array args = new JS.Array();
                             args.addElement(new Integer(bytesDownloaded));
                             args.addElement(new Integer(is instanceof KnownLength ? ((KnownLength)is).getLength() : 0));
-                            callback.call(args);
+                            // FIXME
+                            // new JS.Thread(callback, callbackScope).resume();
                             return null;
                         } });
                         return ret;
diff --git a/src/org/xwt/Scheduler.java b/src/org/xwt/Scheduler.java
new file mode 100644 (file)
index 0000000..95fd6bf
--- /dev/null
@@ -0,0 +1,34 @@
+// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import java.util.*;
+import org.xwt.js.*;
+import org.xwt.util.*;
+
+// FEATURE: reimplement Watcher
+/** Implements cooperative multitasking */
+public class Scheduler {
+
+    private static Scheduler singleton = new Scheduler();
+    public static void run() { singleton.do_run(); }
+    protected Scheduler() { }
+
+    public static abstract class Task implements Callback { public abstract Object call(Object o); }
+
+    private static Queue runnable = new Queue(50);
+
+    public static void add(Task t) { singleton.runnable.append(t); }
+    public void do_run() {
+        while(true) {
+            Task t = (Task)runnable.remove(true);
+            try {
+                t.call(null);
+                for(int i=0; i<Surface.allSurfaces.size(); i++)
+                    ((Surface)Surface.allSurfaces.elementAt(i)).render();
+            } catch (Exception e) {
+                Log.log(Scheduler.class, "Task threw an exception: " + e);
+                Log.log(Scheduler.class, e);
+            }
+        }
+    }
+}
index c30a34b..9f48b2c 100644 (file)
@@ -14,7 +14,7 @@ import java.util.*;
  *
  *  Note that the members in the section 'state variables' are either
  *  in real-time (the actual size/position/state), or in
- *  MessageQueue-time (the size/position/state at the time that the
+ *  Scheduler-time (the size/position/state at the time that the
  *  now-executing message was enqueued). This distinction is important.
  */
 // FIXME: put the scar box back in
@@ -25,10 +25,16 @@ public abstract class Surface extends PixelBuffer {
         
     // Static Data ////////////////////////////////////////////////////////////////////////////////
 
+    // FIXME
+    private abstract static class Message extends Scheduler.Task {
+        public abstract void perform();
+        public Object call(Object arg) { perform(); return null; }
+    }
+
     /**< the most recently enqueued Move message; used to throttle the message rate */
     private static Message lastMoveMessage = null;
 
-    /** all instances of Surface which need to be refreshed by the MessageQueue */
+    /** 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 */
@@ -37,9 +43,9 @@ public abstract class Surface extends PixelBuffer {
     public static boolean alt = false;          ///< true iff the alt button is pressed down, in real time
     public static boolean control = false;      ///< true iff the control button is pressed down, in real time
     public static boolean shift = false;        ///< true iff the shift button is pressed down, in real time
-    public static boolean button1 = false;      ///< true iff button 1 is depressed, in MessageQueue-time
-    public static boolean button2 = false;      ///< true iff button 2 is depressed, in MessageQueue-time
-    public static boolean button3 = false;      ///< true iff button 3 is depressed, in MessageQueue-time
+    public static boolean button1 = false;      ///< true iff button 1 is depressed, in Scheduler-time
+    public static boolean button2 = false;      ///< true iff button 2 is depressed, in Scheduler-time
+    public static boolean button3 = false;      ///< true iff button 3 is depressed, in Scheduler-time
 
      
 
@@ -48,8 +54,8 @@ public abstract class Surface extends PixelBuffer {
     public Box root;      /**< The Box at the root of this surface */
     public String cursor = "default";
 
-    public int mousex;                    ///< the x position of the mouse, relative to this Surface, in MessageQueue-time
-    public int mousey;                    ///< the y position of the mouse, relative to this Surface, in MessageQueue-time
+    public int mousex;                    ///< the x position of the mouse, relative to this Surface, in Scheduler-time
+    public int mousey;                    ///< the y position of the mouse, relative to this Surface, in Scheduler-time
     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
 
@@ -103,7 +109,7 @@ public abstract class Surface extends PixelBuffer {
         else if (button == 2) new SimpleMessage("Press2", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
         else if (button == 3) {
             final Box who = Box.whoIs(root, mousex, mousey);
-            Message.Q.add(new Message() { public void perform() {
+            Scheduler.add(new Message() { public void perform() {
                 Platform.clipboardReadEnabled = true;
                 root.put("Press3", Boolean.TRUE);
                 Platform.clipboardReadEnabled = false;
@@ -157,11 +163,11 @@ public abstract class Surface extends PixelBuffer {
         else if (control) key = "C-" + key;
 
         final String fkey = key;
-        Message.Q.add(new KMessage(key));
+        Scheduler.add(new KMessage(key));
     }
 
     // This is implemented as a private static class instead of an anonymous class to work around a GCJ bug
-    private class KMessage implements Message {
+    private class KMessage extends Message {
         String key = null;
         public KMessage(String k) { key = k; }
         public void perform() {
@@ -184,7 +190,7 @@ public abstract class Surface extends PixelBuffer {
         if (key.toLowerCase().equals("alt")) alt = false;
         else if (key.toLowerCase().equals("control")) control = false;
         else if (key.toLowerCase().equals("shift")) shift = false;
-        Message.Q.add(new Message() { public void perform() {
+        Scheduler.add(new Message() { public void perform() {
             /* FIXME
             outer: for(int i=0; i<keywatchers.size(); i++) {
                 Box b = (Box)keywatchers.elementAt(i);
@@ -203,7 +209,7 @@ public abstract class Surface extends PixelBuffer {
      *  message), the subclass should use (-1,-1).
      */
     protected final void Move(final int newmousex, final int newmousey) {
-        Message.Q.add(lastMoveMessage = new Message() { public void perform() {
+        Scheduler.add(lastMoveMessage = new Message() { public void perform() {
             synchronized(Surface.this) {
 
                 // if move messages are arriving faster than we can process them, we just start ignoring them
@@ -228,7 +234,7 @@ public abstract class Surface extends PixelBuffer {
     }
 
     protected final void SizeChange(final int width, final int height) {
-        Message.Q.add(new Message() { public void perform() {
+        Scheduler.add(new Message() { public void perform() {
             if (width == root.width && height == root.height) return;
             root.needs_reflow = true;
             do { abort = false; root.reflow(width, height); } while(abort);
@@ -237,7 +243,7 @@ public abstract class Surface extends PixelBuffer {
     }
 
     protected final void PosChange(final int x, final int y) {
-        Message.Q.add(new Message() { public void perform() {
+        Scheduler.add(new Message() { public void perform() {
             root.x = x;
             root.y = y;
             root.put("PosChange", Boolean.TRUE);
@@ -248,7 +254,12 @@ public abstract class Surface extends PixelBuffer {
     protected final void Minimized(boolean b) { minimized = b; new SimpleMessage("Minimized", b ? Boolean.TRUE : Boolean.FALSE, root); }
     protected final void Maximized(boolean b) { maximized = b; new SimpleMessage("Maximized", b ? Boolean.TRUE : Boolean.FALSE, root); }
     protected final void Focused(boolean b) { new SimpleMessage("Focused", b ? Boolean.TRUE : Boolean.FALSE, root); }
-    public static void Refresh() { Message.Q.refresh(); }
+    public static void Refresh() {
+        Scheduler.add(new Scheduler.Task() { public Object call(Object arg) {
+                for(int i=0; i<allSurfaces.size(); i++)
+                    ((Surface)allSurfaces.elementAt(i)).render();
+                return null;
+        }}); }
 
     public final void setMaximized(boolean b) { if (b != maximized) _setMaximized(maximized = b); }
     public final void setMinimized(boolean b) { if (b != minimized) _setMinimized(minimized = b); }
@@ -337,7 +348,7 @@ public abstract class Surface extends PixelBuffer {
     }
 
     // FEATURE: reinstate recycler
-    public class SimpleMessage implements Message {
+    public class SimpleMessage extends Message {
         
         private Box boxContainingMouse;
         private Object value;
@@ -347,7 +358,7 @@ public abstract class Surface extends PixelBuffer {
             this.boxContainingMouse = boxContainingMouse;
             this.name = name;
             this.value = value;
-            Message.Q.add(this);
+            Scheduler.add(this);
         }
         
         public void perform() { boxContainingMouse.put(name, value); }
index e7e72b5..1b2d9b6 100644 (file)
@@ -97,7 +97,7 @@ public class Template {
         if (staticscript == null) return staticScope;
         JS.CompiledFunction temp = staticscript;
         staticscript = null;
-        temp.call(new JS.Array(), staticScope);
+        new JS.Thread(temp, staticScope).resume();
         return staticScope;
     }
     
@@ -124,7 +124,7 @@ public class Template {
             b.put(b.numChildren(), kid);
         }
 
-        if (script != null) script.call(new JS.Array(), pis);
+        if (script != null) new JS.Thread(script, pis).resume();
 
         for(int i=0; keys != null && i<keys.length; i++)
             if (vals[i] instanceof String && ((String)vals[i]).charAt(0) == '$') b.put(keys[i], pis.get(vals[i]));
@@ -354,13 +354,11 @@ public class Template {
             if (super.has(key)) return super.get(key);
             if (key.equals("xwt")) return xwt;
             if (key.equals("static")) return myStatic;
-            if (Box.SpecialBoxProperty.specialBoxProperties.get(key.toString()) != null) return getParentScope().get(key);
-            throw new JS.Exn("must declare " + key + " before using it!");
+            return super.get(key);
         }
         public void put(Object key, Object val) {
             if (super.has(key)) super.put(key, val);
-            else if (Box.SpecialBoxProperty.specialBoxProperties.get(key.toString()) != null) getParentScope().put(key, val);
-            else throw new JS.Exn("must declare " + key + " before using it!");
+            super.put(key, val);
         }
     }
 
diff --git a/src/org/xwt/ThreadMessage.java b/src/org/xwt/ThreadMessage.java
deleted file mode 100644 (file)
index 0eeefef..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
-package org.xwt;
-
-import java.util.*;
-import org.xwt.util.*;
-import org.xwt.js.*;
-
-/**
- *  A background thread. All threads created with <tt>xwt.thread</tt>
- *  are instances of this class. ThreadMessage objects can be enqueued
- *  in MessageQueue.
- *
- *  INVARIANT: only one thread can be executing JavaScript scripts or
- *             accessing Box instances at any given time. For
- *             performance reasons, no locks are employed to enforce
- *             this invariant.
- */
-public class ThreadMessage extends Thread implements Message {
-
-    private volatile static int threadcount = 0;
-
-    /** the JavaScript function that we are executing */
-    volatile JS.Callable f;
-
-    /** the ThreadMessage thread blocks on this before executing any JavaScript */
-    Semaphore go = new Semaphore();
-
-    /** The Message.Q (main) thread blocks on this while the ThreadMessage thread is running JavaScript code */
-    Semaphore done = new Semaphore();
-
-    /** used to pool ThreadMessages that are not in use */
-    private static Queue spare = new Queue(50);
-
-    /** queue of functions waiting to be spawned; used if threadcount == Platform.maxThreads() */
-    private static Queue waiting = new Queue(50);
-
-    private ThreadMessage() { start(); }
-    private static Object[] emptyobj = new Object[] { };
-
-    /** creates a new thread to execute function <tt>f</tt> */
-    public static synchronized void newthread(JS.Callable f) {
-        ThreadMessage ret = (ThreadMessage)spare.remove(false);
-        if (ret == null) {
-            if (threadcount < Platform.maxThreads()) ret = new ThreadMessage();
-            else {
-                waiting.append(f);
-                return;
-            }
-        }
-        ret.f = f;
-        Message.Q.add(ret);
-    }
-
-    /** attempts to put this thread into the background to perform a blocking operation; returns false if unable to do so */
-    public static boolean suspendThread() {
-        // put ourselves in the background
-        Thread thread = Thread.currentThread();
-        if (!(thread instanceof ThreadMessage)) {
-            if (Log.on) Log.logJS(ThreadMessage.class, "attempt to perform background-only operation in a foreground thread");
-            return false;
-        }
-        ThreadMessage mythread = (ThreadMessage)thread;
-        mythread.setPriority(Thread.MIN_PRIORITY);
-        mythread.done.release();
-        return true;
-    }
-
-    /** re-enqueues this thread */
-    public static void resumeThread() {
-        ThreadMessage mythread = (ThreadMessage)Thread.currentThread();
-        Message.Q.add(mythread);
-        mythread.setPriority(Thread.NORM_PRIORITY);
-        mythread.go.block();        
-    }
-    
-    public void run() {
-        try {
-            threadcount++;
-            while (true) {
-                try {
-                    go.block();
-                    f.call(new JS.Array());
-                } catch (JS.Exn e) {
-                    if (Log.on) Log.log(this, "WARNING: uncaught ecmascript exception: " + e);
-                }
-                done.release();
-                synchronized(waiting) {
-                    if (waiting.size() > 0) {
-                        f = (JS.Callable)waiting.remove(false);
-                        Message.Q.add(this);
-                    } else if (spare.size() < 10) {
-                        spare.append(this);
-                    } else {
-                        threadcount--;
-                        return;
-                    }
-                }
-            }
-        } catch (Throwable t) {
-            if (Log.on) Log.log(this, "caught throwable at thread entry point; this should never happen");
-            if (Log.on) Log.log(this, t);
-            if (Log.on) Log.log(this, "reaping thread");
-            return;
-        }
-    }
-
-    /** this is invoked in the Message.Q thread */
-    public void perform() {
-        go.release();
-        done.block();
-    }
-
-}
index df51b53..2895d9f 100644 (file)
@@ -96,29 +96,31 @@ public class Trap {
 
     private Trap() { }
 
-    public Object perform() throws JS.Exn {
+    public Object perform() {
         try {
             if (f.getNumFormalArgs() > 0) return cascade();
-            return f.call(new TrapArgs(this));
+            JS.Thread.current().setTailCall(f, new TrapArgs(this));
+            return null;
         } catch (Exception e) {
             Log.log(this, "Exception thrown from within trap: " + e);
             return null;
         }
     }
     
-    public void perform(Object val) throws JS.Exn {
+    public void perform(Object val) {
         try {
             if (f.getNumFormalArgs() == 0) cascade(val);
             TrapArgs ta = new TrapArgs(this, val);
-            Object ret = f.call(ta);
-            if (ret != Boolean.FALSE && !ta.cascadeHappened) cascade(val);
+            JS.Thread.current().setTailCall(f, ta);
+            // FIXME: re-add autocascades
+            //if (ret != Boolean.FALSE && !ta.cascadeHappened) cascade(val);
         } catch (Exception e) {
             Log.log(this, "Exception thrown from within trap: " + e);
         }
     }
     
     public Object cascade() {
-        if (next != null) return next.perform();
+        if (next != null) { next.perform(); return null; }
         return trapee.get(name, true);
     }
 
index 7325476..ac83249 100644 (file)
@@ -304,8 +304,8 @@ class XMLRPC extends JS.Callable {
         InputStream is = http.POST("text/xml", content);
         try {
             BufferedReader br = !Log.verbose ?
-                new BufferedReader(new InputStreamReader(new Filter(is))) :
-                new BufferedReader(new FilterReader(new InputStreamReader(new Filter(is))) {
+                new BufferedReader(new InputStreamReader(is)) :
+                new BufferedReader(new FilterReader(new InputStreamReader(is)) {
                         public int read() throws IOException {
                             int i = super.read();
                             if (Log.on) Log.log(this, "recv: " + ((char)i));
@@ -360,9 +360,6 @@ class XMLRPC extends JS.Callable {
     }
 
     public final Object call(JS.Array args) throws JS.Exn {
-
-        if (!ThreadMessage.suspendThread()) return null;
-
         try {
             return call2(args);
         } catch (IOException se) {
@@ -372,10 +369,7 @@ class XMLRPC extends JS.Callable {
         } catch (JS.Exn jse) {
             if (Log.on) Log.log(this, jse.toString());
             throw jse;
-        } finally {
-            ThreadMessage.resumeThread();
         }
-
     }
 
     /** When you get a property from an XMLRPC, it just returns another XMLRPC with the property name tacked onto methodname. */
@@ -402,24 +396,4 @@ class XMLRPC extends JS.Callable {
         public AccessibleCharArrayWriter(int i) { super(i); }
     }
 
-    /** private filter class to make sure that network transfers don't interfere with UI responsiveness */
-    private static class Filter extends FilterInputStream {
-        public Filter(InputStream is) { super(is); }
-        public int read() throws IOException {
-            java.lang.Thread.yield();
-            while(Message.Q.nonThreadEventsInQueue > 0) try { java.lang.Thread.sleep(100); } catch (Exception e) { };
-            return super.read();
-        }
-        public int read(byte[] b) throws IOException {
-            java.lang.Thread.yield();
-            while(Message.Q.nonThreadEventsInQueue > 0) try { java.lang.Thread.sleep(100); } catch (Exception e) { };
-            return super.read(b);
-        }
-        public int read(byte[] b, int i, int j) throws IOException {
-            java.lang.Thread.yield();
-            while(Message.Q.nonThreadEventsInQueue > 0) try { java.lang.Thread.sleep(100); } catch (Exception e) { };
-            return super.read(b, i, j);
-        }
-    }
-
 }
index 2d4ef45..c5f7a13 100644 (file)
@@ -50,9 +50,13 @@ public final class XWT extends JS.Obj {
         else return rr.get(name);
     }
 
-    public void put(Object name, Object value) {
-        if (name.equals("thread") && value != null && value instanceof JS.Callable) ThreadMessage.newthread((JS.Callable)value);
-        else if (name.equals("clipboard")) Platform.setClipBoard(value.toString());
+    public void put(Object name, final Object value) {
+        if (name.equals("thread") && value != null && (value instanceof JS.Callable || value instanceof JS.CompiledFunction)) {
+            Scheduler.add(new Scheduler.Task() { public Object call(Object arg) {
+                new JS.Thread((CompiledFunction)value).resume();
+                return null;
+            } });
+        } else if (name.equals("clipboard")) Platform.setClipBoard(value.toString());
         else if (name.equals("frame")) Platform.createSurface((Box)value, true, true);
         else if (name.equals("window")) Platform.createSurface((Box)value, false, true);
         else if (name.equals("proxyAuthorization")) {
@@ -88,7 +92,7 @@ public final class XWT extends JS.Obj {
 
         } else if (method.equals("watchProgress")) {
             if (checkOnly) return Boolean.TRUE;
-            return new Res.ProgressWatcher((Res)args.elementAt(0), (JS.Callable)args.elementAt(1));
+            return new Res.ProgressWatcher((Res)args.elementAt(0), (JS.CompiledFunction)args.elementAt(1));
 
         } else if (method.equals("yield")) {
             if (checkOnly) return Boolean.TRUE;
@@ -231,20 +235,22 @@ public final class XWT extends JS.Obj {
         }
     }
 
-    public static void sleep(int i) {
-        java.lang.Thread thread = java.lang.Thread.currentThread();
-        if (!(thread instanceof ThreadMessage)) {
-            if (Log.on) Log.log(XWT.class, "cannot sleep() or yield() in the foreground thread");
-        } else {
-            ThreadMessage mythread = (ThreadMessage)thread;
-            mythread.done.release();
-            if (i > 0) try { java.lang.Thread.sleep(i); } catch (Exception e) { }
-            Message.Q.add(mythread);
-            mythread.go.block();
-        }
+    public static void sleep(final int i) {
+        final JS.Thread jsthread = JS.Thread.current();
+        final long currentTime = System.currentTimeMillis();
+        final Scheduler.Task task = new Scheduler.Task() { public Object call(Object arg) {
+            if (System.currentTimeMillis() - currentTime < i) {
+                Scheduler.add(this);
+            } else {
+                jsthread.resume();
+            }
+            return null;
+        } };
+        jsthread.pause();
+        Scheduler.add(task);
     }
     
-    private static class XWTMath extends JS.Obj {
+    private static class XWTMath extends org.xwt.js.Math {
         public XWTMath() {
             JS gs = new JS.GlobalScope();
             put("isNaN",gs.get("isNaN"));
index fa07d95..6f44cca 100644 (file)
             var new_xwt = xwt.clone(new_rr);
 
             xwt.thread = function() {
-for(var i=0; 100>i; i++) { progress(i, 100); xwt.sleep(100); }
-                xwt.yield();
+for(var i=0; 100>i; i++) {
+progress(i, 100);
+xwt.yield();
+}
                 new_xwt.apply(xwt.box, new_xwt["main.xwt"]);
                 thisbox = null;
             }
         }
 
-        <box height="236" align="bottomleft" packed="false" x="20" y="0">
+        <box height="233" align="bottomleft" packed="false" x="20" y="0">
             <box textcolor="white" id="text" shrink="true"/>
         </box>
         <box packed="false" id="bar" x="20" y="236" width="354" height="20" align="left">