Home | History | Annotate | Download | only in mac
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 #import "SkNSView.h"
     10 #include "SkCanvas.h"
     11 #include "SkCGUtils.h"
     12 #include "SkEvent.h"
     13 SK_COMPILE_ASSERT(SK_SUPPORT_GPU, not_implemented_for_non_gpu_build);
     14 
     15 //#define FORCE_REDRAW
     16 // Can be dropped when we no longer support 10.6.
     17 #define RETINA_API_AVAILABLE (defined(MAC_OS_X_VERSION_10_7) && \
     18                               MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
     19 @implementation SkNSView
     20 @synthesize fWind, fTitle, fOptionsDelegate, fGLContext;
     21 
     22 - (id)initWithCoder:(NSCoder*)coder {
     23     if ((self = [super initWithCoder:coder])) {
     24         self = [self initWithDefaults];
     25         [self setUpWindow];
     26     }
     27     return self;
     28 }
     29 
     30 - (id)initWithFrame:(NSRect)frameRect {
     31     if ((self = [super initWithFrame:frameRect])) {
     32         self = [self initWithDefaults];
     33         [self setUpWindow];
     34     }
     35     return self;
     36 }
     37 
     38 - (id)initWithDefaults {
     39 #if RETINA_API_AVAILABLE
     40     [self setWantsBestResolutionOpenGLSurface:YES];
     41 #endif
     42     fRedrawRequestPending = false;
     43     fWind = NULL;
     44     return self;
     45 }
     46 
     47 - (void)setUpWindow {
     48     [[NSNotificationCenter defaultCenter] addObserver:self
     49                                           selector:@selector(backingPropertiesChanged:)
     50                                           name:@"NSWindowDidChangeBackingPropertiesNotification"
     51                                           object:[self window]];
     52     if (NULL != fWind) {
     53         fWind->setVisibleP(true);
     54         NSSize size = self.frame.size;
     55 #if RETINA_API_AVAILABLE
     56         size = [self convertSizeToBacking:self.frame.size];
     57 #endif
     58         fWind->resize((int) size.width, (int) size.height,
     59                       SkBitmap::kARGB_8888_Config);
     60     }
     61 }
     62 
     63 -(BOOL) isFlipped {
     64     return YES;
     65 }
     66 
     67 - (BOOL)acceptsFirstResponder {
     68     return YES;
     69 }
     70 
     71 - (float)scaleFactor {
     72     NSWindow *window = [self window];
     73 #if RETINA_API_AVAILABLE
     74     if (window) {
     75         return [window backingScaleFactor];
     76     }
     77     return [[NSScreen mainScreen] backingScaleFactor];
     78 #else
     79     if (window) {
     80         return [window userSpaceScaleFactor];
     81     }
     82     return [[NSScreen mainScreen] userSpaceScaleFactor];
     83 #endif
     84 }
     85 
     86 - (void)backingPropertiesChanged:(NSNotification *)notification {
     87     CGFloat oldBackingScaleFactor = (CGFloat)[
     88         [notification.userInfo objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue
     89     ];
     90     CGFloat newBackingScaleFactor = [self scaleFactor];
     91     if (oldBackingScaleFactor == newBackingScaleFactor) {
     92         return;
     93     }
     94     
     95     // TODO: need a better way to force a refresh (that works).
     96     // [fGLContext update] does not appear to update if the point size has not changed,
     97     // even if the backing size has changed.
     98     [self setFrameSize:NSMakeSize(self.frame.size.width + 1, self.frame.size.height + 1)];
     99 }
    100 
    101 - (void)resizeSkView:(NSSize)newSize {
    102 #if RETINA_API_AVAILABLE
    103     newSize = [self convertSizeToBacking:newSize];
    104 #endif
    105     if (NULL != fWind &&
    106             (fWind->width()  != newSize.width ||
    107              fWind->height() != newSize.height))
    108     {
    109         fWind->resize((int) newSize.width, (int) newSize.height);
    110         if (NULL != fGLContext) {
    111             glClear(GL_STENCIL_BUFFER_BIT);
    112             [fGLContext update];
    113         }
    114     }
    115 }
    116 
    117 - (void) setFrameSize:(NSSize)newSize {
    118     [super setFrameSize:newSize];
    119     [self resizeSkView:newSize];
    120 }
    121 
    122 - (void)dealloc {
    123     delete fWind;
    124     self.fGLContext = nil;
    125     self.fTitle = nil;
    126     [super dealloc];
    127 }
    128 
    129 ////////////////////////////////////////////////////////////////////////////////
    130 
    131 - (void)drawSkia {
    132     fRedrawRequestPending = false;
    133     if (NULL != fWind) {
    134         SkAutoTUnref<SkCanvas> canvas(fWind->createCanvas());
    135         fWind->draw(canvas);
    136 #ifdef FORCE_REDRAW
    137         fWind->inval(NULL);
    138 #endif
    139     }
    140 }
    141 
    142 - (void)setSkTitle:(const char *)title {
    143     self.fTitle = [NSString stringWithUTF8String:title];
    144     [[self window] setTitle:self.fTitle];
    145 }
    146 
    147 - (BOOL)onHandleEvent:(const SkEvent&)evt {
    148     return false;
    149 }
    150 
    151 #include "SkOSMenu.h"
    152 - (void)onAddMenu:(const SkOSMenu*)menu {
    153     [self.fOptionsDelegate view:self didAddMenu:menu];
    154 }
    155 
    156 - (void)onUpdateMenu:(const SkOSMenu*)menu {
    157     [self.fOptionsDelegate view:self didUpdateMenu:menu];
    158 }
    159 
    160 - (void)postInvalWithRect:(const SkIRect*)r {
    161     if (!fRedrawRequestPending) {
    162         fRedrawRequestPending = true;
    163         [self setNeedsDisplay:YES];
    164         [self performSelector:@selector(drawSkia) withObject:nil afterDelay:0];
    165     }
    166 }
    167 ///////////////////////////////////////////////////////////////////////////////
    168 
    169 #include "SkKey.h"
    170 enum {
    171 	SK_MacReturnKey		= 36,
    172 	SK_MacDeleteKey		= 51,
    173 	SK_MacEndKey		= 119,
    174 	SK_MacLeftKey		= 123,
    175 	SK_MacRightKey		= 124,
    176 	SK_MacDownKey		= 125,
    177 	SK_MacUpKey			= 126,
    178     SK_Mac0Key          = 0x52,
    179     SK_Mac1Key          = 0x53,
    180     SK_Mac2Key          = 0x54,
    181     SK_Mac3Key          = 0x55,
    182     SK_Mac4Key          = 0x56,
    183     SK_Mac5Key          = 0x57,
    184     SK_Mac6Key          = 0x58,
    185     SK_Mac7Key          = 0x59,
    186     SK_Mac8Key          = 0x5b,
    187     SK_Mac9Key          = 0x5c
    188 };
    189 
    190 static SkKey raw2key(UInt32 raw)
    191 {
    192 	static const struct {
    193 		UInt32  fRaw;
    194 		SkKey   fKey;
    195 	} gKeys[] = {
    196 		{ SK_MacUpKey,		kUp_SkKey		},
    197 		{ SK_MacDownKey,	kDown_SkKey		},
    198 		{ SK_MacLeftKey,	kLeft_SkKey		},
    199 		{ SK_MacRightKey,   kRight_SkKey	},
    200 		{ SK_MacReturnKey,  kOK_SkKey		},
    201 		{ SK_MacDeleteKey,  kBack_SkKey		},
    202 		{ SK_MacEndKey,		kEnd_SkKey		},
    203         { SK_Mac0Key,       k0_SkKey        },
    204         { SK_Mac1Key,       k1_SkKey        },
    205         { SK_Mac2Key,       k2_SkKey        },
    206         { SK_Mac3Key,       k3_SkKey        },
    207         { SK_Mac4Key,       k4_SkKey        },
    208         { SK_Mac5Key,       k5_SkKey        },
    209         { SK_Mac6Key,       k6_SkKey        },
    210         { SK_Mac7Key,       k7_SkKey        },
    211         { SK_Mac8Key,       k8_SkKey        },
    212         { SK_Mac9Key,       k9_SkKey        }
    213 	};
    214     
    215 	for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++)
    216 		if (gKeys[i].fRaw == raw)
    217 			return gKeys[i].fKey;
    218 	return kNONE_SkKey;
    219 }
    220 
    221 - (void)keyDown:(NSEvent *)event {
    222     if (NULL == fWind)
    223         return;
    224     
    225     SkKey key = raw2key([event keyCode]);
    226     if (kNONE_SkKey != key)
    227         fWind->handleKey(key);
    228     else{
    229         unichar c = [[event characters] characterAtIndex:0];
    230         fWind->handleChar((SkUnichar)c);
    231     }
    232 }
    233 
    234 - (void)keyUp:(NSEvent *)event {
    235     if (NULL == fWind)
    236         return;
    237     
    238     SkKey key = raw2key([event keyCode]);
    239     if (kNONE_SkKey != key)
    240         fWind->handleKeyUp(key);
    241  // else
    242  //     unichar c = [[event characters] characterAtIndex:0];
    243 }
    244 
    245 static const struct {
    246     unsigned    fNSModifierMask;
    247     unsigned    fSkModifierMask;
    248 } gModifierMasks[] = {
    249     { NSAlphaShiftKeyMask,  kShift_SkModifierKey },
    250     { NSShiftKeyMask,       kShift_SkModifierKey },
    251     { NSControlKeyMask,     kControl_SkModifierKey },
    252     { NSAlternateKeyMask,   kOption_SkModifierKey },
    253     { NSCommandKeyMask,     kCommand_SkModifierKey },
    254 };
    255 
    256 static unsigned convertNSModifiersToSk(NSUInteger nsModi) {
    257     unsigned skModi = 0;
    258     for (size_t i = 0; i < SK_ARRAY_COUNT(gModifierMasks); ++i) {
    259         if (nsModi & gModifierMasks[i].fNSModifierMask) {
    260             skModi |= gModifierMasks[i].fSkModifierMask;
    261         }
    262     }
    263     return skModi;
    264 }
    265 
    266 - (void)mouseDown:(NSEvent *)event {
    267     NSPoint p = [event locationInWindow];
    268     unsigned modi = convertNSModifiersToSk([event modifierFlags]);
    269 
    270     if ([self mouse:p inRect:[self bounds]] && NULL != fWind) {
    271         NSPoint loc = [self convertPoint:p fromView:nil];
    272 #if RETINA_API_AVAILABLE
    273         loc = [self convertPointToBacking:loc]; //y-up
    274         loc.y = -loc.y;
    275 #endif
    276         fWind->handleClick((int) loc.x, (int) loc.y,
    277                            SkView::Click::kDown_State, self, modi);
    278     }
    279 }
    280 
    281 - (void)mouseDragged:(NSEvent *)event {
    282     NSPoint p = [event locationInWindow];
    283     unsigned modi = convertNSModifiersToSk([event modifierFlags]);
    284 
    285     if ([self mouse:p inRect:[self bounds]] && NULL != fWind) {
    286         NSPoint loc = [self convertPoint:p fromView:nil];
    287 #if RETINA_API_AVAILABLE
    288         loc = [self convertPointToBacking:loc]; //y-up
    289         loc.y = -loc.y;
    290 #endif
    291         fWind->handleClick((int) loc.x, (int) loc.y,
    292                            SkView::Click::kMoved_State, self, modi);
    293     }
    294 }
    295 
    296 - (void)mouseMoved:(NSEvent *)event {
    297     NSPoint p = [event locationInWindow];
    298     unsigned modi = convertNSModifiersToSk([event modifierFlags]);
    299     
    300     if ([self mouse:p inRect:[self bounds]] && NULL != fWind) {
    301         NSPoint loc = [self convertPoint:p fromView:nil];
    302 #if RETINA_API_AVAILABLE
    303         loc = [self convertPointToBacking:loc]; //y-up
    304         loc.y = -loc.y;
    305 #endif
    306         fWind->handleClick((int) loc.x, (int) loc.y,
    307                            SkView::Click::kMoved_State, self, modi);
    308     }
    309 }
    310 
    311 - (void)mouseUp:(NSEvent *)event {
    312     NSPoint p = [event locationInWindow];
    313     unsigned modi = convertNSModifiersToSk([event modifierFlags]);
    314     
    315     if ([self mouse:p inRect:[self bounds]] && NULL != fWind) {
    316         NSPoint loc = [self convertPoint:p fromView:nil];
    317 #if RETINA_API_AVAILABLE
    318         loc = [self convertPointToBacking:loc]; //y-up
    319         loc.y = -loc.y;
    320 #endif
    321         fWind->handleClick((int) loc.x, (int) loc.y,
    322                            SkView::Click::kUp_State, self, modi);
    323     }
    324 }
    325 
    326 ///////////////////////////////////////////////////////////////////////////////
    327 #include <OpenGL/OpenGL.h>
    328 
    329 namespace { 
    330 CGLContextObj createGLContext(int msaaSampleCount) {
    331     GLint major, minor;
    332     CGLGetVersion(&major, &minor);
    333     
    334     static const CGLPixelFormatAttribute attributes[] = {
    335         kCGLPFAStencilSize, (CGLPixelFormatAttribute) 8,
    336         kCGLPFAAccelerated,
    337         kCGLPFADoubleBuffer,
    338         (CGLPixelFormatAttribute)0
    339     };
    340     
    341     CGLPixelFormatObj format;
    342     GLint npix = 0;
    343     if (msaaSampleCount > 0) {
    344         static int kAttributeCount = SK_ARRAY_COUNT(attributes);
    345         CGLPixelFormatAttribute msaaAttributes[kAttributeCount + 5];
    346         memcpy(msaaAttributes, attributes, sizeof(attributes));
    347         SkASSERT(0 == msaaAttributes[kAttributeCount - 1]);
    348         msaaAttributes[kAttributeCount - 1] = kCGLPFASampleBuffers;
    349         msaaAttributes[kAttributeCount + 0] = (CGLPixelFormatAttribute)1;
    350         msaaAttributes[kAttributeCount + 1] = kCGLPFAMultisample;
    351         msaaAttributes[kAttributeCount + 2] = kCGLPFASamples;
    352         msaaAttributes[kAttributeCount + 3] =
    353                                     (CGLPixelFormatAttribute)msaaSampleCount;
    354         msaaAttributes[kAttributeCount + 4] = (CGLPixelFormatAttribute)0;
    355         CGLChoosePixelFormat(msaaAttributes, &format, &npix);
    356     }
    357     if (!npix) {
    358         CGLChoosePixelFormat(attributes, &format, &npix);
    359     }
    360     CGLContextObj ctx;
    361     CGLCreateContext(format, NULL, &ctx);
    362     CGLDestroyPixelFormat(format);
    363     
    364     static const GLint interval = 1;
    365     CGLSetParameter(ctx, kCGLCPSwapInterval, &interval);
    366     CGLSetCurrentContext(ctx);
    367     return ctx;
    368 }
    369 }
    370 
    371 - (void)viewDidMoveToWindow {
    372     [super viewDidMoveToWindow];
    373     
    374     //Attaching view to fGLContext requires that the view to be part of a window,
    375     //and that the NSWindow instance must have a CoreGraphics counterpart (or 
    376     //it must NOT be deferred or should have been on screen at least once)
    377     if ([fGLContext view] != self && nil != self.window) {
    378         [fGLContext setView:self];
    379     }
    380 }
    381 - (bool)attach:(SkOSWindow::SkBackEndTypes)attachType
    382         withMSAASampleCount:(int) sampleCount
    383         andGetInfo:(SkOSWindow::AttachmentInfo*) info {
    384     if (nil == fGLContext) {
    385         CGLContextObj ctx = createGLContext(sampleCount);
    386         fGLContext = [[NSOpenGLContext alloc] initWithCGLContextObj:ctx];
    387         CGLReleaseContext(ctx);
    388         if (NULL == fGLContext) {
    389             return false;
    390         }
    391         [fGLContext setView:self];
    392     }
    393 
    394     [fGLContext makeCurrentContext];
    395     CGLPixelFormatObj format = CGLGetPixelFormat((CGLContextObj)[fGLContext CGLContextObj]);
    396     CGLDescribePixelFormat(format, 0, kCGLPFASamples, &info->fSampleCount);
    397     CGLDescribePixelFormat(format, 0, kCGLPFAStencilSize, &info->fStencilBits);
    398     NSSize size = self.bounds.size;
    399 #if RETINA_API_AVAILABLE
    400     size = [self convertSizeToBacking:size];
    401 #endif
    402     glViewport(0, 0, (int) size.width, (int) size.height);
    403     glClearColor(0, 0, 0, 0);
    404     glClearStencil(0);
    405     glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    406     return true;
    407 }
    408 
    409 - (void)detach {
    410     [fGLContext release];
    411     fGLContext = nil;
    412 }
    413 
    414 - (void)present {
    415     if (nil != fGLContext) {
    416         [fGLContext flushBuffer];
    417     }
    418 }
    419 @end
    420