2003/05/08 13:38:40
authorejones <ejones@xwt.org>
Fri, 30 Jan 2004 07:00:05 +0000 (07:00 +0000)
committerejones <ejones@xwt.org>
Fri, 30 Jan 2004 07:00:05 +0000 (07:00 +0000)
darcs-hash:20040130070005-dddfd-7a608be1ae96f76ee7e1ffd9b0b17f6bed59125f.gz

src/org/xwt/Main.java
src/org/xwt/Platform.java
src/org/xwt/plat/Carbon.cc
src/org/xwt/plat/Carbon.java
src/org/xwt/plat/Win32.java

index 4eabbce..7713361 100644 (file)
@@ -130,8 +130,9 @@ public class Main extends Applet {
                 });
            
             // gcj-win32 exit()'s when the original thread dies, so we have to deadlock ourselves
-            if (Log.on) Log.log(Main.class, "main thread blocking on new semaphore");
-            new org.xwt.util.Semaphore().block();
+            //if (Log.on) Log.log(Main.class, "main thread blocking on new semaphore");
+            //new org.xwt.util.Semaphore().block();
+            Platform.running();
 
         } catch (Throwable e) {
             e.printStackTrace();
index 5bb1687..e2c148c 100644 (file)
@@ -57,8 +57,8 @@ public class Platform {
             String os_version = System.getProperty("os.version", "");
             String platform_class = null;
             
-            //if (os_name.startsWith("Mac OS X")) platform_class = "MacOSX";
-            if (vendor.startsWith("Free Software Foundation")) {
+            if (os_name.startsWith("Darwin")) platform_class = "Carbon";
+            else if (vendor.startsWith("Free Software Foundation")) {
                 if (os_name.startsWith("Window")) platform_class = "Win32";
                 else platform_class = "X11";
             } else if (version.startsWith("1.1") && vendor.startsWith("Netscape")) platform_class = "Netscape";
@@ -175,6 +175,9 @@ public class Platform {
                 public Object get() { return o; }
             };
     }
+
+       /** Called once XWT is initialized and the application is running. */
+       protected void _running() {}
     
     /** quits XWT */
     protected void _exit() {
@@ -351,6 +354,12 @@ public class Platform {
         platform._newBrowserWindow(url);
     }
 
+       /** Called once XWT is initialized and the application is running. */
+       public static void running() {
+               Log.log(Platform.class, "XWT is running");
+               platform._running();
+       }
+       
     /** quits XWT */
     public static void exit() {
         Log.log(Platform.class, "exiting via Platform.exit()");
index 326392a..92b2dfc 100644 (file)
 #include <org/xwt/plat/Carbon$CarbonSurface.h>
 #include <org/xwt/plat/Carbon$CarbonPicture.h>
 #include <org/xwt/plat/Carbon$CarbonDoubleBuffer.h>
+#include <org/xwt/plat/Carbon$WrappedRawData.h>
 #include <java/lang/System.h>
 #include <java/lang/Error.h>
 #include <java/io/PrintStream.h>
 
+// Apple's version of GCC automatically defines this symbol.
+// Since XWT is built with a "stock" version of GCC, we need to define
+// this manually to get the system header files to allow us to include them
+#define __APPLE_CPP__ 1
+
+// Standard GCC doesn't support precompiled headers,
+// meaning that it is WAY faster to just include the individual headers
+#include <CoreFoundation/CFString.h>
+#include <HIToolbox/MacWindows.h>
+#include <HIToolbox/MacApplication.h>
+#include <HIToolbox/Scrap.h>
+#include <HIToolbox/Appearance.h>
+#include <HIToolbox/CarbonEvents.h>
+#include <HIToolbox/HIView.h>
+#include <QD/ATSUnicode.h>
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) < (b) ? (b) : (a))
+
+#define FORMAT_RECT( x ) x.left, x.top, x.right, x.bottom
+#define PRINT_RECT( x ) fprintf( stderr, #x " = (%d, %d) (%d, %d)\n", x.left, x.top, x.right, x.bottom )
+#define PRINT_CGRECT( rect ) fprintf( stderr, #rect " = %f x %f @ (%f, %f)\n", rect.size.width, rect.size.height, rect.origin.x, rect.origin.y );
+
+
+static const EventTypeSpec CarbonSurfaceEventInfo[] = {
+       { kEventClassWindow, kEventWindowUpdate },
+       { kEventClassWindow, kEventWindowBoundsChanged },
+       { kEventClassWindow, kEventWindowActivated },
+       { kEventClassWindow, kEventWindowDeactivated },
+       { kEventClassWindow, kEventWindowClose },
+       { kEventClassWindow, kEventWindowZoomed },
+       { kEventClassWindow, kEventWindowExpanded },
+       { kEventClassWindow, kEventWindowCollapsed },
+
+
+       { kEventClassKeyboard, kEventRawKeyDown },
+       { kEventClassKeyboard, kEventRawKeyRepeat },
+       { kEventClassKeyboard, kEventRawKeyUp },
+       { kEventClassKeyboard, kEventRawKeyModifiersChanged },
+
+       
+       { kEventClassMouse, kEventMouseDown },
+       { kEventClassMouse, kEventMouseUp },
+       { kEventClassMouse, kEventMouseMoved },
+       { kEventClassMouse, kEventMouseDragged },
+       { kEventClassMouse, kEventMouseExited },
+
+       
+};
+
+static inline UniChar GetCharacterWithoutModifiers( EventRef rawKeyboardEvent )
+{
+       UInt32 keyCode;
+       // Get the key code from the raw key event
+       GetEventParameter( rawKeyboardEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof( keyCode ), NULL, &keyCode );
+
+       // Get the current keyboard layout
+ // TODO: If this is a performance sink, we need to cache these values
+       SInt16 lastKeyLayoutID = GetScriptVariable( /*currentKeyScript*/ GetScriptManagerVariable(smKeyScript), smScriptKeys);
+       Handle uchrHandle = GetResource('uchr', lastKeyLayoutID);
+
+       // Translate the key press ignoring ctrl and option
+       UInt32 ignoredDeadKeys = 0;
+       UInt32 ignoredActualLength = 0;
+       UniChar unicodeKey = 0;
+
+       // We actually want the current shift key value
+       UInt32 modifiers = (GetCurrentEventKeyModifiers() & shiftKey) >> 8;
+       OSStatus err = UCKeyTranslate( reinterpret_cast<UCKeyboardLayout*>( *uchrHandle ), keyCode, kUCKeyActionDown,
+                                                               /* modifierKeyState */ modifiers, LMGetKbdType(), kUCKeyTranslateNoDeadKeysMask, &ignoredDeadKeys,
+                                                               /* buffer length */ 1,
+                                                               /* actual length */ &ignoredActualLength,
+                                                               /* string */ &unicodeKey );
+       assert( err == noErr );
+
+       return unicodeKey;
+}
+
+static CFStringRef JavaToCFString(java::lang::String* s) {
+       int len = min(1024, JvGetStringUTFLength(s));
+
+    char* buffer = (char*) malloc( len );
+    JvGetStringUTFRegion(s, 0, s->length(), buffer);
+
+       // Create a CFString from the UTF8 string
+       CFStringRef string = CFStringCreateWithBytes( NULL, (UInt8*) buffer, len, kCFStringEncodingUTF8, false );
+       assert( string != NULL );
+
+       free( buffer );
+       buffer = NULL;
+
+       return string;
+}
+
+static jstring CFToJavaString( CFStringRef s ) {
+       // TODO: I can probably use CFStringCreateExternalRepresentation
+       // First determine the size of the buffer required
+       const CFRange entireString = CFRangeMake( 0, CFStringGetLength( s ) );
+       CFIndex requiredSize = 0;
+       CFIndex charsConverted = CFStringGetBytes( s, entireString, kCFStringEncodingUTF8, 0, false, NULL, 0, &requiredSize );
+       assert( charsConverted == CFStringGetLength( s ) );
+
+       // Allocate the buffer, plus an additional byte for the null byte
+       UInt8* buffer = (UInt8*) malloc( requiredSize + 1 );
+       assert( buffer != NULL );
+
+       // Convert the string
+       charsConverted = CFStringGetBytes( s, entireString, kCFStringEncodingUTF8, 0, false, buffer, requiredSize, NULL );
+       assert( charsConverted == CFStringGetLength( s ) );
+
+       buffer[requiredSize] = '\0';
+
+       jstring string = JvNewStringUTF( (char*) buffer );
+       assert( string != NULL );
+
+       free( buffer );
+       buffer = NULL;
+
+       return string;
+}
+
+static jstring UniCharToXWTString( UniChar character )
+{
+       switch ( character ) {
+               case '\t': return JvNewStringLatin1("tab");
+               case kEscapeCharCode: return JvNewStringLatin1("escape");
+               case '\n':
+               case kEnterCharCode:
+               case kReturnCharCode:
+                       return JvNewStringLatin1("enter");
+               case kBackspaceCharCode: return JvNewStringLatin1("back_space");
+                       // escape == clear
+               //case kClearCharCode: return JvNewStringLatin1("clear");
+               //case VK_PAUSE: return JvNewStringLatin1("pause");
+               case kPageUpCharCode: return JvNewStringLatin1("page_up");
+               case kPageDownCharCode: return JvNewStringLatin1("page_down");
+               case kEndCharCode: return JvNewStringLatin1("end");
+               case kHomeCharCode: return JvNewStringLatin1("home");
+               case kLeftArrowCharCode: return JvNewStringLatin1("left");
+               case kUpArrowCharCode: return JvNewStringLatin1("up");
+               case kRightArrowCharCode: return JvNewStringLatin1("right");
+               case kDownArrowCharCode: return JvNewStringLatin1("down");
+                       // TODO: Is help always the insert key in the mac world? on my keyboard it is
+               case kHelpCharCode: return JvNewStringLatin1("insert");
+               case kDeleteCharCode: return JvNewStringLatin1("delete");
+                       // TODO: How can I determine if a numpad key was pressed?
+               //case VK_NUMPAD0: return JvNewStringLatin1("numpad0");
+               //case VK_NUMPAD1: return JvNewStringLatin1("numpad1");
+               //case VK_NUMPAD2: return JvNewStringLatin1("numpad2");
+               //case VK_NUMPAD3: return JvNewStringLatin1("numpad3");
+               //case VK_NUMPAD4: return JvNewStringLatin1("numpad4");
+               //case VK_NUMPAD5: return JvNewStringLatin1("numpad5");
+               //case VK_NUMPAD6: return JvNewStringLatin1("numpad6");
+               //case VK_NUMPAD7: return JvNewStringLatin1("numpad7");
+               //case VK_NUMPAD8: return JvNewStringLatin1("numpad8");
+               //case VK_NUMPAD9: return JvNewStringLatin1("numpad9");
+                       // TODO: How do I get the function key?
+               case kFunctionKeyCharCode: return JvNewStringLatin1("f1");
+               //case VK_F2: return JvNewStringLatin1("f2");
+               //case VK_F3: return JvNewStringLatin1("f3");
+               //case VK_F4: return JvNewStringLatin1("f4");
+               //case VK_F5: return JvNewStringLatin1("f5");
+               //case VK_F6: return JvNewStringLatin1("f6");
+               //case VK_F7: return JvNewStringLatin1("f7");
+               //case VK_F8: return JvNewStringLatin1("f8");
+               //case VK_F9: return JvNewStringLatin1("f9");
+               //case VK_F10: return JvNewStringLatin1("f10");
+               //case VK_F11: return JvNewStringLatin1("f11");
+               //case VK_F12: return JvNewStringLatin1("f12");
+                       
+               default:
+                       // Convert the character into a CFString, then into a java string
+                       CFStringRef string = CFStringCreateWithCharactersNoCopy( NULL, &character, 1, kCFAllocatorNull );
+                       assert( string != NULL );
+
+                       jstring str = CFToJavaString( string );
+
+                       CFRelease( string );
+                       string = NULL;
+
+                       return str;
+       }
+
+       // We should never get here
+       assert( false );
+       return NULL;
+}
+
+static void ModifierKeyTest( org::xwt::plat::Carbon$CarbonSurface* surface, UInt32 changedModifiers, UInt32 currentModifiers, UInt32 keyMask, const char* string )
+{
+       // If the modifier key changed
+       if ( changedModifiers & keyMask ) {
+               jstring str = JvNewStringLatin1( string );
+
+               // And it is currently down, dispatch a keypressed event
+               if ( currentModifiers & keyMask ) surface->KeyPressed( str );
+               // otherwise dispatch a key released event
+               else surface->KeyReleased( str );
+       }
+}
+
+// TODO: Do I have to make the event handlers "pascal", or use "NewEventHandlerUPP"?
+pascal static OSStatus CarbonSurfaceEventHandler( EventHandlerCallRef handler, EventRef event, void* data ) {
+       UInt32 eventClass = GetEventClass( event );
+       UInt32 eventKind = GetEventKind( event );
+       OSStatus result = eventNotHandledErr;
+       org::xwt::plat::Carbon$CarbonSurface* surface = (org::xwt::plat::Carbon$CarbonSurface*)data;
+
+       //JvSynchronize dummy( surface );
+       
+       switch(eventClass) {
+               case kEventClassWindow:
+                       switch(eventKind)
+                               {
+                               case kEventWindowUpdate:
+                                       {
+                                               //fprintf( stderr, "window update " );
+
+                                               // Create the graphics context
+                                               result = CreateCGContextForPort(GetWindowPort( (WindowRef) surface->window), (CGContextRef*) &surface->gc);
+                                               assert( result == noErr && surface->gc != NULL );
+
+                                               // Quartz uses the bottom left as its origin. Translate to use the top left.
+                                               Rect bounds;
+                                               GetWindowBounds( (WindowRef) surface->window, kWindowContentRgn, &bounds );
+                                               assert( result == noErr );
+                                               CGContextTranslateCTM( (CGContextRef) surface->gc, 0, bounds.bottom - bounds.top );
+                                               CGContextScaleCTM( (CGContextRef) surface->gc, 1, -1 );
+
+                                               // Convert the context to a CGRect with local coordinates
+                                               CGRect contentBounds = CGRectMake( 0, 0, bounds.right - bounds.left, bounds.bottom - bounds.top );
+
+                                               //PRINT_CGRECT( contentBounds );
+
+                                               // Get the invalid region
+                                               Rect globalInvalidRect;
+                                               result = GetWindowBounds( (WindowRef) surface->window, kWindowUpdateRgn, &globalInvalidRect );
+                                               assert( result == noErr );
+
+                                               CGRect invalidRect = CGRectMake( globalInvalidRect.left - bounds.left, globalInvalidRect.top - bounds.top, globalInvalidRect.right - globalInvalidRect.left, globalInvalidRect.bottom - globalInvalidRect.top );
+
+                                               //invalidRect = CGRectIntersection( invalidRect, contentBounds );
+
+                                               //PRINT_CGRECT( invalidRect );
+
+                                               BeginUpdate( (WindowRef) surface->window );
+
+                                               // Erase the region with white
+                                               // TODO: Draw the window background instead
+                                               //result = SetThemeWindowBackground( (WindowRef) surface->window, ThemeBrush inBrush, true );
+                                               CGContextSetRGBFillColor( (CGContextRef) surface->gc, 1.0, 1.0, 1.0, 1.0 );
+                                               CGContextFillRect( (CGContextRef) surface->gc, invalidRect );                   
+
+                                               //CGContextSetRGBFillColor( (CGContextRef) surface->gc, red, green, blue, 0.5 );
+                                               //CGContextFillRect( (CGContextRef) surface->gc, CGRectMake( 0, 0, (bounds.right - bounds.left), (bounds.bottom - bounds.top) ) );
+                                               // Add the invalid region to XWT's dirty regions
+                                               surface->Dirty( (jint) invalidRect.origin.x, (jint) invalidRect.origin.y, (jint) invalidRect.size.width, (jint) invalidRect.size.height );
+                                               // Then tell it to actually do the drawing
+                                               surface->blitDirtyScreenRegions();
+
+                                               EndUpdate( (WindowRef) surface->window );
+
+                                               // Free the context
+                                               CGContextFlush( (CGContextRef) surface->gc );
+                                               CGContextRelease( (CGContextRef) surface->gc );
+                                               surface->gc = NULL;
+                                               result = noErr;
+                                               break;
+                                       }
+
+                               case kEventWindowBoundsChanged:
+                                       {
+                                               // We can't use the event parameter: it is the window bounds, not the content area bounds
+                                               HIRect currentBounds;
+                                               result = GetEventParameter( event, kEventParamCurrentBounds, typeHIRect, NULL, sizeof( currentBounds ), NULL, &currentBounds );
+                                               assert( result == noErr );
+                                               
+                                               // Get the event attributes to determine if size and/or origin have changed
+                                               UInt32 attributes;
+                                               result = GetEventParameter( event, kEventParamAttributes, typeUInt32, NULL, sizeof( attributes ), NULL, &attributes );
+                                               assert( result == noErr );
+
+                                               if ( attributes & kWindowBoundsChangeSizeChanged ) {
+                                                       // If maximize is set, unset it
+                                                       if ( attributes & kWindowBoundsChangeUserResize && surface->maximized ) {
+                                                               //fprintf( stderr, "maximized = 0\n" );
+                                                               surface->Maximized( false );
+                                                       }
+                                                       
+                                                       //fprintf( stderr, "size changed\n" );
+                                                       surface->SizeChange( (jint) currentBounds.size.width, (jint) currentBounds.size.height );
+
+                                                       
+                                               }
+                                               
+                                               if ( attributes & kWindowBoundsChangeOriginChanged ) {
+                                                       //fprintf( stderr, "origin changed\n" );
+                                                       surface->PosChange( (jint) currentBounds.origin.x, (jint) currentBounds.origin.y );
+                                               }
+
+                                               result = noErr;
+                                               break;
+                                       }
+
+                               case kEventWindowActivated:
+                               case kEventWindowDeactivated:
+                                       {
+                                               //fprintf( stderr, "focus = %d\n", (eventKind == kEventWindowActivated) );
+                                               surface->Focused( (eventKind == kEventWindowActivated) );
+                                               result = noErr;
+                                               break;
+                                       }
+
+                               case kEventWindowZoomed:
+                                       {
+                                               // Toggle maximized whenever we recieve this event
+                                               //fprintf( stderr, "maximized: %d\n", ! surface->maximized );
+                                               surface->Maximized( ! surface->maximized );
+                                               result = noErr;
+                                               break;
+                                       }
+                                       
+                               case kEventWindowCollapsed:
+                                       {
+                                               //fprintf( stderr, "minimized true\n" );
+                                               surface->Minimized( true );
+                                               result = noErr;
+                                               break;
+                                       }
+
+                               case kEventWindowExpanded:
+                                       {
+                                               //fprintf( stderr, "minimized false\n" );
+                                               surface->Minimized( false );
+                                               result = noErr;
+                                               break;
+                                       }
+                                       
+                               case kEventWindowClose:
+                                       {
+                                               //fprintf( stderr, "close\n" );
+                                               surface->Close();
+                                               result = noErr;
+                                               break;
+                                       }
+                               default:
+                                       throw new java::lang::Error(JvNewStringLatin1("Unhandled Window Event"));
+                               }
+                       break;
+                       
+               case kEventClassMouse:
+                       {
+                               HIPoint where;
+                               result = GetEventParameter( event, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(where), NULL, &where );
+                               assert( result == noErr );
+
+                               // Create a hit test event to ask the standard window event handler where this press is
+                               EventRef hitTest;
+                               result = CreateEvent( NULL, kEventClassWindow, kEventWindowHitTest, 0, kEventAttributeNone, &hitTest );
+                               assert( result == noErr );
+
+                               result = SetEventParameter( hitTest, kEventParamMouseLocation, typeHIPoint, sizeof(where), &where );
+                               assert( result == noErr );
+
+                               result = SetEventParameter( hitTest, kEventParamDirectObject, typeWindowRef, sizeof(surface->window), &surface->window );
+                               assert( result == noErr );
+
+                               result = SendEventToEventTarget( hitTest, GetWindowEventTarget( (WindowRef) surface->window ) );
+                               assert( result == noErr );
+
+                               WindowDefPartCode part;
+                               result = GetEventParameter( hitTest, kEventParamWindowDefPart, typeWindowDefPartCode, NULL, sizeof(part), NULL, &part );
+                               assert( result == noErr );
+
+                               ReleaseEvent( hitTest );
+                               hitTest = NULL;
+
+                               // ignore the event if it is not in the content region.
+                               if ( part != wInContent )
+                                       {
+                                       return eventNotHandledErr;
+                                       }
+
+                               // TODO: Can I get the content region's bounds as an HIRect?
+                               Rect bounds;
+                               GetWindowBounds( (WindowRef) surface->window, kWindowContentRgn, &bounds );
+                               assert( result == noErr );
+                               // Convert the context to a CGRect with local coordinates
+                               CGRect contentBounds = CGRectMake( bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top );
+
+                               // Now convert the point to content area relative coordinates:
+                               where.x -= contentBounds.origin.x;
+                               where.y -= contentBounds.origin.y;
+                               
+                               switch ( eventKind )
+                                       {
+                                       case kEventMouseDown:
+                                               {
+                                                       //fprintf( stderr, "mouse down (%f, %f)\n", where.x, where.y );
+                                                       EventMouseButton mouseButton;
+                                                       result = GetEventParameter( event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(mouseButton), NULL, &mouseButton );
+                                                       assert( result == noErr );
+
+                                                       surface->Press( mouseButton );
+                                                       result = eventNotHandledErr;
+                                                       break;
+                                               }
+
+                                       case kEventMouseUp:
+                                               {
+                                                       //fprintf( stderr, "mouse up (%f, %f)\n", where.x, where.y );
+                                                       EventMouseButton mouseButton;
+                                                       result = GetEventParameter( event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(mouseButton), NULL, &mouseButton );
+                                                       assert( result == noErr );
+
+                                                       UInt32 clickCount;
+                                                       result = GetEventParameter( event, kEventParamClickCount, typeUInt32, NULL, sizeof(clickCount), NULL, &clickCount );
+                                                       assert( result == noErr );
+
+                                                       surface->Release( mouseButton );
+
+                                                       // deliver click/double click events
+                                                       if ( clickCount == 1 ) {
+                                                               //fprintf( stderr, "click (%f, %f)\n", where.x, where.y );
+                                                               surface->Click( mouseButton );
+
+                                                               //result = ActivateWindow( (WindowRef) surface->window, true );
+                                                               //assert( result == noErr );
+
+                                                               //ProcessSerialNumber psn = { 0, kCurrentProcess };
+                                                               //result = SetFrontProcess( &psn );
+                                                               //assert( result == noErr );
+                                                       } else if ( clickCount == 2 ) {
+                                                               //fprintf( stderr, "double click (%f, %f)\n", where.x, where.y );
+                                                               surface->DoubleClick( mouseButton );
+                                                       }
+                                                       
+                                                       result = noErr;
+                                                       break;
+                                               }
+
+                                       case kEventMouseMoved:
+                                       case kEventMouseDragged:
+                                               {
+                                                       surface->Move( (jint) where.x, (jint) where.y );
+                                                       result = noErr;
+                                                       //fprintf( stderr, "mousemoved (%f, %f)\n", where.x, where.y );
+                                                       break;
+                                               }
+
+                                       /*case kEventMouseExited:
+                                               {
+                                                       // Move the mouse to (-1, -1) when it exits the window 
+                                                       surface->Move( -1, -1 );
+                                                       result = noErr;
+                                                       fprintf( stderr, "mouse exited\n" );
+                                                       break;
+                                               }*/
+                                               
+
+                                       /*case kEventMouseWheelMoved:
+                                       {
+                                               EventMouseWheelAxis inAxis;
+                                               result = inEvent.GetParameter<EventMouseWheelAxis>( kEventParamMouseWheelAxis, typeMouseWheelAxis, &inAxis );
+                                               assert( noErr == result );
+
+                                               SInt32 inDelta;
+                                               result = inEvent.GetParameter<SInt32>( kEventParamMouseWheelDelta, typeSInt32, &inDelta );
+                                               assert( noErr == result );
+
+                                               result = MouseWheelMoved( inAxis, inDelta, inKeyModifiers );
+                                               break;
+                                       }
+                                       */
+
+
+                                               // some other kind of Mouse event: This is (in my opinion) an error
+                                       default:
+                                               assert( false );
+                                               break;
+                                       }
+                               break;
+                       }
+
+
+               case kEventClassKeyboard:
+                       switch (eventKind)
+                       {
+                               case kEventRawKeyDown:
+                               case kEventRawKeyRepeat:
+                                       {
+                                               // TODO: This breaks international input
+                                               UniChar character = GetCharacterWithoutModifiers( event );
+                                               surface->KeyPressed( UniCharToXWTString( character ) );
+                                               
+                                               result = noErr;
+                                               break;
+                                       }
+
+                               case kEventRawKeyUp:
+                                       {
+                                               UniChar character = GetCharacterWithoutModifiers( event );
+                                               surface->KeyReleased( UniCharToXWTString( character ) );
+                                               
+                                               result = noErr;
+                                               break;
+                                       }
+
+                               case kEventRawKeyModifiersChanged:
+                                       {
+                                               static UInt32 previousModifiers = 0;
+                                               UInt32 currentModifiers = GetCurrentEventKeyModifiers();
+                                               UInt32 changedModifiers = previousModifiers ^ currentModifiers;
+
+                                               ModifierKeyTest( surface, changedModifiers, currentModifiers, shiftKey, "shift" );
+                                               ModifierKeyTest( surface, changedModifiers, currentModifiers, alphaLock, "caps_lock" );
+                                               ModifierKeyTest( surface, changedModifiers, currentModifiers, controlKey, "control" );
+                                               ModifierKeyTest( surface, changedModifiers, currentModifiers, optionKey, "alt" );
+                                               ModifierKeyTest( surface, changedModifiers, currentModifiers, kEventKeyModifierNumLockMask, "num_lock" );
+                                               
+                                               previousModifiers = currentModifiers;
+                                               result = noErr;
+                                               break;
+                                       }
+                                       
+                               default:
+                                       assert( false );
+                                       break;
+                       }
+                       break;
+                       
+               default:
+                       throw new java::lang::Error(JvNewStringLatin1("Unhandled Window Event"));
+       }
+
+       return result;
+}
+
+/** Creates a next layout from string and style. If unicodeBuffer is not null, the caller is responsable for releasing it. */
+static ATSUTextLayout CreateTextLayout( CFStringRef string, ATSUStyle style, CFStringRef* unicodeBuffer ) {
+       assert( string != NULL && unicodeBuffer != NULL && *unicodeBuffer == NULL && style != NULL );
+
+       // Create layout
+       ATSUTextLayout layout;
+       OSStatus result = ATSUCreateTextLayout( &layout );
+       assert( result == noErr && layout != NULL );
+
+       // Try to get the unicode buffer directly
+       CFIndex unicodeLength = CFStringGetLength( string );
+       assert( unicodeLength != 0 );
+       const UniChar* unicodeString = CFStringGetCharactersPtr( string );
+       if ( unicodeString == NULL )
+               {
+               // That failed. Allocate a buffer using CFAllocator so the CFString can own it
+               UniChar* buffer = (UniChar*) CFAllocatorAllocate( NULL, unicodeLength * sizeof( UniChar ), 0 );
+               assert( buffer != NULL );
+
+               // Get a copy of the text
+               CFStringGetCharacters( string, CFRangeMake( 0, unicodeLength ), buffer );
+
+               // Create a CFString which wraps and takes ownership of the buffer
+               *unicodeBuffer = CFStringCreateWithCharactersNoCopy( NULL, buffer, unicodeLength, NULL );
+               assert( *unicodeBuffer != NULL );
+
+               unicodeString = buffer;
+               }
+               
+       assert( unicodeString != NULL );
+       //UniChar* unicode_string = malloc( sizeof( UniChar ) * unicode_length );
+       //CFStringGetCharacters( string, CFRangeMake( 0, unicode_length ), unicode_string );
+       ATSUSetTextPointerLocation( layout, unicodeString, kATSUFromTextBeginning, kATSUToTextEnd, unicodeLength );
+
+       // Set the style
+       result = ATSUSetRunStyle( layout, style, kATSUFromTextBeginning, kATSUToTextEnd );
+       assert( result == noErr );
+
+       // Using this setting makes rendering match TextEdit's (except for the antialiasing rules)
+       ATSLineLayoutOptions rendering = kATSLineFractDisable; //kATSLineUseDeviceMetrics;
+       ATSUAttributeTag tag = kATSULineLayoutOptionsTag;
+       ByteCount size = sizeof( rendering );
+       ATSUAttributeValuePtr value = &rendering;
+       result = ATSUSetLayoutControls( layout, 1, &tag, &size, &value );
+       assert( noErr == result );
+
+       // Allow ATSUI to use its default font fallbacks for Unicode. This means that the font may not
+       // match exactly, but it will display the characters if it can.
+       result = ATSUSetTransientFontMatching( layout, true );
+       assert( noErr == result );
+
+    return layout;
+}
+
+static void SetARGBFillColor( CGContextRef gc, jint argb )
+{
+       uint8_t* colour = (uint8_t*) &argb;
+       float alpha = colour[0] / 255.0;
+       float red = colour[1] / 255.0;
+       float green = colour[2] / 255.0;
+       float blue = colour[3] / 255.0;
+
+       CGContextSetRGBFillColor( gc, red, green, blue, alpha );
+}
 
 // CarbonDoubleBuffer //////////////////////////////////////////////////////////////////////
 
+void org::xwt::plat::Carbon$CarbonDoubleBuffer::setClip(jint x1, jint y1, jint x2, jint y2) {
+       // Restore the previous graphics state, which should be the initial state as saved in
+       // natInit(). This sets the clipping region to the entire buffer.
+       CGContextRestoreGState( (CGContextRef) gc );
+       // We then immediately save the state again, so the we can reset the clipping area again
+       CGContextSaveGState( (CGContextRef) gc );
+
+       // Set the clipping area to the rectangle
+    CGContextClipToRect( (CGContextRef) gc, CGRectMake( x1, y1, x2 - x1, y2 - y1 ) );
+}
+
+void org::xwt::plat::Carbon$CarbonDoubleBuffer::drawPicture(org::xwt::Picture* s, jint x, jint y) {
+       org::xwt::plat::Carbon$CarbonPicture* source = (org::xwt::plat::Carbon$CarbonPicture*)s;
+
+       CGRect destination = CGRectMake( x, y, source->width, source->height );
+       HIViewDrawCGImage( (CGContextRef) gc, &destination, (CGImageRef) source->image );
+}
+
 void org::xwt::plat::Carbon$CarbonDoubleBuffer::drawPicture(org::xwt::Picture* s,
                                                            jint dx1, jint dy1, jint dx2, jint dy2, jint sx1, jint sy1, jint sx2, jint sy2) {
     org::xwt::plat::Carbon$CarbonPicture* source = (org::xwt::plat::Carbon$CarbonPicture*)s;
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+
+       //fprintf( stderr, "drawing stretched picture\n" );
+       //CGContextSetRGBFillColor( (CGContextRef) gc, 1.0, 0.0, 0.0, 1.0 );
+       //CGContextFillRect( (CGContextRef) gc, CGRectMake( dx1, dy1, dx2 - dx1, dy2 - dy1 ) );
+       //return;
+       
+       // Some fancy clipping work is required here: draw only inside of the destination points
+       CGContextSaveGState( (CGContextRef) gc );
+       CGContextClipToRect( (CGContextRef) gc, CGRectMake( dx1, dy1, dx2 - dx1, dy2 - dy1 ) );
+
+       // Some fancy scaling work is required here as well:
+       // We want to copy a rectangle from the source image to a destination rectangle.
+       // We are clipped to the destination rectangle, so now we must scale the source image
+       // and line it up correctly.
+       float destinationRectWidth = dx2 - dx1;
+       float destinationRectHeight = dy2 - dy1;
+       float sourceRectWidth = sx2 - sx1;
+       float sourceRectHeight = sy2 - sy1;
+       float horizontalScalingFactor = destinationRectWidth / sourceRectWidth;
+       float verticalScalingFactor = destinationRectHeight / sourceRectHeight;
+
+       // TODO: Work out the math for doing this. For now, no scaling is possible.
+       CGRect destinationRect = CGRectMake( dx1 - sx1 * horizontalScalingFactor, dy1 - sy1 * verticalScalingFactor,
+                                                                         source->width * horizontalScalingFactor, source->height * verticalScalingFactor );
+       HIViewDrawCGImage( (CGContextRef) gc, &destinationRect, (CGImageRef) source->image );
+
+       // Undo the clipping fun
+       CGContextRestoreGState( (CGContextRef) gc );
+}
+
+/*void CarbonPictureRelease( void* info, const void* data, size_t size )
+{
+       // TODO: This should release a java reference, which should be
+       // obtained when this is created.
+       // It'll work without it, but that's only by "chance".
+}*/
+
+static CGImageRef CreateCGImageFromData( void* data, int width, int height, CGImageAlphaInfo alphaFormat )
+{
+       // I'm assuming that data is in RGBA format
+       const size_t NUM_COMPONENTS = 4;
+       const size_t BITS_PER_COMPONENT = 8;
+       const size_t BITS_PER_PIXEL = BITS_PER_COMPONENT * NUM_COMPONENTS;
+       const size_t BYTES_PER_PIXEL = BITS_PER_PIXEL / 8;
+       const size_t BYTES_PER_ROW = BYTES_PER_PIXEL * width;
+
+       CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+       assert( colorSpace != NULL );
+
+       CGDataProviderRef dataProvider = CGDataProviderCreateWithData( NULL, data, width * height * BYTES_PER_PIXEL, NULL /*CarbonPictureRelease*/ );
+       assert( dataProvider != NULL );
+
+       CGImageRef image = CGImageCreate ( width, height, BITS_PER_COMPONENT, BITS_PER_PIXEL, BYTES_PER_ROW,
+                                                                       colorSpace, alphaFormat, dataProvider, NULL, 1, kCGRenderingIntentDefault );
+       assert( image != NULL );
+
+       CGDataProviderRelease( dataProvider );
+       dataProvider = NULL;
+
+       CGColorSpaceRelease( colorSpace );
+       colorSpace = NULL;
+
+       return image;
+}
+
+void org::xwt::plat::Carbon$CarbonDoubleBuffer::natInit() {
+       // FIRST: We create a CGBitmapContextRef so we can draw on this buffer
+       const int BYTES_PER_PIXEL = 4; // RGBA
+       const int BITS_PER_COMPONENT = 8;
+       
+    // Create a new bitmap context, along with the RAM for the bitmap itself
+       const int bitmapBytesPerRow   = (width * BYTES_PER_PIXEL);
+       const int bitmapByteCount     = (bitmapBytesPerRow * height);
+
+       //fprintf( stderr, "alloced %ld bytes for %d x %d double buffer\n", bitmapByteCount, width, height );
+       
+       // create an RGB color space
+       CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+       assert( colorSpace != NULL );
+
+       // create the bitmap
+       bitmapData = (gnu::gcj::RawData*) malloc( bitmapByteCount );
+       assert( bitmapData != NULL );
+
+       // create the context
+       // TODO: Use kCGImageAlphaPremultipliedLast for better performance
+       gc = (gnu::gcj::RawData*) CGBitmapContextCreate( bitmapData, width, height, BITS_PER_COMPONENT, bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedFirst );
+       assert( gc != NULL );
+
+       // the context retains the color space, so we can release it
+       // OPTIMIZATION: We could pass this colorSpace into CreateCGImageFromData
+       CGColorSpaceRelease( colorSpace );
+       colorSpace = NULL;
+
+       // Clear the double buffer to transparent
+       CGContextClearRect( (CGContextRef) gc, CGRectMake( 0, 0, width, height ) );
+       //CGContextSetRGBFillColor( (CGContextRef) gc, 1.0, 1.0, 1.0, 1.0 );
+       //CGContextFillRect( (CGContextRef) gc, CGRectMake( 0, 0, width, height ) );
+
+       // Shift the coordinate origin to the top left corner (default = bottom right)
+       CGContextTranslateCTM( (CGContextRef) gc, 0, height );
+       CGContextScaleCTM( (CGContextRef) gc, 1, -1 );
+
+
+       // SECOND: We create a CGImageRef that wraps this buffer, so we can copy it to a Surface
+       image = (gnu::gcj::RawData*) CreateCGImageFromData( bitmapData, width, height, kCGImageAlphaPremultipliedFirst );
+       assert( image != NULL );
+
+       // THIRD: Save the current graphics state so we can set and reset the clipping state
+       // We need to do this because the only way to make the clipping region larger is to
+       // restore a previous graphics state. See setClip.
+       CGContextSaveGState( (CGContextRef) gc );
 }
 
 void org::xwt::plat::Carbon$CarbonDoubleBuffer::finalize() {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       CGContextRelease( (CGContextRef) gc );
+       CGImageRelease( (CGImageRef) image );
+    free( bitmapData );
+       bitmapData = NULL;
 }
 
-void org::xwt::plat::Carbon$CarbonSurface::blit(org::xwt::DoubleBuffer* db, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) {
+void org::xwt::plat::Carbon$CarbonSurface::blit(org::xwt::DoubleBuffer* db, jint sx, jint sy, jint dx1, jint dy1, jint dx2, jint dy2) {
     org::xwt::plat::Carbon$CarbonDoubleBuffer *xdb = (org::xwt::plat::Carbon$CarbonDoubleBuffer*)db;
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       assert( xdb != NULL && xdb->gc != NULL && xdb->image != NULL );
+
+       // This method is synchronized to prevent crappy threading fun
+       //JvSynchronize dummy(this);
+       
+       /*fprintf( stderr, "reading image data...\n" );
+       uint32_t* data = (uint32_t*) xdb->bitmapData;
+       for ( int i = 0; i < xdb->width * xdb->height; ++ i )
+               {
+               uint32_t temp = data[i];
+               ++ temp;
+               }
+       fprintf( stderr, "DONE\n" );*/
+
+       
+       // If we do not have a graphics context we simply mark this area as dirty and return.
+       // We'll get a WindowPaint event later on.
+       if ( gc == NULL )
+               {
+               Rect area;
+               area.left = dx1;
+               area.top = dy1;
+               area.right = dx2;
+               area.bottom = dy2;
+               // Make the rectangle where we would blit this buffer as invalid
+               OSStatus err = InvalWindowRect( (WindowRef) window, &area );
+               //OSStatus err = QDAddRectToDirtyRegion( GetWindowPort( (WindowRef) window ), &area );
+               assert( err == noErr );
+
+               //fprintf( stderr, "blit: but marking invalid " );
+               //PRINT_RECT( area );
+               return;
+               }
+       assert( gc != NULL );
+
+       //fprintf( stderr, "blit\n" );
+       
+       // Make sure that the CGImage is up to date
+       CGContextFlush( (CGContextRef) xdb->gc );
+
+       // Some fancy clipping work is required here: draw only inside of the destination rectangle
+       CGContextSaveGState( (CGContextRef) gc );
+       CGContextClipToRect( (CGContextRef) gc, CGRectMake( dx1, dy1, dx2 - dx1, dy2 - dy1 ) );
+
+       // Draw the image on the surface
+       CGRect destination = CGRectMake( dx1 - sx, dy1 - sy, xdb->width, xdb->height );
+       HIViewDrawCGImage( (CGContextRef) gc, &destination, (CGImageRef) xdb->image );
+
+       // Undo the clipping fun
+       CGContextRestoreGState( (CGContextRef)gc );     
 }
 
 void org::xwt::plat::Carbon$CarbonDoubleBuffer::fillRect (jint x, jint y, jint x2, jint y2, jint argb) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       // Set the Fill color to match
+       SetARGBFillColor( (CGContextRef) gc, argb );
+       CGContextFillRect( (CGContextRef) gc, CGRectMake( x, y, x2 - x, y2 - y ) );
 }
 
 void org::xwt::plat::Carbon$CarbonDoubleBuffer::drawString(::java::lang::String* font, ::java::lang::String* text, jint x, jint y, jint argb) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
-}
+       ATSUStyle style = (ATSUStyle) ((org::xwt::plat::Carbon*)org::xwt::plat::Carbon::platform)->_getATSUStyle( font );
+       CFStringRef string = JavaToCFString( text );
+
+       CFStringRef unicodeBuffer = NULL;
+       ATSUTextLayout layout = CreateTextLayout( string, style, &unicodeBuffer );
+
+       // Associate the graphics context with the text layout object
+       ATSUAttributeTag tag = kATSUCGContextTag;
+       ByteCount size = sizeof( gc );
+       ATSUAttributeValuePtr value = &gc;
+       OSStatus result = ATSUSetLayoutControls( layout, 1, &tag, &size, &value );
+       assert( result == noErr );
+
+    // The Quartz RGB fill color influences the ATSUI color
+       SetARGBFillColor( (CGContextRef) gc, argb );
 
+       // I've flipped the context to "standard" coordinates, but draw text needs it to be flipped back
+       CGContextSaveGState( (CGContextRef) gc );
+       CGContextScaleCTM( (CGContextRef) gc, 1.0, -1.0 );
+       y = -y;
+
+       // Draw text
+       result = ATSUDrawText( layout, kATSUFromTextBeginning, kATSUToTextEnd, X2Fix( x ), X2Fix( y ) );
+       assert( result == noErr );
+
+       CGContextRestoreGState( (CGContextRef) gc );
+
+       result = ATSUDisposeTextLayout( layout );
+       assert( result == noErr );
+       layout = NULL;
+
+       if ( unicodeBuffer != NULL ) {
+               CFRelease( unicodeBuffer );
+               unicodeBuffer = NULL;
+       }
+
+       CFRelease( string );
+       string = NULL;
+}
 
 // CarbonSurface //////////////////////////////////////////////////////////////////////
 
 void org::xwt::plat::Carbon$CarbonSurface::setIcon(org::xwt::Picture* pic) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       org::xwt::plat::Carbon$CarbonPicture *picture = (org::xwt::plat::Carbon$CarbonPicture*)pic;
+
+       // TODO: This sets the dock icon. Maybe I should set the document icon (the icon beside the window title)?
+       //       or set the "window preview" that is shown when a window is collapsed?
+       // TODO: This doesn't seem to work. Maybe it is a size problem?
+       OSStatus result = SetApplicationDockTileImage( (CGImageRef) picture->image );
+       assert( result == noErr );
 }
 
 void org::xwt::plat::Carbon$CarbonSurface::setTitleBarText(java::lang::String* s) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
-}
+       CFStringRef string = JavaToCFString( s );
+       OSStatus result = SetWindowTitleWithCFString( (WindowRef) window, string );
+    assert( result == noErr );
 
-void org::xwt::plat::Carbon$CarbonSurface::setLimits(jint minw, jint minh, jint maxw, jint maxh) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       CFRelease( string );
+       string = NULL;
 }
 
 void org::xwt::plat::Carbon$CarbonSurface::setSize (jint width, jint height) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       Rect bounds;
+       OSStatus result = GetWindowBounds( (WindowRef) window, kWindowContentRgn, &bounds );
+       assert( result == noErr );
+
+       bounds.right = bounds.left + width;
+       bounds.bottom = bounds.top + height;
+       
+    result = SetWindowBounds( (WindowRef) window, kWindowContentRgn, &bounds );
+       assert( result == noErr );
+       result = ConstrainWindowToScreen( (WindowRef) window, kWindowContentRgn, kWindowConstrainStandardOptions, NULL, NULL );
+       assert( result == noErr );
+
+       fprintf( stderr, "set size (%d, %d) (%d, %d)\n", FORMAT_RECT( bounds ) );
+       // TODO: Shouldn't this deliver a "size changed" event automatically?
+       //SizeChange( width, height );
 }
 
 void org::xwt::plat::Carbon$CarbonSurface::setLocation (jint x, jint y) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       Rect bounds;
+       OSStatus result = GetWindowBounds( (WindowRef) window, kWindowStructureRgn, &bounds );
+       assert( result == noErr );
+
+       int temp = bounds.right - bounds.left;
+       bounds.left = x;
+       bounds.right = x + temp;
+       temp = bounds.bottom - bounds.top;
+       bounds.top = y;
+       bounds.bottom = y + temp;
+
+    result = SetWindowBounds( (WindowRef) window, kWindowStructureRgn, &bounds );
+       assert( result == noErr );
+       result = ConstrainWindowToScreen( (WindowRef) window, kWindowStructureRgn, kWindowConstrainStandardOptions, NULL, NULL );
+       assert( result == noErr );
+
+       fprintf( stderr, "set location (%d, %d) (%d, %d)\n", FORMAT_RECT( bounds ) );
+}
+
+void org::xwt::plat::Carbon$CarbonSurface::natInit (jboolean framed) {
+       // Create a standard window. Maybe I should be using the Appearance Manager ones?
+       OSStatus result = noErr;
+       Rect bounds;
+       bounds.left = 0;
+       bounds.top = 0;
+       bounds.right = 100;
+       bounds.bottom = 100;
+       if ( framed ) {
+               OSStatus result = CreateNewWindow( kDocumentWindowClass,
+                                                                         kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute | kWindowLiveResizeAttribute,
+                                                                         &bounds,
+                                                                         (WindowRef*) &window
+                                                                         );
+       }
+       else {
+               OSStatus result = CreateNewWindow( kPlainWindowClass,
+                                                                         kWindowStandardHandlerAttribute | kWindowLiveResizeAttribute,
+                                                                         &bounds,
+                                                                         (WindowRef*) &window
+                                                                         );
+       }
+       assert( result == noErr && window != NULL );
+
+       // Install the event handler
+       result = InstallEventHandler( GetWindowEventTarget( (WindowRef) window ),
+                                                          CarbonSurfaceEventHandler,
+                                                          GetEventTypeCount( CarbonSurfaceEventInfo ), CarbonSurfaceEventInfo,
+                                                          this,
+                                                          NULL );
+       assert( result == noErr );
+       
+       // Make sure that this window is NOT visible
+       //HideWindow( (WindowRef) window );
 }
+
 void org::xwt::plat::Carbon$CarbonSurface::toFront() {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+    BringToFront( (WindowRef) window );
+       //OSStatus result = ActivateWindow( (WindowRef) window, true );
+       //assert( result == noErr );
+
+       // Using the default "current process" or actually fetching the current process doesn't work
+       ProcessSerialNumber currentProcess = { 0, kCurrentProcess }; 
+       OSStatus result = GetCurrentProcess( &currentProcess );
+       assert( result == noErr );
+       result = SetFrontProcessWithOptions( &currentProcess, kSetFrontProcessFrontWindowOnly );
+       assert( result == noErr );
 }
+
 void org::xwt::plat::Carbon$CarbonSurface::toBack() {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+    SendBehind( (WindowRef) window, NULL );
+}
+
+void org::xwt::plat::Carbon$CarbonSurface::syncCursor() {
+       ThemeCursor curs;
+    if (cursor->equals(JvNewStringLatin1("crosshair"))) curs = kThemeCrossCursor;
+    //else if (cursor->equals(east)) curs = kThemeArrowCursor;
+    else if (cursor->equals(JvNewStringLatin1("hand"))) curs = kThemePointingHandCursor;
+    //else if (cursor->equals(move)) curs = kThemeArrowCursor;
+    //else if (cursor->equals(north)) curs = kThemeArrowCursor;
+    //else if (cursor->equals(northeast)) curs = kThemeArrowCursor;
+    //else if (cursor->equals(northwest)) curs = kThemeArrowCursor;
+    //else if (cursor->equals(south)) curs = kThemeArrowCursor;
+    //else if (cursor->equals(southeast)) curs = kThemeArrowCursor;
+    //else if (cursor->equals(southwest)) curs = kThemeArrowCursor;
+    else if (cursor->equals(JvNewStringLatin1("text"))) curs = kThemeIBeamCursor;
+    //else if (cursor->equals(west)) curs = kThemeArrowCursor;
+    else if (cursor->equals(JvNewStringLatin1("wait_string"))) curs = kThemeSpinningCursor;
+    else curs = kThemeArrowCursor;
+       
+       // TODO: This should PROBABLY be activated/deactivated when over the window using mouse tracking regions
+       SetThemeCursor( curs );
 }
 
 void org::xwt::plat::Carbon$CarbonSurface::_dispose() {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+    ReleaseWindow( (WindowRef) window );
+       window = NULL;
 }
 
 void org::xwt::plat::Carbon$CarbonSurface::setInvisible(jboolean i) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       //fprintf( stderr, "invisible = %d\n", i );
+    if ( i ) {
+               HideWindow( (WindowRef) window );
+       } else {
+               ShowWindow( (WindowRef) window );
+       }
 }
 
 void org::xwt::plat::Carbon$CarbonSurface::_setMaximized(jboolean b) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       Point ideal;
+       // TODO: Probably should use the actual screen size here
+       ideal.h = CGDisplayPixelsWide( kCGDirectMainDisplay );
+       ideal.v = CGDisplayPixelsHigh( kCGDirectMainDisplay );
+       //fprintf( stderr, "maximized = %d\n", b );
+    OSStatus result = ZoomWindowIdeal( (WindowRef) window, ( b ? inZoomOut : inZoomIn ), &ideal );
+       // TODO: It seems to work, so why does it return an error? Theory: XWT is not in a bundle.
+       assert( result == noErr );
 }
 
 void org::xwt::plat::Carbon$CarbonSurface::_setMinimized(jboolean b) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+    OSStatus result = CollapseWindow( (WindowRef) window, b );
+       // TODO: It seems to work, so why does it return an error? Theory: XWT is not in a bundle.
+       //assert( result == noErr );
 }
 
+void org::xwt::plat::Carbon$CarbonPicture::natInit() {
+       assert( data->length == width * height );
+       image = (gnu::gcj::RawData*) CreateCGImageFromData( elements(data), width, height, kCGImageAlphaFirst );
+       assert( image != NULL );
+}
 
-
+void org::xwt::plat::Carbon$CarbonPicture::finalize() {
+       CGImageRelease( (CGImage*) image );
+       image = NULL;
+}
 
 // Carbon ///////////////////////////////////////////////////////////////////
 
 jint org::xwt::plat::Carbon::_getScreenWidth() {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       //fprintf( stderr, "screen width = %d\n", CGDisplayPixelsWide( kCGDirectMainDisplay ) );
+       return CGDisplayPixelsWide( kCGDirectMainDisplay );
 }
+
 jint org::xwt::plat::Carbon::_getScreenHeight() {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       //fprintf( stderr, "screen height = %d\n", CGDisplayPixelsHigh( kCGDirectMainDisplay ) );
+    return CGDisplayPixelsHigh( kCGDirectMainDisplay );
 }
 
 jstring org::xwt::plat::Carbon::_getClipBoard() {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       // Get the clipboard reference
+       ScrapRef scrap = NULL;
+       OSStatus result = GetCurrentScrap( &scrap );
+       assert( result == noErr );
+
+       ScrapFlavorFlags flavorFlags;
+       result = GetScrapFlavorFlags ( scrap, kScrapFlavorTypeUnicode, &flavorFlags );
+
+       if ( result == noErr ) {
+               // No error, we have unicode data in a Scrap. Find out how many bytes of data it is.
+               Size bytes = 0;
+               result = GetScrapFlavorSize( scrap, kScrapFlavorTypeUnicode, &bytes );
+               assert( result == noErr );
+               const Size numUniChars = bytes / sizeof( UniChar );
+
+               // Allocate a buffer for the text using Core Foundation
+               UniChar* buffer = reinterpret_cast<UniChar*>( CFAllocatorAllocate( NULL, bytes, 0 ) );
+               assert( buffer != NULL );
+
+               // Get a copy of the text
+               Size nextBytes = bytes;
+               result = GetScrapFlavorData( scrap, kScrapFlavorTypeUnicode, &nextBytes, buffer );
+               assert( result == noErr );
+
+               // Create a CFString which wraps and takes ownership of the buffer
+               CFStringRef string = CFStringCreateWithCharactersNoCopy( NULL, buffer, numUniChars, NULL );
+               assert( string != NULL );
+               buffer = NULL; // string now owns this buffer
+
+               jstring ret = CFToJavaString( string );
+
+               // Default allocator releases both the CFString and the UniChar buffer (text)
+               CFRelease( string );
+               string = NULL;
+
+               return ret;
+       } else {
+               return JvNewStringLatin1("");
+       }       
 }
 
 void org::xwt::plat::Carbon::_setClipBoard(jstring s) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       CFStringRef string = JavaToCFString( s );
+
+       OSStatus result = ClearCurrentScrap();
+       assert( result == noErr );
+
+       ScrapRef scrap = NULL;
+       result = GetCurrentScrap( &scrap );
+       assert( result == noErr );
+
+       CFIndex numUniChars = CFStringGetLength( string );
+
+       // Try to get a pointer to the unicode data first, then do the copy if needed
+       const UniChar* buffer = CFStringGetCharactersPtr( string );
+       if ( buffer != NULL )
+               {
+               result = PutScrapFlavor ( scrap, kScrapFlavorTypeUnicode, 0, sizeof( UniChar ) * numUniChars, buffer );
+               assert( result == noErr );
+               }
+       else
+               {
+               UniChar* buffer = (UniChar*) malloc( sizeof( UniChar ) * numUniChars );
+               assert( buffer != NULL );
+               CFStringGetCharacters( string, CFRangeMake( 0, numUniChars ), buffer );
+
+               result = PutScrapFlavor ( scrap, kScrapFlavorTypeUnicode, 0, sizeof( UniChar ) * numUniChars, buffer );
+               assert( result == noErr );
+
+               // Done with the UniChar* buffer
+               free( buffer );
+               buffer = NULL;
+               }
+               
+       // Done with the CFString
+       CFRelease( string );
+       string = NULL;
 }
 
+/*
 JArray<java::lang::String*>* org::xwt::plat::Carbon::listNativeFonts() {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       ATSFontFamilyIterator iterator;
+       OSStatus result = ATSFontFamilyIteratorCreate( kATSFontContextUnspecified, NULL, NULL, kATSOptionFlagsDefault, &iterator );
+       if ( result != noErr ) throw new java::lang::Error(JvNewStringLatin1("ASSERT"));
+
+       // Iterate over fonts twice: The first time just to count the fonts so we can create the array
+       // The second time gets the font names
+       ATSFontFamilyRef font = NULL;
+       int i = -1;
+       while ( result == noErr )
+               {
+               i ++;
+               result = ATSFontFamilyIteratorNext( iterator, &font );
+               }
+       if ( result != kATSIterationCompleted ) throw new java::lang::Error(JvNewStringLatin1("ASSERT"));
+
+       result = ATSFontFamilyIteratorReset( kATSFontContextUnspecified, NULL, NULL, kATSOptionFlagsDefault, &iterator );
+       if ( result != noErr ) throw new java::lang::Error(JvNewStringLatin1("ASSERT"));
+
+       JArray<jstring>* fonts = (JArray<jstring>*)JvNewObjectArray( i, &(::java::lang::String::class$), NULL);
+
+       ATSFontFamilyRef font = NULL;
+       result = ATSFontFamilyIteratorNext( iterator, &font );
+       while( result == noErr )
+               {
+               CFStringRef name = NULL;
+               result = ATSFontFamilyGetName( font, kATSOptionFlagsDefault, &name );
+               if ( result != noErr || name == NULL ) throw new java::lang::Error(JvNewStringLatin1("ASSERT"));
+
+               //const char* cstring = CFStringGetCStringPtr( name, kCFStringEncodingMacRoman );
+               //if ( cstring != NULL ) fprintf( stderr, "%s\n", cstring );
+               
+               jstring xwtName = CFToJavaString( name );
+
+               // 
+               xwtName = xwtName->replace(' ', '_')->toLowerCase()->concat( "0" ); 
+               CFRelease( name );
+               name = NULL;
+
+               result = ATSFontFamilyIteratorNext( iterator, &font );
+               i ++;
+               }
+       if ( result != kATSIterationCompleted ) throw new java::lang::Error(JvNewStringLatin1("ASSERT"));
+
+       result = ATSFontFamilyIteratorRelease ( &iterator );
+       if ( result != noErr ) throw new java::lang::Error(JvNewStringLatin1("ASSERT"));
+
+    return fonts;
 }
+*/
+
 
-gnu::gcj::RawData* org::xwt::plat::Carbon::fontStringToStruct(jstring s) {
+/*gnu::gcj::RawData* org::xwt::plat::Carbon::fontStringToStruct(jstring s) {
     throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+}*/
+
+// HACK: WARNING: Undocumented Hack! This may break in the future
+// For now, however, it allows a non-bundled app to create Windows! Yay!
+extern "C" { OSErr CPSEnableForegroundOperation(ProcessSerialNumber *psn); }
+
+void org::xwt::plat::Carbon::natInit() {
+       // HACK: WARNING: Undocumented Hack! This may break in the future
+       // For now, however, it allows a non-bundled app to create Windows! Yay!
+       ProcessSerialNumber currentProcess = { 0, kCurrentProcess }; // Using the default "current process" doesn't work
+       OSStatus result = GetCurrentProcess( &currentProcess );
+       assert( result == noErr );
+       result = CPSEnableForegroundOperation( &currentProcess );
+       assert( result == noErr );
+
+       // Initiaize the native font hashtable
+       // Get a copy of all available fonts
+       ItemCount numFonts;
+       result = ATSUFontCount( &numFonts );
+       assert( result == noErr );
+
+       ATSFontRef* fonts = (ATSFontRef*) malloc( sizeof(ATSFontRef) * numFonts );
+       assert( fonts != NULL );
+
+       ItemCount currentNumFonts;
+       result = ATSUGetFontIDs( fonts, numFonts, &currentNumFonts );
+       assert( result == noErr );
+       numFonts = min( currentNumFonts, numFonts ); // Just to be safe, maybe the number of fonts changed (unlikely!)
+
+       const ByteCount MAX_NAME_LENGTH = 100;
+       char familyName[MAX_NAME_LENGTH+1];
+       for ( ItemCount i = 0; i < numFonts; i ++ ) {
+               ByteCount actualBytes = 0;
+               result = ATSUFindFontName( fonts[i], kFontFamilyName, (FontPlatformCode) kFontMacintoshPlatform, kFontNoScriptCode, kFontNoLanguageCode,
+                                                         MAX_NAME_LENGTH, familyName, &actualBytes, NULL );
+
+               // If we were able to get a macintosh string for the font family name, use it to make a jstring
+               if ( result == noErr && actualBytes > 0 )
+                       {
+                       familyName[actualBytes] = '\0';
+
+                       jstring xwtName = JvNewStringLatin1( familyName, actualBytes );
+
+                       // Convert the font family name to an XWT font name
+                       xwtName = xwtName->replace(' ', '_')->toLowerCase();
+                       // Map the first font we find to this family name. The first font is the "Regular" font.
+                       if ( nativeFontCache->get( xwtName ) == NULL )
+                               nativeFontCache->put( (java::lang::Object*) xwtName, new org::xwt::plat::Carbon$WrappedRawData( (::gnu::gcj::RawData*) fonts[i] ) );
+                       }
+               else
+                       {
+                       //fprintf( stderr, "FONT HAS NO MAC NAME\n" );
+                       }
+       }
+       
+       free( fonts );
+       fonts = NULL;
+}
+
+jint org::xwt::plat::Carbon::_stringWidth(::java::lang::String* font, ::java::lang::String* text) {
+       // If the string doesn't have any characters, return zero immediately 
+       if ( text->length() == 0 ) return 0;
+       
+       ATSUStyle style = (ATSUStyle) _getATSUStyle( font );
+       CFStringRef string = JavaToCFString( text );
+
+       CFStringRef unicodeBuffer = NULL;
+       ATSUTextLayout layout = CreateTextLayout( string, style, &unicodeBuffer );
+
+       unsigned long actualNumberOfBounds = 0;
+       ATSTrapezoid glyphBounds;
+
+       // We get a single bound, since the text should only require one. If it requires more, there is an issue
+       OSStatus result = ATSUGetGlyphBounds( layout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 1, &glyphBounds, &actualNumberOfBounds );
+       assert( result == noErr && actualNumberOfBounds == 1 );
+
+       ATSUDisposeTextLayout( layout );
+       layout = NULL;
+
+       if ( unicodeBuffer != NULL ) {
+               CFRelease( unicodeBuffer );
+               unicodeBuffer = NULL;
+               }
+
+       CFRelease( string );
+       string = NULL;
+
+       return Fix2Long( glyphBounds.upperRight.x - glyphBounds.upperLeft.x );
 }
 
 jint org::xwt::plat::Carbon::_getMaxAscent(::java::lang::String* font) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       ATSUStyle style = (ATSUStyle) _getATSUStyle( font );
+       
+       ByteCount actualSize = 0;
+       ATSUTextMeasurement ascent;
+       OSStatus result = ATSUGetAttribute( style, kATSUAscentTag, sizeof( ascent ), &ascent, &actualSize );
+       assert( result == kATSUNotSetErr );
+
+       //fprintf( stderr, "ascent = %ld\n", Fix2Long( ascent ) );
+
+       return Fix2Long( ascent );
 }
 jint org::xwt::plat::Carbon::_getMaxDescent(::java::lang::String* font) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+       ATSUStyle style = (ATSUStyle) _getATSUStyle( font );
+
+       ByteCount actualSize = 0;
+       ATSUTextMeasurement descent;
+       OSStatus result = ATSUGetAttribute( style, kATSUDescentTag, sizeof( descent ), &descent, &actualSize );
+       assert( result == kATSUNotSetErr );
+
+       //fprintf( stderr, "descent = %ld\n", Fix2Long( descent ) );
+
+       return Fix2Long( descent );
 }
-jint org::xwt::plat::Carbon::_stringWidth(::java::lang::String* font, ::java::lang::String* text) {
-    throw new java::lang::Error(JvNewStringLatin1("FIXME"));
+
+gnu::gcj::RawData* org::xwt::plat::Carbon::_createATSUStyle( gnu::gcj::RawData* fontRef, jint fontSize, jboolean isBold, jboolean isItalic, jboolean isUnderline ) {
+       ATSUStyle style;
+       OSStatus result = ATSUCreateStyle( &style );
+       assert( result == noErr && style != NULL );
+
+    // Font
+    ATSFontRef font = (ATSFontRef) fontRef;
+       Fixed size = X2Fix( fontSize );
+       Boolean bold = isBold;
+       Boolean italic = isItalic;
+       Boolean underline = isUnderline;
+
+       const ATSUAttributeTag tags[] = { kATSUFontTag, kATSUSizeTag, kATSUQDBoldfaceTag, kATSUQDItalicTag, kATSUQDUnderlineTag };
+       ByteCount sizes[] = { sizeof( font ), sizeof( size ), sizeof( bold ), sizeof( italic ), sizeof( underline ) };
+       ATSUAttributeValuePtr values[] = { &font, &size, &bold, &italic, &underline };
+
+       result = ATSUSetAttributes( style, sizeof( tags ) / sizeof( *tags ), tags, sizes, values );
+       assert( result == noErr );
+
+       // TODO: Why do I have to turn this off manually? This shouldn't this be the default?
+       ATSUFontFeatureType featureType = kLigaturesType;
+       ATSUFontFeatureSelector selector = kCommonLigaturesOffSelector;
+       result = ATSUSetFontFeatures( style, 1, &featureType, &selector );
+       assert( result == noErr );
+
+       return (gnu::gcj::RawData*) style;
 }
 
+
+/** Called once XWT is initialized and the application is running. */
+void org::xwt::plat::Carbon::_running() {
+       // TODO: This is going to be a bit magical:
+       // Let's see what happens when we mix multithreading and OS X applications.
+       // Theory: Bad stuff may happen if events are delivered and other stuff is going on at
+       // the same time. However, I'm going to pray that XWT's Java stuff locks things for me,
+       // So by the time I call into the Java code, i'm the only code path active?
+       RunApplicationEventLoop();
+
+       org::xwt::plat::Carbon::exit(); 
+}
index a0d4e01..e7a6125 100644 (file)
@@ -15,31 +15,118 @@ import org.xwt.*;
 /** 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() { throw new Error("FIXME"); }
+    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); }
-    protected boolean _needsAutoClick() { throw new Error("FIXME"); }
+    protected boolean _needsAutoClick() { return false; }
     protected native int _getScreenWidth();
     protected native int _getScreenHeight();
     protected native String _getClipBoard();
     protected native void _setClipBoard(String s);
-    protected native int _stringWidth(String font, String text);
+       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() { throw new Error("FIXME"); }
+    protected boolean _needsAutoDoubleClick() { return false; }
+
+       /** 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;
+       }
+
+       /** 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() { }
-    public void init() { throw new Error("FIXME"); }
 
+    public void init() {
+               natInit();
+
+               // 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";
+               }
+
+               // Make sure that the default font exists
+               if ( _getATSUStyle( _getDefaultFont() ) == null ) throw new Error( "Default font does not exist" );
+       }
+       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; }
+    }
 
     // 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);
@@ -48,15 +135,15 @@ public class Carbon extends POSIX {
         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();
+        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);
+        //public native void setLimits(int minw, int minh, int maxw, int maxh);
         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); throw new Error("FIXME"); }
+        public CarbonSurface(Box root, boolean framed) { super(root); natInit( framed ); }
 
     }
 
@@ -65,36 +152,55 @@ public class Carbon extends POSIX {
 
     /** Implements a Picture */
     public static class CarbonPicture 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 CarbonPicture(int[] data, int w, int h) {
             this.data = data;
             this.width = w;
             this.height = h;
-           throw new Error("FIXME");
+                       natInit();
         }
 
     }
 
     /** 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; throw new Error("FIXME"); }
-        public void setClip(int x, int y, int x2, int y2) { throw new Error("FIXME"); }
-        public void drawPicture(Picture source, int x, int y) { throw new Error("FIXME"); }
-        public void drawPicture(Picture source, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2) { throw new Error("FIXME"); }
+        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();
     }
 
index a6551df..36652fd 100644 (file)
@@ -84,6 +84,14 @@ public class Win32 extends GCJ {
         int maxDescent;
     }
 
+       /** Called once XWT is initialized and the application is running. On Win32, we need to block the main thread
+       *       on a semaphore because if the main thread exits, the whole application quits. */
+       protected void _running() {
+               // gcj-win32 exit()'s when the original thread dies, so we have to deadlock ourselves
+               if (Log.on) Log.log(Main.class, "main thread blocking on new semaphore");
+               new org.xwt.util.Semaphore().block();
+       }
+
     /** takes a parsed font and finds the closest platform-specific font */
     static native Win32Font mapFont(Platform.ParsedFont pf);