From: brian Date: Fri, 30 Jan 2004 07:04:46 +0000 (+0000) Subject: 2003/09/05 04:28:35 X-Git-Tag: RC3~713 X-Git-Url: http://git.megacz.com/?p=org.ibex.core.git;a=commitdiff_plain;h=63080366bcef40f25ed20462441e8ff2ccad3649 2003/09/05 04:28:35 darcs-hash:20040130070446-aa32f-86fd19d3dca1a438674278e3aa3eac046eaa927a.gz --- diff --git a/src/org/xwt/plat/Carbon.cc b/src/org/xwt/plat/Carbon.cc index 92b2dfc..914ba77 100644 --- a/src/org/xwt/plat/Carbon.cc +++ b/src/org/xwt/plat/Carbon.cc @@ -1,1338 +1,1061 @@ -// Copyright 2002 Adam Megacz, see the COPYING file for licensing [LGPL] -// see below for copyright information on the second portion of this file +// Copyright 2003 Adam Megacz, see the COPYING file for licensing [LGPL] +// Authors: Brian Alliet and Evan Jones +#ifndef __APPLE_CC__ +#define FSF_GCC +#define __APPLE_CC__ +#else +#define APPLE_GCC +#endif #include "POSIX.cc" -#include -#include -#include -#include -#include -#include +#include "OpenGL.cc" + +#include #include -#include - -// 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 -#include -#include -#include -#include -#include -#include -#include - -#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( *uchrHandle ), keyCode, kUCKeyActionDown, - /* modifierKeyState */ modifiers, LMGetKbdType(), kUCKeyTranslateNoDeadKeysMask, &ignoredDeadKeys, - /* buffer length */ 1, - /* actual length */ &ignoredActualLength, - /* string */ &unicodeKey ); - assert( err == noErr ); - - return unicodeKey; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +// For proxy stuff +#include +// For LSOpenURLRef +#include + +#include + +#include + +using namespace org::xwt::plat; +using gnu::gcj::RawData; +using org::xwt::util::Semaphore; +using java::lang::Object; + +namespace org { namespace xwt { namespace plat { + +namespace carbon { } +using namespace carbon; + +#pragma mark ------ Carbon Namespace ------ +namespace carbon { + // We put everything that isn't in org.xwt.plat.Carbon in + // org.xwt.plat.carbon to prevent namespace conflicts + + template static inline int CompileTimeCheck() { const int something_is_wrong=1; something_is_wrong++; return 0; } + template <> static inline int CompileTimeCheck() { return 0; } + const static int unichar_check = CompileTimeCheck(); + + void funcFailed(char *func,int r); + static inline void checkStatus(OSStatus r, char *func) { if(r != noErr) funcFailed(func,r); } + static inline void checkStatus(GLboolean b, char *func) { if(!b) funcFailed(func,-1); } + static inline void checkStatus(void *p, char *func) { if(!p) funcFailed(func,-1); } + + jstring cfStringToJString(CFStringRef s) { + CFIndex length = CFStringGetLength(s); + CFRange range = CFRangeMake(0,length); + jstring js = JvAllocString(length); + UniChar *buf = (UniChar*)JvGetStringChars(js); + CFStringGetCharacters(s,range,buf); + return js; + } + + #pragma mark ------ SmartCFString ------ + class SmartCFString { + private: + CFStringRef p; + void release() { if(p) CFRelease(p); } + void checkNull() { if(!p) throw new java::lang::Error(JvNewStringLatin1("CFString function failed")); } + public: + // Constructors + SmartCFString() : p(0) { } + SmartCFString(const char *s) : p(0) { *this = s; } + SmartCFString(jstring js) : p(0) { *this = js; } + SmartCFString(CFStringRef cf) : p(0) { *this = cf; } + // Destructor + ~SmartCFString() { release(); } + // Assignment + SmartCFString& operator= (const char *s) { + release(); + if(!s) s = "(null)"; + p = CFStringCreateWithCString(kCFAllocatorDefault,s,kCFStringEncodingISOLatin1); + checkNull(); + return *this; + } + SmartCFString& operator= (jstring js) { + if(!js) return *this = "(null)"; + release(); + UniChar *buf = (UniChar*) JvGetStringChars(js); + CFIndex length = js->length(); + p = CFStringCreateWithCharacters(kCFAllocatorDefault,buf,length); + checkNull(); + return *this; + } + SmartCFString& operator= (CFStringRef cf) { + if(cf == NULL) return *this = "(null)"; + release(); + p = cf; + return *this; + } + operator CFStringRef() { return p; } + operator jstring() { return getJString(); } + + jstring getJString() { return cfStringToJString(p); } + + bool equals(const char *s) { + SmartCFString cfs(s); + return equals(cfs); + } + + bool equals(CFStringRef cfs) { + return CFStringCompare(p,cfs,0) == kCFCompareEqualTo; + } + }; + + // CHECKME: Is just making up your own four char codes really correct? + const static UInt32 kEventClassCarbonMessage = 'xwta'; + const static UInt32 kEventCarbonMessage = 'xwtb'; + const static UInt32 kEventParamCarbonMessage = 'xwtc'; + + pascal OSStatus carbon::carbonMessageEventHandler(EventHandlerCallRef handler, EventRef e, void *userData); + void fileDialogEventHandler(NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, void *userData); + +} // end namespace + +jboolean Carbon::isJaguar() { + SInt32 version; + OSStatus r = Gestalt(gestaltSystemVersion, &version); + checkStatus(r,"Gestalt"); + return version >= 0x1020; } -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; +void Carbon$CarbonMessage::natInit() { + OSStatus r; + + EventHandlerUPP upp = NewEventHandlerUPP(carbonMessageEventHandler); + EventTypeSpec eventTypes = { kEventClassCarbonMessage, kEventCarbonMessage }; + r = InstallEventHandler(GetApplicationEventTarget(),upp,1,&eventTypes,NULL,NULL); + checkStatus(r,"InstallEventHandler"); +} - return string; +void Carbon$CarbonMessage::add(Carbon$CarbonMessage *msg) { + EventRef event; + OSStatus r; + + GCJ$Retainer::retain(msg); + + r = CreateEvent(kCFAllocatorDefault,kEventClassCarbonMessage,kEventCarbonMessage,0,kEventAttributeNone,&event); + checkStatus(r,"CreateEvent"); + r = SetEventParameter(event,kEventParamCarbonMessage,typeVoidPtr,sizeof(msg),(void*)&msg); + checkStatus(r,"SetEventParameter"); + r = PostEventToQueue(GetMainEventQueue(),event,kEventPriorityHigh); + checkStatus(r,"PostEventToQueue"); + ReleaseEvent(event); } -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 ); +pascal OSStatus carbon::carbonMessageEventHandler(EventHandlerCallRef handler, EventRef e, void *userData) { + UInt32 eKind = GetEventKind(e); + UInt32 eClass = GetEventClass(e); + OSStatus r; + Carbon$CarbonMessage *msg; + if(eClass != kEventClassCarbonMessage || eKind != kEventCarbonMessage) + return eventNotHandledErr; + r = GetEventParameter(e,kEventParamCarbonMessage,typeVoidPtr,NULL,sizeof(msg),NULL,&msg); + checkStatus(r,"GetEventParameter"); + msg->perform(); + GCJ$Retainer::release(msg); + return noErr; +} - // Convert the string - charsConverted = CFStringGetBytes( s, entireString, kCFStringEncodingUTF8, 0, false, buffer, requiredSize, NULL ); - assert( charsConverted == CFStringGetLength( s ) ); - buffer[requiredSize] = '\0'; +#pragma mark ------ Utility Functions ------ - jstring string = JvNewStringUTF( (char*) buffer ); - assert( string != NULL ); +void carbon::funcFailed(char *func,int r){ + fprintf(stderr,"%s() failed (%d)\n",func,r); + exit(EXIT_FAILURE); +} - free( buffer ); - buffer = NULL; +#pragma mark ------ CarbonPicture Methods ------ - return string; -} +#pragma mark ----- Carbon Surface Methods ---- -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 ); - } +void Carbon$CarbonSurface::natSyncCursor(jint n) { + ThemeCursor c; + // see Carbon.java for what these numbers mean + switch(n) { + case 1: c = kThemeWatchCursor; + case 2: c = kThemePlusCursor; + case 3: c = kThemeIBeamCursor; + case 4: c = kThemePointingHandCursor; + case 5: c = kThemeOpenHandCursor; + case 6: c = kThemeResizeLeftRightCursor; + default: c = kThemeArrowCursor; + } + SetThemeCursor(c); } - -// 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, ¤tBounds ); - 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( kEventParamMouseWheelAxis, typeMouseWheelAxis, &inAxis ); - assert( noErr == result ); - - SInt32 inDelta; - result = inEvent.GetParameter( 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; + +void Carbon$CarbonSurface::natSetInvisible(jboolean b) { + WindowRef window = (WindowRef) rawWindowRef; + fprintf(stderr,"Making window %s\n",b?"invisible":"visible"); + if(b) HideWindow(window); + else ShowWindow(window); } -/** 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; +void Carbon$CarbonSurface::nat_setMaximized(jboolean b) { + WindowRef window = (WindowRef) rawWindowRef; + Point ideal = { 10000, 10000 }; + OSStatus r = ZoomWindowIdeal(window,(b?inZoomOut:inZoomIn),&ideal); + checkStatus(r,"ZoomWindowIdeal"); } -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 ); +void Carbon$CarbonSurface::nat_setMinimized(jboolean b) { + WindowRef window = (WindowRef) rawWindowRef; + if((IsWindowCollapsed(window) ? 1 : 0) == (b ? 1 : 0)) return; + OSStatus r = CollapseWindow(window,b); + checkStatus(r,"CollapseWindow"); } -// 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 Carbon$CarbonSurface::natSetTitleBarText(jstring js) { + SmartCFString s = js; + WindowRef window = (WindowRef) rawWindowRef; + SetWindowTitleWithCFString(window,s); } -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 Carbon$CarbonSurface::natToBack() { + WindowRef window = (WindowRef) rawWindowRef; + SendBehind(window,NULL); } -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; - - //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 Carbon$CarbonSurface::natToFront() { + WindowRef window = (WindowRef) rawWindowRef; + fprintf(stderr,"SelectWindow()\n"); + SelectWindow(window); } -/*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; +#pragma mark ---- Window Event Handler ---- +namespace carbon { +static const EventTypeSpec eventTypeSpecs[] = { + // kEventClassCommand + // { kEventClassCommand, ??? }, + + // kEventClassWindow + { kEventClassWindow, kEventWindowUpdate }, + { kEventClassWindow, kEventWindowBoundsChanged }, + { kEventClassWindow, kEventWindowActivated }, + { kEventClassWindow, kEventWindowDeactivated }, + { kEventClassWindow, kEventWindowZoomed }, + { kEventClassWindow, kEventWindowCollapsed }, + { kEventClassWindow, kEventWindowExpanded }, + { kEventClassWindow, kEventWindowClose }, + { kEventClassWindow, kEventWindowClosed }, + + // kEventClassKeyboard + { kEventClassKeyboard, kEventRawKeyDown }, + { kEventClassKeyboard, kEventRawKeyRepeat }, + { kEventClassKeyboard, kEventRawKeyUp }, + { kEventClassKeyboard, kEventRawKeyModifiersChanged }, + + // kEventClassMouse + { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseUp }, + { kEventClassMouse, kEventMouseMoved }, + { kEventClassMouse, kEventMouseDragged }, + { kEventClassMouse, kEventMouseWheelMoved }, +}; + +pascal OSStatus windowEventHandler(EventHandlerCallRef handler, EventRef e, void *userData) { + Carbon$CarbonSurface *surface = (Carbon$CarbonSurface*) userData; + UInt32 eKind = GetEventKind(e); + UInt32 eClass = GetEventClass(e); + OSStatus r; + + switch(eClass) { + case kEventClassCommand: + switch(eKind) { + // TODO: handle menu items + } + break; + case kEventClassKeyboard: + switch(eKind) { + case kEventRawKeyDown: + case kEventRawKeyRepeat: + case kEventRawKeyUp: { + UInt32 keyCode; + jstring js; + + r = GetEventParameter(e,kEventParamKeyCode,typeUInt32,NULL,sizeof(keyCode),NULL,&keyCode); + checkStatus(r,"GetEventParameter"); + + switch(keyCode) { + // These values were obtained by experimentation. I can't find any constants for them + // in the header files + case 126: js = JvNewStringLatin1("up"); break; + case 125: js = JvNewStringLatin1("down"); break; + case 124: js = JvNewStringLatin1("right"); break; + case 123: js = JvNewStringLatin1("left"); break; + case 122: js = JvNewStringLatin1("f1"); break; + case 120: js = JvNewStringLatin1("f2"); break; + case 99: js = JvNewStringLatin1("f3"); break; + case 118: js = JvNewStringLatin1("f4"); break; + case 96: js = JvNewStringLatin1("f5"); break; + case 97: js = JvNewStringLatin1("f6"); break; + case 98: js = JvNewStringLatin1("f7"); break; + case 100: js = JvNewStringLatin1("f8"); break; + case 101: js = JvNewStringLatin1("f9"); break; + case 109: js = JvNewStringLatin1("f10"); break; + case 103: js = JvNewStringLatin1("f11"); break; + case 111: js = JvNewStringLatin1("f12"); break; + case 105: js = JvNewStringLatin1("f13"); break; + case 114: js = JvNewStringLatin1("insert"); break; + case 117: js = JvNewStringLatin1("delete"); break; + case 116: js = JvNewStringLatin1("page_up"); break; + case 121: js = JvNewStringLatin1("page_down"); break; + case 115: js = JvNewStringLatin1("home"); break; + case 119: js = JvNewStringLatin1("end"); break; + case 71: js = JvNewStringLatin1("num_lock"); break; + case 53: js = JvNewStringLatin1("escape"); break; + case 51: js = JvNewStringLatin1("back_space"); break; + case 36: js = JvNewStringLatin1("enter"); break; + case 48: js = JvNewStringLatin1("tab"); break; + case 76: js = JvNewStringLatin1("enter"); break; // number pad enter + default: { + UInt32 size; + UInt32 modifiers = surface->modifiers; + r = GetEventParameter(e,kEventParamKeyUnicodes,typeUnicodeText,NULL,0,&size,NULL); + checkStatus(r,"GetEventParameter"); + if(size == 0 || (modifiers & controlKey && size>sizeof(UniChar))) return eventNotHandledErr; + + js = JvAllocString(size/sizeof(UniChar)); + UniChar *buf = (UniChar*)JvGetStringChars(js); + r = GetEventParameter(e,kEventParamKeyUnicodes,typeUnicodeText,NULL,size,NULL,buf); + checkStatus(r,"GetEventParameter"); + + if(!buf[0]) return eventNotHandledErr; // shouldn't happen + // odd, when the ctrl key is pressed a-"`" become 1-31, this brings them back to the corect values + if(modifiers & controlKey && buf[0] < 32) buf[0] += 0x60; + break; + } + } + + if(eKind == kEventRawKeyUp) + surface->KeyReleased(js); + else + surface->KeyPressed(js); + return noErr; + } + case kEventRawKeyModifiersChanged: { + const static struct { + UInt32 mask; + jstring xwtKey; + } modifiersTable[] = { + { shiftKey, JvNewStringLatin1("shift") }, + { alphaLock, JvNewStringLatin1("caps_lock") }, + { controlKey, JvNewStringLatin1("control") }, + { optionKey, JvNewStringLatin1("alt") }, + { kEventKeyModifierNumLockMask, JvNewStringLatin1("num_lock") }, + { 0, 0 } + }; + + UInt32 oldModifiers = (UInt32) surface->modifiers; + UInt32 newModifiers; + r = GetEventParameter(e,kEventParamKeyModifiers,typeUInt32,NULL,sizeof(newModifiers),NULL,&newModifiers); + checkStatus(r,"GetEventParameter"); + surface->modifiers = (jint) newModifiers; + UInt32 changedModifiers = oldModifiers ^ newModifiers; + + for(int i=0;modifiersTable[i].mask;i++) { + UInt32 mask = modifiersTable[i].mask; + if(!(changedModifiers & mask)) continue; + if(newModifiers & mask) + surface->KeyPressed(modifiersTable[i].xwtKey); + else + surface->KeyReleased(modifiersTable[i].xwtKey); + } + return noErr; + } + } + break; + case kEventClassMouse: + // The default handler gets first dibs on mouse events + // (this catches the titlebar, resize box, etc) + r = CallNextEventHandler(handler, e); + if(r != eventNotHandledErr && eKind != kEventMouseMoved && eKind != kEventMouseDragged) return r; + + switch(eKind) { + case kEventMouseMoved: + case kEventMouseDragged: { + WindowRef window = (WindowRef) surface->rawWindowRef; + Point p; + Rect rect; + + r = GetEventParameter(e,kEventParamMouseLocation,typeQDPoint,NULL,sizeof(p),NULL,&p); + checkStatus(r,"GetEventParameter"); + r = GetWindowBounds(window,kWindowContentRgn,&rect); + checkStatus(r,"GetWindowBounds"); + surface->Move(p.h-rect.left,p.v-rect.top); + return noErr; + } + case kEventMouseDown: + case kEventMouseUp: { + EventMouseButton button; + UInt32 clickCount; + jint xwtButton; + r = GetEventParameter(e,kEventParamMouseButton,typeMouseButton,NULL,sizeof(button),NULL,&button); + checkStatus(r,"GetEventParameter"); + r = GetEventParameter(e,kEventParamClickCount,typeUInt32,NULL,sizeof(clickCount),NULL,&clickCount); + checkStatus(r,"GetEventParameter"); + + switch(button) { + case kEventMouseButtonPrimary: xwtButton = 1; break; + case kEventMouseButtonSecondary: xwtButton = 2; break; + case kEventMouseButtonTertiary: xwtButton = 3; break; + default: return noErr; + } + if(eKind == kEventMouseDown) { + surface->Press(xwtButton); + } else { + surface->Release(xwtButton); + while(clickCount > 1) { + surface->DoubleClick(xwtButton); + clickCount-=2; + } + if(clickCount) surface->Click(xwtButton); + } + return noErr; + } + case kEventMouseWheelMoved: { + EventMouseWheelAxis axis; + SInt32 delta; + r = GetEventParameter(e,kEventParamMouseWheelAxis,typeMouseWheelAxis,NULL,sizeof(axis),NULL,&axis); + checkStatus(r,"GetEventParameter"); + if(axis != kEventMouseWheelAxisY) break; + r = GetEventParameter(e,kEventParamMouseWheelDelta,typeSInt32,NULL,sizeof(delta),NULL,&delta); + checkStatus(r,"GetEventParameter"); + fprintf(stderr,"kEventMouseWheelMoved: delta: %d",delta); + // surface->MouseWheelMoved(...) IMPROVMENT: mouse wheel support in xwt + return noErr; + } + } + break; + + case kEventClassWindow: { + WindowRef window; + r = GetEventParameter(e,kEventParamDirectObject,typeWindowRef,NULL,sizeof(window),NULL,&window); + checkStatus(r,"kEventClassWindow/GetEventParameter"); + + if((RawData*)window != surface->rawWindowRef) Carbon::abort(JvNewStringLatin1("window != surface->window")); + + switch(eKind) { + case kEventWindowUpdate: { + surface->Dirty(0,0,surface->width,surface->height); + return noErr; + } + case kEventWindowBoundsChanged: { + UInt32 attr; + Rect rect; + r = GetEventParameter(e,kEventParamAttributes,typeUInt32,NULL,sizeof(attr),NULL,&attr); + checkStatus(r,"kEventWindowBoundsChanged/GetEventParameter"); + r = GetWindowBounds(window,kWindowContentRgn,&rect); + checkStatus(r,"GetWindowBounds"); + if(attr & kWindowBoundsChangeSizeChanged) { + jint w = rect.right-rect.left; + jint h = rect.bottom-rect.top; + if(attr & kWindowBoundsChangeUserResize && surface->maximized) + surface->Maximized(false); + surface->reshape(w,h); + surface->SizeChange(w,h); + surface->Dirty(0,0,w,h); + surface->blitDirtyScreenRegions(); + } + if(attr & kWindowBoundsChangeOriginChanged) { + surface->PosChange(rect.left,rect.top); + } + return noErr; + } + case kEventWindowActivated: + case kEventWindowDeactivated: { + surface->Focused(eKind == kEventWindowActivated); + return noErr; + } + case kEventWindowZoomed: { + fprintf(stderr,"Zoomed....\n"); + surface->Maximized(true); + return noErr; + } + case kEventWindowCollapsed: { + surface->Minimized(true); + return noErr; + } + case kEventWindowExpanded: { + surface->Minimized(false); + return noErr; + } + case kEventWindowClose: { + surface->Close(); + return noErr; + } + case kEventWindowClosed: { + DisposeEventHandlerUPP((EventHandlerUPP)surface->rawEventHandlerUPP); + GCJ$Retainer::release(surface); + return noErr; + } + } + } + break; + } + return eventNotHandledErr; } - -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 ); +} // end namespace + +void Carbon$CarbonSurface::natInit(jboolean framed) { + WindowRef window; + Rect rect; + WindowClass wc = framed ? kDocumentWindowClass : kPlainWindowClass; + // FIXME: unframed windows should appear in the window menu + // This probably needs a hack similar to whats in Cocoa.mm + WindowAttributes attr = kWindowStandardHandlerAttribute| + (framed ? kWindowInWindowMenuAttribute|kWindowStandardDocumentAttributes|kWindowLiveResizeAttribute : 0); + OSStatus r; + + rect.top = 0; rect.left = 0; rect.bottom = 10; rect.right=10; + r = CreateNewWindow(wc,attr,&rect,&window); + checkStatus(r,"CreateNewWindow"); + + GCJ$Retainer::retain(this); // Need to account for the EventHandlers pointer to us + EventHandlerUPP upp = NewEventHandlerUPP(windowEventHandler); + + r = InstallWindowEventHandler(window,upp,sizeof(eventTypeSpecs)/sizeof(EventTypeSpec),eventTypeSpecs,this,NULL); + checkStatus(r,"InstallWindowEventHandler"); + + rawWindowRef = (RawData*) window; + rawEventHandlerUPP = (RawData*) upp; } -void org::xwt::plat::Carbon$CarbonDoubleBuffer::finalize() { - CGContextRelease( (CGContextRef) gc ); - CGImageRelease( (CGImageRef) image ); - free( bitmapData ); - bitmapData = NULL; +void Carbon$CarbonSurface::natDispose() { + WindowRef window = (WindowRef) rawWindowRef; + DisposeWindow(window); } -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; - 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 Carbon$CarbonSurface::natSetIcon(org::xwt::Picture *_p) { } -void org::xwt::plat::Carbon$CarbonDoubleBuffer::fillRect (jint x, jint y, jint x2, jint y2, jint argb) { - // Set the Fill color to match - SetARGBFillColor( (CGContextRef) gc, argb ); - CGContextFillRect( (CGContextRef) gc, CGRectMake( x, y, x2 - x, y2 - y ) ); +void Carbon$CarbonSurface::natSetLocation(jint x, jint y) { + WindowRef window = (WindowRef) rawWindowRef; + Rect rect; + OSStatus r = GetWindowBounds(window,kWindowStructureRgn,&rect); + checkStatus(r,"GetWindowBounds"); + rect.bottom = y + (rect.bottom - rect.top); + rect.right = x + (rect.right - rect.left); + rect.top = y; + rect.left = x; + r = SetWindowBounds(window,kWindowStructureRgn,&rect); + checkStatus(r,"SetWindowBounds"); + r = ConstrainWindowToScreen(window,kWindowStructureRgn,kWindowConstrainMoveRegardlessOfFit,NULL,NULL); + checkStatus(r,"ConstrainWindowToScreen"); } -void org::xwt::plat::Carbon$CarbonDoubleBuffer::drawString(::java::lang::String* font, ::java::lang::String* text, jint x, jint y, jint argb) { - 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; +void Carbon$CarbonSurface::natSetSize(jint w, jint h) { + WindowRef window = (WindowRef) rawWindowRef; + Rect rect; + OSStatus r = GetWindowBounds(window,kWindowStructureRgn,&rect); + checkStatus(r,"GetWindowBounds"); + rect.bottom = rect.top + h; + rect.right = rect.left + w; + r = SetWindowBounds(window,kWindowStructureRgn,&rect); + checkStatus(r,"SetWindowBounds"); + r = ConstrainWindowToScreen(window,kWindowStructureRgn,kWindowConstrainMoveRegardlessOfFit,NULL,NULL); + checkStatus(r,"ConstrainWindowToScreen"); } -// CarbonSurface ////////////////////////////////////////////////////////////////////// - -void org::xwt::plat::Carbon$CarbonSurface::setIcon(org::xwt::Picture* pic) { - 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 Carbon$CarbonSurface::natSetLimits(jint minw, jint minh, jint maxw, jint maxh) { + WindowRef window = (WindowRef) rawWindowRef; + const int maxMax = 32767; + const int minMinW = 80; + const int minMinH = 20; + HISize min,max; + min.width = minw > maxMax ? maxMax : (minw < minMinW ? minMinW : minw); + min.height = minh > maxMax ? maxMax : (minh < minMinH ? minMinH : minh); + max.width = maxw > maxMax ? maxMax : (maxw < minMinW ? minMinW : maxw); + max.height = maxh > maxMax ? maxMax : (maxh < minMinH ? minMinH : maxh); + OSStatus r = SetWindowResizeLimits(window,&min,&max); + checkStatus(r,"SetWindowResizeLimits"); } -void org::xwt::plat::Carbon$CarbonSurface::setTitleBarText(java::lang::String* s) { - CFStringRef string = JavaToCFString( s ); - OSStatus result = SetWindowTitleWithCFString( (WindowRef) window, string ); - assert( result == noErr ); - CFRelease( string ); - string = NULL; +#pragma mark ------ Carbon Methods ------ +void carbon::fileDialogEventHandler(NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, void *userData) { + Carbon$FileDialogHelper *helper = (Carbon$FileDialogHelper*) userData; + NavDialogRef dialog = callBackParms->context; + OSStatus r; + switch(callBackSelector) { + case kNavCBUserAction: { + NavUserAction action = NavDialogGetUserAction(dialog); + if(action == kNavUserActionNone || action == kNavUserActionCancel) { + helper->fileName = 0; + } else { + NavReplyRecord reply; + r = NavDialogGetReply(dialog,&reply); + checkStatus(r,"NavDialogGetReply"); + + AEKeyword keyword; + FSRef ref; + char buf[4096]; + r = AEGetNthPtr(&reply.selection,1,typeFSRef,&keyword,NULL,&ref,sizeof(ref),NULL); + checkStatus(r,"AEGetNthPtr"); + r = FSRefMakePath(&ref,(UInt8*)buf,sizeof(buf)-1); + checkStatus(r,"FSRefMakePath"); + helper->fileName = JvNewStringLatin1(buf); + if(helper->save) helper->saveName = cfStringToJString(reply.saveFileName); + r = NavDisposeReply(&reply); + checkStatus(r,"NavDialogGetReply"); + } + helper->sem->release(); + break; + } + case kNavCBTerminate: + DisposeNavEventUPP((NavEventUPP)helper->rawUPP); + NavDialogDispose(dialog); + break; + } } -void org::xwt::plat::Carbon$CarbonSurface::setSize (jint width, jint height) { - 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 Carbon::natFileDialog(Carbon$FileDialogHelper *helper,jstring suggestion_, jboolean write) { + NavDialogRef dlg; + SmartCFString suggestion = suggestion_; + CFStringRef message = CFSTR("By selecting a file in this dialog you are giving this XWT application permission to access that file."); + OSStatus r; + WindowRef window = FrontWindow(); + NavDialogCreationOptions options; + + NavEventUPP handler = NewNavEventUPP(carbon::fileDialogEventHandler); + + NavGetDefaultDialogCreationOptions(&options); + options.optionFlags = + (options.optionFlags|kNavAllFilesInPopup|kNavSelectAllReadableItem|kNavDontUseCustomFrame|kNavDontConfirmReplacement) + &~(kNavAllowStationery|kNavAllowMultipleFiles); + options.clientName = CFSTR("XWT"); + if(write) + options.saveFileName = suggestion; + options.message = message; + options.modality = window ? kWindowModalityWindowModal : kWindowModalityAppModal; + options.parentWindow = window; + + if(write) + r = NavCreatePutFileDialog(&options,0,0,handler,helper,&dlg); + else + r = NavCreateGetFileDialog(&options,NULL,handler,NULL,NULL,helper,&dlg); + checkStatus(r,"NavCreate(Get/Put)FileDialog"); + + helper->rawUPP = (RawData*)handler; + NavDialogRun(dlg); } -void org::xwt::plat::Carbon$CarbonSurface::setLocation (jint x, jint y) { - 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 ) ); +jstring Carbon::natGetClipBoard() { + ScrapRef scrap; + OSStatus r; + Size size,size2; + + r = GetCurrentScrap(&scrap); + checkStatus(r,"GetCurrentScrap"); + + r = GetScrapFlavorSize( scrap, kScrapFlavorTypeUnicode, &size); + if(r == scrapFlavorNotFoundErr) return JvNewStringLatin1(""); + checkStatus(r,"GetScrapFlavorSize"); + + unsigned int length = size/sizeof(UniChar); + + jstring js = JvAllocString(length); + UniChar *buf = (UniChar*) JvGetStringChars(js); + size2 = size; + r = GetScrapFlavorData(scrap,kScrapFlavorTypeUnicode,&size2,buf); + if(r == scrapFlavorNotFoundErr); + checkStatus(r,"GetScrapFlavorData"); + if(size2 != size) return JvNewStringLatin1(""); + + return js; } -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 Carbon::natSetClipBoard(jstring js) { + unsigned int length = js->length(); + ScrapRef scrap; + OSStatus r; -void org::xwt::plat::Carbon$CarbonSurface::toFront() { - 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( ¤tProcess ); - assert( result == noErr ); - result = SetFrontProcessWithOptions( ¤tProcess, kSetFrontProcessFrontWindowOnly ); - assert( result == noErr ); + r = GetCurrentScrap(&scrap); + checkStatus(r,"GetCurrentScrap"); + + r = PutScrapFlavor(scrap,kScrapFlavorTypeUnicode,0,sizeof(UniChar)*length,JvGetStringChars(js)); + checkStatus(r,"PutScrapFlavor"); } -void org::xwt::plat::Carbon$CarbonSurface::toBack() { - SendBehind( (WindowRef) window, NULL ); +Proxy *Carbon::natDetectProxy() { + using org::xwt::Proxy; + Proxy *p=0; + CFStringRef string; + CFNumberRef number; + SmartCFString smartString; + CFArrayRef exceptionList; + int i; + + CFDictionaryRef proxyInfo = SCDynamicStoreCopyProxies(NULL); + if(proxyInfo == NULL) return 0; + +#define doproto(proto,var) \ + number = (CFNumberRef) CFDictionaryGetValue(proxyInfo,kSCPropNetProxies ## proto ## Enable); \ + if(number != NULL && CFGetTypeID(number) != CFNumberGetTypeID()) number = NULL; \ + if(number && CFNumberGetValue(number,kCFNumberIntType,&i) && i) { \ + string = (CFStringRef) CFDictionaryGetValue(proxyInfo, kSCPropNetProxies ## proto ## Proxy);\ + if(string != NULL && CFGetTypeID(string) != CFStringGetTypeID()) string = NULL; \ + number = (CFNumberRef) CFDictionaryGetValue(proxyInfo, kSCPropNetProxies ## proto ## Port); \ + if(number != NULL && CFGetTypeID(number) != CFNumberGetTypeID()) number = NULL; \ + if(string && number && CFNumberGetValue(number,kCFNumberIntType,&i) && i) { \ + if(!p) p = new Proxy(); \ + p->var ## ProxyHost = cfStringToJString(string); \ + p->var ## ProxyPort = i; \ + } \ + } +doproto(HTTP,http) +doproto(HTTPS,https) +doproto(SOCKS,socks) +#undef doproto + + exceptionList = (CFArrayRef) CFDictionaryGetValue(proxyInfo,kSCPropNetProxiesExceptionsList); + if(exceptionList != NULL && CFGetTypeID(exceptionList) != CFArrayGetTypeID()) exceptionList = NULL; + if(p && exceptionList && CFArrayGetCount(exceptionList)) { + CFIndex count = CFArrayGetCount(exceptionList); + p->excluded = (JArray*) + JvNewObjectArray(count,&java::lang::String::class$,0); + for(i=0;iexcluded)[i] = string ? cfStringToJString(string) : JvNewStringLatin1("(null)"); + } + } + CFRelease(proxyInfo); + + return p; +/* + exceptionList = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesExceptionsList]; + if(p && exceptionList && [exceptionList count]) { + NSLog(@"excl: %@",exceptionList); + p->excluded = (JArray*) + JvNewObjectArray([exceptionList count],&java::lang::String::class$,0); + for(i=0;i<[exceptionList count];i++) + elements(p->excluded)[i] = [[exceptionList objectAtIndex: i] jstring]; + } + return p; + */ + +/* + using org::xwt::Proxy; + AutoARP pool; + Proxy *p=0; + NSString *host; + NSNumber *port; + NSArray *exceptionList; + unsigned int i; + NSDictionary *proxyInfo = (NSDictionary*)SCDynamicStoreCopyProxies(NULL); + + if(proxyInfo == NULL) return 0; + if([[proxyInfo objectForKey: (NSString*) kSCPropNetProxiesHTTPEnable] boolValue]) { + host = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesHTTPProxy]; + port = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesHTTPPort]; + if(host && [port intValue]) { + if(!p) p = new Proxy(); + p->httpProxyHost = [host jstring]; + p->httpProxyPort = [port intValue]; + } + } + if([[proxyInfo objectForKey: (NSString*) kSCPropNetProxiesHTTPSEnable] boolValue]) { + host = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesHTTPSProxy]; + port = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesHTTPSPort]; + if(host && [port intValue]) { + if(!p) p = new Proxy(); + p->httpsProxyHost = [host jstring]; + p->httpsProxyPort = [port intValue]; + } + } + if([[proxyInfo objectForKey: (NSString*) kSCPropNetProxiesSOCKSEnable] boolValue]) { + host = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesSOCKSProxy]; + port = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesSOCKSPort]; + if(host && [port intValue]) { + if(!p) p = new Proxy(); + p->socksProxyHost = [host jstring]; + p->socksProxyPort = [port intValue]; + } + } + + exceptionList = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesExceptionsList]; + if(p && exceptionList && [exceptionList count]) { + NSLog(@"excl: %@",exceptionList); + p->excluded = (JArray*) + JvNewObjectArray([exceptionList count],&java::lang::String::class$,0); + for(i=0;i<[exceptionList count];i++) + elements(p->excluded)[i] = [[exceptionList objectAtIndex: i] jstring]; + } + return p; +*/ } -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 ); +jint Carbon::cgScreenWidth() { + return CGDisplayPixelsWide(kCGDirectMainDisplay); } -void org::xwt::plat::Carbon$CarbonSurface::_dispose() { - ReleaseWindow( (WindowRef) window ); - window = NULL; +jint Carbon::cgScreenHeight() { + return CGDisplayPixelsHigh(kCGDirectMainDisplay); } -void org::xwt::plat::Carbon$CarbonSurface::setInvisible(jboolean i) { - //fprintf( stderr, "invisible = %d\n", i ); - if ( i ) { - HideWindow( (WindowRef) window ); - } else { - ShowWindow( (WindowRef) window ); - } +void Carbon::_newBrowserWindow(jstring js) { + SmartCFString cfs = js; + CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault,cfs,NULL); + SmartCFString scheme = CFURLCopyScheme(url); + if(scheme.equals(CFStringRef("http"))) + LSOpenCFURLRef(url,NULL); + CFRelease(url); } -void org::xwt::plat::Carbon$CarbonSurface::_setMaximized(jboolean b) { - 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 Carbon::_exit() { + QuitApplicationEventLoop(); } -void org::xwt::plat::Carbon$CarbonSurface::_setMinimized(jboolean b) { - 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 ); +#define XWT_CARBON_NO_BUNDLE_HACK +#ifdef XWT_CARBON_NO_BUNDLE_HACK +extern "C" { + OSErr CPSEnableForegroundOperation(ProcessSerialNumber *psn); + OSErr CPSSetFrontProcess(ProcessSerialNumber *psn); } - -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 ); +#endif +void Carbon::natInit() { + OSStatus r; + #ifdef XWT_CARBON_NO_BUNDLE_HACK + { + ProcessSerialNumber currentProcess = { 0, kCurrentProcess }; + + ::fprintf(stderr,"Doing XWT_CARBON_NO_BUNDLE_HACK\n"); + r = GetCurrentProcess(¤tProcess); + checkStatus(r,"GetCurrentProcess"); + r = CPSEnableForegroundOperation( ¤tProcess ); + checkStatus(r,"CPSEnableForegroundOperation"); + r = CPSSetFrontProcess(¤tProcess); + checkStatus(r,"CPSSetFrontProcess"); + } + #else + { + IBNibRef nib; + r = CreateNibReference(CFSTR("MainMenu"), &nib); + checkStatus(r,"CreateNibReference"); + r = SetMenuBarFromNib(nib, CFSTR("MenuBar")); + checkStatus(r,"SetMenuBarFromNib"); + DisposeNibReference(nib); + + // FIXME: Install menu event handler + } + #endif } -void org::xwt::plat::Carbon$CarbonPicture::finalize() { - CGImageRelease( (CGImage*) image ); - image = NULL; +void Carbon::_running() { + RunApplicationEventLoop(); + ExitToShell(); } -// Carbon /////////////////////////////////////////////////////////////////// +#pragma mark ------ OpenGL Functions ----- -jint org::xwt::plat::Carbon::_getScreenWidth() { - //fprintf( stderr, "screen width = %d\n", CGDisplayPixelsWide( kCGDirectMainDisplay ) ); - return CGDisplayPixelsWide( kCGDirectMainDisplay ); +void Carbon$CarbonOpenGL::activateSharedContext() { + AGLContext ctx = (AGLContext) rawSharedContext; + aglSetCurrentContext(ctx); } -jint org::xwt::plat::Carbon::_getScreenHeight() { - //fprintf( stderr, "screen height = %d\n", CGDisplayPixelsHigh( kCGDirectMainDisplay ) ); - return CGDisplayPixelsHigh( kCGDirectMainDisplay ); +jboolean Carbon$CarbonOpenGL::initPixelFormat() { + GLint attr[] = { + AGL_NO_RECOVERY, + AGL_RGBA, + AGL_DEPTH_SIZE, 32, + AGL_RED_SIZE, 8, + AGL_GREEN_SIZE, 8, + AGL_RED_SIZE, 8, + AGL_ALPHA_SIZE, 8, + AGL_NONE + }; + + rawPixelFormat = (RawData*) aglChoosePixelFormat(NULL,0,attr); + return rawPixelFormat != 0; } -jstring org::xwt::plat::Carbon::_getClipBoard() { - // 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( 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 Carbon$CarbonOpenGL::initSharedContext() { + AGLPixelFormat fmt = (AGLPixelFormat) rawPixelFormat; + rawSharedContext = (RawData*) aglCreateContext(fmt,NULL); + checkStatus(rawSharedContext,"aglCreateContext"); } -void org::xwt::plat::Carbon::_setClipBoard(jstring s) { - 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; +void Carbon$GLCarbonDoubleBuffer::natInit() { + WindowClass wc = kPlainWindowClass; + WindowAttributes attr = 0; + WindowRef window; + Rect rect; + OSStatus r; + AGLContext ctx,shared; + AGLPixelFormat fmt; + GLboolean b; + GLuint tex; + GLuint target; + + rect.top = rect.left = 0; + rect.right = width + rect.left; + rect.bottom = height + rect.top; + + r = CreateNewWindow(wc,attr,&rect,&window); + checkStatus(r,"CreateNewWindow"); + + shared = (AGLContext) gl->rawSharedContext; + fmt = (AGLPixelFormat) gl->rawPixelFormat; + ctx = aglCreateContext(fmt,shared); + checkStatus(ctx, "aglCreateContext"); + + b = aglSetDrawable(ctx,GetWindowPort(window)); + checkStatus(b,"aglSetDrawable"); + + aglSetCurrentContext(ctx); + aglUpdateContext(ctx); + drawableInit(width,height); + glClear(GL_COLOR_BUFFER_BIT); + + aglSetCurrentContext(shared); + target = rectTexture ? GL_TEXTURE_RECTANGLE_EXT : GL_TEXTURE_2D; + glEnable(target); + glGenTextures(1,&tex); + glBindTexture(target,tex); + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + aglSurfaceTexture (shared, target, GL_RGBA, ctx); + checkGLError(); + glDisable(target); + + //ShowWindow(window); + textureName = (jint) tex; + rawWindowRef = (RawData*) window; + rawCTX = (RawData*) ctx; } -/* -JArray* org::xwt::plat::Carbon::listNativeFonts() { - 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* fonts = (JArray*)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; +void Carbon$GLCarbonDoubleBuffer::activateContext() { + AGLContext ctx = (AGLContext) rawCTX; + aglSetCurrentContext(ctx); } -*/ - -/*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( ¤tProcess ); - assert( result == noErr ); - result = CPSEnableForegroundOperation( ¤tProcess ); - 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, ¤tNumFonts ); - 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; +void Carbon$GLCarbonDoubleBuffer::natCleanup(RawData* rawWindowRef, RawData* rawCTX) { + WindowRef window = (WindowRef) rawWindowRef; + AGLContext ctx = (AGLContext) rawCTX; + aglDestroyContext(ctx); + DisposeWindow(window); } -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 ); +void Carbon$GLCarbonSurface::natBlit(Carbon$GLCarbonDoubleBuffer *db, jint sx1, jint sy1, jint dx1, jint dy1, jint dx2, jint dy2) { + AGLContext ctx = (AGLContext) rawCTX; + int sx2 = sx1 + (dx2-dx1); + int sy2 = sy1 + (dy2-dy1); + db->activateContext(); + glFlush(); + checkGLError(); + aglSetCurrentContext(ctx); + checkGLError(); + if(db->rectTexture) { + glEnable(GL_TEXTURE_RECTANGLE_EXT); + checkGLError(); + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, db->textureName); + checkGLError(); + glBegin(GL_QUADS); + glTexCoord2i (sx1, sy1 ); + glVertex3i (dx1, dy1, 0); + glTexCoord2i (sx2, sy1 ); + glVertex3i (dx2, dy1, 0); + glTexCoord2i (sx2, sy2 ); + glVertex3i (dx2, dy2, 0); + glTexCoord2i (sx1, sy2 ); + glVertex3i (dx1, dy2, 0); + glEnd(); + checkGLError(); + glDisable(GL_TEXTURE_RECTANGLE_EXT); + checkGLError(); + } else { + float tx1,ty1,tx2,ty2; // normalized texture coords + tx1 = (float) sx1 / (float) db->width; + ty1 = (float) sy1 / (float) db->height; + tx2 = (float) sx2 / (float) db->width; + ty2 = (float) sy2 / (float) db->height; + + glColor4f(1.0f,1.0f,1.0f,1.0f); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, db->textureName); + checkGLError(); + glBegin(GL_QUADS); + glTexCoord2f (tx1, ty1 ); + glVertex3i (dx1, dy1, 0); + glTexCoord2f (tx2, ty1 ); + glVertex3i (dx2, dy1, 0); + glTexCoord2f (tx2, ty2 ); + glVertex3i (dx2, dy2, 0); + glTexCoord2f (tx1, ty2 ); + glVertex3i (dx1, dy2, 0); + glEnd(); + glDisable(GL_TEXTURE_2D); + checkGLError(); + } + glFlush(); } -jint org::xwt::plat::Carbon::_getMaxAscent(::java::lang::String* font) { - 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 ); +void Carbon$GLCarbonSurface::natReshape(jint w, jint h) { + AGLContext ctx = (AGLContext) rawCTX; + + aglSetCurrentContext (ctx); + aglUpdateContext(ctx); + + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, w, h, 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef (0.375, 0.375, 0.0); + checkGLError(); } -jint org::xwt::plat::Carbon::_getMaxDescent(::java::lang::String* font) { - 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 ); +void Carbon$GLCarbonSurface::natInit() { + WindowRef window = (WindowRef) rawWindowRef; + AGLContext ctx,shared; + AGLPixelFormat fmt; + GLboolean b; + + shared = (AGLContext) gl->rawSharedContext; + fmt = (AGLPixelFormat) gl->rawPixelFormat; + ctx = aglCreateContext(fmt,shared); + checkStatus(ctx, "aglCreateContext"); + + b = aglSetDrawable(ctx,GetWindowPort(window)); + checkStatus(b,"aglSetDrawable"); + + aglSetCurrentContext(ctx); + checkGLError(); + + rawCTX = (RawData*)ctx; + + reshape(10,10); + + aglSetCurrentContext(ctx); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); } -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; +void Carbon$GLCarbonSurface::natDispose() { + AGLContext ctx = (AGLContext) rawCTX; + aglDestroyContext(ctx); + Carbon$CarbonSurface::natDispose(); } - -/** 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(); -} +} } } // end namepsace org::xwt::plat diff --git a/src/org/xwt/plat/Carbon.java b/src/org/xwt/plat/Carbon.java index 5dc019f..c6ec350 100644 --- a/src/org/xwt/plat/Carbon.java +++ b/src/org/xwt/plat/Carbon.java @@ -1,204 +1,321 @@ -// 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); } - } diff --git a/src/org/xwt/plat/GCJ.cc b/src/org/xwt/plat/GCJ.cc index c1333e4..fef47a5 100644 --- a/src/org/xwt/plat/GCJ.cc +++ b/src/org/xwt/plat/GCJ.cc @@ -108,3 +108,8 @@ void org::xwt::plat::GCJ$JPEG::nativeDecompress() { jpeg_destroy_decompress(&cinfo); } +// C++ new/delete operators (JvMalloc never fails) +void* operator new(size_t size) { return JvMalloc(size); } +void* operator new[](size_t size) { return JvMalloc(size);} +void operator delete(void *p) { JvFree(p); } +void operator delete[](void *p) { JvFree(p); } diff --git a/src/org/xwt/plat/GCJ.java b/src/org/xwt/plat/GCJ.java index 6f545c0..98f3d9b 100644 --- a/src/org/xwt/plat/GCJ.java +++ b/src/org/xwt/plat/GCJ.java @@ -49,6 +49,33 @@ public abstract class GCJ extends Platform { private JPEG(InputStream is) { this.is = is; nativeDecompress(); buffer = null; } private native void nativeDecompress(); } - + + // FIXME: This could be optimized (a lot) by using a custom hashtable + public final static class Retainer { + private static Hash table = new Hash(); + private final static class Key { + private Object o; + public Key(Object o) { this.o = o; } + public int hashCode() { return o == null ? 0 : o.hashCode(); } + public boolean equals(Object o2) { return (o2 instanceof Key) && ((Key)o2).o == o; } + } + private final static class Entry { + private int refCount; + public Entry() { inc(); } + public void inc() { refCount++; } + public boolean dec() { return --refCount == 0; } + } + public static synchronized void retain(Object o) { + Key k = new Key(o); + Entry r = (Entry) table.get(k); + if(r == null) table.put(k,new Entry()); + else r.inc(); + } + public static synchronized void release(Object o) { + Key k = new Key(o); + Entry e = (Entry) table.get(k); + if(e == null) throw new Error("Retainer::Release on unknown object"); + if(e.dec()) { table.remove(k); } + } + } } - diff --git a/src/org/xwt/plat/OpenGL.cc b/src/org/xwt/plat/OpenGL.cc new file mode 100644 index 0000000..f23c179 --- /dev/null +++ b/src/org/xwt/plat/OpenGL.cc @@ -0,0 +1,269 @@ +// Copyright 2003 Adam Megacz, see the COPYING file for licensing [LGPL] +// Author: Brian Alliet + +// IMPROVMENT: use alpha testing? might be faster + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef __APPLE__ +#include +#include +#include +#else +#warning I am not sure if these is correct +#include +#include +#include +#endif + +#include + +namespace org { namespace xwt { namespace plat { + +#define checkGLError() checkGLError2(__FILE__,__LINE__) +static void checkGLError2(const char *file,int line) { + GLenum err = glGetError(); + if(err == GL_NO_ERROR) return; + + fprintf(stderr,"%s:%d: GL Error: %s",file,line,(char *) gluErrorString(err)); + exit(EXIT_FAILURE); +} + +void OpenGL::natInit() { + GLint i; + const GLubyte *s; + activateSharedContext(); + s = glGetString(GL_EXTENSIONS); + rectangularTextures = (jboolean) gluCheckExtension((GLubyte*)"GL_EXT_texture_rectangle",s); + glGetIntegerv(GL_MAX_TEXTURE_SIZE,&i); + maxTexSize = (jint) i; + if(rectangularTextures) { + glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_EXT,&i); + maxRectTexSize = (jint) i; + } + s = glGetString(GL_VENDOR); + vendor = JvNewStringLatin1(s==0 ? "" : (char*)s); + s = glGetString(GL_RENDERER); + renderer = JvNewStringLatin1(s==0 ? "" : (char*)s); + s = glGetString(GL_VERSION); + version = JvNewStringLatin1(s==0 ? "" : (char*)s); +} + +void OpenGL$GLDoubleBuffer::drawableInit(jint width, jint height) { + glClearColor (0.3f, 0.7f, 1.0f, 1.0f); + glClearDepth( 0.0f ); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + glShadeModel(GL_SMOOTH); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glViewport(0,0,width,height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0,width,height,0,-1,1); + // CHECKME: This causes textures to be blurry, but something like this + // (but not this) might be required to draw straight lines + //glMatrixMode(GL_MODELVIEW); + //glLoadIdentity(); + //glTranslatef(0.375, 0.375, 0.0); + + checkGLError(); +} + +void OpenGL$GLDoubleBuffer::setColor(jint argb) { + float alpha = ((argb >> 24) & 0xff) / 255.0; + float red = ((argb >> 16) & 0xff) / 255.0; + float green = ((argb >> 8) & 0xff) / 255.0; + float blue = ((argb >> 0) & 0xff) / 255.0; + glColor4f(red,green,blue,alpha); +} + +void OpenGL$GLDoubleBuffer::fillTrapezoid(jint x1, jint x2, jint y1, jint x3, jint x4, jint y2, jint color) { + activateContext(); + setColor(color); + glBegin(GL_QUADS); + glVertex3f(x1,y1,0.0f ); + glVertex3f(x3,y2,0.0f ); + glVertex3f(x4,y2,0.0f ); + glVertex3f(x2,y1,0.0f ); + glEnd(); +} + +void OpenGL$GLDoubleBuffer::fillRect(jint x1, jint y1, jint x2, jint y2,jint color) { + activateContext(); + setColor(color); + glBegin(GL_QUADS); + glVertex3f(x1,y2,0.0f ); + glVertex3f(x2,y2,0.0f ); + glVertex3f(x2,y1,0.0f ); + glVertex3f(x1,y1,0.0f ); + glEnd(); +} + +void OpenGL$GLDoubleBuffer::setClip(jint x1, jint y1, jint x2, jint y2) { + //fprintf(stderr,"setClip: %d %d %d %d\n",x1,y1,x2,y2); + if(x1==0 && y1==0 && x2==width && y2==height) { + if(glScissorEnabled) { + activateContext(); + glDisable(GL_SCISSOR_TEST); + glScissorEnabled = false; + } + return; + } + activateContext(); + if(!glScissorEnabled) { + glEnable(GL_SCISSOR_TEST); + glScissorEnabled = true; + } + if((x1 >= x2) || (y1 >= y2)) + glScissor(0,0,0,0); + else + glScissor(x1,height-y2,x2-x1,y2-y1); + checkGLError(); +} + +void OpenGL$GLDoubleBuffer::resetClip() { + activateContext(); + if(glScissorEnabled) { + glDisable(GL_SCISSOR_TEST); + glScissorEnabled = false; + } +} + +void OpenGL$RectGLPicture::natInit(JArray *data_) { + unsigned char *buf; + int i,j; + int size = width*height; + jint *data = elements(data_); + buf = new unsigned char[size*4]; + GLuint tex; + + for(i=0,j=0;i> 16) & 0xff; + buf[j+1] = (pixel >> 8) & 0xff; + buf[j+2] = (pixel >> 0) & 0xff; + buf[j+3] = (pixel >> 24) & 0xff; + } + + gl->activateSharedContext(); + glEnable(GL_TEXTURE_RECTANGLE_EXT); + glGenTextures(1,&tex); + glBindTexture(GL_TEXTURE_RECTANGLE_EXT,tex); + glPixelStorei(GL_PACK_ALIGNMENT,1); + glTexImage2D(GL_TEXTURE_RECTANGLE_EXT,0,4,width,height,0,GL_RGBA,GL_UNSIGNED_BYTE,buf); + delete buf; + + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexParameterf(GL_TEXTURE_RECTANGLE_EXT,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_RECTANGLE_EXT,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glDisable(GL_TEXTURE_RECTANGLE_EXT); + checkGLError(); + + textureName = (jint)tex; +} + +void OpenGL$RectGLPicture::draw(jint dx1, jint dy1, jint dx2, jint dy2,jint sx1, jint sy1, jint sx2, jint sy2) { + glColor4f(1.0f,1.0f,1.0f,1.0f); + glEnable(GL_TEXTURE_RECTANGLE_EXT); + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, textureName); + glBegin(GL_QUADS); + glTexCoord2i (sx1, sy1 ); + glVertex3i (dx1, dy1, 0); + glTexCoord2i (sx2, sy1 ); + glVertex3i (dx2, dy1, 0); + glTexCoord2i (sx2, sy2 ); + glVertex3i (dx2, dy2, 0); + glTexCoord2i (sx1, sy2 ); + glVertex3i (dx1, dy2, 0); + glEnd(); + glDisable(GL_TEXTURE_RECTANGLE_EXT); + checkGLError(); + +} + +void OpenGL::natDeleteTexture(jint tex_) { + activateSharedContext(); + GLuint tex = (GLuint) tex_; + glDeleteTextures(1,&tex); +} + +void OpenGL$SquareGLPicture::natInit(JArray *data_) { + unsigned char *buf; + int row,col,p; + jint *data = elements(data_); + buf = new unsigned char[texWidth*texHeight*4]; + GLuint tex; + + p=0; + for(row=0;row> 16) & 0xff; + buf[p+1] = (pixel >> 8) & 0xff; + buf[p+2] = (pixel >> 0) & 0xff; + buf[p+3] = (pixel >> 24) & 0xff; + p+=4; + } + for(;col < texWidth;col++,p+=4) + buf[p+0] = buf[p+1] = buf[p+2] = buf[p+3] = 0; + } + for(;row < texHeight;row++) + for(col=0;colactivateSharedContext(); + glEnable(GL_TEXTURE_2D); + glGenTextures(1,&tex); + glBindTexture(GL_TEXTURE_2D,tex); + glPixelStorei(GL_PACK_ALIGNMENT,1); + glTexImage2D(GL_TEXTURE_2D,0,4,texWidth,texHeight,0,GL_RGBA,GL_UNSIGNED_BYTE,buf); + checkGLError(); + delete buf; + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glDisable(GL_TEXTURE_2D); + checkGLError(); + + textureName = (jint)tex; +} + +void OpenGL$SquareGLPicture::draw(jint dx1, jint dy1, jint dx2, jint dy2,jint sx1, jint sy1, jint sx2, jint sy2) { + float tx1,ty1,tx2,ty2; // normalized texture coords + tx1 = (float) sx1 / (float) texWidth; + ty1 = (float) sy1 / (float) texHeight; + tx2 = (float) sx2 / (float) texWidth; + ty2 = (float) sy2 / (float) texHeight; + + glColor4f(1.0f,1.0f,1.0f,1.0f); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, textureName); + checkGLError(); + glBegin(GL_QUADS); + glTexCoord2f (tx1, ty1 ); + glVertex3i (dx1, dy1, 0); + glTexCoord2f (tx2, ty1 ); + glVertex3i (dx2, dy1, 0); + glTexCoord2f (tx2, ty2 ); + glVertex3i (dx2, dy2, 0); + glTexCoord2f (tx1, ty2 ); + glVertex3i (dx1, dy2, 0); + glEnd(); + glDisable(GL_TEXTURE_2D); + checkGLError(); +} + +} } } // end org::xwt::plat diff --git a/src/org/xwt/plat/OpenGL.java b/src/org/xwt/plat/OpenGL.java new file mode 100644 index 0000000..8f25506 --- /dev/null +++ b/src/org/xwt/plat/OpenGL.java @@ -0,0 +1,255 @@ +// Copyright 2003 Adam Megacz, see the COPYING file for licensing [LGPL] +// Author: Brian Alliet + +package org.xwt.plat; +import org.xwt.*; +import org.xwt.util.*; + +abstract class OpenGL { + static final boolean pretendToBeACrappyVideoCard = false; + boolean rectangularTextures; + boolean hasRectangularTextures() { return rectangularTextures; } + int maxTexSize; + int maxRectTexSize; + String version; + String renderer; + String vendor; + + private native void natInit(); + + private static float parseVersion(String s) { + int end = s.indexOf(' '); + if(end < 0) end = s.length(); + try { + return Float.parseFloat(s.substring(0,end)); + } catch(NumberFormatException e) { + return 1.1f; // just a guess + } + } + // This MUST be called after OpenGL is instansiated (and activateSharedContext is functioning) + public void init() throws NotSupportedException { + natInit(); + float v = parseVersion(version); + // If we disable linear filtering (and therefor GL_CLAMP_TO_EDGE) we could probably get by with less + if(v < 1.2) throw new NotSupportedException("OpenGL 1.2 or greater is required. (you have: " + version +" - " + v + ")"); + if(pretendToBeACrappyVideoCard) { + maxTexSize = 512; + maxRectTexSize = 0; + rectangularTextures = false; + } + Log.log(this,"Renderer: " + renderer); + Log.log(this,"Version: " + version); + Log.log(this,"Vendor: " + vendor); + Log.log(this,"Rectangular textures: " + (rectangularTextures ? "supported" : "unsupported")); + Log.log(this,"Max texture size: " + maxTexSize); + Log.log(this,"Max rectangular texture size: " + maxRectTexSize); + } + + protected abstract void activateSharedContext(); + + public static class NotSupportedException extends Exception { + public NotSupportedException(String s) { super(s); } + } + + public static abstract class GLDoubleBuffer extends DoubleBuffer { + protected int width; + protected int height; + public int getWidth() { return width; } + public int getHeight() { return height; } + + private boolean glScissorEnabled = false; + + public GLDoubleBuffer(int width, int height) { + this.width = width; + this.height = height; + } + + // This should activate the drawing context and prepare the double buffer for drawing + protected abstract void activateContext(); + + protected static native void drawableInit(int w, int h); + + private static native void setColor(int color); + + public native void setClip(int x, int y, int x2, int y2); + public native void resetClip(); + public native void fillRect(int x, int y, int x2, int y2, int color); + public native void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int color); + + public void drawString(String font, String text, int x, int y, int color) { + //System.out.println("drawString(): " + text); + } + + public void drawPicture(org.xwt.Picture source, int x, int y) { + activateContext(); + GLPicture p = (GLPicture) source; + p.draw(x,y); + } + public void drawPicture(org.xwt.Picture source, int dx1, int dy1, int dx2, int dy2,int sx1, int sy1, int sx2, int sy2) { + activateContext(); + GLPicture p = (GLPicture) source; + p.draw(dx1,dy1,dx2,dy2,sx1,sy1,sx2,sy2); + } + } + + public final static int roundToPowerOf2(int n) { + if(((n-1)&n)==0) return n; + for(int x=2;x!=0;x<<=1) if(n < x) return x; + return 0; + } + + public Picture createPicture(int[] data, int w, int h) { + if(w*h != data.length) throw new Error("w*h != data.length"); + if(rectangularTextures && w <= maxRectTexSize && h <= maxRectTexSize) new RectGLPicture(data,w,h,this); + if(w <= maxTexSize && h <= maxTexSize) return new SquareGLPicture(data,w,h,this); + return new MosaicGLPicture(data,w,h,this); + } + + private native void natDeleteTexture(int tex); + public void deleteTexture(final int tex) { + // CHECKME: Is this safe to do from finalize()? + // natDeleteTexture MUST be run from the message queue thread + MessageQueue.add(new Message() { public void perform() { + natDeleteTexture(tex); + }}); + } + + private static abstract class GLPicture extends Picture { + protected int width; + protected int height; + + public final int getWidth() { return width; } + public final int getHeight() { return height; } + + public GLPicture(int w, int h) { + this.width = w; + this.height = h; + } + + public void draw(int x, int y) { draw(x,y,x+width,y+height,0,0,width,height); } + public abstract void draw(int dx1, int dy1, int dx2, int dy2,int sx1, int sy1, int sx2, int sy2); + protected abstract void finalize(); + } + + private static class RectGLPicture extends GLPicture { + private OpenGL gl; + public int textureName; + + public native void natInit(int[] data); + + public RectGLPicture(int[] data,int w, int h, OpenGL gl) { + super(w,h); + this.gl = gl; + natInit(data); + } + + public native void draw(int dx1, int dy1, int dx2, int dy2,int sx1, int sy1, int sx2, int sy2); + protected void finalize() { gl.deleteTexture(textureName); } + } + + private static class SquareGLPicture extends GLPicture { + private int texHeight; + private int texWidth; + private OpenGL gl; + public int textureName; + + public native void natInit(int[] data); + + public SquareGLPicture(int[] data,int w, int h, OpenGL gl) { + super(w,h); + this.gl = gl; + if(w > 0x7fffffff || h > 0x7fffffff) throw new Error("insane texture size: " + w + "x" + h); + texHeight = roundToPowerOf2(h); + texWidth = roundToPowerOf2(w); + natInit(data); + } + + public native void draw(int dx1, int dy1, int dx2, int dy2,int sx1, int sy1, int sx2, int sy2); + protected void finalize() { gl.deleteTexture(textureName); } + } + + private static class MosaicGLPicture extends GLPicture { + private static final int overlap = 8; + + int psize; + GLPicture[][] pics; + + private static float wastedSpace(int w,int h,int size) { + int w2 = size * ((w+size-1)/size); + int h2 = size * ((h+size-1)/size); + return 1.0f-(float)(w*h)/(float)(w2*h2); + } + + private static int[] subData(int[] data, int x, int y, int w, int h, int rowSize) { + int[] sub = new int[w*h]; + int row = y; + try { + for(int i=0;i 0.40) psize/=2; + Log.log(this,"Using psize: " + psize + " for " + w + "x" + h + " image (wasted space: " + wastedSpace(w,h,psize)); + + int rows = (h+psize-1)/psize; + int cols = (w+psize-1)/psize; + int tmp,tmp2; + + pics = new GLPicture[rows][cols]; + for(int i=0;i b ? a : b; } + private static final int min(int a, int b) { return a < b ? a : b; } + + public void draw(int dx1, int dy1, int dx2, int dy2,int sx1, int sy1, int sx2, int sy2) { + double xscale = (double)(dx2-dx1)/(double)(sx2-sx1); + double yscale = (double)(dy2-dy1)/(double)(sy2-sy1); + int totalWidth = width; + int totalHeight = height; + // *{x,y}{1,2} key: d=dest s=src, p=bounds of this picture, i=intersection of s and p, pd = dest of this pic + //System.out.println("Starting draw..." + sx1 + "," + sy1 + " " + sx2 + "," + sy2 + " to " + dx1 +"," + dy1 + " " + dx2 + "," + dy2); + for(int i=0;i= ix2 || iy1 >= iy2) continue; // no intersection + + int pdx1 = dx1 + (int) (xscale*(ix1-sx1)); + int pdy1 = dy1 + (int) (yscale*(iy1-sy1)); + int pdx2 = dx2 - (int) (xscale*(sx2-ix2)); + int pdy2 = dy2 - (int) (yscale*(sy2-iy2)); + + //System.out.println("" + i + "," + j + " is good... drawing from " + (ix1-px1) + "," + (iy1-py1) + " " + (ix2-px1) + "," + (iy2-py1) + " to " + pdx1 + "," + pdy1 + " to " + pdx2 + "," + pdy2); + + pics[i][j].draw(pdx1,pdy1,pdx2,pdy2,ix1-px1,iy1-py1,ix2-px1,iy2-py1); + } + } + } + } +}