#import #include #include 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; size_t asyncRet; BBSyncOp syncOp; BBObject *syncInfo; }AsyncOp; 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( int key ){ return (key>=0 && key<128) ? key_table[key] : 0; } int bbSystemTranslateChar( int chr ){ switch(chr){ case 127:return 8; case 63272:return 127; } return chr; } int bbSystemTranslateMods( int 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=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 || !view ){ point = [NSEvent mouseLocation]; *x=point.x; *y=point.y; return 1; } 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.xasyncRet=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]; char *p=bbStringToUTF8String( dir ); realpath( p,tmp ); bbMemFree(p); nsdir=[NSString stringWithUTF8String:tmp]; } if( file->length ){ nsfile=tmpNSString(file); } if( title->length ){ nstitle=tmpNSString(title); } if( exts->length ){ char *p=bbStringToUTF8String(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]]; bbMemFree(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]; char *p=bbStringToUTF8String(dir); realpath( p,tmp ); bbMemFree(p); 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,size_t asyncRet ){ AsyncOp *p=(AsyncOp*)malloc( sizeof(AsyncOp) ); NSEvent *event=appDefEvent( BB_RESERVEDEVENTSUBTYPE1,(NSInteger)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,(NSInteger)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; }