92b2dfc13c2f474dd13596987a7292814ca470c8
[org.ibex.core.git] / src / org / xwt / plat / Carbon.cc
1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [LGPL]
2 // see below for copyright information on the second portion of this file
3
4 #include "POSIX.cc"
5 #include <org/xwt/plat/Carbon.h>
6 #include <org/xwt/plat/Carbon$CarbonSurface.h>
7 #include <org/xwt/plat/Carbon$CarbonPicture.h>
8 #include <org/xwt/plat/Carbon$CarbonDoubleBuffer.h>
9 #include <org/xwt/plat/Carbon$WrappedRawData.h>
10 #include <java/lang/System.h>
11 #include <java/lang/Error.h>
12 #include <java/io/PrintStream.h>
13
14 // Apple's version of GCC automatically defines this symbol.
15 // Since XWT is built with a "stock" version of GCC, we need to define
16 // this manually to get the system header files to allow us to include them
17 #define __APPLE_CPP__ 1
18
19 // Standard GCC doesn't support precompiled headers,
20 // meaning that it is WAY faster to just include the individual headers
21 #include <CoreFoundation/CFString.h>
22 #include <HIToolbox/MacWindows.h>
23 #include <HIToolbox/MacApplication.h>
24 #include <HIToolbox/Scrap.h>
25 #include <HIToolbox/Appearance.h>
26 #include <HIToolbox/CarbonEvents.h>
27 #include <HIToolbox/HIView.h>
28 #include <QD/ATSUnicode.h>
29
30 #define min(a, b) ((a) < (b) ? (a) : (b))
31 #define max(a, b) ((a) < (b) ? (b) : (a))
32
33 #define FORMAT_RECT( x ) x.left, x.top, x.right, x.bottom
34 #define PRINT_RECT( x ) fprintf( stderr, #x " = (%d, %d) (%d, %d)\n", x.left, x.top, x.right, x.bottom )
35 #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 );
36
37
38 static const EventTypeSpec CarbonSurfaceEventInfo[] = {
39         { kEventClassWindow, kEventWindowUpdate },
40         { kEventClassWindow, kEventWindowBoundsChanged },
41         { kEventClassWindow, kEventWindowActivated },
42         { kEventClassWindow, kEventWindowDeactivated },
43         { kEventClassWindow, kEventWindowClose },
44         { kEventClassWindow, kEventWindowZoomed },
45         { kEventClassWindow, kEventWindowExpanded },
46         { kEventClassWindow, kEventWindowCollapsed },
47
48
49         { kEventClassKeyboard, kEventRawKeyDown },
50         { kEventClassKeyboard, kEventRawKeyRepeat },
51         { kEventClassKeyboard, kEventRawKeyUp },
52         { kEventClassKeyboard, kEventRawKeyModifiersChanged },
53
54         
55         { kEventClassMouse, kEventMouseDown },
56         { kEventClassMouse, kEventMouseUp },
57         { kEventClassMouse, kEventMouseMoved },
58         { kEventClassMouse, kEventMouseDragged },
59         { kEventClassMouse, kEventMouseExited },
60
61         
62 };
63
64 static inline UniChar GetCharacterWithoutModifiers( EventRef rawKeyboardEvent )
65 {
66         UInt32 keyCode;
67         // Get the key code from the raw key event
68         GetEventParameter( rawKeyboardEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof( keyCode ), NULL, &keyCode );
69
70         // Get the current keyboard layout
71  // TODO: If this is a performance sink, we need to cache these values
72         SInt16 lastKeyLayoutID = GetScriptVariable( /*currentKeyScript*/ GetScriptManagerVariable(smKeyScript), smScriptKeys);
73         Handle uchrHandle = GetResource('uchr', lastKeyLayoutID);
74
75         // Translate the key press ignoring ctrl and option
76         UInt32 ignoredDeadKeys = 0;
77         UInt32 ignoredActualLength = 0;
78         UniChar unicodeKey = 0;
79
80         // We actually want the current shift key value
81         UInt32 modifiers = (GetCurrentEventKeyModifiers() & shiftKey) >> 8;
82         OSStatus err = UCKeyTranslate( reinterpret_cast<UCKeyboardLayout*>( *uchrHandle ), keyCode, kUCKeyActionDown,
83                                                                 /* modifierKeyState */ modifiers, LMGetKbdType(), kUCKeyTranslateNoDeadKeysMask, &ignoredDeadKeys,
84                                                                 /* buffer length */ 1,
85                                                                 /* actual length */ &ignoredActualLength,
86                                                                 /* string */ &unicodeKey );
87         assert( err == noErr );
88
89         return unicodeKey;
90 }
91
92 static CFStringRef JavaToCFString(java::lang::String* s) {
93         int len = min(1024, JvGetStringUTFLength(s));
94
95     char* buffer = (char*) malloc( len );
96     JvGetStringUTFRegion(s, 0, s->length(), buffer);
97
98         // Create a CFString from the UTF8 string
99         CFStringRef string = CFStringCreateWithBytes( NULL, (UInt8*) buffer, len, kCFStringEncodingUTF8, false );
100         assert( string != NULL );
101
102         free( buffer );
103         buffer = NULL;
104
105         return string;
106 }
107
108 static jstring CFToJavaString( CFStringRef s ) {
109         // TODO: I can probably use CFStringCreateExternalRepresentation
110         // First determine the size of the buffer required
111         const CFRange entireString = CFRangeMake( 0, CFStringGetLength( s ) );
112         CFIndex requiredSize = 0;
113         CFIndex charsConverted = CFStringGetBytes( s, entireString, kCFStringEncodingUTF8, 0, false, NULL, 0, &requiredSize );
114         assert( charsConverted == CFStringGetLength( s ) );
115
116         // Allocate the buffer, plus an additional byte for the null byte
117         UInt8* buffer = (UInt8*) malloc( requiredSize + 1 );
118         assert( buffer != NULL );
119
120         // Convert the string
121         charsConverted = CFStringGetBytes( s, entireString, kCFStringEncodingUTF8, 0, false, buffer, requiredSize, NULL );
122         assert( charsConverted == CFStringGetLength( s ) );
123
124         buffer[requiredSize] = '\0';
125
126         jstring string = JvNewStringUTF( (char*) buffer );
127         assert( string != NULL );
128
129         free( buffer );
130         buffer = NULL;
131
132         return string;
133 }
134
135 static jstring UniCharToXWTString( UniChar character )
136 {
137         switch ( character ) {
138                 case '\t': return JvNewStringLatin1("tab");
139                 case kEscapeCharCode: return JvNewStringLatin1("escape");
140                 case '\n':
141                 case kEnterCharCode:
142                 case kReturnCharCode:
143                         return JvNewStringLatin1("enter");
144                 case kBackspaceCharCode: return JvNewStringLatin1("back_space");
145                         // escape == clear
146                 //case kClearCharCode: return JvNewStringLatin1("clear");
147                 //case VK_PAUSE: return JvNewStringLatin1("pause");
148                 case kPageUpCharCode: return JvNewStringLatin1("page_up");
149                 case kPageDownCharCode: return JvNewStringLatin1("page_down");
150                 case kEndCharCode: return JvNewStringLatin1("end");
151                 case kHomeCharCode: return JvNewStringLatin1("home");
152                 case kLeftArrowCharCode: return JvNewStringLatin1("left");
153                 case kUpArrowCharCode: return JvNewStringLatin1("up");
154                 case kRightArrowCharCode: return JvNewStringLatin1("right");
155                 case kDownArrowCharCode: return JvNewStringLatin1("down");
156                         // TODO: Is help always the insert key in the mac world? on my keyboard it is
157                 case kHelpCharCode: return JvNewStringLatin1("insert");
158                 case kDeleteCharCode: return JvNewStringLatin1("delete");
159                         // TODO: How can I determine if a numpad key was pressed?
160                 //case VK_NUMPAD0: return JvNewStringLatin1("numpad0");
161                 //case VK_NUMPAD1: return JvNewStringLatin1("numpad1");
162                 //case VK_NUMPAD2: return JvNewStringLatin1("numpad2");
163                 //case VK_NUMPAD3: return JvNewStringLatin1("numpad3");
164                 //case VK_NUMPAD4: return JvNewStringLatin1("numpad4");
165                 //case VK_NUMPAD5: return JvNewStringLatin1("numpad5");
166                 //case VK_NUMPAD6: return JvNewStringLatin1("numpad6");
167                 //case VK_NUMPAD7: return JvNewStringLatin1("numpad7");
168                 //case VK_NUMPAD8: return JvNewStringLatin1("numpad8");
169                 //case VK_NUMPAD9: return JvNewStringLatin1("numpad9");
170                         // TODO: How do I get the function key?
171                 case kFunctionKeyCharCode: return JvNewStringLatin1("f1");
172                 //case VK_F2: return JvNewStringLatin1("f2");
173                 //case VK_F3: return JvNewStringLatin1("f3");
174                 //case VK_F4: return JvNewStringLatin1("f4");
175                 //case VK_F5: return JvNewStringLatin1("f5");
176                 //case VK_F6: return JvNewStringLatin1("f6");
177                 //case VK_F7: return JvNewStringLatin1("f7");
178                 //case VK_F8: return JvNewStringLatin1("f8");
179                 //case VK_F9: return JvNewStringLatin1("f9");
180                 //case VK_F10: return JvNewStringLatin1("f10");
181                 //case VK_F11: return JvNewStringLatin1("f11");
182                 //case VK_F12: return JvNewStringLatin1("f12");
183                         
184                 default:
185                         // Convert the character into a CFString, then into a java string
186                         CFStringRef string = CFStringCreateWithCharactersNoCopy( NULL, &character, 1, kCFAllocatorNull );
187                         assert( string != NULL );
188
189                         jstring str = CFToJavaString( string );
190
191                         CFRelease( string );
192                         string = NULL;
193
194                         return str;
195         }
196
197         // We should never get here
198         assert( false );
199         return NULL;
200 }
201
202 static void ModifierKeyTest( org::xwt::plat::Carbon$CarbonSurface* surface, UInt32 changedModifiers, UInt32 currentModifiers, UInt32 keyMask, const char* string )
203 {
204         // If the modifier key changed
205         if ( changedModifiers & keyMask ) {
206                 jstring str = JvNewStringLatin1( string );
207
208                 // And it is currently down, dispatch a keypressed event
209                 if ( currentModifiers & keyMask ) surface->KeyPressed( str );
210                 // otherwise dispatch a key released event
211                 else surface->KeyReleased( str );
212         }
213 }
214
215 // TODO: Do I have to make the event handlers "pascal", or use "NewEventHandlerUPP"?
216 pascal static OSStatus CarbonSurfaceEventHandler( EventHandlerCallRef handler, EventRef event, void* data ) {
217         UInt32 eventClass = GetEventClass( event );
218         UInt32 eventKind = GetEventKind( event );
219         OSStatus result = eventNotHandledErr;
220         org::xwt::plat::Carbon$CarbonSurface* surface = (org::xwt::plat::Carbon$CarbonSurface*)data;
221
222         //JvSynchronize dummy( surface );
223         
224         switch(eventClass) {
225                 case kEventClassWindow:
226                         switch(eventKind)
227                                 {
228                                 case kEventWindowUpdate:
229                                         {
230                                                 //fprintf( stderr, "window update " );
231
232                                                 // Create the graphics context
233                                                 result = CreateCGContextForPort(GetWindowPort( (WindowRef) surface->window), (CGContextRef*) &surface->gc);
234                                                 assert( result == noErr && surface->gc != NULL );
235
236                                                 // Quartz uses the bottom left as its origin. Translate to use the top left.
237                                                 Rect bounds;
238                                                 GetWindowBounds( (WindowRef) surface->window, kWindowContentRgn, &bounds );
239                                                 assert( result == noErr );
240                                                 CGContextTranslateCTM( (CGContextRef) surface->gc, 0, bounds.bottom - bounds.top );
241                                                 CGContextScaleCTM( (CGContextRef) surface->gc, 1, -1 );
242
243                                                 // Convert the context to a CGRect with local coordinates
244                                                 CGRect contentBounds = CGRectMake( 0, 0, bounds.right - bounds.left, bounds.bottom - bounds.top );
245
246                                                 //PRINT_CGRECT( contentBounds );
247
248                                                 // Get the invalid region
249                                                 Rect globalInvalidRect;
250                                                 result = GetWindowBounds( (WindowRef) surface->window, kWindowUpdateRgn, &globalInvalidRect );
251                                                 assert( result == noErr );
252
253                                                 CGRect invalidRect = CGRectMake( globalInvalidRect.left - bounds.left, globalInvalidRect.top - bounds.top, globalInvalidRect.right - globalInvalidRect.left, globalInvalidRect.bottom - globalInvalidRect.top );
254
255                                                 //invalidRect = CGRectIntersection( invalidRect, contentBounds );
256
257                                                 //PRINT_CGRECT( invalidRect );
258
259                                                 BeginUpdate( (WindowRef) surface->window );
260
261                                                 // Erase the region with white
262                                                 // TODO: Draw the window background instead
263                                                 //result = SetThemeWindowBackground( (WindowRef) surface->window, ThemeBrush inBrush, true );
264                                                 CGContextSetRGBFillColor( (CGContextRef) surface->gc, 1.0, 1.0, 1.0, 1.0 );
265                                                 CGContextFillRect( (CGContextRef) surface->gc, invalidRect );                   
266
267                                                 //CGContextSetRGBFillColor( (CGContextRef) surface->gc, red, green, blue, 0.5 );
268                                                 //CGContextFillRect( (CGContextRef) surface->gc, CGRectMake( 0, 0, (bounds.right - bounds.left), (bounds.bottom - bounds.top) ) );
269                                                 // Add the invalid region to XWT's dirty regions
270                                                 surface->Dirty( (jint) invalidRect.origin.x, (jint) invalidRect.origin.y, (jint) invalidRect.size.width, (jint) invalidRect.size.height );
271                                                 // Then tell it to actually do the drawing
272                                                 surface->blitDirtyScreenRegions();
273
274                                                 EndUpdate( (WindowRef) surface->window );
275
276                                                 // Free the context
277                                                 CGContextFlush( (CGContextRef) surface->gc );
278                                                 CGContextRelease( (CGContextRef) surface->gc );
279                                                 surface->gc = NULL;
280                                                 result = noErr;
281                                                 break;
282                                         }
283
284                                 case kEventWindowBoundsChanged:
285                                         {
286                                                 // We can't use the event parameter: it is the window bounds, not the content area bounds
287                                                 HIRect currentBounds;
288                                                 result = GetEventParameter( event, kEventParamCurrentBounds, typeHIRect, NULL, sizeof( currentBounds ), NULL, &currentBounds );
289                                                 assert( result == noErr );
290                                                 
291                                                 // Get the event attributes to determine if size and/or origin have changed
292                                                 UInt32 attributes;
293                                                 result = GetEventParameter( event, kEventParamAttributes, typeUInt32, NULL, sizeof( attributes ), NULL, &attributes );
294                                                 assert( result == noErr );
295
296                                                 if ( attributes & kWindowBoundsChangeSizeChanged ) {
297                                                         // If maximize is set, unset it
298                                                         if ( attributes & kWindowBoundsChangeUserResize && surface->maximized ) {
299                                                                 //fprintf( stderr, "maximized = 0\n" );
300                                                                 surface->Maximized( false );
301                                                         }
302                                                         
303                                                         //fprintf( stderr, "size changed\n" );
304                                                         surface->SizeChange( (jint) currentBounds.size.width, (jint) currentBounds.size.height );
305
306                                                         
307                                                 }
308                                                 
309                                                 if ( attributes & kWindowBoundsChangeOriginChanged ) {
310                                                         //fprintf( stderr, "origin changed\n" );
311                                                         surface->PosChange( (jint) currentBounds.origin.x, (jint) currentBounds.origin.y );
312                                                 }
313
314                                                 result = noErr;
315                                                 break;
316                                         }
317
318                                 case kEventWindowActivated:
319                                 case kEventWindowDeactivated:
320                                         {
321                                                 //fprintf( stderr, "focus = %d\n", (eventKind == kEventWindowActivated) );
322                                                 surface->Focused( (eventKind == kEventWindowActivated) );
323                                                 result = noErr;
324                                                 break;
325                                         }
326
327                                 case kEventWindowZoomed:
328                                         {
329                                                 // Toggle maximized whenever we recieve this event
330                                                 //fprintf( stderr, "maximized: %d\n", ! surface->maximized );
331                                                 surface->Maximized( ! surface->maximized );
332                                                 result = noErr;
333                                                 break;
334                                         }
335                                         
336                                 case kEventWindowCollapsed:
337                                         {
338                                                 //fprintf( stderr, "minimized true\n" );
339                                                 surface->Minimized( true );
340                                                 result = noErr;
341                                                 break;
342                                         }
343
344                                 case kEventWindowExpanded:
345                                         {
346                                                 //fprintf( stderr, "minimized false\n" );
347                                                 surface->Minimized( false );
348                                                 result = noErr;
349                                                 break;
350                                         }
351                                         
352                                 case kEventWindowClose:
353                                         {
354                                                 //fprintf( stderr, "close\n" );
355                                                 surface->Close();
356                                                 result = noErr;
357                                                 break;
358                                         }
359                                 default:
360                                         throw new java::lang::Error(JvNewStringLatin1("Unhandled Window Event"));
361                                 }
362                         break;
363                         
364                 case kEventClassMouse:
365                         {
366                                 HIPoint where;
367                                 result = GetEventParameter( event, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(where), NULL, &where );
368                                 assert( result == noErr );
369
370                                 // Create a hit test event to ask the standard window event handler where this press is
371                                 EventRef hitTest;
372                                 result = CreateEvent( NULL, kEventClassWindow, kEventWindowHitTest, 0, kEventAttributeNone, &hitTest );
373                                 assert( result == noErr );
374
375                                 result = SetEventParameter( hitTest, kEventParamMouseLocation, typeHIPoint, sizeof(where), &where );
376                                 assert( result == noErr );
377
378                                 result = SetEventParameter( hitTest, kEventParamDirectObject, typeWindowRef, sizeof(surface->window), &surface->window );
379                                 assert( result == noErr );
380
381                                 result = SendEventToEventTarget( hitTest, GetWindowEventTarget( (WindowRef) surface->window ) );
382                                 assert( result == noErr );
383
384                                 WindowDefPartCode part;
385                                 result = GetEventParameter( hitTest, kEventParamWindowDefPart, typeWindowDefPartCode, NULL, sizeof(part), NULL, &part );
386                                 assert( result == noErr );
387
388                                 ReleaseEvent( hitTest );
389                                 hitTest = NULL;
390
391                                 // ignore the event if it is not in the content region.
392                                 if ( part != wInContent )
393                                         {
394                                         return eventNotHandledErr;
395                                         }
396
397                                 // TODO: Can I get the content region's bounds as an HIRect?
398                                 Rect bounds;
399                                 GetWindowBounds( (WindowRef) surface->window, kWindowContentRgn, &bounds );
400                                 assert( result == noErr );
401                                 // Convert the context to a CGRect with local coordinates
402                                 CGRect contentBounds = CGRectMake( bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top );
403
404                                 // Now convert the point to content area relative coordinates:
405                                 where.x -= contentBounds.origin.x;
406                                 where.y -= contentBounds.origin.y;
407                                 
408                                 switch ( eventKind )
409                                         {
410                                         case kEventMouseDown:
411                                                 {
412                                                         //fprintf( stderr, "mouse down (%f, %f)\n", where.x, where.y );
413                                                         EventMouseButton mouseButton;
414                                                         result = GetEventParameter( event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(mouseButton), NULL, &mouseButton );
415                                                         assert( result == noErr );
416
417                                                         surface->Press( mouseButton );
418                                                         result = eventNotHandledErr;
419                                                         break;
420                                                 }
421
422                                         case kEventMouseUp:
423                                                 {
424                                                         //fprintf( stderr, "mouse up (%f, %f)\n", where.x, where.y );
425                                                         EventMouseButton mouseButton;
426                                                         result = GetEventParameter( event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(mouseButton), NULL, &mouseButton );
427                                                         assert( result == noErr );
428
429                                                         UInt32 clickCount;
430                                                         result = GetEventParameter( event, kEventParamClickCount, typeUInt32, NULL, sizeof(clickCount), NULL, &clickCount );
431                                                         assert( result == noErr );
432
433                                                         surface->Release( mouseButton );
434
435                                                         // deliver click/double click events
436                                                         if ( clickCount == 1 ) {
437                                                                 //fprintf( stderr, "click (%f, %f)\n", where.x, where.y );
438                                                                 surface->Click( mouseButton );
439
440                                                                 //result = ActivateWindow( (WindowRef) surface->window, true );
441                                                                 //assert( result == noErr );
442
443                                                                 //ProcessSerialNumber psn = { 0, kCurrentProcess };
444                                                                 //result = SetFrontProcess( &psn );
445                                                                 //assert( result == noErr );
446                                                         } else if ( clickCount == 2 ) {
447                                                                 //fprintf( stderr, "double click (%f, %f)\n", where.x, where.y );
448                                                                 surface->DoubleClick( mouseButton );
449                                                         }
450                                                         
451                                                         result = noErr;
452                                                         break;
453                                                 }
454
455                                         case kEventMouseMoved:
456                                         case kEventMouseDragged:
457                                                 {
458                                                         surface->Move( (jint) where.x, (jint) where.y );
459                                                         result = noErr;
460                                                         //fprintf( stderr, "mousemoved (%f, %f)\n", where.x, where.y );
461                                                         break;
462                                                 }
463
464                                         /*case kEventMouseExited:
465                                                 {
466                                                         // Move the mouse to (-1, -1) when it exits the window 
467                                                         surface->Move( -1, -1 );
468                                                         result = noErr;
469                                                         fprintf( stderr, "mouse exited\n" );
470                                                         break;
471                                                 }*/
472                                                 
473
474                                         /*case kEventMouseWheelMoved:
475                                         {
476                                                 EventMouseWheelAxis inAxis;
477                                                 result = inEvent.GetParameter<EventMouseWheelAxis>( kEventParamMouseWheelAxis, typeMouseWheelAxis, &inAxis );
478                                                 assert( noErr == result );
479
480                                                 SInt32 inDelta;
481                                                 result = inEvent.GetParameter<SInt32>( kEventParamMouseWheelDelta, typeSInt32, &inDelta );
482                                                 assert( noErr == result );
483
484                                                 result = MouseWheelMoved( inAxis, inDelta, inKeyModifiers );
485                                                 break;
486                                         }
487                                         */
488
489
490                                                 // some other kind of Mouse event: This is (in my opinion) an error
491                                         default:
492                                                 assert( false );
493                                                 break;
494                                         }
495                                 break;
496                         }
497
498
499                 case kEventClassKeyboard:
500                         switch (eventKind)
501                         {
502                                 case kEventRawKeyDown:
503                                 case kEventRawKeyRepeat:
504                                         {
505                                                 // TODO: This breaks international input
506                                                 UniChar character = GetCharacterWithoutModifiers( event );
507                                                 surface->KeyPressed( UniCharToXWTString( character ) );
508                                                 
509                                                 result = noErr;
510                                                 break;
511                                         }
512
513                                 case kEventRawKeyUp:
514                                         {
515                                                 UniChar character = GetCharacterWithoutModifiers( event );
516                                                 surface->KeyReleased( UniCharToXWTString( character ) );
517                                                 
518                                                 result = noErr;
519                                                 break;
520                                         }
521
522                                 case kEventRawKeyModifiersChanged:
523                                         {
524                                                 static UInt32 previousModifiers = 0;
525                                                 UInt32 currentModifiers = GetCurrentEventKeyModifiers();
526                                                 UInt32 changedModifiers = previousModifiers ^ currentModifiers;
527
528                                                 ModifierKeyTest( surface, changedModifiers, currentModifiers, shiftKey, "shift" );
529                                                 ModifierKeyTest( surface, changedModifiers, currentModifiers, alphaLock, "caps_lock" );
530                                                 ModifierKeyTest( surface, changedModifiers, currentModifiers, controlKey, "control" );
531                                                 ModifierKeyTest( surface, changedModifiers, currentModifiers, optionKey, "alt" );
532                                                 ModifierKeyTest( surface, changedModifiers, currentModifiers, kEventKeyModifierNumLockMask, "num_lock" );
533                                                 
534                                                 previousModifiers = currentModifiers;
535                                                 result = noErr;
536                                                 break;
537                                         }
538                                         
539                                 default:
540                                         assert( false );
541                                         break;
542                         }
543                         break;
544                         
545                 default:
546                         throw new java::lang::Error(JvNewStringLatin1("Unhandled Window Event"));
547         }
548
549         return result;
550 }
551
552 /** Creates a next layout from string and style. If unicodeBuffer is not null, the caller is responsable for releasing it. */
553 static ATSUTextLayout CreateTextLayout( CFStringRef string, ATSUStyle style, CFStringRef* unicodeBuffer ) {
554         assert( string != NULL && unicodeBuffer != NULL && *unicodeBuffer == NULL && style != NULL );
555
556         // Create layout
557         ATSUTextLayout layout;
558         OSStatus result = ATSUCreateTextLayout( &layout );
559         assert( result == noErr && layout != NULL );
560
561         // Try to get the unicode buffer directly
562         CFIndex unicodeLength = CFStringGetLength( string );
563         assert( unicodeLength != 0 );
564         const UniChar* unicodeString = CFStringGetCharactersPtr( string );
565         if ( unicodeString == NULL )
566                 {
567                 // That failed. Allocate a buffer using CFAllocator so the CFString can own it
568                 UniChar* buffer = (UniChar*) CFAllocatorAllocate( NULL, unicodeLength * sizeof( UniChar ), 0 );
569                 assert( buffer != NULL );
570
571                 // Get a copy of the text
572                 CFStringGetCharacters( string, CFRangeMake( 0, unicodeLength ), buffer );
573
574                 // Create a CFString which wraps and takes ownership of the buffer
575                 *unicodeBuffer = CFStringCreateWithCharactersNoCopy( NULL, buffer, unicodeLength, NULL );
576                 assert( *unicodeBuffer != NULL );
577
578                 unicodeString = buffer;
579                 }
580                 
581         assert( unicodeString != NULL );
582         //UniChar* unicode_string = malloc( sizeof( UniChar ) * unicode_length );
583         //CFStringGetCharacters( string, CFRangeMake( 0, unicode_length ), unicode_string );
584         ATSUSetTextPointerLocation( layout, unicodeString, kATSUFromTextBeginning, kATSUToTextEnd, unicodeLength );
585
586         // Set the style
587         result = ATSUSetRunStyle( layout, style, kATSUFromTextBeginning, kATSUToTextEnd );
588         assert( result == noErr );
589
590         // Using this setting makes rendering match TextEdit's (except for the antialiasing rules)
591         ATSLineLayoutOptions rendering = kATSLineFractDisable; //kATSLineUseDeviceMetrics;
592         ATSUAttributeTag tag = kATSULineLayoutOptionsTag;
593         ByteCount size = sizeof( rendering );
594         ATSUAttributeValuePtr value = &rendering;
595         result = ATSUSetLayoutControls( layout, 1, &tag, &size, &value );
596         assert( noErr == result );
597
598         // Allow ATSUI to use its default font fallbacks for Unicode. This means that the font may not
599         // match exactly, but it will display the characters if it can.
600         result = ATSUSetTransientFontMatching( layout, true );
601         assert( noErr == result );
602
603     return layout;
604 }
605
606 static void SetARGBFillColor( CGContextRef gc, jint argb )
607 {
608         uint8_t* colour = (uint8_t*) &argb;
609         float alpha = colour[0] / 255.0;
610         float red = colour[1] / 255.0;
611         float green = colour[2] / 255.0;
612         float blue = colour[3] / 255.0;
613
614         CGContextSetRGBFillColor( gc, red, green, blue, alpha );
615 }
616
617 // CarbonDoubleBuffer //////////////////////////////////////////////////////////////////////
618
619 void org::xwt::plat::Carbon$CarbonDoubleBuffer::setClip(jint x1, jint y1, jint x2, jint y2) {
620         // Restore the previous graphics state, which should be the initial state as saved in
621         // natInit(). This sets the clipping region to the entire buffer.
622         CGContextRestoreGState( (CGContextRef) gc );
623         // We then immediately save the state again, so the we can reset the clipping area again
624         CGContextSaveGState( (CGContextRef) gc );
625
626         // Set the clipping area to the rectangle
627     CGContextClipToRect( (CGContextRef) gc, CGRectMake( x1, y1, x2 - x1, y2 - y1 ) );
628 }
629
630 void org::xwt::plat::Carbon$CarbonDoubleBuffer::drawPicture(org::xwt::Picture* s, jint x, jint y) {
631         org::xwt::plat::Carbon$CarbonPicture* source = (org::xwt::plat::Carbon$CarbonPicture*)s;
632
633         CGRect destination = CGRectMake( x, y, source->width, source->height );
634         HIViewDrawCGImage( (CGContextRef) gc, &destination, (CGImageRef) source->image );
635 }
636
637 void org::xwt::plat::Carbon$CarbonDoubleBuffer::drawPicture(org::xwt::Picture* s,
638                                                             jint dx1, jint dy1, jint dx2, jint dy2, jint sx1, jint sy1, jint sx2, jint sy2) {
639     org::xwt::plat::Carbon$CarbonPicture* source = (org::xwt::plat::Carbon$CarbonPicture*)s;
640
641         //fprintf( stderr, "drawing stretched picture\n" );
642         //CGContextSetRGBFillColor( (CGContextRef) gc, 1.0, 0.0, 0.0, 1.0 );
643         //CGContextFillRect( (CGContextRef) gc, CGRectMake( dx1, dy1, dx2 - dx1, dy2 - dy1 ) );
644         //return;
645         
646         // Some fancy clipping work is required here: draw only inside of the destination points
647         CGContextSaveGState( (CGContextRef) gc );
648         CGContextClipToRect( (CGContextRef) gc, CGRectMake( dx1, dy1, dx2 - dx1, dy2 - dy1 ) );
649
650         // Some fancy scaling work is required here as well:
651         // We want to copy a rectangle from the source image to a destination rectangle.
652         // We are clipped to the destination rectangle, so now we must scale the source image
653         // and line it up correctly.
654         float destinationRectWidth = dx2 - dx1;
655         float destinationRectHeight = dy2 - dy1;
656         float sourceRectWidth = sx2 - sx1;
657         float sourceRectHeight = sy2 - sy1;
658         float horizontalScalingFactor = destinationRectWidth / sourceRectWidth;
659         float verticalScalingFactor = destinationRectHeight / sourceRectHeight;
660
661         // TODO: Work out the math for doing this. For now, no scaling is possible.
662         CGRect destinationRect = CGRectMake( dx1 - sx1 * horizontalScalingFactor, dy1 - sy1 * verticalScalingFactor,
663                                                                           source->width * horizontalScalingFactor, source->height * verticalScalingFactor );
664         HIViewDrawCGImage( (CGContextRef) gc, &destinationRect, (CGImageRef) source->image );
665
666         // Undo the clipping fun
667         CGContextRestoreGState( (CGContextRef) gc );
668 }
669
670 /*void CarbonPictureRelease( void* info, const void* data, size_t size )
671 {
672         // TODO: This should release a java reference, which should be
673         // obtained when this is created.
674         // It'll work without it, but that's only by "chance".
675 }*/
676
677 static CGImageRef CreateCGImageFromData( void* data, int width, int height, CGImageAlphaInfo alphaFormat )
678 {
679         // I'm assuming that data is in RGBA format
680         const size_t NUM_COMPONENTS = 4;
681         const size_t BITS_PER_COMPONENT = 8;
682         const size_t BITS_PER_PIXEL = BITS_PER_COMPONENT * NUM_COMPONENTS;
683         const size_t BYTES_PER_PIXEL = BITS_PER_PIXEL / 8;
684         const size_t BYTES_PER_ROW = BYTES_PER_PIXEL * width;
685
686         CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
687         assert( colorSpace != NULL );
688
689         CGDataProviderRef dataProvider = CGDataProviderCreateWithData( NULL, data, width * height * BYTES_PER_PIXEL, NULL /*CarbonPictureRelease*/ );
690         assert( dataProvider != NULL );
691
692         CGImageRef image = CGImageCreate ( width, height, BITS_PER_COMPONENT, BITS_PER_PIXEL, BYTES_PER_ROW,
693                                                                         colorSpace, alphaFormat, dataProvider, NULL, 1, kCGRenderingIntentDefault );
694         assert( image != NULL );
695
696         CGDataProviderRelease( dataProvider );
697         dataProvider = NULL;
698
699         CGColorSpaceRelease( colorSpace );
700         colorSpace = NULL;
701
702         return image;
703 }
704
705 void org::xwt::plat::Carbon$CarbonDoubleBuffer::natInit() {
706         // FIRST: We create a CGBitmapContextRef so we can draw on this buffer
707         const int BYTES_PER_PIXEL = 4; // RGBA
708         const int BITS_PER_COMPONENT = 8;
709         
710     // Create a new bitmap context, along with the RAM for the bitmap itself
711         const int bitmapBytesPerRow   = (width * BYTES_PER_PIXEL);
712         const int bitmapByteCount     = (bitmapBytesPerRow * height);
713
714         //fprintf( stderr, "alloced %ld bytes for %d x %d double buffer\n", bitmapByteCount, width, height );
715         
716         // create an RGB color space
717         CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
718         assert( colorSpace != NULL );
719
720         // create the bitmap
721         bitmapData = (gnu::gcj::RawData*) malloc( bitmapByteCount );
722         assert( bitmapData != NULL );
723
724         // create the context
725         // TODO: Use kCGImageAlphaPremultipliedLast for better performance
726         gc = (gnu::gcj::RawData*) CGBitmapContextCreate( bitmapData, width, height, BITS_PER_COMPONENT, bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedFirst );
727         assert( gc != NULL );
728
729         // the context retains the color space, so we can release it
730         // OPTIMIZATION: We could pass this colorSpace into CreateCGImageFromData
731         CGColorSpaceRelease( colorSpace );
732         colorSpace = NULL;
733
734         // Clear the double buffer to transparent
735         CGContextClearRect( (CGContextRef) gc, CGRectMake( 0, 0, width, height ) );
736         //CGContextSetRGBFillColor( (CGContextRef) gc, 1.0, 1.0, 1.0, 1.0 );
737         //CGContextFillRect( (CGContextRef) gc, CGRectMake( 0, 0, width, height ) );
738
739         // Shift the coordinate origin to the top left corner (default = bottom right)
740         CGContextTranslateCTM( (CGContextRef) gc, 0, height );
741         CGContextScaleCTM( (CGContextRef) gc, 1, -1 );
742
743
744         // SECOND: We create a CGImageRef that wraps this buffer, so we can copy it to a Surface
745         image = (gnu::gcj::RawData*) CreateCGImageFromData( bitmapData, width, height, kCGImageAlphaPremultipliedFirst );
746         assert( image != NULL );
747
748         // THIRD: Save the current graphics state so we can set and reset the clipping state
749         // We need to do this because the only way to make the clipping region larger is to
750         // restore a previous graphics state. See setClip.
751         CGContextSaveGState( (CGContextRef) gc );
752 }
753
754 void org::xwt::plat::Carbon$CarbonDoubleBuffer::finalize() {
755         CGContextRelease( (CGContextRef) gc );
756         CGImageRelease( (CGImageRef) image );
757     free( bitmapData );
758         bitmapData = NULL;
759 }
760
761 void org::xwt::plat::Carbon$CarbonSurface::blit(org::xwt::DoubleBuffer* db, jint sx, jint sy, jint dx1, jint dy1, jint dx2, jint dy2) {
762     org::xwt::plat::Carbon$CarbonDoubleBuffer *xdb = (org::xwt::plat::Carbon$CarbonDoubleBuffer*)db;
763         assert( xdb != NULL && xdb->gc != NULL && xdb->image != NULL );
764
765         // This method is synchronized to prevent crappy threading fun
766         //JvSynchronize dummy(this);
767         
768         /*fprintf( stderr, "reading image data...\n" );
769         uint32_t* data = (uint32_t*) xdb->bitmapData;
770         for ( int i = 0; i < xdb->width * xdb->height; ++ i )
771                 {
772                 uint32_t temp = data[i];
773                 ++ temp;
774                 }
775         fprintf( stderr, "DONE\n" );*/
776
777         
778         // If we do not have a graphics context we simply mark this area as dirty and return.
779         // We'll get a WindowPaint event later on.
780         if ( gc == NULL )
781                 {
782                 Rect area;
783                 area.left = dx1;
784                 area.top = dy1;
785                 area.right = dx2;
786                 area.bottom = dy2;
787                 // Make the rectangle where we would blit this buffer as invalid
788                 OSStatus err = InvalWindowRect( (WindowRef) window, &area );
789                 //OSStatus err = QDAddRectToDirtyRegion( GetWindowPort( (WindowRef) window ), &area );
790                 assert( err == noErr );
791
792                 //fprintf( stderr, "blit: but marking invalid " );
793                 //PRINT_RECT( area );
794                 return;
795                 }
796         assert( gc != NULL );
797
798         //fprintf( stderr, "blit\n" );
799         
800         // Make sure that the CGImage is up to date
801         CGContextFlush( (CGContextRef) xdb->gc );
802
803         // Some fancy clipping work is required here: draw only inside of the destination rectangle
804         CGContextSaveGState( (CGContextRef) gc );
805         CGContextClipToRect( (CGContextRef) gc, CGRectMake( dx1, dy1, dx2 - dx1, dy2 - dy1 ) );
806
807         // Draw the image on the surface
808         CGRect destination = CGRectMake( dx1 - sx, dy1 - sy, xdb->width, xdb->height );
809         HIViewDrawCGImage( (CGContextRef) gc, &destination, (CGImageRef) xdb->image );
810
811         // Undo the clipping fun
812         CGContextRestoreGState( (CGContextRef)gc );     
813 }
814
815 void org::xwt::plat::Carbon$CarbonDoubleBuffer::fillRect (jint x, jint y, jint x2, jint y2, jint argb) {
816         // Set the Fill color to match
817         SetARGBFillColor( (CGContextRef) gc, argb );
818         CGContextFillRect( (CGContextRef) gc, CGRectMake( x, y, x2 - x, y2 - y ) );
819 }
820
821 void org::xwt::plat::Carbon$CarbonDoubleBuffer::drawString(::java::lang::String* font, ::java::lang::String* text, jint x, jint y, jint argb) {
822         ATSUStyle style = (ATSUStyle) ((org::xwt::plat::Carbon*)org::xwt::plat::Carbon::platform)->_getATSUStyle( font );
823         CFStringRef string = JavaToCFString( text );
824
825         CFStringRef unicodeBuffer = NULL;
826         ATSUTextLayout layout = CreateTextLayout( string, style, &unicodeBuffer );
827
828         // Associate the graphics context with the text layout object
829         ATSUAttributeTag tag = kATSUCGContextTag;
830         ByteCount size = sizeof( gc );
831         ATSUAttributeValuePtr value = &gc;
832         OSStatus result = ATSUSetLayoutControls( layout, 1, &tag, &size, &value );
833         assert( result == noErr );
834
835     // The Quartz RGB fill color influences the ATSUI color
836         SetARGBFillColor( (CGContextRef) gc, argb );
837
838         // I've flipped the context to "standard" coordinates, but draw text needs it to be flipped back
839         CGContextSaveGState( (CGContextRef) gc );
840         CGContextScaleCTM( (CGContextRef) gc, 1.0, -1.0 );
841         y = -y;
842
843         // Draw text
844         result = ATSUDrawText( layout, kATSUFromTextBeginning, kATSUToTextEnd, X2Fix( x ), X2Fix( y ) );
845         assert( result == noErr );
846
847         CGContextRestoreGState( (CGContextRef) gc );
848
849         result = ATSUDisposeTextLayout( layout );
850         assert( result == noErr );
851         layout = NULL;
852
853         if ( unicodeBuffer != NULL ) {
854                 CFRelease( unicodeBuffer );
855                 unicodeBuffer = NULL;
856         }
857
858         CFRelease( string );
859         string = NULL;
860 }
861
862 // CarbonSurface //////////////////////////////////////////////////////////////////////
863
864 void org::xwt::plat::Carbon$CarbonSurface::setIcon(org::xwt::Picture* pic) {
865         org::xwt::plat::Carbon$CarbonPicture *picture = (org::xwt::plat::Carbon$CarbonPicture*)pic;
866
867         // TODO: This sets the dock icon. Maybe I should set the document icon (the icon beside the window title)?
868         //       or set the "window preview" that is shown when a window is collapsed?
869         // TODO: This doesn't seem to work. Maybe it is a size problem?
870         OSStatus result = SetApplicationDockTileImage( (CGImageRef) picture->image );
871         assert( result == noErr );
872 }
873
874 void org::xwt::plat::Carbon$CarbonSurface::setTitleBarText(java::lang::String* s) {
875         CFStringRef string = JavaToCFString( s );
876         OSStatus result = SetWindowTitleWithCFString( (WindowRef) window, string );
877     assert( result == noErr );
878
879         CFRelease( string );
880         string = NULL;
881 }
882
883 void org::xwt::plat::Carbon$CarbonSurface::setSize (jint width, jint height) {
884         Rect bounds;
885         OSStatus result = GetWindowBounds( (WindowRef) window, kWindowContentRgn, &bounds );
886         assert( result == noErr );
887
888         bounds.right = bounds.left + width;
889         bounds.bottom = bounds.top + height;
890         
891     result = SetWindowBounds( (WindowRef) window, kWindowContentRgn, &bounds );
892         assert( result == noErr );
893         result = ConstrainWindowToScreen( (WindowRef) window, kWindowContentRgn, kWindowConstrainStandardOptions, NULL, NULL );
894         assert( result == noErr );
895
896         fprintf( stderr, "set size (%d, %d) (%d, %d)\n", FORMAT_RECT( bounds ) );
897         // TODO: Shouldn't this deliver a "size changed" event automatically?
898         //SizeChange( width, height );
899 }
900
901 void org::xwt::plat::Carbon$CarbonSurface::setLocation (jint x, jint y) {
902         Rect bounds;
903         OSStatus result = GetWindowBounds( (WindowRef) window, kWindowStructureRgn, &bounds );
904         assert( result == noErr );
905
906         int temp = bounds.right - bounds.left;
907         bounds.left = x;
908         bounds.right = x + temp;
909         temp = bounds.bottom - bounds.top;
910         bounds.top = y;
911         bounds.bottom = y + temp;
912
913     result = SetWindowBounds( (WindowRef) window, kWindowStructureRgn, &bounds );
914         assert( result == noErr );
915         result = ConstrainWindowToScreen( (WindowRef) window, kWindowStructureRgn, kWindowConstrainStandardOptions, NULL, NULL );
916         assert( result == noErr );
917
918         fprintf( stderr, "set location (%d, %d) (%d, %d)\n", FORMAT_RECT( bounds ) );
919 }
920
921 void org::xwt::plat::Carbon$CarbonSurface::natInit (jboolean framed) {
922         // Create a standard window. Maybe I should be using the Appearance Manager ones?
923         OSStatus result = noErr;
924         Rect bounds;
925         bounds.left = 0;
926         bounds.top = 0;
927         bounds.right = 100;
928         bounds.bottom = 100;
929         if ( framed ) {
930                 OSStatus result = CreateNewWindow( kDocumentWindowClass,
931                                                                           kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute | kWindowLiveResizeAttribute,
932                                                                           &bounds,
933                                                                           (WindowRef*) &window
934                                                                           );
935         }
936         else {
937                 OSStatus result = CreateNewWindow( kPlainWindowClass,
938                                                                           kWindowStandardHandlerAttribute | kWindowLiveResizeAttribute,
939                                                                           &bounds,
940                                                                           (WindowRef*) &window
941                                                                           );
942         }
943         assert( result == noErr && window != NULL );
944
945         // Install the event handler
946         result = InstallEventHandler( GetWindowEventTarget( (WindowRef) window ),
947                                                            CarbonSurfaceEventHandler,
948                                                            GetEventTypeCount( CarbonSurfaceEventInfo ), CarbonSurfaceEventInfo,
949                                                            this,
950                                                            NULL );
951         assert( result == noErr );
952         
953         // Make sure that this window is NOT visible
954         //HideWindow( (WindowRef) window );
955 }
956
957 void org::xwt::plat::Carbon$CarbonSurface::toFront() {
958     BringToFront( (WindowRef) window );
959         //OSStatus result = ActivateWindow( (WindowRef) window, true );
960         //assert( result == noErr );
961
962         // Using the default "current process" or actually fetching the current process doesn't work
963         ProcessSerialNumber currentProcess = { 0, kCurrentProcess }; 
964         OSStatus result = GetCurrentProcess( &currentProcess );
965         assert( result == noErr );
966         result = SetFrontProcessWithOptions( &currentProcess, kSetFrontProcessFrontWindowOnly );
967         assert( result == noErr );
968 }
969
970 void org::xwt::plat::Carbon$CarbonSurface::toBack() {
971     SendBehind( (WindowRef) window, NULL );
972 }
973
974 void org::xwt::plat::Carbon$CarbonSurface::syncCursor() {
975         ThemeCursor curs;
976     if (cursor->equals(JvNewStringLatin1("crosshair"))) curs = kThemeCrossCursor;
977     //else if (cursor->equals(east)) curs = kThemeArrowCursor;
978     else if (cursor->equals(JvNewStringLatin1("hand"))) curs = kThemePointingHandCursor;
979     //else if (cursor->equals(move)) curs = kThemeArrowCursor;
980     //else if (cursor->equals(north)) curs = kThemeArrowCursor;
981     //else if (cursor->equals(northeast)) curs = kThemeArrowCursor;
982     //else if (cursor->equals(northwest)) curs = kThemeArrowCursor;
983     //else if (cursor->equals(south)) curs = kThemeArrowCursor;
984     //else if (cursor->equals(southeast)) curs = kThemeArrowCursor;
985     //else if (cursor->equals(southwest)) curs = kThemeArrowCursor;
986     else if (cursor->equals(JvNewStringLatin1("text"))) curs = kThemeIBeamCursor;
987     //else if (cursor->equals(west)) curs = kThemeArrowCursor;
988     else if (cursor->equals(JvNewStringLatin1("wait_string"))) curs = kThemeSpinningCursor;
989     else curs = kThemeArrowCursor;
990         
991         // TODO: This should PROBABLY be activated/deactivated when over the window using mouse tracking regions
992         SetThemeCursor( curs );
993 }
994
995 void org::xwt::plat::Carbon$CarbonSurface::_dispose() {
996     ReleaseWindow( (WindowRef) window );
997         window = NULL;
998 }
999
1000 void org::xwt::plat::Carbon$CarbonSurface::setInvisible(jboolean i) {
1001         //fprintf( stderr, "invisible = %d\n", i );
1002     if ( i ) {
1003                 HideWindow( (WindowRef) window );
1004         } else {
1005                 ShowWindow( (WindowRef) window );
1006         }
1007 }
1008
1009 void org::xwt::plat::Carbon$CarbonSurface::_setMaximized(jboolean b) {
1010         Point ideal;
1011         // TODO: Probably should use the actual screen size here
1012         ideal.h = CGDisplayPixelsWide( kCGDirectMainDisplay );
1013         ideal.v = CGDisplayPixelsHigh( kCGDirectMainDisplay );
1014         //fprintf( stderr, "maximized = %d\n", b );
1015     OSStatus result = ZoomWindowIdeal( (WindowRef) window, ( b ? inZoomOut : inZoomIn ), &ideal );
1016         // TODO: It seems to work, so why does it return an error? Theory: XWT is not in a bundle.
1017         assert( result == noErr );
1018 }
1019
1020 void org::xwt::plat::Carbon$CarbonSurface::_setMinimized(jboolean b) {
1021     OSStatus result = CollapseWindow( (WindowRef) window, b );
1022         // TODO: It seems to work, so why does it return an error? Theory: XWT is not in a bundle.
1023         //assert( result == noErr );
1024 }
1025
1026 void org::xwt::plat::Carbon$CarbonPicture::natInit() {
1027         assert( data->length == width * height );
1028         image = (gnu::gcj::RawData*) CreateCGImageFromData( elements(data), width, height, kCGImageAlphaFirst );
1029         assert( image != NULL );
1030 }
1031
1032 void org::xwt::plat::Carbon$CarbonPicture::finalize() {
1033         CGImageRelease( (CGImage*) image );
1034         image = NULL;
1035 }
1036
1037 // Carbon ///////////////////////////////////////////////////////////////////
1038
1039 jint org::xwt::plat::Carbon::_getScreenWidth() {
1040         //fprintf( stderr, "screen width = %d\n", CGDisplayPixelsWide( kCGDirectMainDisplay ) );
1041         return CGDisplayPixelsWide( kCGDirectMainDisplay );
1042 }
1043
1044 jint org::xwt::plat::Carbon::_getScreenHeight() {
1045         //fprintf( stderr, "screen height = %d\n", CGDisplayPixelsHigh( kCGDirectMainDisplay ) );
1046     return CGDisplayPixelsHigh( kCGDirectMainDisplay );
1047 }
1048
1049 jstring org::xwt::plat::Carbon::_getClipBoard() {
1050         // Get the clipboard reference
1051         ScrapRef scrap = NULL;
1052         OSStatus result = GetCurrentScrap( &scrap );
1053         assert( result == noErr );
1054
1055         ScrapFlavorFlags flavorFlags;
1056         result = GetScrapFlavorFlags ( scrap, kScrapFlavorTypeUnicode, &flavorFlags );
1057
1058         if ( result == noErr ) {
1059                 // No error, we have unicode data in a Scrap. Find out how many bytes of data it is.
1060                 Size bytes = 0;
1061                 result = GetScrapFlavorSize( scrap, kScrapFlavorTypeUnicode, &bytes );
1062                 assert( result == noErr );
1063                 const Size numUniChars = bytes / sizeof( UniChar );
1064
1065                 // Allocate a buffer for the text using Core Foundation
1066                 UniChar* buffer = reinterpret_cast<UniChar*>( CFAllocatorAllocate( NULL, bytes, 0 ) );
1067                 assert( buffer != NULL );
1068
1069                 // Get a copy of the text
1070                 Size nextBytes = bytes;
1071                 result = GetScrapFlavorData( scrap, kScrapFlavorTypeUnicode, &nextBytes, buffer );
1072                 assert( result == noErr );
1073
1074                 // Create a CFString which wraps and takes ownership of the buffer
1075                 CFStringRef string = CFStringCreateWithCharactersNoCopy( NULL, buffer, numUniChars, NULL );
1076                 assert( string != NULL );
1077                 buffer = NULL; // string now owns this buffer
1078
1079                 jstring ret = CFToJavaString( string );
1080
1081                 // Default allocator releases both the CFString and the UniChar buffer (text)
1082                 CFRelease( string );
1083                 string = NULL;
1084
1085                 return ret;
1086         } else {
1087                 return JvNewStringLatin1("");
1088         }       
1089 }
1090
1091 void org::xwt::plat::Carbon::_setClipBoard(jstring s) {
1092         CFStringRef string = JavaToCFString( s );
1093
1094         OSStatus result = ClearCurrentScrap();
1095         assert( result == noErr );
1096
1097         ScrapRef scrap = NULL;
1098         result = GetCurrentScrap( &scrap );
1099         assert( result == noErr );
1100
1101         CFIndex numUniChars = CFStringGetLength( string );
1102
1103         // Try to get a pointer to the unicode data first, then do the copy if needed
1104         const UniChar* buffer = CFStringGetCharactersPtr( string );
1105         if ( buffer != NULL )
1106                 {
1107                 result = PutScrapFlavor ( scrap, kScrapFlavorTypeUnicode, 0, sizeof( UniChar ) * numUniChars, buffer );
1108                 assert( result == noErr );
1109                 }
1110         else
1111                 {
1112                 UniChar* buffer = (UniChar*) malloc( sizeof( UniChar ) * numUniChars );
1113                 assert( buffer != NULL );
1114                 CFStringGetCharacters( string, CFRangeMake( 0, numUniChars ), buffer );
1115
1116                 result = PutScrapFlavor ( scrap, kScrapFlavorTypeUnicode, 0, sizeof( UniChar ) * numUniChars, buffer );
1117                 assert( result == noErr );
1118
1119                 // Done with the UniChar* buffer
1120                 free( buffer );
1121                 buffer = NULL;
1122                 }
1123                 
1124         // Done with the CFString
1125         CFRelease( string );
1126         string = NULL;
1127 }
1128
1129 /*
1130 JArray<java::lang::String*>* org::xwt::plat::Carbon::listNativeFonts() {
1131         ATSFontFamilyIterator iterator;
1132         OSStatus result = ATSFontFamilyIteratorCreate( kATSFontContextUnspecified, NULL, NULL, kATSOptionFlagsDefault, &iterator );
1133         if ( result != noErr ) throw new java::lang::Error(JvNewStringLatin1("ASSERT"));
1134
1135         // Iterate over fonts twice: The first time just to count the fonts so we can create the array
1136         // The second time gets the font names
1137         ATSFontFamilyRef font = NULL;
1138         int i = -1;
1139         while ( result == noErr )
1140                 {
1141                 i ++;
1142                 result = ATSFontFamilyIteratorNext( iterator, &font );
1143                 }
1144         if ( result != kATSIterationCompleted ) throw new java::lang::Error(JvNewStringLatin1("ASSERT"));
1145
1146         result = ATSFontFamilyIteratorReset( kATSFontContextUnspecified, NULL, NULL, kATSOptionFlagsDefault, &iterator );
1147         if ( result != noErr ) throw new java::lang::Error(JvNewStringLatin1("ASSERT"));
1148
1149         JArray<jstring>* fonts = (JArray<jstring>*)JvNewObjectArray( i, &(::java::lang::String::class$), NULL);
1150
1151         ATSFontFamilyRef font = NULL;
1152         result = ATSFontFamilyIteratorNext( iterator, &font );
1153         while( result == noErr )
1154                 {
1155                 CFStringRef name = NULL;
1156                 result = ATSFontFamilyGetName( font, kATSOptionFlagsDefault, &name );
1157                 if ( result != noErr || name == NULL ) throw new java::lang::Error(JvNewStringLatin1("ASSERT"));
1158
1159                 //const char* cstring = CFStringGetCStringPtr( name, kCFStringEncodingMacRoman );
1160                 //if ( cstring != NULL ) fprintf( stderr, "%s\n", cstring );
1161                 
1162                 jstring xwtName = CFToJavaString( name );
1163
1164                 // 
1165                 xwtName = xwtName->replace(' ', '_')->toLowerCase()->concat( "0" ); 
1166                 CFRelease( name );
1167                 name = NULL;
1168
1169                 result = ATSFontFamilyIteratorNext( iterator, &font );
1170                 i ++;
1171                 }
1172         if ( result != kATSIterationCompleted ) throw new java::lang::Error(JvNewStringLatin1("ASSERT"));
1173
1174         result = ATSFontFamilyIteratorRelease ( &iterator );
1175         if ( result != noErr ) throw new java::lang::Error(JvNewStringLatin1("ASSERT"));
1176
1177     return fonts;
1178 }
1179 */
1180
1181
1182 /*gnu::gcj::RawData* org::xwt::plat::Carbon::fontStringToStruct(jstring s) {
1183     throw new java::lang::Error(JvNewStringLatin1("FIXME"));
1184 }*/
1185
1186 // HACK: WARNING: Undocumented Hack! This may break in the future
1187 // For now, however, it allows a non-bundled app to create Windows! Yay!
1188 extern "C" { OSErr CPSEnableForegroundOperation(ProcessSerialNumber *psn); }
1189
1190 void org::xwt::plat::Carbon::natInit() {
1191         // HACK: WARNING: Undocumented Hack! This may break in the future
1192         // For now, however, it allows a non-bundled app to create Windows! Yay!
1193         ProcessSerialNumber currentProcess = { 0, kCurrentProcess }; // Using the default "current process" doesn't work
1194         OSStatus result = GetCurrentProcess( &currentProcess );
1195         assert( result == noErr );
1196         result = CPSEnableForegroundOperation( &currentProcess );
1197         assert( result == noErr );
1198
1199         // Initiaize the native font hashtable
1200         // Get a copy of all available fonts
1201         ItemCount numFonts;
1202         result = ATSUFontCount( &numFonts );
1203         assert( result == noErr );
1204
1205         ATSFontRef* fonts = (ATSFontRef*) malloc( sizeof(ATSFontRef) * numFonts );
1206         assert( fonts != NULL );
1207
1208         ItemCount currentNumFonts;
1209         result = ATSUGetFontIDs( fonts, numFonts, &currentNumFonts );
1210         assert( result == noErr );
1211         numFonts = min( currentNumFonts, numFonts ); // Just to be safe, maybe the number of fonts changed (unlikely!)
1212
1213         const ByteCount MAX_NAME_LENGTH = 100;
1214         char familyName[MAX_NAME_LENGTH+1];
1215         for ( ItemCount i = 0; i < numFonts; i ++ ) {
1216                 ByteCount actualBytes = 0;
1217                 result = ATSUFindFontName( fonts[i], kFontFamilyName, (FontPlatformCode) kFontMacintoshPlatform, kFontNoScriptCode, kFontNoLanguageCode,
1218                                                           MAX_NAME_LENGTH, familyName, &actualBytes, NULL );
1219
1220                 // If we were able to get a macintosh string for the font family name, use it to make a jstring
1221                 if ( result == noErr && actualBytes > 0 )
1222                         {
1223                         familyName[actualBytes] = '\0';
1224
1225                         jstring xwtName = JvNewStringLatin1( familyName, actualBytes );
1226
1227                         // Convert the font family name to an XWT font name
1228                         xwtName = xwtName->replace(' ', '_')->toLowerCase();
1229                         // Map the first font we find to this family name. The first font is the "Regular" font.
1230                         if ( nativeFontCache->get( xwtName ) == NULL )
1231                                 nativeFontCache->put( (java::lang::Object*) xwtName, new org::xwt::plat::Carbon$WrappedRawData( (::gnu::gcj::RawData*) fonts[i] ) );
1232                         }
1233                 else
1234                         {
1235                         //fprintf( stderr, "FONT HAS NO MAC NAME\n" );
1236                         }
1237         }
1238         
1239         free( fonts );
1240         fonts = NULL;
1241 }
1242
1243 jint org::xwt::plat::Carbon::_stringWidth(::java::lang::String* font, ::java::lang::String* text) {
1244         // If the string doesn't have any characters, return zero immediately 
1245         if ( text->length() == 0 ) return 0;
1246         
1247         ATSUStyle style = (ATSUStyle) _getATSUStyle( font );
1248         CFStringRef string = JavaToCFString( text );
1249
1250         CFStringRef unicodeBuffer = NULL;
1251         ATSUTextLayout layout = CreateTextLayout( string, style, &unicodeBuffer );
1252
1253         unsigned long actualNumberOfBounds = 0;
1254         ATSTrapezoid glyphBounds;
1255
1256         // We get a single bound, since the text should only require one. If it requires more, there is an issue
1257         OSStatus result = ATSUGetGlyphBounds( layout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 1, &glyphBounds, &actualNumberOfBounds );
1258         assert( result == noErr && actualNumberOfBounds == 1 );
1259
1260         ATSUDisposeTextLayout( layout );
1261         layout = NULL;
1262
1263         if ( unicodeBuffer != NULL ) {
1264                 CFRelease( unicodeBuffer );
1265                 unicodeBuffer = NULL;
1266                 }
1267
1268         CFRelease( string );
1269         string = NULL;
1270
1271         return Fix2Long( glyphBounds.upperRight.x - glyphBounds.upperLeft.x );
1272 }
1273
1274 jint org::xwt::plat::Carbon::_getMaxAscent(::java::lang::String* font) {
1275         ATSUStyle style = (ATSUStyle) _getATSUStyle( font );
1276         
1277         ByteCount actualSize = 0;
1278         ATSUTextMeasurement ascent;
1279         OSStatus result = ATSUGetAttribute( style, kATSUAscentTag, sizeof( ascent ), &ascent, &actualSize );
1280         assert( result == kATSUNotSetErr );
1281
1282         //fprintf( stderr, "ascent = %ld\n", Fix2Long( ascent ) );
1283
1284         return Fix2Long( ascent );
1285 }
1286 jint org::xwt::plat::Carbon::_getMaxDescent(::java::lang::String* font) {
1287         ATSUStyle style = (ATSUStyle) _getATSUStyle( font );
1288
1289         ByteCount actualSize = 0;
1290         ATSUTextMeasurement descent;
1291         OSStatus result = ATSUGetAttribute( style, kATSUDescentTag, sizeof( descent ), &descent, &actualSize );
1292         assert( result == kATSUNotSetErr );
1293
1294         //fprintf( stderr, "descent = %ld\n", Fix2Long( descent ) );
1295
1296         return Fix2Long( descent );
1297 }
1298
1299 gnu::gcj::RawData* org::xwt::plat::Carbon::_createATSUStyle( gnu::gcj::RawData* fontRef, jint fontSize, jboolean isBold, jboolean isItalic, jboolean isUnderline ) {
1300         ATSUStyle style;
1301         OSStatus result = ATSUCreateStyle( &style );
1302         assert( result == noErr && style != NULL );
1303
1304     // Font
1305     ATSFontRef font = (ATSFontRef) fontRef;
1306         Fixed size = X2Fix( fontSize );
1307         Boolean bold = isBold;
1308         Boolean italic = isItalic;
1309         Boolean underline = isUnderline;
1310
1311         const ATSUAttributeTag tags[] = { kATSUFontTag, kATSUSizeTag, kATSUQDBoldfaceTag, kATSUQDItalicTag, kATSUQDUnderlineTag };
1312         ByteCount sizes[] = { sizeof( font ), sizeof( size ), sizeof( bold ), sizeof( italic ), sizeof( underline ) };
1313         ATSUAttributeValuePtr values[] = { &font, &size, &bold, &italic, &underline };
1314
1315         result = ATSUSetAttributes( style, sizeof( tags ) / sizeof( *tags ), tags, sizes, values );
1316         assert( result == noErr );
1317
1318         // TODO: Why do I have to turn this off manually? This shouldn't this be the default?
1319         ATSUFontFeatureType featureType = kLigaturesType;
1320         ATSUFontFeatureSelector selector = kCommonLigaturesOffSelector;
1321         result = ATSUSetFontFeatures( style, 1, &featureType, &selector );
1322         assert( result == noErr );
1323
1324         return (gnu::gcj::RawData*) style;
1325 }
1326
1327
1328 /** Called once XWT is initialized and the application is running. */
1329 void org::xwt::plat::Carbon::_running() {
1330         // TODO: This is going to be a bit magical:
1331         // Let's see what happens when we mix multithreading and OS X applications.
1332         // Theory: Bad stuff may happen if events are delivered and other stuff is going on at
1333         // the same time. However, I'm going to pray that XWT's Java stuff locks things for me,
1334         // So by the time I call into the Java code, i'm the only code path active?
1335         RunApplicationEventLoop();
1336
1337         org::xwt::plat::Carbon::exit(); 
1338 }