Added new macos glgraphics stuff...test only...
authorMark Sibly <blitzmunter@gmail.com>
Mon, 21 Sep 2015 02:15:20 +0000 (14:15 +1200)
committerMark Sibly <blitzmunter@gmail.com>
Mon, 21 Sep 2015 02:15:20 +0000 (14:15 +1200)
mod/brl.mod/glgraphics.mod/_glgraphics.macos.m [new file with mode: 0644]
mod/brl.mod/system.mod/_system.macos.m [new file with mode: 0644]

diff --git a/mod/brl.mod/glgraphics.mod/_glgraphics.macos.m b/mod/brl.mod/glgraphics.mod/_glgraphics.macos.m
new file mode 100644 (file)
index 0000000..83f98c4
--- /dev/null
@@ -0,0 +1,305 @@
+
+#include <AppKit/AppKit.h>
+#include <Carbon/Carbon.h>
+#include <OpenGL/gl.h>
+#include <OpenGL/OpenGL.h>
+
+#include <brl.mod/system.mod/system.h>
+
+enum{
+       FLAGS_BACKBUFFER=       0x2,
+       FLAGS_ALPHABUFFER=      0x4,
+       FLAGS_DEPTHBUFFER=      0x8,
+       FLAGS_STENCILBUFFER=0x10,
+       FLAGS_ACCUMBUFFER=      0x20
+};
+
+enum{
+       MODE_WIDGET=            1,
+       MODE_WINDOW=            2,
+       MODE_DISPLAY=           3
+};
+
+@interface BBGLWindow : NSWindow{
+}
+@end
+@implementation BBGLWindow
+-(void)sendEvent:(NSEvent*)event{
+       bbSystemEmitOSEvent( event,[self contentView],&bbNullObject );
+       switch( [event type] ){
+       case NSKeyDown:case NSKeyUp:
+               //prevent 'beeps'!
+               return;
+       }
+       [super sendEvent:event];
+}
+-(BOOL)windowShouldClose:(id)sender{
+       bbSystemEmitEvent( BBEVENT_APPTERMINATE,&bbNullObject,0,0,0,0,&bbNullObject );
+       return NO;
+}
+- (BOOL)canBecomeKeyWindow{
+       return YES;
+}
+@end
+
+typedef struct BBGLContext BBGLContext;
+
+struct BBGLContext{
+       int mode,width,height,depth,hertz,flags,sync;
+
+       BBGLWindow *window;
+       NSView *view;
+       
+       NSOpenGLContext *glContext;
+};
+
+static BBGLContext *_currentContext;
+
+static CFDictionaryRef oldDisplayMode;
+
+extern void bbFlushAutoreleasePool();
+
+void bbGLGraphicsClose( BBGLContext *context );
+void bbGLGraphicsGetSettings( BBGLContext *context,int *width,int *height,int *depth,int *hertz,int *flags );
+void bbGLGraphicsSetGraphics( BBGLContext *context );
+
+static int _initAttrs( CGLPixelFormatAttribute attrs[16],int flags ){
+       int n=0;
+       if( flags & FLAGS_BACKBUFFER ) attrs[n++]=kCGLPFADoubleBuffer;
+       if( flags & FLAGS_ALPHABUFFER ){ attrs[n++]=kCGLPFAAlphaSize;attrs[n++]=1; }
+       if( flags & FLAGS_DEPTHBUFFER ){ attrs[n++]=kCGLPFADepthSize;attrs[n++]=1; }
+       if( flags & FLAGS_STENCILBUFFER ){ attrs[n++]=kCGLPFAStencilSize;attrs[n++]=1; }
+       if( flags & FLAGS_ACCUMBUFFER ){ attrs[n++]=kCGLPFAAccumSize;attrs[n++]=1; }
+       attrs[n++]=kCGLPFANoRecovery;
+       attrs[n]=0;
+       return n;
+}
+
+static NSOpenGLContext *_sharedContext;
+
+static void _validateSize( BBGLContext *context ){
+       NSRect rect;
+       
+       if( !context || context->mode!=MODE_WIDGET ) return;
+       
+       rect=[context->view bounds];
+       if( rect.size.width==context->width && rect.size.height==context->height ) return;
+       
+       context->width=rect.size.width;
+       context->height=rect.size.height;
+
+       if( context->glContext ) [context->glContext update];
+}
+
+static void _validateContext( BBGLContext *context ){
+       int flags;
+       NSOpenGLContext *shared;
+       NSOpenGLContext *glContext;
+       NSOpenGLPixelFormat *glFormat;
+       CGLPixelFormatAttribute attrs[16];
+       
+       if( !context || context->glContext ) return;
+
+       flags=context->flags;
+       
+       _initAttrs( attrs,flags );
+
+       glFormat=[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+       glContext=[[NSOpenGLContext alloc] initWithFormat:glFormat shareContext:_sharedContext];
+       [glFormat release];
+
+       if( !glContext ) bbExThrowCString( "Unable to create GL Context" );
+       
+       context->glContext=glContext;
+       
+       [glContext setView:context->view];
+       
+       switch( context->mode ){
+       case MODE_DISPLAY:
+               CGLSetParameter( [glContext CGLContextObj],kCGLCPSurfaceBackingSize,&context->width );
+               CGLEnable( [glContext CGLContextObj],kCGLCESurfaceBackingSize );
+               break;
+       }
+}
+
+void bbGLGraphicsShareContexts(){
+       NSOpenGLPixelFormat *glFormat;
+       CGLPixelFormatAttribute attrs[16];
+       
+       if( _sharedContext ) return;
+
+       _initAttrs( attrs,0 );
+       glFormat=[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+       _sharedContext=[[NSOpenGLContext alloc] initWithFormat:glFormat shareContext:0];
+       [glFormat release];
+}
+
+int bbGLGraphicsGraphicsModes( int *modes,int count ){
+       int i=0,n=0,sz;
+       CFArrayRef displayModeArray;
+
+       displayModeArray=CGDisplayAvailableModes( kCGDirectMainDisplay );
+       sz=CFArrayGetCount( displayModeArray );
+       
+       while( i<sz && n<count ){
+
+               CFNumberRef number;
+               CFDictionaryRef displayMode;
+               int width,height,depth,hertz;
+
+               displayMode=(CFDictionaryRef)CFArrayGetValueAtIndex( displayModeArray,i++ );
+
+               number=CFDictionaryGetValue( displayMode,kCGDisplayBitsPerPixel );
+               CFNumberGetValue( number,kCFNumberLongType,&depth );
+
+               if( depth<16 ) continue;
+
+               number=CFDictionaryGetValue( displayMode,kCGDisplayWidth );
+               CFNumberGetValue( number,kCFNumberLongType,&width );
+
+               number=CFDictionaryGetValue( displayMode,kCGDisplayHeight );
+               CFNumberGetValue( number,kCFNumberLongType,&height );
+
+               number=CFDictionaryGetValue( displayMode,kCGDisplayRefreshRate );
+               CFNumberGetValue( number,kCFNumberLongType,&hertz );
+
+               *modes++=width;
+               *modes++=height;
+               *modes++=depth;
+               *modes++=hertz;
+               ++n;
+       }
+       
+       return n;
+}
+
+BBGLContext *bbGLGraphicsAttachGraphics( NSView *view,int flags ){
+       NSRect rect;
+       BBGLContext *context;
+
+       rect=[view bounds];
+       
+       context=(BBGLContext*)malloc( sizeof(BBGLContext) );
+       memset( context,0,sizeof(BBGLContext) );
+
+       context->mode=MODE_WIDGET;      
+       context->width=rect.size.width;
+       context->height=rect.size.height;
+       context->flags=flags;
+       context->sync=-1;
+       
+       context->view=view;
+       
+       return context;
+}
+
+BBGLContext *bbGLGraphicsCreateGraphics( int width,int height,int depth,int hertz,int flags ){
+       int mode;
+       BBGLWindow *window=0;
+       NSView *view=0;
+       BBGLContext *context;
+       int sysv=0;
+       
+       Gestalt( 'sysv',&sysv );
+       
+       //fullscreen mode only available in Tiger++
+       //
+       if( depth && sysv>=0x1070 ){
+       
+               view=[[NSView alloc] initWithFrame:[[NSScreen mainScreen] frame]];
+               [view enterFullScreenMode:[NSScreen mainScreen] withOptions:nil];
+               mode=MODE_DISPLAY;
+               
+       }else{
+               
+               window=[[BBGLWindow alloc]
+                       initWithContentRect:NSMakeRect( 0,0,width,height )
+                       styleMask:NSTitledWindowMask|NSClosableWindowMask
+                       backing:NSBackingStoreBuffered
+                       defer:YES];
+
+               if( !window ) return 0;
+               
+               view=[window contentView];
+               
+               [window setDelegate:window];
+               [window setAcceptsMouseMovedEvents:YES];
+
+               [window setTitle:[NSString stringWithUTF8String:bbTmpUTF8String(bbAppTitle)]];
+               [window center];
+
+               [window makeKeyAndOrderFront:NSApp];
+               
+               mode=MODE_WINDOW;
+       }
+       
+       context=(BBGLContext*)malloc( sizeof(BBGLContext) );
+       memset( context,0,sizeof(BBGLContext) );
+       
+       context->mode=mode;
+       context->width=width;
+       context->height=height;
+       context->depth=depth;
+       context->hertz=hertz;
+       context->flags=flags;
+       context->sync=-1;
+       context->window=window;
+       context->view=view;
+       
+       return context;
+}
+
+void bbGLGraphicsGetSettings( BBGLContext *context,int *width,int *height,int *depth,int *hertz, int *flags ){
+       _validateSize( context );
+       *width=context->width;
+       *height=context->height;
+       *depth=context->depth;
+       *hertz=context->hertz;
+       *flags=context->flags;
+}
+
+void bbGLGraphicsClose( BBGLContext *context ){
+       if( context==_currentContext ) bbGLGraphicsSetGraphics( 0 );
+
+       [context->glContext clearDrawable];
+       [context->glContext release];
+       
+       switch( context->mode ){
+       case MODE_WINDOW:
+               bbSystemViewClosed( [context->window contentView] );
+               [context->window close];
+               break;
+       case MODE_DISPLAY:
+               bbSystemViewClosed( context->view );
+               [context->view exitFullScreenModeWithOptions:nil];
+               [context->view release];
+               break;
+       }
+       free( context );
+}
+
+void bbGLGraphicsSetGraphics( BBGLContext *context ){
+       if( context ){
+               _validateSize( context );
+               _validateContext( context );
+               [context->glContext makeCurrentContext];
+       }else{
+               [NSOpenGLContext clearCurrentContext];
+       }
+       _currentContext=context;
+}
+
+void bbGLGraphicsFlip( int sync ){
+       if( !_currentContext ) return;
+       
+       sync=sync ? 1 : 0;
+       
+       static int _sync=-1;
+       
+       if( sync!=_currentContext->sync ){
+               _currentContext->sync=sync;
+               [_currentContext->glContext setValues:(long*)&sync forParameter:kCGLCPSwapInterval];
+       }
+       
+       [_currentContext->glContext flushBuffer];
+}
diff --git a/mod/brl.mod/system.mod/_system.macos.m b/mod/brl.mod/system.mod/_system.macos.m
new file mode 100644 (file)
index 0000000..af1da7d
--- /dev/null
@@ -0,0 +1,691 @@
+
+#import <brl.mod/system.mod/system.h>
+
+#include <AppKit/AppKit.h>
+
+#include <Carbon/Carbon.h>
+
+static unsigned char key_table[]={
+       //0...
+       KEY_A,          KEY_S,          KEY_D,          KEY_F,          KEY_H,          KEY_G,          KEY_Z,          KEY_X,
+       KEY_C,          KEY_V,          0,                      KEY_B,          KEY_Q,          KEY_W,          KEY_E,          KEY_R,
+       KEY_Y,          KEY_T,          KEY_1,          KEY_2,          KEY_3,          KEY_4,          KEY_6,          KEY_5,
+       KEY_EQUALS,     KEY_9,          KEY_7,          KEY_MINUS,      KEY_8,          KEY_0,          KEY_CLOSEBRACKET,KEY_O,
+       //32...
+       KEY_U,          KEY_OPENBRACKET,KEY_I,  KEY_P,          KEY_ENTER,  KEY_L,              KEY_J,          KEY_QUOTES,
+       KEY_K,          KEY_SEMICOLON,KEY_BACKSLASH,KEY_COMMA,KEY_SLASH,KEY_N,          KEY_M,          KEY_PERIOD,
+       KEY_TAB,        KEY_SPACE,  KEY_TILDE,  KEY_BACKSPACE,0,                KEY_ESC,        0,                      0,
+       0,                      0,                      0,                      0,                      0,                      0,                      0,                      0,
+       //64...
+       0,                      KEY_NUMDECIMAL,0,               KEY_NUMMULTIPLY,0,              KEY_NUMADD,     0,                      0,
+       0,                      0,                      0,                      KEY_NUMDIVIDE,KEY_ENTER,0,                      KEY_NUMSUBTRACT,0,
+       //80...
+       0,                      0,                      KEY_NUM0,       KEY_NUM1,       KEY_NUM2,       KEY_NUM3,       KEY_NUM4,       KEY_NUM5,
+       KEY_NUM6,       KEY_NUM7,       0,                      KEY_NUM8,       KEY_NUM9,       0,                      0,                      0,
+       //96...
+       KEY_F5,         KEY_F6,         KEY_F7,         KEY_F3,         KEY_F8,         KEY_F9,         0,                      KEY_F11,
+       0,                      0,                      0,                      0,                      0,                      KEY_F10,        0,                      KEY_F12,
+       0,                      0,                      KEY_INSERT,     KEY_HOME,       KEY_PAGEUP,     KEY_DELETE,     KEY_F4,         KEY_END,
+       KEY_F2,         KEY_PAGEDOWN,KEY_F1,    KEY_LEFT,       KEY_RIGHT,      KEY_DOWN,       KEY_UP,         0,
+       //128...
+};
+
+void bbSystemPoll();
+void bbFlushAutoreleasePool();
+
+static int mods,deltaMods;
+static NSDate *distantPast,*distantFuture;
+
+static int mouseVisible=1;
+static int displayCaptured=0;
+
+static NSView *mouseView;
+static BBObject *mouseSource;
+static NSTrackingRectTag mouseTrackTag;
+
+static NSEvent *anullEvent;
+static NSView *capturedView;
+
+static int appWaiting;
+static NSWindow *keyWin;
+
+#define LSHIFTMASK 0x2
+#define RSHIFTMASK 0x4
+#define LCTRLMASK 0x1
+#define RCTRLMASK 0x2000
+#define LSYSMASK 0x8
+#define RSYSMASK 0x10
+#define LALTMASK 0x20
+#define RALTMASK 0x40
+
+typedef struct AsyncOp{
+       BBAsyncOp asyncOp;
+       int asyncInfo;
+       int asyncRet;
+       BBSyncOp syncOp;
+       BBObject *syncInfo;
+}AsyncOp;
+
+@interface BBSystemAppDelegate : NSObject{
+}
+@end
+
+static BBSystemAppDelegate *appDelegate;
+
+static NSString *tmpNSString( BBString *str ){
+       return [NSString stringWithCharacters:str->buf length:str->length];
+}
+
+static BBString *stringFromNSString( NSString *nsstr ){
+       return bbStringFromUTF8String( [nsstr UTF8String] );
+}
+
+static NSString *appTitle(){
+       return tmpNSString( bbAppTitle );
+}
+
+int bbSystemTranslateKey( key ){
+       return (key>=0 && key<128) ? key_table[key] : 0;
+}
+
+int bbSystemTranslateChar( chr ){
+       switch(chr){
+       case 127:return 8;
+       case 63272:return 127;
+       }
+       return chr;
+}
+
+int bbSystemTranslateMods( mods ){
+       int n=0;
+       if( mods & NSShiftKeyMask ) n|=MODIFIER_SHIFT;
+       if( mods & NSControlKeyMask ) n|=MODIFIER_CONTROL;
+       if( mods & NSAlternateKeyMask ) n|=MODIFIER_OPTION;
+       if( mods & NSCommandKeyMask ) n|=MODIFIER_SYSTEM;
+       return n;
+}
+
+static void updateMouseVisibility(){
+       static int cursorVisible=1;
+       
+       int visible=mouseVisible;
+
+       if( !visible && !displayCaptured ){
+               
+               NSArray *windows=(NSArray*)[NSApp windows];
+               int count=[windows count],i;
+               
+               visible=1;
+
+               for( i=0;i<count;++i ){
+                       NSRect rect;
+                       NSPoint point;
+                       NSView *view;
+                       NSWindow *window;
+               
+                       window=[windows objectAtIndex:i];
+                       view=[window contentView];
+                       if( !view ) continue;
+               
+                       rect=[view bounds];
+                       point=[window mouseLocationOutsideOfEventStream];
+                       point=[view convertPoint:point fromView:nil];
+                       
+                       if( ![view isFlipped] ) point.y=rect.size.height-point.y;
+                       
+                       if( point.x<0 || point.y<0 || point.x>=rect.size.width || point.y>=rect.size.height ) continue;
+               
+                       visible=0;
+                       break;
+               }
+       }
+       if( visible ){
+               if( !CGCursorIsVisible() ){
+                       CGDisplayShowCursor( kCGDirectMainDisplay );
+               }
+       }else{
+               if( CGCursorIsVisible() ){
+                       CGDisplayHideCursor( kCGDirectMainDisplay );
+               }
+       }
+}
+
+static int mouseViewPos( NSView *view,int *x,int *y ){
+       NSRect rect;
+       NSPoint point;
+       NSWindow *window;
+
+       if( displayCaptured ){
+       
+               Point point;
+               CGLContextObj gl;
+               
+               GetMouse( &point );
+
+               if( gl=CGLGetCurrentContext() ){
+               
+                       GLint enabled=0;
+                       CGLIsEnabled( gl,kCGLCESurfaceBackingSize,&enabled );
+                       
+                       if( enabled ){
+                       
+                               NSRect frame;
+                               GLint size[2];
+                               
+                               frame=[[NSScreen mainScreen] frame];
+                               CGLGetParameter( gl,kCGLCPSurfaceBackingSize,size );
+                               
+                               *x=point.h * size[0] / frame.size.width;
+                               *y=point.v * size[1] / frame.size.height;
+                               return 1;
+                       } 
+               }
+               
+               *x=point.h;
+               *y=point.v;
+               return 1;
+       }
+       
+       if( !view ) return *x=*y=0;
+       
+       window=[view window];
+       point=[window mouseLocationOutsideOfEventStream];
+       rect=[view bounds];
+       point=[view convertPoint:point fromView:nil];
+       if( ![view isFlipped] ) point.y=rect.size.height-point.y;
+       *x=point.x;
+       *y=point.y;
+       return point.x>=0 && point.y>=0 && point.x<rect.size.width && point.y<rect.size.height;
+}
+
+static void setMouseView( NSView *view,int x,int y,BBObject *source ){
+       if( view==mouseView ) return;
+
+       if( mouseView ){
+               int x,y;
+               mouseViewPos( mouseView,&x,&y );
+               bbSystemEmitEvent( BBEVENT_MOUSELEAVE,mouseSource,0,0,x,y,&bbNullObject );
+               if( mouseSource ){
+                       BBRELEASE( mouseSource );
+               }
+               [mouseView removeTrackingRect:mouseTrackTag];
+               [mouseView release];
+       }
+
+       mouseView=[view retain];
+
+       if( mouseView ){
+               mouseSource=source;
+               if( mouseSource ){
+                       BBRETAIN( mouseSource );
+               }
+               bbSystemEmitEvent( BBEVENT_MOUSEENTER,mouseSource,0,0,x,y,&bbNullObject );
+               mouseTrackTag=[mouseView addTrackingRect:[mouseView bounds] owner:appDelegate userData:0 assumeInside:YES];
+       }
+}
+
+static NSEvent *appDefEvent( int subtype,int data1,int data2 ){
+       return [NSEvent 
+       otherEventWithType:NSApplicationDefined
+       location:NSMakePoint(0,0)
+       modifierFlags:0
+       timestamp:0
+       windowNumber:0
+       context:0
+       subtype:subtype
+       data1:data1
+       data2:data2];
+}
+
+@implementation BBSystemAppDelegate
+-(void)applicationWillTerminate:(NSNotification*)notification{
+       exit(0);
+}
+-(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender{
+       bbSystemEmitEvent( BBEVENT_APPTERMINATE,&bbNullObject,0,0,0,0,&bbNullObject );
+       return NSTerminateCancel;
+}
+-(void)applicationDidBecomeActive:(NSNotification *)aNotification{
+       bbSystemEmitEvent( BBEVENT_APPRESUME,&bbNullObject,0,0,0,0,&bbNullObject );
+}
+-(void)applicationDidResignActive:(NSNotification *)aNotification{
+       bbSystemEmitEvent( BBEVENT_APPSUSPEND,&bbNullObject,0,0,0,0,&bbNullObject );
+}
+-(BOOL)application:(NSApplication*)app openFile:(NSString*)path{
+       BBString *t=bbStringFromCString( [path cString] );
+       bbSystemEmitEvent( BBEVENT_APPOPENFILE,&bbNullObject,0,0,0,0,(BBObject*)t );
+       return YES;
+}
+-(void)mouseEntered:(NSEvent*)event{
+       //Never gets here hopefully!
+}
+-(void)mouseExited:(NSEvent*)event{
+       setMouseView( 0,0,0,0 );
+}
+-(void)asyncOpThread:(NSEvent*)event{
+       AsyncOp *p=(AsyncOp*)[event data1];
+       p->asyncRet=p->asyncOp( p->asyncInfo );
+       [NSApp postEvent:event atStart:NO];
+}
+@end
+
+static void updateDisplayCaptured(){
+       displayCaptured=CGDisplayIsCaptured( kCGDirectMainDisplay );
+}
+
+static void updateEvents( NSDate *until ){
+       NSEvent *event;
+
+       updateDisplayCaptured();
+       updateMouseVisibility();
+       
+       while( event=[NSApp nextEventMatchingMask:NSAnyEventMask untilDate:until inMode:NSDefaultRunLoopMode dequeue:YES] ){
+               if( [event type]==NSApplicationDefined ){
+                       if( [event subtype]==BB_RESERVEDEVENTSUBTYPE1 ){
+                               AsyncOp *p=(AsyncOp*)[event data1];
+                               p->syncOp( p->syncInfo,p->asyncRet );
+                               if( p->asyncOp ){
+                                       BBRELEASE( p->syncInfo );
+                               }
+                               free( p );
+                               continue;
+                       }
+               }
+               if( displayCaptured ){
+                       bbSystemEmitOSEvent( event,0,&bbNullObject );
+               }else{
+                       [NSApp sendEvent:event];
+               }
+               until=distantPast;
+       }
+
+       bbFlushAutoreleasePool();
+}
+
+static void checkDisplay(){
+       updateDisplayCaptured();
+       if( displayCaptured ) bbExThrowCString( "GUI unavailable in fullscreen mode" );
+}
+
+static void beginPanel(){
+       checkDisplay();
+       keyWin=[NSApp keyWindow];
+       if( !keyWin ) [NSApp activateIgnoringOtherApps:YES];
+}
+
+static void endPanel(){
+       if( keyWin ) [keyWin makeKeyWindow];
+}
+
+static NSWindow *appMainWindow(){
+       int i;
+       if( ![[NSApp windows] count] ) return 0;
+       for( i=0;i<10;++i ){
+               NSWindow *window=[NSApp mainWindow];
+               if( window ) return window;
+               bbSystemPoll();
+       }
+       return 0;
+}
+
+void bbSystemPoll(){
+       updateEvents( distantPast );
+}
+
+void bbSystemWait(){
+       appWaiting=1;
+       updateEvents( distantFuture );
+       appWaiting=0;
+}
+
+void bbSystemIntr(){
+       if( !appWaiting ) return;
+       appWaiting=0;
+       [NSApp postEvent:anullEvent atStart:NO];
+}
+
+void bbSystemViewClosed( NSView *view ){
+       if( view!=mouseView ) return;
+       [mouseView removeTrackingRect:mouseTrackTag];
+       mouseView=0;
+}
+
+void bbSystemEmitOSEvent( NSEvent *event,NSView *view,BBObject *source ){
+       int inView;
+       NSEventType type;
+       NSString *characters;
+       int ev=0,data=0,x=0,y=0,oldMods=mods,mask;
+       float f;
+       
+       mods=[event modifierFlags];
+
+       type=[event type];
+
+       switch( type ){
+       case NSKeyDown:
+               if( data=bbSystemTranslateKey( [event keyCode] ) ){
+                       ev=[event isARepeat] ? BBEVENT_KEYREPEAT : BBEVENT_KEYDOWN;
+                       bbSystemEmitEvent( ev,source,data,bbSystemTranslateMods(mods),0,0,&bbNullObject );
+               }
+               characters=[event characters];
+               if( [characters length]!=1 ) return;
+               data=[characters characterAtIndex:0];
+               if( data>=0xf700 && data<=0xf8ff ) return;
+               ev=BBEVENT_KEYCHAR;
+               data=bbSystemTranslateChar( data );
+               break;
+       case NSKeyUp:
+               data=bbSystemTranslateKey( [event keyCode] );
+               if( !data ) return;
+               ev=BBEVENT_KEYUP;
+               break;
+       case NSFlagsChanged:
+               deltaMods=mods^oldMods;
+               if( deltaMods & (mask=LSHIFTMASK) ) data=KEY_LSHIFT;
+               else if( deltaMods & (mask=RSHIFTMASK) ) data=KEY_RSHIFT;
+               else if( deltaMods & (mask=LCTRLMASK) ) data=KEY_LCONTROL;
+               else if( deltaMods & (mask=RCTRLMASK) ) data=KEY_RCONTROL;
+               else if( deltaMods & (mask=LALTMASK) ) data=KEY_LALT;
+               else if( deltaMods & (mask=RALTMASK) ) data=KEY_RALT;
+               else if( deltaMods & (mask=LSYSMASK) ) data=KEY_LSYS;
+               else if( deltaMods & (mask=RSYSMASK) ) data=KEY_RSYS;
+               if( !data ) return;
+               ev=(mods & mask) ? BBEVENT_KEYDOWN : BBEVENT_KEYUP;
+               break;
+       case NSLeftMouseDown:
+       case NSRightMouseDown:
+       case NSOtherMouseDown:
+               inView=mouseViewPos( view,&x,&y );
+               if( !inView ) return;
+               setMouseView( view,x,y,source );
+               capturedView=mouseView;
+               ev=BBEVENT_MOUSEDOWN;
+               data=(type==NSLeftMouseDown) ? 1 : (type==NSRightMouseDown ? 2 : 3);
+               break;
+       case NSLeftMouseUp:
+       case NSRightMouseUp:
+       case NSOtherMouseUp:
+               inView=mouseViewPos( view,&x,&y );
+               if( !inView && !capturedView ) return;
+               capturedView=0;
+               ev=BBEVENT_MOUSEUP;
+               data=(type==NSLeftMouseUp) ? 1 : (type==NSRightMouseUp ? 2 : 3);
+               break;
+       case NSMouseMoved:
+       case NSLeftMouseDragged:
+       case NSRightMouseDragged:
+       case NSOtherMouseDragged:
+               inView=mouseViewPos( view,&x,&y );
+               setMouseView( inView ? view : 0,x,y,source );
+               if( !inView && !capturedView ) return;
+               ev=BBEVENT_MOUSEMOVE;
+               data=(type==NSLeftMouseDragged) ? 1 : (type==NSRightMouseDragged ? 2 : (type==NSOtherMouseDragged ? 3 : 0));
+               break;
+       case NSScrollWheel:
+               inView=mouseViewPos( view,&x,&y );
+               if( !inView && view!=capturedView ) return;
+               ev=BBEVENT_MOUSEWHEEL;
+               f=[event deltaY];
+               data=f>0 ? ceil(f) : floor(f);
+               break;
+       default:
+               return;
+       }
+       bbSystemEmitEvent( ev,source,data,bbSystemTranslateMods(mods),x,y,&bbNullObject );
+}
+
+void bbSystemMoveMouse( int x,int y ){
+       NSEvent *event;
+       CGPoint cgPoint={x,y};
+
+       CGSetLocalEventsSuppressionInterval(0.0);
+
+       if( !CGDisplayIsCaptured(kCGDirectMainDisplay) ){
+               NSPoint nsPoint={x,y};
+       
+               NSWindow *window=appMainWindow();
+               
+               if( !window ) return;
+
+               nsPoint.y=[[window contentView] bounds].size.height-1-nsPoint.y;
+               nsPoint=[window convertBaseToScreen:nsPoint];
+
+               cgPoint.x=nsPoint.x;
+               cgPoint.y=CGDisplayPixelsHigh(kCGDirectMainDisplay)-nsPoint.y;
+       }
+
+       CGDisplayMoveCursorToPoint( kCGDirectMainDisplay,cgPoint );
+
+       bbSystemEmitEvent( BBEVENT_MOUSEMOVE,&bbNullObject,0,bbSystemTranslateMods(mods),x,y,&bbNullObject );
+}
+
+void bbSystemSetMouseVisible( int visible ){
+       mouseVisible=visible;
+       updateDisplayCaptured();
+       updateMouseVisibility();
+}
+
+void bbSystemStartup(){
+       anullEvent=[appDefEvent( -1,0,0 ) retain];
+       distantPast=[[NSDate distantPast] retain];
+       distantFuture=[[NSDate distantFuture] retain];
+       appDelegate=[[BBSystemAppDelegate alloc] init];
+       [NSApp setDelegate:appDelegate];
+}
+
+typedef int (*AlertPanel)( 
+       NSString *title,
+       NSString *msg,
+       NSString *defaultButton,
+       NSString *alternateButton,
+       NSString *otherButton );
+
+void bbSystemNotify( BBString *text,int serious ){
+       AlertPanel panel=serious ? (void*)NSRunCriticalAlertPanel : (void*)NSRunAlertPanel;
+       
+       beginPanel();
+       panel(
+               appTitle(),
+               tmpNSString(text),
+               @"OK",0,0 );
+       
+       endPanel();
+}
+
+int bbSystemConfirm( BBString *text,int serious ){
+       int n;
+       AlertPanel panel=serious ? (void*)NSRunCriticalAlertPanel : (void*)NSRunAlertPanel;
+       
+       beginPanel();
+       n=panel(
+               appTitle(),
+               tmpNSString(text),
+               @"OK",@"Cancel",0 );
+
+       endPanel();
+       
+       switch( n ){
+       case NSAlertDefaultReturn:return 1;
+       }
+       return 0;
+}
+
+int bbSystemProceed( BBString *text,int serious ){
+       int n;
+       AlertPanel panel=serious ? (void*)NSRunCriticalAlertPanel : (void*)NSRunAlertPanel;
+       
+       beginPanel();
+       n=panel(
+               appTitle(),
+               tmpNSString(text),
+               @"Yes",@"No",@"Cancel" );
+       endPanel();
+       
+       switch( n ){
+       case NSAlertDefaultReturn:return 1;
+       case NSAlertAlternateReturn:return 0;
+       }
+       return -1;
+}
+
+BBString *bbSystemRequestFile( BBString *title,BBString *exts,int save,BBString *file,BBString *dir ){
+
+       NSString *nsdir=0;
+       NSString *nsfile=0;
+       NSString *nstitle=0;
+       NSMutableArray *nsexts=0;
+
+       BBString *str=&bbEmptyString;
+       
+       if( dir->length ){
+               char tmp[PATH_MAX];
+               realpath( bbTmpUTF8String(dir),tmp );
+               nsdir=[NSString stringWithUTF8String:tmp];
+       }
+       
+       if( file->length ){
+               nsfile=tmpNSString(file);
+       }
+       
+       if( title->length ){
+               nstitle=tmpNSString(title);
+       }
+       
+       if( exts->length ){
+               char *p=bbTmpUTF8String(exts),*t;
+               nsexts=[NSMutableArray arrayWithCapacity:10];
+               while( t=strchr(p,',') ){
+                       *t=0;
+                       [nsexts addObject:[NSString stringWithUTF8String:p]];
+                       p=t+1;
+               }
+               if( *p ) [nsexts addObject:[NSString stringWithUTF8String:p]];
+       }
+
+       beginPanel();
+
+       if( save ){
+               NSSavePanel *panel=[NSSavePanel savePanel];
+               
+               if( nstitle ) [panel setTitle:nstitle];
+               
+               if( nsexts ){
+                       [panel setAllowedFileTypes:nsexts];
+                       [panel setAllowsOtherFileTypes:YES];
+               }
+               
+               if( [panel runModalForDirectory:nsdir file:nsfile]==NSFileHandlingPanelOKButton ){
+                       str=stringFromNSString([panel filename]);
+               }
+
+       }else{
+               NSOpenPanel *panel=[NSOpenPanel openPanel];
+
+               if( nstitle ) [panel setTitle:nstitle];
+               
+               if( [panel runModalForDirectory:nsdir file:nsfile types:nsexts]==NSFileHandlingPanelOKButton ){
+                       str=stringFromNSString([panel filename]);
+               }
+       }
+       endPanel();
+
+       return str;
+}
+
+BBString *bbSystemRequestDir( BBString *title,BBString *dir ){
+
+       NSString *nsdir=0;
+       NSString *nstitle=0;
+       NSOpenPanel *panel;
+
+       BBString *str=&bbEmptyString;
+
+       if( dir->length ){
+               char tmp[PATH_MAX];
+               realpath( bbTmpUTF8String(dir),tmp );
+               nsdir=[NSString stringWithUTF8String:tmp];
+       }
+       
+       if( title->length ){
+               nstitle=tmpNSString(title);
+       }
+
+       panel=[NSOpenPanel openPanel];
+       
+       [panel setCanChooseFiles:NO];
+       [panel setCanChooseDirectories:YES];
+       [panel setCanCreateDirectories:YES];
+       
+       if( title ) [panel setTitle:nstitle];
+       
+       beginPanel();
+       if( [panel runModalForDirectory:nsdir file:0 types:0]==NSFileHandlingPanelOKButton ){
+               str=stringFromNSString([panel filename]);
+       }
+       endPanel();
+       
+       return str;
+}
+
+int bbOpenURL( BBString *bburl ){
+       NSURL   *url;
+       NSString *loc;
+       int             res=0;
+
+       checkDisplay();
+       loc=tmpNSString(bburl);
+       url=[NSURL URLWithString:[loc stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+       if( url ) res=[[NSWorkspace sharedWorkspace] openURL:url];
+       return res;
+}
+
+void bbSystemPostSyncOp( BBSyncOp syncOp,BBObject *syncInfo,int asyncRet ){
+       AsyncOp *p=(AsyncOp*)malloc( sizeof(AsyncOp) );
+       NSEvent *event=appDefEvent( BB_RESERVEDEVENTSUBTYPE1,(int)p,0 );
+       p->asyncOp=0;
+       p->asyncRet=asyncRet;
+       p->syncOp=syncOp;
+       p->syncInfo=syncInfo;
+       [NSApp postEvent:event atStart:NO];
+}
+
+void bbSystemStartAsyncOp( BBAsyncOp asyncOp,int asyncInfo,BBSyncOp syncOp,BBObject *syncInfo ){
+       AsyncOp *p=(AsyncOp*)malloc( sizeof( AsyncOp ) );
+       NSEvent *event=appDefEvent( BB_RESERVEDEVENTSUBTYPE1,(int)p,0 );
+       BBRETAIN( syncInfo );
+       p->asyncOp=asyncOp;
+       p->asyncInfo=asyncInfo;
+       p->syncOp=syncOp;
+       p->syncInfo=syncInfo;
+       [NSThread detachNewThreadSelector:@selector(asyncOpThread:) toTarget:appDelegate withObject:event];
+}
+
+static NSScreen *DesktopScreen(){
+       NSArray *screens;
+       if( screens=[NSScreen screens] ){
+               if( [screens count] ) return [screens objectAtIndex:0];
+       }
+       return 0;
+}
+
+int bbSystemDesktopWidth(){
+       NSScreen *screen=DesktopScreen();
+       if( screen ) return [screen frame].size.width;
+       return 640;
+}
+
+int bbSystemDesktopHeight(){
+       NSScreen *screen=DesktopScreen();
+       if( screen ) return [screen frame].size.height;
+       return 480;
+}
+
+int bbSystemDesktopDepth(){
+       NSScreen *screen=DesktopScreen();
+       if( screen ) return NSBitsPerPixelFromDepth( [screen depth] );
+       return 32;
+}
+
+int bbSystemDesktopHertz(){
+       return 60;
+}