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