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