2003/09/07 03:10:15
[org.ibex.core.git] / src / org / xwt / plat / Darwin.cc
1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [LGPL]
2 // Authors: Brian Alliet and Evan Jones
3 #ifndef __APPLE_CC__
4 #define FSF_GCC
5 #define __APPLE_CC__
6 #else
7 #define APPLE_GCC
8 #endif
9
10 #include "POSIX.cc"
11 #include "OpenGL.cc"
12
13 #include <java/lang/Object.h>
14 #include <java/lang/Error.h>
15
16 #include <org/xwt/plat/Carbon.h>
17 #include <org/xwt/plat/Carbon$CarbonSurface.h>
18 #include <org/xwt/plat/Carbon$GLCarbonSurface.h>
19 #include <org/xwt/plat/Carbon$GLCarbonDoubleBuffer.h>
20 #include <org/xwt/plat/Carbon$CarbonMessage.h>
21 #include <org/xwt/plat/Carbon$CarbonOpenGL.h>
22 #include <org/xwt/plat/Carbon$FileDialogHelper.h>
23 #include <org/xwt/plat/GCJ$Retainer.h>
24 #include <org/xwt/Proxy.h>
25 #include <org/xwt/util/Semaphore.h>
26
27 #include <Carbon/Carbon.h>
28 #include <CoreFoundation/CoreFoundation.h>
29 // For proxy stuff
30 #include <SystemConfiguration/SystemConfiguration.h>
31 // For LSOpenURLRef
32 #include <ApplicationServices/ApplicationServices.h>
33
34 #include <AGL/agl.h>
35
36 #include <stdlib.h>
37
38 using namespace org::xwt::plat;
39 using gnu::gcj::RawData;
40 using org::xwt::util::Semaphore;
41 using java::lang::Object;
42
43 namespace org { namespace xwt { namespace plat {
44
45 namespace carbon { }
46 using namespace carbon;
47
48 #pragma mark ------ Carbon Namespace ------
49 namespace carbon {
50     // We put everything that isn't in org.xwt.plat.Carbon in 
51     // org.xwt.plat.carbon to prevent namespace conflicts
52     
53     template <bool CHECK> static inline int CompileTimeCheck() { const int something_is_wrong=1; something_is_wrong++; return 0; }
54     template <> static inline int CompileTimeCheck<true>() { return 0; }
55     const static int unichar_check = CompileTimeCheck<sizeof(jchar)==sizeof(UniChar)>();
56
57     void funcFailed(char *func,int r);
58     static inline void checkStatus(OSStatus r, char *func) { if(r != noErr) funcFailed(func,r); }
59     static inline void checkStatus(GLboolean b, char *func) { if(!b) funcFailed(func,-1); }
60     static inline void checkStatus(void *p, char *func) { if(!p) funcFailed(func,-1); }
61
62     jstring cfStringToJString(CFStringRef s) {
63         CFIndex length = CFStringGetLength(s);
64         CFRange range = CFRangeMake(0,length);
65         jstring js = JvAllocString(length);
66         UniChar *buf = (UniChar*)JvGetStringChars(js);
67         CFStringGetCharacters(s,range,buf);
68         return js;
69     }
70     
71     #pragma mark ------ SmartCFString ------
72     class SmartCFString {
73         private:
74             CFStringRef p;
75             void release() { if(p) CFRelease(p); }
76             void checkNull() { if(!p) throw new java::lang::Error(JvNewStringLatin1("CFString function failed")); }
77         public:
78             // Constructors
79             SmartCFString() : p(0) { }
80             SmartCFString(const char *s) : p(0) { *this = s; }
81             SmartCFString(jstring js) : p(0) { *this = js; }
82             SmartCFString(CFStringRef cf) : p(0) { *this = cf; }
83             // Destructor
84             ~SmartCFString() { release(); }
85             // Assignment
86             SmartCFString& operator= (const char *s) {
87                 release();
88                 if(!s) s = "(null)";
89                 p = CFStringCreateWithCString(kCFAllocatorDefault,s,kCFStringEncodingISOLatin1);
90                 checkNull();
91                 return *this;
92             }
93             SmartCFString& operator= (jstring js) {
94                 if(!js) return *this = "(null)";
95                 release();
96                 UniChar *buf = (UniChar*) JvGetStringChars(js);
97                 CFIndex length = js->length();
98                 p = CFStringCreateWithCharacters(kCFAllocatorDefault,buf,length);
99                 checkNull();
100                 return *this;
101             }
102             SmartCFString& operator= (CFStringRef cf) {
103                 if(cf == NULL) return *this = "(null)";
104                 release();
105                 p = cf;
106                 return *this;
107             }
108             operator CFStringRef() { return p; }
109             operator jstring() { return getJString(); }
110             
111             jstring getJString() { return cfStringToJString(p); }
112             
113             bool equals(const char *s) {
114                 SmartCFString cfs(s);
115                 return equals(cfs);
116             }
117             
118             bool equals(CFStringRef cfs) {
119                 return CFStringCompare(p,cfs,0) == kCFCompareEqualTo;
120             }
121     };
122     
123     // CHECKME: Is just making up your own four char codes really correct?
124     const static UInt32 kEventClassCarbonMessage = 'xwta';
125     const static UInt32 kEventCarbonMessage = 'xwtb';
126     const static UInt32 kEventParamCarbonMessage = 'xwtc';
127     
128     pascal OSStatus carbon::carbonMessageEventHandler(EventHandlerCallRef handler, EventRef e, void *userData);
129     void fileDialogEventHandler(NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, void *userData);
130
131 } // end namespace
132
133 jboolean Carbon::isJaguar() {
134     SInt32 version;
135     OSStatus r = Gestalt(gestaltSystemVersion, &version);
136     checkStatus(r,"Gestalt");
137     return version >= 0x1020;
138 }
139
140 void Carbon$CarbonMessage::natInit() {
141     OSStatus r;
142     
143     EventHandlerUPP upp = NewEventHandlerUPP(carbonMessageEventHandler);
144     EventTypeSpec eventTypes = { kEventClassCarbonMessage, kEventCarbonMessage };
145     r = InstallEventHandler(GetApplicationEventTarget(),upp,1,&eventTypes,NULL,NULL);
146     checkStatus(r,"InstallEventHandler");
147 }
148
149 void Carbon$CarbonMessage::add(Carbon$CarbonMessage *msg) {
150     EventRef event;
151     OSStatus r;
152     
153     GCJ$Retainer::retain(msg);
154
155     r = CreateEvent(kCFAllocatorDefault,kEventClassCarbonMessage,kEventCarbonMessage,0,kEventAttributeNone,&event);
156     checkStatus(r,"CreateEvent");
157     r = SetEventParameter(event,kEventParamCarbonMessage,typeVoidPtr,sizeof(msg),(void*)&msg);
158     checkStatus(r,"SetEventParameter");
159     r = PostEventToQueue(GetMainEventQueue(),event,kEventPriorityHigh);
160     checkStatus(r,"PostEventToQueue");
161     ReleaseEvent(event);
162 }
163
164
165 pascal OSStatus carbon::carbonMessageEventHandler(EventHandlerCallRef handler, EventRef e, void *userData) {
166     UInt32 eKind = GetEventKind(e);
167     UInt32 eClass = GetEventClass(e);
168     OSStatus r;
169     Carbon$CarbonMessage *msg;
170     if(eClass != kEventClassCarbonMessage || eKind != kEventCarbonMessage)
171         return eventNotHandledErr;
172     r = GetEventParameter(e,kEventParamCarbonMessage,typeVoidPtr,NULL,sizeof(msg),NULL,&msg);
173     checkStatus(r,"GetEventParameter");
174     msg->perform();
175     GCJ$Retainer::release(msg);
176     return noErr;
177 }
178
179
180 #pragma mark ------ Utility Functions ------
181
182 void carbon::funcFailed(char *func,int r){
183     fprintf(stderr,"%s() failed (%d)\n",func,r);
184     exit(EXIT_FAILURE);
185
186
187 #pragma mark ------ CarbonPicture Methods ------ 
188
189 #pragma mark ----- Carbon Surface Methods ----
190
191
192 void Carbon$CarbonSurface::natSyncCursor(jint n) {
193     ThemeCursor c;
194     // see Carbon.java for what these numbers mean
195     switch(n) {
196         case 1: c = kThemeWatchCursor;
197         case 2: c = kThemePlusCursor;
198         case 3: c = kThemeIBeamCursor;
199         case 4: c = kThemePointingHandCursor;
200         case 5: c = kThemeOpenHandCursor;
201         case 6: c = kThemeResizeLeftRightCursor;
202         default: c = kThemeArrowCursor;
203     }
204     SetThemeCursor(c);
205 }
206    
207 void Carbon$CarbonSurface::natSetInvisible(jboolean b) {
208     WindowRef window = (WindowRef) rawWindowRef;
209     fprintf(stderr,"Making window %s\n",b?"invisible":"visible");
210     if(b) HideWindow(window);
211     else ShowWindow(window);
212 }
213
214 void Carbon$CarbonSurface::nat_setMaximized(jboolean b) {
215     WindowRef window = (WindowRef) rawWindowRef;
216     Point ideal = { 10000, 10000 };
217     OSStatus r = ZoomWindowIdeal(window,(b?inZoomOut:inZoomIn),&ideal);
218     checkStatus(r,"ZoomWindowIdeal");
219 }
220
221 void Carbon$CarbonSurface::nat_setMinimized(jboolean b) {
222     WindowRef window = (WindowRef) rawWindowRef;
223     if((IsWindowCollapsed(window) ? 1 : 0) == (b ? 1 : 0)) return;
224     OSStatus r = CollapseWindow(window,b);
225     checkStatus(r,"CollapseWindow");
226 }
227
228 void Carbon$CarbonSurface::natSetTitleBarText(jstring js) {
229     SmartCFString s = js;
230     WindowRef window = (WindowRef) rawWindowRef;
231     SetWindowTitleWithCFString(window,s);
232 }
233
234 void Carbon$CarbonSurface::natToBack() {
235     WindowRef window = (WindowRef) rawWindowRef;
236     SendBehind(window,NULL);
237 }
238
239 void Carbon$CarbonSurface::natToFront() {
240     WindowRef window = (WindowRef) rawWindowRef;
241     fprintf(stderr,"SelectWindow()\n");
242     SelectWindow(window);
243 }
244
245 #pragma mark ---- Window Event Handler ----
246 namespace carbon {
247 static const EventTypeSpec eventTypeSpecs[] = {
248     // kEventClassCommand
249     // { kEventClassCommand, ??? },
250     
251     // kEventClassWindow
252     { kEventClassWindow, kEventWindowUpdate },
253     { kEventClassWindow, kEventWindowBoundsChanged },
254     { kEventClassWindow, kEventWindowActivated },
255     { kEventClassWindow, kEventWindowDeactivated },
256     { kEventClassWindow, kEventWindowZoomed },
257     { kEventClassWindow, kEventWindowCollapsed },
258     { kEventClassWindow, kEventWindowExpanded },
259     { kEventClassWindow, kEventWindowClose },
260     { kEventClassWindow, kEventWindowClosed },
261     
262     // kEventClassKeyboard
263     { kEventClassKeyboard, kEventRawKeyDown },
264     { kEventClassKeyboard, kEventRawKeyRepeat },
265     { kEventClassKeyboard, kEventRawKeyUp },
266     { kEventClassKeyboard, kEventRawKeyModifiersChanged },
267     
268     // kEventClassMouse
269     { kEventClassMouse, kEventMouseDown },
270     { kEventClassMouse, kEventMouseUp },
271     { kEventClassMouse, kEventMouseMoved },
272     { kEventClassMouse, kEventMouseDragged },
273     { kEventClassMouse, kEventMouseWheelMoved },
274 };
275     
276 pascal OSStatus windowEventHandler(EventHandlerCallRef handler, EventRef e, void *userData) {
277     Carbon$CarbonSurface *surface = (Carbon$CarbonSurface*) userData;
278     UInt32 eKind = GetEventKind(e);
279     UInt32 eClass = GetEventClass(e);
280     OSStatus r;
281     
282     switch(eClass) {
283         case kEventClassCommand:
284             switch(eKind) {
285                 // TODO: handle menu items
286             }
287             break;
288         case kEventClassKeyboard:
289             switch(eKind) {
290                 case kEventRawKeyDown:
291                 case kEventRawKeyRepeat: 
292                 case kEventRawKeyUp: {
293                     UInt32 keyCode;
294                     jstring js;
295                     
296                     r = GetEventParameter(e,kEventParamKeyCode,typeUInt32,NULL,sizeof(keyCode),NULL,&keyCode);
297                     checkStatus(r,"GetEventParameter");
298                     
299                     switch(keyCode) {
300                         // These values were obtained by experimentation. I can't find any constants for them
301                         // in the header files
302                         case 126: js = JvNewStringLatin1("up"); break;
303                         case 125: js = JvNewStringLatin1("down"); break;
304                         case 124: js = JvNewStringLatin1("right"); break;
305                         case 123: js = JvNewStringLatin1("left"); break;
306                         case 122: js = JvNewStringLatin1("f1"); break;
307                         case 120: js = JvNewStringLatin1("f2"); break;
308                         case 99:  js = JvNewStringLatin1("f3"); break;
309                         case 118: js = JvNewStringLatin1("f4"); break;
310                         case 96:  js = JvNewStringLatin1("f5"); break;
311                         case 97:  js = JvNewStringLatin1("f6"); break;
312                         case 98:  js = JvNewStringLatin1("f7"); break;
313                         case 100: js = JvNewStringLatin1("f8"); break;
314                         case 101: js = JvNewStringLatin1("f9"); break;
315                         case 109: js = JvNewStringLatin1("f10"); break;
316                         case 103: js = JvNewStringLatin1("f11"); break;
317                         case 111: js = JvNewStringLatin1("f12"); break;
318                         case 105: js = JvNewStringLatin1("f13"); break;
319                         case 114: js = JvNewStringLatin1("insert"); break;
320                         case 117: js = JvNewStringLatin1("delete"); break;
321                         case 116: js = JvNewStringLatin1("page_up"); break;
322                         case 121: js = JvNewStringLatin1("page_down"); break;
323                         case 115: js = JvNewStringLatin1("home"); break;
324                         case 119: js = JvNewStringLatin1("end"); break;
325                         case 71:  js = JvNewStringLatin1("num_lock"); break;
326                         case 53:  js = JvNewStringLatin1("escape"); break;
327                         case 51:  js = JvNewStringLatin1("back_space"); break;
328                         case 36:  js = JvNewStringLatin1("enter"); break;
329                         case 48:  js = JvNewStringLatin1("tab"); break;
330                         case 76:  js = JvNewStringLatin1("enter"); break; // number pad enter
331                         default: {                            
332                             UInt32 size;
333                             UInt32 modifiers = surface->modifiers;
334                             r = GetEventParameter(e,kEventParamKeyUnicodes,typeUnicodeText,NULL,0,&size,NULL);
335                             checkStatus(r,"GetEventParameter");
336                             if(size == 0 || (modifiers & controlKey && size>sizeof(UniChar))) return eventNotHandledErr;
337                             
338                             js = JvAllocString(size/sizeof(UniChar));
339                             UniChar *buf = (UniChar*)JvGetStringChars(js);
340                             r = GetEventParameter(e,kEventParamKeyUnicodes,typeUnicodeText,NULL,size,NULL,buf);
341                             checkStatus(r,"GetEventParameter");
342
343                             if(!buf[0]) return eventNotHandledErr; // shouldn't happen
344                             // odd, when the ctrl key is pressed a-"`" become 1-31, this brings them back to the corect values
345                             if(modifiers & controlKey && buf[0] < 32) buf[0] += 0x60;
346                             break;
347                         }
348                     }
349                     
350                     if(eKind == kEventRawKeyUp)
351                         surface->KeyReleased(js);
352                     else
353                         surface->KeyPressed(js);
354                     return noErr;
355                 }
356                 case kEventRawKeyModifiersChanged: {
357                     const static struct {
358                         UInt32 mask;
359                         jstring xwtKey;
360                     } modifiersTable[] = {
361                         { shiftKey,  JvNewStringLatin1("shift")     },
362                         { alphaLock, JvNewStringLatin1("caps_lock") },
363                         { controlKey,  JvNewStringLatin1("control")   },
364                         { optionKey,  JvNewStringLatin1("alt")       },
365                         { kEventKeyModifierNumLockMask,  JvNewStringLatin1("num_lock")  },
366                         { 0, 0 }                    
367                     };
368                     
369                     UInt32 oldModifiers = (UInt32) surface->modifiers;
370                     UInt32 newModifiers;
371                     r = GetEventParameter(e,kEventParamKeyModifiers,typeUInt32,NULL,sizeof(newModifiers),NULL,&newModifiers);
372                     checkStatus(r,"GetEventParameter");
373                     surface->modifiers = (jint) newModifiers;
374                     UInt32 changedModifiers = oldModifiers ^ newModifiers;
375                     
376                     for(int i=0;modifiersTable[i].mask;i++) {
377                         UInt32 mask = modifiersTable[i].mask;
378                         if(!(changedModifiers & mask)) continue;
379                         if(newModifiers & mask)
380                             surface->KeyPressed(modifiersTable[i].xwtKey);
381                         else
382                             surface->KeyReleased(modifiersTable[i].xwtKey);
383                     }
384                     return noErr;
385                 }
386             }
387             break;
388         case kEventClassMouse:
389             // The default handler gets first dibs on mouse events
390             // (this catches the titlebar, resize box, etc)
391             r = CallNextEventHandler(handler, e);
392             if(r != eventNotHandledErr && eKind != kEventMouseMoved && eKind != kEventMouseDragged) return r;
393             
394             switch(eKind) {
395                 case kEventMouseMoved:
396                 case kEventMouseDragged: {
397                     WindowRef window = (WindowRef) surface->rawWindowRef;
398                     Point p;
399                     Rect rect;
400                     
401                     r = GetEventParameter(e,kEventParamMouseLocation,typeQDPoint,NULL,sizeof(p),NULL,&p);
402                     checkStatus(r,"GetEventParameter");
403                     r = GetWindowBounds(window,kWindowContentRgn,&rect);
404                     checkStatus(r,"GetWindowBounds");
405                     surface->Move(p.h-rect.left,p.v-rect.top);
406                     return noErr;
407                 }
408                 case kEventMouseDown: 
409                 case kEventMouseUp: {
410                     EventMouseButton button;
411                     UInt32 clickCount;
412                     jint xwtButton;
413                     r = GetEventParameter(e,kEventParamMouseButton,typeMouseButton,NULL,sizeof(button),NULL,&button);
414                     checkStatus(r,"GetEventParameter");
415                     r = GetEventParameter(e,kEventParamClickCount,typeUInt32,NULL,sizeof(clickCount),NULL,&clickCount);
416                     checkStatus(r,"GetEventParameter");
417                     
418                     switch(button) {
419                         case kEventMouseButtonPrimary:   xwtButton = 1; break;
420                         case kEventMouseButtonSecondary: xwtButton = 2; break;
421                         case kEventMouseButtonTertiary:  xwtButton = 3; break;
422                         default: return noErr;
423                     }
424                     if(eKind == kEventMouseDown) {
425                         surface->Press(xwtButton);
426                     } else {
427                         surface->Release(xwtButton);
428                         while(clickCount > 1) {
429                             surface->DoubleClick(xwtButton);
430                             clickCount-=2;
431                         }
432                         if(clickCount) surface->Click(xwtButton);
433                     }
434                     return noErr;
435                 }
436                 case kEventMouseWheelMoved: {
437                     EventMouseWheelAxis axis;
438                     SInt32 delta;
439                     r = GetEventParameter(e,kEventParamMouseWheelAxis,typeMouseWheelAxis,NULL,sizeof(axis),NULL,&axis);
440                     checkStatus(r,"GetEventParameter");
441                     if(axis != kEventMouseWheelAxisY) break;
442                     r = GetEventParameter(e,kEventParamMouseWheelDelta,typeSInt32,NULL,sizeof(delta),NULL,&delta);
443                     checkStatus(r,"GetEventParameter");
444                     fprintf(stderr,"kEventMouseWheelMoved: delta: %d",delta);
445                     // surface->MouseWheelMoved(...) IMPROVMENT: mouse wheel support in xwt
446                     return noErr;
447                 }
448             }
449             break;
450         
451         case kEventClassWindow: {
452             WindowRef window;
453             r = GetEventParameter(e,kEventParamDirectObject,typeWindowRef,NULL,sizeof(window),NULL,&window);
454             checkStatus(r,"kEventClassWindow/GetEventParameter");
455             
456             if((RawData*)window != surface->rawWindowRef) Carbon::abort(JvNewStringLatin1("window != surface->window"));
457             
458             switch(eKind) {
459                 case kEventWindowUpdate: {
460                     surface->Dirty(0,0,surface->width,surface->height);
461                     return noErr;
462                 }
463                 case kEventWindowBoundsChanged: {
464                     UInt32 attr;
465                     Rect rect;
466                     r = GetEventParameter(e,kEventParamAttributes,typeUInt32,NULL,sizeof(attr),NULL,&attr);
467                     checkStatus(r,"kEventWindowBoundsChanged/GetEventParameter");
468                     r = GetWindowBounds(window,kWindowContentRgn,&rect);
469                     checkStatus(r,"GetWindowBounds");
470                     if(attr & kWindowBoundsChangeSizeChanged) {
471                         jint w = rect.right-rect.left;
472                         jint h = rect.bottom-rect.top;
473                         if(attr & kWindowBoundsChangeUserResize && surface->maximized)
474                             surface->Maximized(false);
475                         surface->reshape(w,h);
476                         surface->SizeChange(w,h);
477                         surface->Dirty(0,0,w,h);
478                         surface->blitDirtyScreenRegions();
479                     }
480                     if(attr & kWindowBoundsChangeOriginChanged) {
481                         surface->PosChange(rect.left,rect.top);
482                     }
483                     return noErr;
484                 }
485                 case kEventWindowActivated:
486                 case kEventWindowDeactivated: {
487                     surface->Focused(eKind == kEventWindowActivated);
488                     return noErr;
489                 }
490                 case kEventWindowZoomed: {
491                     fprintf(stderr,"Zoomed....\n");
492                     surface->Maximized(true);
493                     return noErr;
494                 }
495                 case kEventWindowCollapsed: {
496                     surface->Minimized(true);
497                     return noErr;
498                 }
499                 case kEventWindowExpanded: {
500                     surface->Minimized(false);
501                     return noErr;
502                 }
503                 case kEventWindowClose: {
504                     surface->Close();
505                     return noErr;
506                 }
507                 case kEventWindowClosed: {
508                     DisposeEventHandlerUPP((EventHandlerUPP)surface->rawEventHandlerUPP);
509                     GCJ$Retainer::release(surface);
510                     return noErr;
511                 }
512             }
513         }
514         break;
515     }
516     return eventNotHandledErr;
517 }
518 } // end namespace
519
520 void Carbon$CarbonSurface::natInit(jboolean framed) {
521     WindowRef window;
522     Rect rect;
523     WindowClass wc = framed ? kDocumentWindowClass : kPlainWindowClass;
524     // FIXME: unframed windows should appear in the window menu
525     // This probably needs a hack similar to whats in Cocoa.mm
526     WindowAttributes attr =  kWindowStandardHandlerAttribute|
527         (framed ? kWindowInWindowMenuAttribute|kWindowStandardDocumentAttributes|kWindowLiveResizeAttribute : 0);
528     OSStatus r;
529     
530     rect.top = 0; rect.left = 0; rect.bottom = 10; rect.right=10;
531     r = CreateNewWindow(wc,attr,&rect,&window);
532     checkStatus(r,"CreateNewWindow");
533     
534     GCJ$Retainer::retain(this); // Need to account for the EventHandlers pointer to us
535     EventHandlerUPP upp = NewEventHandlerUPP(windowEventHandler);
536     
537     r = InstallWindowEventHandler(window,upp,sizeof(eventTypeSpecs)/sizeof(EventTypeSpec),eventTypeSpecs,this,NULL);
538     checkStatus(r,"InstallWindowEventHandler");
539     
540     rawWindowRef = (RawData*) window;
541     rawEventHandlerUPP = (RawData*) upp;  
542 }
543
544 void Carbon$CarbonSurface::natDispose() {
545     WindowRef window = (WindowRef) rawWindowRef;
546     DisposeWindow(window);
547 }
548
549 void Carbon$CarbonSurface::natSetIcon(org::xwt::Picture *_p) {
550 }
551
552 void Carbon$CarbonSurface::natSetLocation(jint x, jint y) {
553     WindowRef window = (WindowRef) rawWindowRef;
554     Rect rect;
555     OSStatus r = GetWindowBounds(window,kWindowStructureRgn,&rect);
556     checkStatus(r,"GetWindowBounds");
557     rect.bottom = y + (rect.bottom - rect.top);
558     rect.right = x + (rect.right - rect.left);
559     rect.top = y;
560     rect.left = x;
561     r = SetWindowBounds(window,kWindowStructureRgn,&rect);
562     checkStatus(r,"SetWindowBounds");
563     r = ConstrainWindowToScreen(window,kWindowStructureRgn,kWindowConstrainMoveRegardlessOfFit,NULL,NULL);
564     checkStatus(r,"ConstrainWindowToScreen");
565 }
566
567 void Carbon$CarbonSurface::natSetSize(jint w, jint h) {
568     WindowRef window = (WindowRef) rawWindowRef;
569     Rect rect;
570     OSStatus r = GetWindowBounds(window,kWindowStructureRgn,&rect);
571     checkStatus(r,"GetWindowBounds");
572     rect.bottom = rect.top + h;
573     rect.right = rect.left + w;
574     r = SetWindowBounds(window,kWindowStructureRgn,&rect);
575     checkStatus(r,"SetWindowBounds");
576     r = ConstrainWindowToScreen(window,kWindowStructureRgn,kWindowConstrainMoveRegardlessOfFit,NULL,NULL);
577     checkStatus(r,"ConstrainWindowToScreen");
578 }
579
580 void Carbon$CarbonSurface::natSetLimits(jint minw, jint minh, jint maxw, jint maxh) {
581     WindowRef window = (WindowRef) rawWindowRef;
582     const int maxMax = 32767;
583     const int minMinW = 80;
584     const int minMinH = 20;
585     HISize min,max;
586     min.width  = minw > maxMax ? maxMax : (minw < minMinW ? minMinW : minw);
587     min.height = minh > maxMax ? maxMax : (minh < minMinH ? minMinH : minh);
588     max.width  = maxw > maxMax ? maxMax : (maxw < minMinW ? minMinW : maxw);
589     max.height = maxh > maxMax ? maxMax : (maxh < minMinH ? minMinH : maxh);
590     OSStatus r = SetWindowResizeLimits(window,&min,&max);
591     checkStatus(r,"SetWindowResizeLimits");
592 }
593
594
595 #pragma mark ------ Carbon Methods ------
596 void carbon::fileDialogEventHandler(NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, void *userData) {
597     Carbon$FileDialogHelper *helper = (Carbon$FileDialogHelper*) userData;
598     NavDialogRef dialog = callBackParms->context;
599     OSStatus r;
600     switch(callBackSelector) {
601         case kNavCBUserAction: {
602             NavUserAction action = NavDialogGetUserAction(dialog);
603             if(action == kNavUserActionNone || action == kNavUserActionCancel) {
604                 helper->fileName = 0;
605             } else {
606                 NavReplyRecord reply;
607                 r = NavDialogGetReply(dialog,&reply);
608                 checkStatus(r,"NavDialogGetReply");
609
610                 AEKeyword keyword;
611                 FSRef ref;
612                 char buf[4096];
613                 r = AEGetNthPtr(&reply.selection,1,typeFSRef,&keyword,NULL,&ref,sizeof(ref),NULL);
614                 checkStatus(r,"AEGetNthPtr");
615                 r = FSRefMakePath(&ref,(UInt8*)buf,sizeof(buf)-1);
616                 checkStatus(r,"FSRefMakePath");
617                 helper->fileName = JvNewStringLatin1(buf);
618                 if(helper->save) helper->saveName = cfStringToJString(reply.saveFileName);
619                 r = NavDisposeReply(&reply);
620                 checkStatus(r,"NavDialogGetReply");
621             }
622             helper->sem->release();
623             break;
624         }
625         case kNavCBTerminate:
626             DisposeNavEventUPP((NavEventUPP)helper->rawUPP);
627             NavDialogDispose(dialog);
628             break;
629     }
630 }
631
632 void Carbon::natFileDialog(Carbon$FileDialogHelper *helper,jstring suggestion_, jboolean write) {
633     NavDialogRef dlg;
634     SmartCFString suggestion = suggestion_;
635     CFStringRef message = CFSTR("By selecting a file in this dialog you are giving this XWT application permission to access that file.");
636     OSStatus r;
637     WindowRef window = FrontWindow();
638     NavDialogCreationOptions options;
639         
640     NavEventUPP handler = NewNavEventUPP(carbon::fileDialogEventHandler);
641     
642     NavGetDefaultDialogCreationOptions(&options);
643     options.optionFlags =
644         (options.optionFlags|kNavAllFilesInPopup|kNavSelectAllReadableItem|kNavDontUseCustomFrame|kNavDontConfirmReplacement)
645         &~(kNavAllowStationery|kNavAllowMultipleFiles);
646     options.clientName = CFSTR("XWT");
647     if(write)
648         options.saveFileName  = suggestion;
649     options.message = message;
650     options.modality = window ? kWindowModalityWindowModal : kWindowModalityAppModal;
651     options.parentWindow = window;
652     
653     if(write)
654         r = NavCreatePutFileDialog(&options,0,0,handler,helper,&dlg);
655     else
656         r = NavCreateGetFileDialog(&options,NULL,handler,NULL,NULL,helper,&dlg);
657     checkStatus(r,"NavCreate(Get/Put)FileDialog");
658     
659     helper->rawUPP = (RawData*)handler;
660     NavDialogRun(dlg);
661 }
662
663 jstring Carbon::natGetClipBoard() {
664     ScrapRef scrap;
665     OSStatus r;
666     Size size,size2;
667
668     r = GetCurrentScrap(&scrap);
669     checkStatus(r,"GetCurrentScrap");
670     
671     r = GetScrapFlavorSize( scrap, kScrapFlavorTypeUnicode, &size);
672     if(r == scrapFlavorNotFoundErr) return JvNewStringLatin1("");
673     checkStatus(r,"GetScrapFlavorSize");
674     
675     unsigned int length = size/sizeof(UniChar);
676     
677     jstring js = JvAllocString(length);
678     UniChar *buf = (UniChar*) JvGetStringChars(js);
679     size2 = size;
680     r = GetScrapFlavorData(scrap,kScrapFlavorTypeUnicode,&size2,buf);
681     if(r == scrapFlavorNotFoundErr);
682     checkStatus(r,"GetScrapFlavorData");
683     if(size2 != size) return JvNewStringLatin1("");
684     
685     return js;
686 }
687
688 void Carbon::natSetClipBoard(jstring js) {
689     unsigned int length = js->length();
690     ScrapRef scrap;
691     OSStatus r;
692
693     r = GetCurrentScrap(&scrap);
694     checkStatus(r,"GetCurrentScrap");
695     
696     r = PutScrapFlavor(scrap,kScrapFlavorTypeUnicode,0,sizeof(UniChar)*length,JvGetStringChars(js));
697     checkStatus(r,"PutScrapFlavor");
698 }
699
700 Proxy *Carbon::natDetectProxy() {
701     using org::xwt::Proxy;
702     Proxy *p=0;
703     CFStringRef string;
704     CFNumberRef number;
705     SmartCFString smartString;
706     CFArrayRef exceptionList;
707     int i;
708     
709     CFDictionaryRef proxyInfo = SCDynamicStoreCopyProxies(NULL);
710     if(proxyInfo == NULL) return 0;
711     
712 #define doproto(proto,var) \
713     number = (CFNumberRef) CFDictionaryGetValue(proxyInfo,kSCPropNetProxies ## proto ## Enable);    \
714     if(number != NULL && CFGetTypeID(number) != CFNumberGetTypeID()) number = NULL;                 \
715     if(number && CFNumberGetValue(number,kCFNumberIntType,&i) && i) {                               \
716         string = (CFStringRef) CFDictionaryGetValue(proxyInfo, kSCPropNetProxies ## proto ## Proxy);\
717         if(string != NULL && CFGetTypeID(string) != CFStringGetTypeID()) string = NULL;             \
718         number = (CFNumberRef) CFDictionaryGetValue(proxyInfo, kSCPropNetProxies ## proto ## Port); \
719         if(number != NULL && CFGetTypeID(number) != CFNumberGetTypeID()) number = NULL;    \
720         if(string && number && CFNumberGetValue(number,kCFNumberIntType,&i) && i) {        \
721             if(!p) p = new Proxy();                                                        \
722             p->var ## ProxyHost = cfStringToJString(string);                           \
723             p->var ## ProxyPort = i;                                                   \
724         }                                                                                  \
725     }                                                                                      
726 doproto(HTTP,http)
727 doproto(HTTPS,https)
728 doproto(SOCKS,socks)
729 #undef doproto
730
731     exceptionList = (CFArrayRef) CFDictionaryGetValue(proxyInfo,kSCPropNetProxiesExceptionsList);
732     if(exceptionList != NULL && CFGetTypeID(exceptionList) != CFArrayGetTypeID()) exceptionList = NULL;
733     if(p && exceptionList && CFArrayGetCount(exceptionList)) {
734         CFIndex count = CFArrayGetCount(exceptionList);
735         p->excluded = (JArray<java::lang::String*>*)
736             JvNewObjectArray(count,&java::lang::String::class$,0);
737         for(i=0;i<count;i++) {
738             string = (CFStringRef) CFArrayGetValueAtIndex(exceptionList,i);
739             if(string != NULL && CFGetTypeID(string) != CFStringGetTypeID()) string = NULL;
740             elements(p->excluded)[i] = string ? cfStringToJString(string) : JvNewStringLatin1("(null)");
741         }
742     }
743     CFRelease(proxyInfo);
744     
745     return p;
746 /*
747     exceptionList = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesExceptionsList];
748     if(p && exceptionList && [exceptionList count]) {
749         NSLog(@"excl: %@",exceptionList);
750         p->excluded = (JArray<java::lang::String*>*)
751             JvNewObjectArray([exceptionList count],&java::lang::String::class$,0);
752         for(i=0;i<[exceptionList count];i++)
753             elements(p->excluded)[i] = [[exceptionList objectAtIndex: i] jstring];
754     }
755     return p;
756     */    
757             
758 /*
759     using org::xwt::Proxy;
760     AutoARP pool;
761     Proxy *p=0;
762     NSString *host;
763     NSNumber *port;
764     NSArray *exceptionList;
765     unsigned int i;
766     NSDictionary *proxyInfo = (NSDictionary*)SCDynamicStoreCopyProxies(NULL);
767     
768     if(proxyInfo == NULL) return 0;
769     if([[proxyInfo objectForKey: (NSString*) kSCPropNetProxiesHTTPEnable] boolValue]) {
770         host = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesHTTPProxy];
771         port = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesHTTPPort];
772         if(host && [port intValue]) {
773             if(!p) p = new Proxy();
774             p->httpProxyHost = [host jstring];
775             p->httpProxyPort = [port intValue];
776         }
777     }
778     if([[proxyInfo objectForKey: (NSString*) kSCPropNetProxiesHTTPSEnable] boolValue]) {
779         host = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesHTTPSProxy];
780         port = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesHTTPSPort];
781         if(host && [port intValue]) {
782             if(!p) p = new Proxy();
783             p->httpsProxyHost = [host jstring];
784             p->httpsProxyPort = [port intValue];
785         }
786     }
787     if([[proxyInfo objectForKey: (NSString*) kSCPropNetProxiesSOCKSEnable] boolValue]) {
788         host = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesSOCKSProxy];
789         port = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesSOCKSPort];
790         if(host && [port intValue]) {
791             if(!p) p = new Proxy();
792             p->socksProxyHost = [host jstring];
793             p->socksProxyPort = [port intValue];
794         }
795     }
796    
797     exceptionList = [proxyInfo objectForKey: (NSString*) kSCPropNetProxiesExceptionsList];
798     if(p && exceptionList && [exceptionList count]) {
799         NSLog(@"excl: %@",exceptionList);
800         p->excluded = (JArray<java::lang::String*>*)
801             JvNewObjectArray([exceptionList count],&java::lang::String::class$,0);
802         for(i=0;i<[exceptionList count];i++)
803             elements(p->excluded)[i] = [[exceptionList objectAtIndex: i] jstring];
804     }
805     return p;
806 */
807 }
808
809 jint Carbon::cgScreenWidth() {
810     return CGDisplayPixelsWide(kCGDirectMainDisplay);
811 }
812
813 jint Carbon::cgScreenHeight() {
814     return CGDisplayPixelsHigh(kCGDirectMainDisplay);
815 }
816
817 void Carbon::_newBrowserWindow(jstring js) {
818     SmartCFString cfs = js;
819     CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault,cfs,NULL);
820     SmartCFString scheme = CFURLCopyScheme(url);
821     if(scheme.equals(CFStringRef("http")))
822         LSOpenCFURLRef(url,NULL);
823     CFRelease(url);
824 }
825
826 void Carbon::_exit() {
827     QuitApplicationEventLoop();
828 }
829
830 #define XWT_CARBON_NO_BUNDLE_HACK
831 #ifdef XWT_CARBON_NO_BUNDLE_HACK
832 extern "C" {
833     OSErr CPSEnableForegroundOperation(ProcessSerialNumber *psn);
834     OSErr CPSSetFrontProcess(ProcessSerialNumber *psn);
835 }
836 #endif
837 void Carbon::natInit() {     
838     OSStatus r;
839     #ifdef XWT_CARBON_NO_BUNDLE_HACK
840     {        
841         ProcessSerialNumber currentProcess = { 0, kCurrentProcess };
842         
843         ::fprintf(stderr,"Doing XWT_CARBON_NO_BUNDLE_HACK\n");
844         r = GetCurrentProcess(&currentProcess);
845         checkStatus(r,"GetCurrentProcess");
846         r = CPSEnableForegroundOperation( &currentProcess );
847         checkStatus(r,"CPSEnableForegroundOperation");
848         r = CPSSetFrontProcess(&currentProcess);
849         checkStatus(r,"CPSSetFrontProcess");        
850     }
851     #else
852     {
853         IBNibRef nib;
854         r = CreateNibReference(CFSTR("MainMenu"), &nib);
855         checkStatus(r,"CreateNibReference");
856         r = SetMenuBarFromNib(nib, CFSTR("MenuBar"));
857         checkStatus(r,"SetMenuBarFromNib");
858         DisposeNibReference(nib);
859         
860         // FIXME: Install menu event handler
861     }
862     #endif
863 }
864
865 void Carbon::_running() {
866     RunApplicationEventLoop();
867     ExitToShell();
868 }
869
870 #pragma mark ------ OpenGL Functions -----
871
872 void Carbon$CarbonOpenGL::activateSharedContext() {
873     AGLContext ctx = (AGLContext) rawSharedContext;
874     aglSetCurrentContext(ctx);
875 }
876
877 jboolean Carbon$CarbonOpenGL::initPixelFormat() {
878     GLint attr[] = {
879         AGL_NO_RECOVERY,
880         AGL_RGBA,
881         AGL_DEPTH_SIZE, 32,
882         AGL_RED_SIZE, 8,
883         AGL_GREEN_SIZE, 8,
884         AGL_RED_SIZE, 8,
885         AGL_ALPHA_SIZE, 8,
886         AGL_NONE
887     };
888     
889     rawPixelFormat = (RawData*) aglChoosePixelFormat(NULL,0,attr);
890     return rawPixelFormat != 0;
891 }
892
893 void Carbon$CarbonOpenGL::initSharedContext() {
894     AGLPixelFormat fmt =  (AGLPixelFormat) rawPixelFormat;
895     rawSharedContext = (RawData*) aglCreateContext(fmt,NULL);
896     checkStatus(rawSharedContext,"aglCreateContext");
897 }
898
899 void Carbon$GLCarbonDoubleBuffer::natInit() {
900     WindowClass wc = kPlainWindowClass;
901     WindowAttributes attr = 0;
902     WindowRef window;
903     Rect rect;
904     OSStatus r;
905     AGLContext ctx,shared;
906     AGLPixelFormat fmt;
907     GLboolean b;
908     GLuint tex;
909     GLuint target;
910     
911     rect.top = rect.left = 0;
912     rect.right = width + rect.left;
913     rect.bottom = height + rect.top; 
914     
915     r = CreateNewWindow(wc,attr,&rect,&window);
916     checkStatus(r,"CreateNewWindow");
917         
918     shared = (AGLContext) gl->rawSharedContext;
919     fmt = (AGLPixelFormat) gl->rawPixelFormat;
920     ctx = aglCreateContext(fmt,shared);
921     checkStatus(ctx, "aglCreateContext");
922     
923     b = aglSetDrawable(ctx,GetWindowPort(window));
924     checkStatus(b,"aglSetDrawable");
925     
926     aglSetCurrentContext(ctx);
927     aglUpdateContext(ctx);
928     drawableInit(width,height);
929     glClear(GL_COLOR_BUFFER_BIT);
930
931     aglSetCurrentContext(shared);
932     target = rectTexture ? GL_TEXTURE_RECTANGLE_EXT : GL_TEXTURE_2D;
933     glEnable(target);
934     glGenTextures(1,&tex);
935     glBindTexture(target,tex);
936     glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
937     glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
938     aglSurfaceTexture (shared, target, GL_RGBA, ctx);
939     checkGLError();
940     glDisable(target);
941     
942     //ShowWindow(window);
943     textureName = (jint) tex;
944     rawWindowRef = (RawData*) window;
945     rawCTX = (RawData*) ctx;
946 }
947
948 void Carbon$GLCarbonDoubleBuffer::activateContext() {
949     AGLContext ctx = (AGLContext) rawCTX;
950     aglSetCurrentContext(ctx);
951 }
952
953 void Carbon$GLCarbonDoubleBuffer::natCleanup(RawData* rawWindowRef, RawData* rawCTX) {
954     WindowRef window = (WindowRef) rawWindowRef;
955     AGLContext ctx = (AGLContext) rawCTX;
956     aglDestroyContext(ctx);
957     DisposeWindow(window);
958 }
959
960 void Carbon$GLCarbonSurface::natBlit(Carbon$GLCarbonDoubleBuffer *db, jint sx1, jint sy1, jint dx1, jint dy1, jint dx2, jint dy2) {
961     AGLContext ctx = (AGLContext) rawCTX;
962     int sx2 = sx1 + (dx2-dx1);
963     int sy2 = sy1 + (dy2-dy1);
964     db->activateContext();
965     glFlush();
966     checkGLError();
967     aglSetCurrentContext(ctx);
968     checkGLError();
969     if(db->rectTexture) {
970         glEnable(GL_TEXTURE_RECTANGLE_EXT);
971         checkGLError();
972         glBindTexture(GL_TEXTURE_RECTANGLE_EXT, db->textureName);
973         checkGLError();
974         glBegin(GL_QUADS); 
975             glTexCoord2i (sx1, sy1   );
976             glVertex3i   (dx1, dy1, 0);   
977             glTexCoord2i (sx2, sy1   ); 
978             glVertex3i   (dx2, dy1, 0);
979             glTexCoord2i (sx2, sy2   ); 
980             glVertex3i   (dx2, dy2, 0);
981             glTexCoord2i (sx1, sy2   );
982             glVertex3i   (dx1, dy2, 0);
983         glEnd();
984         checkGLError();
985         glDisable(GL_TEXTURE_RECTANGLE_EXT);
986         checkGLError();
987     } else {
988         float tx1,ty1,tx2,ty2; // normalized texture coords
989         tx1 = (float) sx1 / (float) db->width;
990         ty1 = (float) sy1 / (float) db->height;
991         tx2 = (float) sx2 / (float) db->width;
992         ty2 = (float) sy2 / (float) db->height;
993
994         glColor4f(1.0f,1.0f,1.0f,1.0f);
995         glEnable(GL_TEXTURE_2D);
996         glBindTexture(GL_TEXTURE_2D, db->textureName);    
997         checkGLError();
998         glBegin(GL_QUADS); 
999             glTexCoord2f (tx1, ty1   );
1000             glVertex3i   (dx1, dy1, 0);
1001             glTexCoord2f (tx2, ty1   ); 
1002             glVertex3i   (dx2, dy1, 0);
1003             glTexCoord2f (tx2, ty2   ); 
1004             glVertex3i   (dx2, dy2, 0);
1005             glTexCoord2f (tx1, ty2   );
1006             glVertex3i   (dx1, dy2, 0);
1007         glEnd();
1008         glDisable(GL_TEXTURE_2D);
1009         checkGLError();
1010     }
1011     glFlush();
1012 }
1013
1014 void Carbon$GLCarbonSurface::natReshape(jint w, jint h) {
1015     AGLContext ctx = (AGLContext) rawCTX;
1016     
1017     aglSetCurrentContext (ctx);
1018     aglUpdateContext(ctx);
1019     
1020     glViewport(0, 0, w, h);
1021     glMatrixMode(GL_PROJECTION);
1022     glLoadIdentity(); 
1023     glOrtho(0, w, h, 0, -1, 1);   
1024     glMatrixMode(GL_MODELVIEW);
1025     glLoadIdentity();
1026     glTranslatef (0.375, 0.375, 0.0);
1027     checkGLError();
1028 }
1029
1030 void Carbon$GLCarbonSurface::natInit() {
1031     WindowRef window = (WindowRef) rawWindowRef;
1032     AGLContext ctx,shared;
1033     AGLPixelFormat fmt;
1034     GLboolean b;
1035     
1036     shared = (AGLContext) gl->rawSharedContext;
1037     fmt = (AGLPixelFormat) gl->rawPixelFormat;
1038     ctx = aglCreateContext(fmt,shared);
1039     checkStatus(ctx, "aglCreateContext");
1040     
1041     b = aglSetDrawable(ctx,GetWindowPort(window));
1042     checkStatus(b,"aglSetDrawable");
1043
1044     aglSetCurrentContext(ctx);
1045     checkGLError();
1046     
1047     rawCTX = (RawData*)ctx;
1048     
1049     reshape(10,10);
1050
1051     aglSetCurrentContext(ctx);
1052     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
1053 }
1054
1055 void Carbon$GLCarbonSurface::natDispose() {
1056     AGLContext ctx = (AGLContext) rawCTX;
1057     aglDestroyContext(ctx);
1058     Carbon$CarbonSurface::natDispose();
1059 }
1060
1061 } } } // end namepsace org::xwt::plat