Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #import "SkNSView.h"
      9 #include "SkCanvas.h"
     10 #include "SkSurface.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 (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 (fWind && (fWind->width()  != newSize.width || fWind->height() != newSize.height)) {
    107         fWind->resize((int) newSize.width, (int) newSize.height);
    108         if (fGLContext) {
    109             glClear(GL_STENCIL_BUFFER_BIT);
    110             [fGLContext update];
    111         }
    112     }
    113 }
    114 
    115 - (void) setFrameSize:(NSSize)newSize {
    116     [super setFrameSize:newSize];
    117     [self resizeSkView:newSize];
    118 }
    119 
    120 - (void)dealloc {
    121     delete fWind;
    122     self.fGLContext = nil;
    123     self.fTitle = nil;
    124     [super dealloc];
    125 }
    126 
    127 ////////////////////////////////////////////////////////////////////////////////
    128 
    129 - (void)drawSkia {
    130     fRedrawRequestPending = false;
    131     if (fWind) {
    132         SkAutoTUnref<SkSurface> surface(fWind->createSurface());
    133         fWind->draw(surface->getCanvas());
    134 #ifdef FORCE_REDRAW
    135         fWind->inval(NULL);
    136 #endif
    137     }
    138 }
    139 
    140 - (void)setSkTitle:(const char *)title {
    141     self.fTitle = [NSString stringWithUTF8String:title];
    142     [[self window] setTitle:self.fTitle];
    143 }
    144 
    145 - (BOOL)onHandleEvent:(const SkEvent&)evt {
    146     return false;
    147 }
    148 
    149 #include "SkOSMenu.h"
    150 - (void)onAddMenu:(const SkOSMenu*)menu {
    151     [self.fOptionsDelegate view:self didAddMenu:menu];
    152 }
    153 
    154 - (void)onUpdateMenu:(const SkOSMenu*)menu {
    155     [self.fOptionsDelegate view:self didUpdateMenu:menu];
    156 }
    157 
    158 - (void)postInvalWithRect:(const SkIRect*)r {
    159     if (!fRedrawRequestPending) {
    160         fRedrawRequestPending = true;
    161         [self setNeedsDisplay:YES];
    162         [self performSelector:@selector(drawSkia) withObject:nil afterDelay:0];
    163     }
    164 }
    165 ///////////////////////////////////////////////////////////////////////////////
    166 
    167 #include "SkKey.h"
    168 enum {
    169 	SK_MacReturnKey		= 36,
    170 	SK_MacDeleteKey		= 51,
    171 	SK_MacEndKey		= 119,
    172 	SK_MacLeftKey		= 123,
    173 	SK_MacRightKey		= 124,
    174 	SK_MacDownKey		= 125,
    175 	SK_MacUpKey			= 126,
    176     SK_Mac0Key          = 0x52,
    177     SK_Mac1Key          = 0x53,
    178     SK_Mac2Key          = 0x54,
    179     SK_Mac3Key          = 0x55,
    180     SK_Mac4Key          = 0x56,
    181     SK_Mac5Key          = 0x57,
    182     SK_Mac6Key          = 0x58,
    183     SK_Mac7Key          = 0x59,
    184     SK_Mac8Key          = 0x5b,
    185     SK_Mac9Key          = 0x5c
    186 };
    187 
    188 static SkKey raw2key(UInt32 raw)
    189 {
    190 	static const struct {
    191 		UInt32  fRaw;
    192 		SkKey   fKey;
    193 	} gKeys[] = {
    194 		{ SK_MacUpKey,		kUp_SkKey		},
    195 		{ SK_MacDownKey,	kDown_SkKey		},
    196 		{ SK_MacLeftKey,	kLeft_SkKey		},
    197 		{ SK_MacRightKey,   kRight_SkKey	},
    198 		{ SK_MacReturnKey,  kOK_SkKey		},
    199 		{ SK_MacDeleteKey,  kBack_SkKey		},
    200 		{ SK_MacEndKey,		kEnd_SkKey		},
    201         { SK_Mac0Key,       k0_SkKey        },
    202         { SK_Mac1Key,       k1_SkKey        },
    203         { SK_Mac2Key,       k2_SkKey        },
    204         { SK_Mac3Key,       k3_SkKey        },
    205         { SK_Mac4Key,       k4_SkKey        },
    206         { SK_Mac5Key,       k5_SkKey        },
    207         { SK_Mac6Key,       k6_SkKey        },
    208         { SK_Mac7Key,       k7_SkKey        },
    209         { SK_Mac8Key,       k8_SkKey        },
    210         { SK_Mac9Key,       k9_SkKey        }
    211 	};
    212 
    213 	for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++)
    214 		if (gKeys[i].fRaw == raw)
    215 			return gKeys[i].fKey;
    216 	return kNONE_SkKey;
    217 }
    218 
    219 - (void)keyDown:(NSEvent *)event {
    220     if (NULL == fWind)
    221         return;
    222 
    223     SkKey key = raw2key([event keyCode]);
    224     if (kNONE_SkKey != key)
    225         fWind->handleKey(key);
    226     else{
    227         unichar c = [[event characters] characterAtIndex:0];
    228         fWind->handleChar((SkUnichar)c);
    229     }
    230 }
    231 
    232 - (void)keyUp:(NSEvent *)event {
    233     if (NULL == fWind)
    234         return;
    235 
    236     SkKey key = raw2key([event keyCode]);
    237     if (kNONE_SkKey != key)
    238         fWind->handleKeyUp(key);
    239  // else
    240  //     unichar c = [[event characters] characterAtIndex:0];
    241 }
    242 
    243 static const struct {
    244     unsigned    fNSModifierMask;
    245     unsigned    fSkModifierMask;
    246 } gModifierMasks[] = {
    247     { NSAlphaShiftKeyMask,  kShift_SkModifierKey },
    248     { NSShiftKeyMask,       kShift_SkModifierKey },
    249     { NSControlKeyMask,     kControl_SkModifierKey },
    250     { NSAlternateKeyMask,   kOption_SkModifierKey },
    251     { NSCommandKeyMask,     kCommand_SkModifierKey },
    252 };
    253 
    254 static unsigned convertNSModifiersToSk(NSUInteger nsModi) {
    255     unsigned skModi = 0;
    256     for (size_t i = 0; i < SK_ARRAY_COUNT(gModifierMasks); ++i) {
    257         if (nsModi & gModifierMasks[i].fNSModifierMask) {
    258             skModi |= gModifierMasks[i].fSkModifierMask;
    259         }
    260     }
    261     return skModi;
    262 }
    263 
    264 - (void)mouseDown:(NSEvent *)event {
    265     NSPoint p = [event locationInWindow];
    266     unsigned modi = convertNSModifiersToSk([event modifierFlags]);
    267 
    268     if ([self mouse:p inRect:[self bounds]] && fWind) {
    269         NSPoint loc = [self convertPoint:p fromView:nil];
    270 #if RETINA_API_AVAILABLE
    271         loc = [self convertPointToBacking:loc]; //y-up
    272         loc.y = -loc.y;
    273 #endif
    274         fWind->handleClick((int) loc.x, (int) loc.y,
    275                            SkView::Click::kDown_State, self, modi);
    276     }
    277 }
    278 
    279 - (void)mouseDragged:(NSEvent *)event {
    280     NSPoint p = [event locationInWindow];
    281     unsigned modi = convertNSModifiersToSk([event modifierFlags]);
    282 
    283     if ([self mouse:p inRect:[self bounds]] && fWind) {
    284         NSPoint loc = [self convertPoint:p fromView:nil];
    285 #if RETINA_API_AVAILABLE
    286         loc = [self convertPointToBacking:loc]; //y-up
    287         loc.y = -loc.y;
    288 #endif
    289         fWind->handleClick((int) loc.x, (int) loc.y,
    290                            SkView::Click::kMoved_State, self, modi);
    291     }
    292 }
    293 
    294 - (void)mouseMoved:(NSEvent *)event {
    295     NSPoint p = [event locationInWindow];
    296     unsigned modi = convertNSModifiersToSk([event modifierFlags]);
    297 
    298     if ([self mouse:p inRect:[self bounds]] && fWind) {
    299         NSPoint loc = [self convertPoint:p fromView:nil];
    300 #if RETINA_API_AVAILABLE
    301         loc = [self convertPointToBacking:loc]; //y-up
    302         loc.y = -loc.y;
    303 #endif
    304         fWind->handleClick((int) loc.x, (int) loc.y,
    305                            SkView::Click::kMoved_State, self, modi);
    306     }
    307 }
    308 
    309 - (void)mouseUp:(NSEvent *)event {
    310     NSPoint p = [event locationInWindow];
    311     unsigned modi = convertNSModifiersToSk([event modifierFlags]);
    312 
    313     if ([self mouse:p inRect:[self bounds]] && fWind) {
    314         NSPoint loc = [self convertPoint:p fromView:nil];
    315 #if RETINA_API_AVAILABLE
    316         loc = [self convertPointToBacking:loc]; //y-up
    317         loc.y = -loc.y;
    318 #endif
    319         fWind->handleClick((int) loc.x, (int) loc.y,
    320                            SkView::Click::kUp_State, self, modi);
    321     }
    322 }
    323 
    324 ///////////////////////////////////////////////////////////////////////////////
    325 #include <OpenGL/OpenGL.h>
    326 
    327 namespace {
    328 CGLContextObj createGLContext(int msaaSampleCount) {
    329     GLint major, minor;
    330     CGLGetVersion(&major, &minor);
    331 
    332     static const CGLPixelFormatAttribute attributes[] = {
    333         kCGLPFAStencilSize, (CGLPixelFormatAttribute) 8,
    334         kCGLPFAAccelerated,
    335         kCGLPFADoubleBuffer,
    336         (CGLPixelFormatAttribute)0
    337     };
    338 
    339     CGLPixelFormatObj format;
    340     GLint npix = 0;
    341     if (msaaSampleCount > 0) {
    342         static int kAttributeCount = SK_ARRAY_COUNT(attributes);
    343         CGLPixelFormatAttribute msaaAttributes[kAttributeCount + 5];
    344         memcpy(msaaAttributes, attributes, sizeof(attributes));
    345         SkASSERT(0 == msaaAttributes[kAttributeCount - 1]);
    346         msaaAttributes[kAttributeCount - 1] = kCGLPFASampleBuffers;
    347         msaaAttributes[kAttributeCount + 0] = (CGLPixelFormatAttribute)1;
    348         msaaAttributes[kAttributeCount + 1] = kCGLPFAMultisample;
    349         msaaAttributes[kAttributeCount + 2] = kCGLPFASamples;
    350         msaaAttributes[kAttributeCount + 3] =
    351                                     (CGLPixelFormatAttribute)msaaSampleCount;
    352         msaaAttributes[kAttributeCount + 4] = (CGLPixelFormatAttribute)0;
    353         CGLChoosePixelFormat(msaaAttributes, &format, &npix);
    354     }
    355     if (!npix) {
    356         CGLChoosePixelFormat(attributes, &format, &npix);
    357     }
    358     CGLContextObj ctx;
    359     CGLCreateContext(format, NULL, &ctx);
    360     CGLDestroyPixelFormat(format);
    361 
    362     static const GLint interval = 1;
    363     CGLSetParameter(ctx, kCGLCPSwapInterval, &interval);
    364     CGLSetCurrentContext(ctx);
    365     return ctx;
    366 }
    367 }
    368 
    369 - (void)viewDidMoveToWindow {
    370     [super viewDidMoveToWindow];
    371 
    372     //Attaching view to fGLContext requires that the view to be part of a window,
    373     //and that the NSWindow instance must have a CoreGraphics counterpart (or
    374     //it must NOT be deferred or should have been on screen at least once)
    375     if ([fGLContext view] != self && nil != self.window) {
    376         [fGLContext setView:self];
    377     }
    378 }
    379 - (bool)attach:(SkOSWindow::SkBackEndTypes)attachType
    380         withMSAASampleCount:(int) sampleCount
    381         andGetInfo:(SkOSWindow::AttachmentInfo*) info {
    382     if (nil == fGLContext) {
    383         CGLContextObj ctx = createGLContext(sampleCount);
    384         fGLContext = [[NSOpenGLContext alloc] initWithCGLContextObj:ctx];
    385         CGLReleaseContext(ctx);
    386         if (NULL == fGLContext) {
    387             return false;
    388         }
    389         [fGLContext setView:self];
    390     }
    391 
    392     [fGLContext makeCurrentContext];
    393     CGLPixelFormatObj format = CGLGetPixelFormat((CGLContextObj)[fGLContext CGLContextObj]);
    394     CGLDescribePixelFormat(format, 0, kCGLPFASamples, &info->fSampleCount);
    395     CGLDescribePixelFormat(format, 0, kCGLPFAStencilSize, &info->fStencilBits);
    396     NSSize size = self.bounds.size;
    397 #if RETINA_API_AVAILABLE
    398     size = [self convertSizeToBacking:size];
    399 #endif
    400     glViewport(0, 0, (int) size.width, (int) size.height);
    401     glClearColor(0, 0, 0, 0);
    402     glClearStencil(0);
    403     glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    404     return true;
    405 }
    406 
    407 - (void)detach {
    408     [fGLContext release];
    409     fGLContext = nil;
    410 }
    411 
    412 - (void)present {
    413     if (nil != fGLContext) {
    414         [fGLContext flushBuffer];
    415     }
    416 }
    417 @end
    418