2003/09/05 04:28:35
[org.ibex.core.git] / src / org / xwt / plat / Carbon.java
index 5dc019f..c6ec350 100644 (file)
-// Copyright 2002 Adam Megacz, see the COPYING file for licensing [LGPL]
+// Copyright 2003 Adam Megacz, see the COPYING file for licensing [LGPL]
+// Authors: Brian Alliet and Evan Jones
+
 package org.xwt.plat;
 
 import gnu.gcj.RawData;
-import java.net.*;
-import java.lang.reflect.*;
-import java.io.*;
-import java.util.*;
 import org.xwt.util.*;
 import org.xwt.*;
+import java.util.*;
 
-/** Platform implementation for Carbon UI on a POSIX-compliant OS (ie Mac OS X) */
 public class Carbon extends POSIX {
-
-        /** hashtable of all OS X fonts; key is an XWT font name, value is WrappedRawData which stores an ATSFontRef.
-        *       Initialized by natInit(). */
-        static Hashtable nativeFontCache = new Hashtable();
-
-        /** Cache of ATSUStyle objects; key is an XWT font spec, value is WrappedRawData which stores an ATSUStyle.
-        * According to an Apple technote, caching the style bjects can really increase performance. */
-        static Hashtable atsuStyleCache = new Hashtable();
-
-        /** List of all XWT font specs. Initialized by init(). */
-        static String[] fontList = null;
-
-    // General Methods ///////////////////////////////////////////////////////
-
-    protected String _getAltKeyName() { return "option"; }
-    protected String[] _listFonts() { return fontList; }
-    protected Picture _createPicture(int[] data, int w, int h) { return new CarbonPicture(data, w, h); }
-    protected DoubleBuffer _createDoubleBuffer(int w, int h, Surface owner) { return new CarbonDoubleBuffer(w, h); }
-    protected Surface _createSurface(Box b, boolean framed) { return new CarbonSurface(b, framed); }
+    static Carbon singleton;
+    private CarbonOpenGL openGL;
+    boolean jaguar; // true if we are on OS X >= 10.2
+    
+    // TEMPORARY HACKS (remove these when we ditch platform fonts)
+    protected int _stringWidth(String font, String text) { return (int)Math.round(6.5 * text.length()); }
+    protected int _getMaxAscent(String font) { return 10; }
+    protected int _getMaxDescent(String font) { return 2; }
+    
+    // General Methods
+    protected String _getAltKeyName() { return "Option"; }
     protected boolean _needsAutoClick() { return false; }
-    protected native int _getScreenWidth();
-    protected native int _getScreenHeight();
-    protected native String _getClipBoard();
-    protected native void _setClipBoard(String s);
-        static String defaultFontName = "lucida_grande";
-    protected String _getDefaultFont() { return defaultFontName + "13"; }
-        protected native int _stringWidth(String fontSpec, String text);
-    protected native int _getMaxAscent(String font);
-    protected native int _getMaxDescent(String font);
     protected boolean _needsAutoDoubleClick() { return false; }
+    protected String getDescriptiveName() { return "GCJ Carbon Binary"; }
+    protected boolean _isCaseSensitive() { return false; /* Well, not always, could be UFS */ }
+    
+    
+    // Native Methods
+    protected int    _getScreenWidth() { return cgScreenWidth(); }
+    protected int    _getScreenHeight() { return cgScreenHeight(); }
+    private native static int cgScreenWidth();
+    private native static int cgScreenHeight();
+    protected native void   _newBrowserWindow(String url);
+    protected native Proxy   natDetectProxy();
+    private   native void    natInit();
+    protected native void   _exit();
+
+    private native String natGetClipBoard();
+    private native void natSetClipBoard(String text);
+    protected void _setClipBoard(final String text) { CarbonMessage.add(new CarbonMessage() { public void perform() { natSetClipBoard(text); } }); }
+    protected String _getClipBoard() {
+        final Semaphore sem = new Semaphore();
+        final String[] result = new String[1]; // Kind of like a pointer
+        CarbonMessage.add(new CarbonMessage() { public void perform() { result[0] = natGetClipBoard(); sem.release(); } });
+        sem.block();
+        return result[0];
+    }
+    
+    private static class FileDialogHelper {
+        public FileDialogHelper(boolean save) { this.save = save; }
+        public boolean save;
+        public Semaphore sem = new Semaphore();
+        public String fileName;
+        public String saveName;
+        public RawData rawUPP;
+    }
+    private native void natFileDialog(FileDialogHelper helper, String suggestedFileName, boolean write);
+    protected String _fileDialog(final String fn, final boolean w) {
+        final FileDialogHelper helper = new FileDialogHelper(w);
+        CarbonMessage.add(new CarbonMessage() { public void perform() { natFileDialog(helper,fn,w); } });
+        helper.sem.block();
+        if(w)
+            return helper.fileName + "/" + helper.saveName;
+        else
+            return helper.fileName;
+    }
 
-        /** Returns the ATSUStyle associated with the given XWT font spec.
-        *       This method first checks its internal cache before creating the
-        *       ATSUStyle object from scratch. */
-        protected RawData _getATSUStyle( String fontSpec ) {
-                WrappedRawData ret = null;
-                ret = (WrappedRawData) atsuStyleCache.get( fontSpec );
-                if (ret != null) return ret.wrapee;
-
-                Platform.ParsedFont pf = new Platform.ParsedFont( fontSpec );
-
-                // Find the font
-                if (pf.name.equals("serif")) pf.name = "lucida_grande";
-                else if (pf.name.equals("sansserif")) pf.name = "helvetica";
-                else if (pf.name.equals("monospace")) pf.name = "courier";
-                else if (pf.name.equals("dialog")) pf.name = "lucida_grande";
-                else if (pf.name.equals("tty")) pf.name = "courier";
-
-                // Find the ATSFontRef
-                WrappedRawData fontRef = (WrappedRawData) nativeFontCache.get( pf.name );
-                // If we couldn't find the font, use the default font
-                if ( fontRef == null ) fontRef = (WrappedRawData) nativeFontCache.get( defaultFontName );
-                if ( fontRef == null ) throw new Error( "Default font cannot be found" );
-                                
-                // Create the ATSUStyle object
-                ret = new WrappedRawData( _createATSUStyle( fontRef.wrapee, pf.size, pf.bold, pf.italic, pf.underline ) );
-
-                // Map this font spec to the ATSFontRef to optimize future requests
-                atsuStyleCache.put( fontSpec, ret );
-
-        return ret.wrapee;
+    
+    // Called by main thread after initialization, this is the event handler
+    protected native void _running();
+    
+    static void abort(String err) {
+        throw new Error(err);
+    }
+    
+    public Carbon() {
+        synchronized(Carbon.class) {
+            if(singleton != null) abort("Tried to instansiate Carbon more than once");
+            singleton = this;
         }
-
-        /** Creates an ATSUStyle object with the specified attributes. */
-        protected native RawData _createATSUStyle( RawData fontRef, int fontSize, boolean isBold, boolean isItalic, boolean isUnderline );
-
-        /** Called once XWT is initialized and the application is running. On Mac OS X this calls
-        *       RunApplicationEventLoop(). */
-        protected native void _running();
-        
-        /** dumps a list of Mac OS X font strings. TODO: Will this be sufficient? */
-    //private native String[] listNativeFonts();
-        /** translates a font string into an ATSUFontRef? TODO: Will this be sufficient? */
-    //public static native gnu.gcj.RawData fontStringToStruct(String s);
-
-    public Carbon() { }
-
+    }
+    
+    protected synchronized Proxy _detectProxy() {
+        return natDetectProxy();
+    }
+    
+    private static native final boolean isJaguar();
+    
     public void init() {
-                natInit();
+        super.init();
+        jaguar = isJaguar();
+        try {
+            openGL = new CarbonOpenGL();
+            openGL.init();
+        } catch(OpenGL.NotSupportedException e) {
+            Log.log(this,"WARNING: OpenGL support not available: " + e);
+            // FIXME: We need to fallback to Quartz2D
+            throw new Error("No OpenGL support");
+        }
+        natInit();
+    }
+    
+    private final class CarbonOpenGL extends OpenGL {
+        public RawData rawPixelFormat;
+        public RawData rawSharedContext;
+        public int maxAglSurfaceTexSize;
+        public int maxSurfaceWidth;
+        public int maxSurfaceHeight;
+        
+        private native boolean initPixelFormat();
+        private native void initSharedContext();
+        
+        public CarbonOpenGL() throws NotSupportedException {
+            if(!jaguar)
+                throw new NotSupportedException("OpenGL requires Mac OS X 10.2 or greater");
+            if(!initPixelFormat())
+                throw new NotSupportedException("Couldn't get an acceptable pixel format");
+            initSharedContext();
+        }
+        
+        public void init() throws NotSupportedException {
+            super.init();
+            maxAglSurfaceTexSize = rectangularTextures ? maxRectTexSize : maxTexSize;
+            if(renderer.startsWith("ATI Radeon 7500")) {
+                maxAglSurfaceTexSize = Math.min(rectangularTextures ? 1600 : 1024,maxAglSurfaceTexSize);
+                Log.log(this,"Working around Radeon 7500 bug: maxAglSurfaceTexSize: " + maxAglSurfaceTexSize);
+            }
+            maxSurfaceWidth = maxSurfaceHeight = maxAglSurfaceTexSize;
+        }
+        protected native void activateSharedContext();
+    }
+    
+    static abstract class CarbonSurface extends Surface {  
+        RawData rawWindowRef;
+        RawData rawEventHandlerUPP;
+        int modifiers;
+         
+        private native void natSetInvisible(boolean i);
+        public void setInvisible(final boolean i) { CarbonMessage.add(new CarbonMessage() { public void perform() { natSetInvisible(i); } }); }
+        private native void nat_setMaximized(boolean b);
+        public void _setMaximized(final boolean b) { CarbonMessage.add(new CarbonMessage() { public void perform() { nat_setMaximized(b); } }); }
+        private native void nat_setMinimized(boolean b);
+        public void _setMinimized(final boolean b) { CarbonMessage.add(new CarbonMessage() { public void perform() { nat_setMinimized(b); } }); }
+        private native void natSetIcon(Picture p);
+        public void setIcon(final Picture p) { CarbonMessage.add(new CarbonMessage() { public void perform() { natSetIcon(p); } }); }
+        private native void natSetTitleBarText(String s);
+        public void setTitleBarText(final String s) { CarbonMessage.add(new CarbonMessage() { public void perform() { natSetTitleBarText(s); } }); }
+        private native void natSetSize(int w, int h);
+        public void setSize(final int w, final int h) { CarbonMessage.add(new CarbonMessage() { public void perform() { natSetSize(w,h); } }); }
+        private native void natSetLocation(int x, int y);
+        public void setLocation(final int x, final int y) { CarbonMessage.add(new CarbonMessage() { public void perform() { natSetLocation(x,y); } }); }
+        private native void natToFront();
+        public void toFront() { CarbonMessage.add(new CarbonMessage() { public void perform() { natToFront(); } }); }
+        private native void natToBack();
+        public void toBack() { CarbonMessage.add(new CarbonMessage() { public void perform() { natToBack(); } }); }
+        private native void natSetLimits(int minWidth, int minHeight, int maxWidth, int maxHeight);
+        public void setLimits(final int mnw, final int mnh, final int mxw, final int mxh) {
+            if(Carbon.singleton.jaguar)
+                CarbonMessage.add(new CarbonMessage() { public void perform() { natSetLimits(mnw,mnh,mxw,mxh); } });
+        }
+        private native void natSyncCursor(int n);
+        public void syncCursor() {
+            int n;
+            if(cursor.equals("default")) n = 0;
+            else if(cursor.equals("wait")) n = 1;
+            else if(cursor.equals("crosshair")) n = 2;
+            else if(cursor.equals("text")) n = 3;
+            else if(cursor.equals("hand")) n = 4;
+            else if(cursor.equals("move")) n = 5;
+            else if(cursor.equals("east") || cursor.equals("west")) n = 6;
+            else n = 0; 
+            final int n_ = n;
+            CarbonMessage.add(new CarbonMessage() { public void perform() { natSyncCursor(n_); } });
+        }
 
-                // nativeFontCache contains font NAMES. Each font exists as an outline font
-                // which can be any size, plus can have real or simulated bold or italic
-        fontList = new String[nativeFontCache.size()*4];
-        Enumeration e = nativeFontCache.keys();
-        for(int i=0; e.hasMoreElements(); i+=4) {
-                        String fontName = (String)e.nextElement() + "0";
-                        
-                        fontList[i] = fontName;
-                        fontList[i+1] = fontName + "i";
-                        fontList[i+2] = fontName + "b";
-                        fontList[i+3] = fontName + "bi";
-                }
+        public void _sizeChange(int w, int h) { SizeChange(w,h); }
+        
+        /* Drawing stuff */
+        public abstract void blit(DoubleBuffer s, int sx, int sy, int dx, int dy, int dx2, int dy2);
 
-                // Make sure that the default font exists
-                if ( _getATSUStyle( _getDefaultFont() ) == null ) throw new Error( "Default font does not exist" );
+        public final void _dispose() { CarbonMessage.add(new CarbonMessage() { public void perform() { natDispose(); } }); }
+        public native void natDispose();
+        
+        public final native void natInit(boolean framed);
+        
+        public CarbonSurface(Box root, final boolean framed) {
+            super(root);
+            final Semaphore sem = new Semaphore();
+            CarbonMessage.add(new CarbonMessage() { public void perform() { CarbonSurface.this.natInit(framed); sem.release(); } });
+            sem.block();
         }
+        
+        public void reshape(int w, int h) { }
+    }
+    
+    static class GLCarbonDoubleBuffer extends OpenGL.GLDoubleBuffer {
+        RawData rawCTX;
+        RawData rawWindowRef;
+        int textureName;
+        boolean rectTexture;
+        CarbonOpenGL gl;
+        
         private native void natInit();
-
-        /** so we can put ATSUStyles and ATSFontRefs into Hashtables */
-    private static class WrappedRawData {
-        public RawData wrapee = null;
-        public WrappedRawData(RawData r) { wrapee = r; }
+        private static native void natCleanup(RawData rawWindowRef, RawData rawCTX);
+        
+        
+        private static final int fixupDimension(CarbonOpenGL gl, int n) {
+            if(!gl.rectangularTextures) n = OpenGL.roundToPowerOf2(n);
+            return Math.min(n,gl.maxAglSurfaceTexSize);
+        }
+        public GLCarbonDoubleBuffer(int w, int h, final CarbonOpenGL gl) {
+            super(fixupDimension(gl,w),fixupDimension(gl,h));
+            this.gl = gl;
+            rectTexture = gl.hasRectangularTextures();
+            final Semaphore sem = new Semaphore();
+            CarbonMessage.add(new CarbonMessage() { public void perform() { GLCarbonDoubleBuffer.this.natInit(); sem.release(); } });
+            sem.block();
+        }
+        public native void activateContext();
+        protected void finalize() {
+            CarbonMessage.add(new CarbonMessage() { public void perform() { natCleanup(rawWindowRef,rawCTX); } });
+            gl.deleteTexture(textureName);
+        }
+    }
+    
+    static class GLCarbonSurface extends CarbonSurface {
+        RawData rawCTX;
+        CarbonOpenGL gl;
+        boolean sizeChange;
+        
+        private final native void natInit();
+        
+        public GLCarbonSurface(Box root, boolean framed, CarbonOpenGL gl) {
+            super(root,framed);
+            this.gl = gl;
+            natInit();
+        }
+        
+        public void setLimits(int mnw,int mnh, int mxw, int mxh) {
+            mxw = Math.min(mxw,gl.maxSurfaceWidth);
+            mxh = Math.min(mxh,gl.maxSurfaceHeight);
+            super.setLimits(mnw,mnh,mxw,mxh);
+        }
+        public void _sizeChange(int w, int h) {
+            sizeChange = true;
+            super._sizeChange(w,h);
+        }
+        
+        public void setSize(int w, int h) {
+            sizeChange = true;
+            w = Math.min(w,gl.maxSurfaceWidth);
+            h = Math.min(h,gl.maxSurfaceWidth);
+            super.setSize(w,h);
+        }
+        
+        private native void natBlit(GLCarbonDoubleBuffer db, int sx, int sy, int dx, int dy, int dx2, int dy2);
+        public synchronized void blit(DoubleBuffer db, int sx, int sy, int dx, int dy, int dx2, int dy2) {
+            natBlit((GLCarbonDoubleBuffer)db,sx,sy,dx,dy,dx2,dy2);
+        }
+        
+        private native void natReshape(int w, int h);
+        public synchronized void reshape(int w, int h) { natReshape(w,h); }
+        
+        public native void natDispose();
     }
 
-    // CarbonSurface /////////////////////////////////////////////////////
-
-    /** Implements a Surface as an Carbon Window */
-    public static class CarbonSurface extends Surface {
-
-                /** The WindowRef that implements this Surface. */
-                gnu.gcj.RawData window = null;
-                /** The CGContextRef. TODO: How do we get this??? */
-        gnu.gcj.RawData gc = null;
-        
-        public native void setInvisible(boolean i);
-        public native void _setMaximized(boolean m);
-        public native void setIcon(Picture p);
-        public native void _setMinimized(boolean b);
-        public native void setTitleBarText(String s);
-        public native void setSize(int w, int h);
-        public native void setLocation(int x, int y);
-        public native void natInit(boolean framed);
-        public native void toFront();
-        public native void toBack();
-        public native void syncCursor();
-        public native void _dispose();
-        //public native void setLimits(int minw, int minh, int maxw, int maxh);
+    /*private class QZCarbonDoubleBuffer extends DoubleBuffer {
+        
+        public QZCarbonDoubleBuffer(int width, int height) {
+        }
+    }
+    
+    private class QZCarbonSurface extends CarbonSurface {
+        public QZCarbonSurface(Box root, boolean framed) {
+            super(b,root);
+        }
         public native void blit(DoubleBuffer s, int sx, int sy, int dx, int dy, int dx2, int dy2);
-
-        public CarbonSurface(Box root, boolean framed) { super(root); natInit( framed ); }
-
     }
-
-
-    // Our Subclass of Picture ///////////////////////////////////////////////
-
-    /** Implements a Picture */
-    public static class CarbonPicture extends Picture {
+    
+    private class QZCarbonPicture extends Picture {
         int width;
         int height;
-        int[] data = null;
-
-                /** A CGImageRef of the picture. */
-                RawData image = null;
-                
-        public int getWidth() { return width; }
-        public int getHeight() { return height; }
-
-                public native void natInit();
-                public native void finalize();
+        
+        public final int getWidth() { return width; }
+        public final int getHeight() { return height; }
                 
-        public CarbonPicture(int[] data, int w, int h) {
-            this.data = data;
+        public QZCarbonPicture(int w, int h) {
             this.width = w;
             this.height = h;
-                        natInit();
         }
-
+    }*/
+    
+    protected DoubleBuffer _createDoubleBuffer(int w, int h, Surface owner) {
+        if(openGL != null)
+            return new GLCarbonDoubleBuffer(w,h,openGL);
+        else
+            return /*new QZCarbonDoubleBuffer(w,h)*/ null;
     }
-
-    /** A Carbon DoubleBuffer */
-    public static class CarbonDoubleBuffer extends DoubleBuffer {
-        int width;
-        int height;
-
-                /** A pointer to the raw bitmap data. */
-                RawData bitmapData;
-                /** A CGBitmapContextRef. */
-                RawData gc;
-                /** A CGImageRef which represents the CGBitmapContext. */
-                RawData image;
-                
-        public int getWidth() { return width; }
-        public int getHeight() { return height; }
-
-        public CarbonDoubleBuffer(int w, int h) {
-                        this.width = w;
-                        this.height = h;
-                        natInit();
-                }
-
-        public native void setClip(int x, int y, int x2, int y2);
-        public native void drawPicture(Picture source, int x, int y);
-        public native void drawPicture(Picture source, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2);
-        public native void fillRect(int x, int y, int x2, int y2, int color);
-        public native void drawString(String font, String text, int x, int y, int color);
-                public native void natInit();
-        public native void finalize();
+    protected Surface _createSurface(Box b, boolean framed) {
+        if(openGL != null)
+            return new GLCarbonSurface(b,framed, openGL);
+        else
+            return /*new QZCarbonSufrace(b,framed)*/ null;
+    }
+    protected Picture _createPicture(int[] data, int w, int h) {
+        if(openGL != null)
+            return openGL.createPicture(data,w,h);
+        else
+            return /*new QZCarbonPicture(data,w,h);*/ null;
+    }
+    
+    /* A message that is sent through the carbon event queue */
+    private static abstract class CarbonMessage {
+        public abstract void perform();
+        
+        static { natInit(); }
+        public static native void natInit();
+        public static native void add(CarbonMessage m);
     }
-
 }