michael@0: /* michael@0: * Copyright 2011 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkTypes.h" michael@0: michael@0: #if defined(SK_BUILD_FOR_MAC) michael@0: michael@0: #include michael@0: michael@0: #include michael@0: #include "SkCGUtils.h" michael@0: michael@0: #include "SkWindow.h" michael@0: #include "SkCanvas.h" michael@0: #include "SkOSMenu.h" michael@0: #include "SkTime.h" michael@0: michael@0: #include "SkGraphics.h" michael@0: #include michael@0: michael@0: static void (*gPrevNewHandler)(); michael@0: michael@0: extern "C" { michael@0: static void sk_new_handler() michael@0: { michael@0: if (SkGraphics::SetFontCacheUsed(0)) michael@0: return; michael@0: if (gPrevNewHandler) michael@0: gPrevNewHandler(); michael@0: else michael@0: sk_throw(); michael@0: } michael@0: } michael@0: michael@0: static SkOSWindow* gCurrOSWin; michael@0: static EventTargetRef gEventTarget; michael@0: static EventQueueRef gCurrEventQ; michael@0: michael@0: static OSStatus MyDrawEventHandler(EventHandlerCallRef myHandler, michael@0: EventRef event, void *userData) { michael@0: // NOTE: GState is save/restored by the HIView system doing the callback, michael@0: // so the draw handler doesn't need to do it michael@0: michael@0: OSStatus status = noErr; michael@0: CGContextRef context; michael@0: HIRect bounds; michael@0: michael@0: // Get the CGContextRef michael@0: status = GetEventParameter (event, kEventParamCGContextRef, michael@0: typeCGContextRef, NULL, michael@0: sizeof (CGContextRef), michael@0: NULL, michael@0: &context); michael@0: michael@0: if (status != noErr) { michael@0: SkDebugf("Got error %d getting the context!\n", status); michael@0: return status; michael@0: } michael@0: michael@0: // Get the bounding rectangle michael@0: HIViewGetBounds ((HIViewRef) userData, &bounds); michael@0: michael@0: gCurrOSWin->doPaint(context); michael@0: return status; michael@0: } michael@0: michael@0: #define SK_MacEventClass FOUR_CHAR_CODE('SKec') michael@0: #define SK_MacEventKind FOUR_CHAR_CODE('SKek') michael@0: #define SK_MacEventParamName FOUR_CHAR_CODE('SKev') michael@0: #define SK_MacEventSinkIDParamName FOUR_CHAR_CODE('SKes') michael@0: michael@0: static void set_bindingside(HISideBinding* side, HIViewRef parent, HIBindingKind kind) { michael@0: side->toView = parent; michael@0: side->kind = kind; michael@0: side->offset = 0; michael@0: } michael@0: michael@0: static void set_axisscale(HIAxisScale* axis, HIViewRef parent) { michael@0: axis->toView = parent; michael@0: axis->kind = kHILayoutScaleAbsolute; michael@0: axis->ratio = 1; michael@0: } michael@0: michael@0: static void set_axisposition(HIAxisPosition* pos, HIViewRef parent, HIPositionKind kind) { michael@0: pos->toView = parent; michael@0: pos->kind = kind; michael@0: pos->offset = 0; michael@0: } michael@0: michael@0: SkOSWindow::SkOSWindow(void* hWnd) : fHWND(hWnd), fAGLCtx(NULL) michael@0: { michael@0: OSStatus result; michael@0: WindowRef wr = (WindowRef)hWnd; michael@0: michael@0: HIViewRef imageView, parent; michael@0: HIViewRef rootView = HIViewGetRoot(wr); michael@0: HIViewFindByID(rootView, kHIViewWindowContentID, &parent); michael@0: result = HIImageViewCreate(NULL, &imageView); michael@0: SkASSERT(result == noErr); michael@0: michael@0: result = HIViewAddSubview(parent, imageView); michael@0: SkASSERT(result == noErr); michael@0: michael@0: fHVIEW = imageView; michael@0: michael@0: HIViewSetVisible(imageView, true); michael@0: HIViewPlaceInSuperviewAt(imageView, 0, 0); michael@0: michael@0: if (true) { michael@0: HILayoutInfo layout; michael@0: layout.version = kHILayoutInfoVersionZero; michael@0: set_bindingside(&layout.binding.left, parent, kHILayoutBindLeft); michael@0: set_bindingside(&layout.binding.top, parent, kHILayoutBindTop); michael@0: set_bindingside(&layout.binding.right, parent, kHILayoutBindRight); michael@0: set_bindingside(&layout.binding.bottom, parent, kHILayoutBindBottom); michael@0: set_axisscale(&layout.scale.x, parent); michael@0: set_axisscale(&layout.scale.y, parent); michael@0: set_axisposition(&layout.position.x, parent, kHILayoutPositionLeft); michael@0: set_axisposition(&layout.position.y, rootView, kHILayoutPositionTop); michael@0: HIViewSetLayoutInfo(imageView, &layout); michael@0: } michael@0: michael@0: HIImageViewSetOpaque(imageView, true); michael@0: HIImageViewSetScaleToFit(imageView, false); michael@0: michael@0: static const EventTypeSpec gTypes[] = { michael@0: { kEventClassKeyboard, kEventRawKeyDown }, michael@0: { kEventClassKeyboard, kEventRawKeyUp }, michael@0: { kEventClassMouse, kEventMouseDown }, michael@0: { kEventClassMouse, kEventMouseDragged }, michael@0: { kEventClassMouse, kEventMouseMoved }, michael@0: { kEventClassMouse, kEventMouseUp }, michael@0: { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }, michael@0: { kEventClassWindow, kEventWindowBoundsChanged }, michael@0: // { kEventClassWindow, kEventWindowDrawContent }, michael@0: { SK_MacEventClass, SK_MacEventKind } michael@0: }; michael@0: michael@0: EventHandlerUPP handlerUPP = NewEventHandlerUPP(SkOSWindow::EventHandler); michael@0: int count = SK_ARRAY_COUNT(gTypes); michael@0: michael@0: result = InstallEventHandler(GetWindowEventTarget(wr), handlerUPP, michael@0: count, gTypes, this, nil); michael@0: SkASSERT(result == noErr); michael@0: michael@0: gCurrOSWin = this; michael@0: gCurrEventQ = GetCurrentEventQueue(); michael@0: gEventTarget = GetWindowEventTarget(wr); michael@0: michael@0: static bool gOnce = true; michael@0: if (gOnce) { michael@0: gOnce = false; michael@0: gPrevNewHandler = set_new_handler(sk_new_handler); michael@0: } michael@0: } michael@0: michael@0: void SkOSWindow::doPaint(void* ctx) michael@0: { michael@0: #if 0 michael@0: this->update(NULL); michael@0: michael@0: const SkBitmap& bm = this->getBitmap(); michael@0: CGImageRef img = SkCreateCGImageRef(bm); michael@0: michael@0: if (img) { michael@0: CGRect r = CGRectMake(0, 0, bm.width(), bm.height()); michael@0: michael@0: CGContextRef cg = reinterpret_cast(ctx); michael@0: michael@0: CGContextSaveGState(cg); michael@0: CGContextTranslateCTM(cg, 0, r.size.height); michael@0: CGContextScaleCTM(cg, 1, -1); michael@0: michael@0: CGContextDrawImage(cg, r, img); michael@0: michael@0: CGContextRestoreGState(cg); michael@0: michael@0: CGImageRelease(img); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void SkOSWindow::updateSize() michael@0: { michael@0: Rect r; michael@0: michael@0: GetWindowBounds((WindowRef)fHWND, kWindowContentRgn, &r); michael@0: this->resize(r.right - r.left, r.bottom - r.top); michael@0: michael@0: #if 0 michael@0: HIRect frame; michael@0: HIViewRef imageView = (HIViewRef)getHVIEW(); michael@0: HIViewRef parent = HIViewGetSuperview(imageView); michael@0: michael@0: HIViewGetBounds(imageView, &frame); michael@0: SkDebugf("------ %d bounds %g %g %g %g\n", r.right - r.left, michael@0: frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); michael@0: #endif michael@0: } michael@0: michael@0: void SkOSWindow::onHandleInval(const SkIRect& r) michael@0: { michael@0: (new SkEvent("inval-imageview", this->getSinkID()))->post(); michael@0: } michael@0: michael@0: bool SkOSWindow::onEvent(const SkEvent& evt) { michael@0: if (evt.isType("inval-imageview")) { michael@0: this->update(NULL); michael@0: michael@0: SkEvent query("ignore-window-bitmap"); michael@0: if (!this->doQuery(&query) || !query.getFast32()) { michael@0: const SkBitmap& bm = this->getBitmap(); michael@0: michael@0: CGImageRef img = SkCreateCGImageRef(bm); michael@0: HIImageViewSetImage((HIViewRef)getHVIEW(), img); michael@0: CGImageRelease(img); michael@0: } michael@0: return true; michael@0: } michael@0: return INHERITED::onEvent(evt); michael@0: } michael@0: michael@0: void SkOSWindow::onSetTitle(const char title[]) michael@0: { michael@0: CFStringRef str = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); michael@0: SetWindowTitleWithCFString((WindowRef)fHWND, str); michael@0: CFRelease(str); michael@0: } michael@0: michael@0: void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu) michael@0: { michael@0: } michael@0: michael@0: static void getparam(EventRef inEvent, OSType name, OSType type, UInt32 size, void* data) michael@0: { michael@0: EventParamType actualType; michael@0: UInt32 actualSize; michael@0: OSStatus status; michael@0: michael@0: status = GetEventParameter(inEvent, name, type, &actualType, size, &actualSize, data); michael@0: SkASSERT(status == noErr); michael@0: SkASSERT(actualType == type); michael@0: SkASSERT(actualSize == size); michael@0: } michael@0: michael@0: enum { michael@0: SK_MacReturnKey = 36, michael@0: SK_MacDeleteKey = 51, michael@0: SK_MacEndKey = 119, michael@0: SK_MacLeftKey = 123, michael@0: SK_MacRightKey = 124, michael@0: SK_MacDownKey = 125, michael@0: SK_MacUpKey = 126, michael@0: michael@0: SK_Mac0Key = 0x52, michael@0: SK_Mac1Key = 0x53, michael@0: SK_Mac2Key = 0x54, michael@0: SK_Mac3Key = 0x55, michael@0: SK_Mac4Key = 0x56, michael@0: SK_Mac5Key = 0x57, michael@0: SK_Mac6Key = 0x58, michael@0: SK_Mac7Key = 0x59, michael@0: SK_Mac8Key = 0x5b, michael@0: SK_Mac9Key = 0x5c michael@0: }; michael@0: michael@0: static SkKey raw2key(UInt32 raw) michael@0: { michael@0: static const struct { michael@0: UInt32 fRaw; michael@0: SkKey fKey; michael@0: } gKeys[] = { michael@0: { SK_MacUpKey, kUp_SkKey }, michael@0: { SK_MacDownKey, kDown_SkKey }, michael@0: { SK_MacLeftKey, kLeft_SkKey }, michael@0: { SK_MacRightKey, kRight_SkKey }, michael@0: { SK_MacReturnKey, kOK_SkKey }, michael@0: { SK_MacDeleteKey, kBack_SkKey }, michael@0: { SK_MacEndKey, kEnd_SkKey }, michael@0: { SK_Mac0Key, k0_SkKey }, michael@0: { SK_Mac1Key, k1_SkKey }, michael@0: { SK_Mac2Key, k2_SkKey }, michael@0: { SK_Mac3Key, k3_SkKey }, michael@0: { SK_Mac4Key, k4_SkKey }, michael@0: { SK_Mac5Key, k5_SkKey }, michael@0: { SK_Mac6Key, k6_SkKey }, michael@0: { SK_Mac7Key, k7_SkKey }, michael@0: { SK_Mac8Key, k8_SkKey }, michael@0: { SK_Mac9Key, k9_SkKey } michael@0: }; michael@0: michael@0: for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++) michael@0: if (gKeys[i].fRaw == raw) michael@0: return gKeys[i].fKey; michael@0: return kNONE_SkKey; michael@0: } michael@0: michael@0: static void post_skmacevent() michael@0: { michael@0: EventRef ref; michael@0: OSStatus status = CreateEvent(nil, SK_MacEventClass, SK_MacEventKind, 0, 0, &ref); michael@0: SkASSERT(status == noErr); michael@0: michael@0: #if 0 michael@0: status = SetEventParameter(ref, SK_MacEventParamName, SK_MacEventParamName, sizeof(evt), &evt); michael@0: SkASSERT(status == noErr); michael@0: status = SetEventParameter(ref, SK_MacEventSinkIDParamName, SK_MacEventSinkIDParamName, sizeof(sinkID), &sinkID); michael@0: SkASSERT(status == noErr); michael@0: #endif michael@0: michael@0: EventTargetRef target = gEventTarget; michael@0: SetEventParameter(ref, kEventParamPostTarget, typeEventTargetRef, sizeof(target), &target); michael@0: SkASSERT(status == noErr); michael@0: michael@0: status = PostEventToQueue(gCurrEventQ, ref, kEventPriorityStandard); michael@0: SkASSERT(status == noErr); michael@0: michael@0: ReleaseEvent(ref); michael@0: } michael@0: michael@0: pascal OSStatus SkOSWindow::EventHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* userData ) michael@0: { michael@0: SkOSWindow* win = (SkOSWindow*)userData; michael@0: OSStatus result = eventNotHandledErr; michael@0: UInt32 wClass = GetEventClass(inEvent); michael@0: UInt32 wKind = GetEventKind(inEvent); michael@0: michael@0: gCurrOSWin = win; // will need to be in TLS. Set this so PostEvent will work michael@0: michael@0: switch (wClass) { michael@0: case kEventClassMouse: { michael@0: Point pt; michael@0: getparam(inEvent, kEventParamMouseLocation, typeQDPoint, sizeof(pt), &pt); michael@0: SetPortWindowPort((WindowRef)win->getHWND()); michael@0: GlobalToLocal(&pt); michael@0: michael@0: switch (wKind) { michael@0: case kEventMouseDown: michael@0: if (win->handleClick(pt.h, pt.v, Click::kDown_State)) { michael@0: result = noErr; michael@0: } michael@0: break; michael@0: case kEventMouseMoved: michael@0: // fall through michael@0: case kEventMouseDragged: michael@0: (void)win->handleClick(pt.h, pt.v, Click::kMoved_State); michael@0: // result = noErr; michael@0: break; michael@0: case kEventMouseUp: michael@0: (void)win->handleClick(pt.h, pt.v, Click::kUp_State); michael@0: // result = noErr; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: break; michael@0: } michael@0: case kEventClassKeyboard: michael@0: if (wKind == kEventRawKeyDown) { michael@0: UInt32 raw; michael@0: getparam(inEvent, kEventParamKeyCode, typeUInt32, sizeof(raw), &raw); michael@0: SkKey key = raw2key(raw); michael@0: if (key != kNONE_SkKey) michael@0: (void)win->handleKey(key); michael@0: } else if (wKind == kEventRawKeyUp) { michael@0: UInt32 raw; michael@0: getparam(inEvent, kEventParamKeyCode, typeUInt32, sizeof(raw), &raw); michael@0: SkKey key = raw2key(raw); michael@0: if (key != kNONE_SkKey) michael@0: (void)win->handleKeyUp(key); michael@0: } michael@0: break; michael@0: case kEventClassTextInput: michael@0: if (wKind == kEventTextInputUnicodeForKeyEvent) { michael@0: UInt16 uni; michael@0: getparam(inEvent, kEventParamTextInputSendText, typeUnicodeText, sizeof(uni), &uni); michael@0: win->handleChar(uni); michael@0: } michael@0: break; michael@0: case kEventClassWindow: michael@0: switch (wKind) { michael@0: case kEventWindowBoundsChanged: michael@0: win->updateSize(); michael@0: break; michael@0: case kEventWindowDrawContent: { michael@0: CGContextRef cg; michael@0: result = GetEventParameter(inEvent, michael@0: kEventParamCGContextRef, michael@0: typeCGContextRef, michael@0: NULL, michael@0: sizeof (CGContextRef), michael@0: NULL, michael@0: &cg); michael@0: if (result != 0) { michael@0: cg = NULL; michael@0: } michael@0: win->doPaint(cg); michael@0: break; michael@0: } michael@0: default: michael@0: break; michael@0: } michael@0: break; michael@0: case SK_MacEventClass: { michael@0: SkASSERT(wKind == SK_MacEventKind); michael@0: if (SkEvent::ProcessEvent()) { michael@0: post_skmacevent(); michael@0: } michael@0: #if 0 michael@0: SkEvent* evt; michael@0: SkEventSinkID sinkID; michael@0: getparam(inEvent, SK_MacEventParamName, SK_MacEventParamName, sizeof(evt), &evt); michael@0: getparam(inEvent, SK_MacEventSinkIDParamName, SK_MacEventSinkIDParamName, sizeof(sinkID), &sinkID); michael@0: #endif michael@0: result = noErr; michael@0: break; michael@0: } michael@0: default: michael@0: break; michael@0: } michael@0: if (result == eventNotHandledErr) { michael@0: result = CallNextEventHandler(inHandler, inEvent); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void SkEvent::SignalNonEmptyQueue() michael@0: { michael@0: post_skmacevent(); michael@0: // SkDebugf("signal nonempty\n"); michael@0: } michael@0: michael@0: static TMTask gTMTaskRec; michael@0: static TMTask* gTMTaskPtr; michael@0: michael@0: static void sk_timer_proc(TMTask* rec) michael@0: { michael@0: SkEvent::ServiceQueueTimer(); michael@0: // SkDebugf("timer task fired\n"); michael@0: } michael@0: michael@0: void SkEvent::SignalQueueTimer(SkMSec delay) michael@0: { michael@0: if (gTMTaskPtr) michael@0: { michael@0: RemoveTimeTask((QElem*)gTMTaskPtr); michael@0: DisposeTimerUPP(gTMTaskPtr->tmAddr); michael@0: gTMTaskPtr = nil; michael@0: } michael@0: if (delay) michael@0: { michael@0: gTMTaskPtr = &gTMTaskRec; michael@0: memset(gTMTaskPtr, 0, sizeof(gTMTaskRec)); michael@0: gTMTaskPtr->tmAddr = NewTimerUPP(sk_timer_proc); michael@0: OSErr err = InstallTimeTask((QElem*)gTMTaskPtr); michael@0: // SkDebugf("installtimetask of %d returned %d\n", delay, err); michael@0: PrimeTimeTask((QElem*)gTMTaskPtr, delay); michael@0: } michael@0: } michael@0: michael@0: #define USE_MSAA 0 michael@0: michael@0: AGLContext create_gl(WindowRef wref) michael@0: { michael@0: GLint major, minor; michael@0: AGLContext ctx; michael@0: michael@0: aglGetVersion(&major, &minor); michael@0: SkDebugf("---- agl version %d %d\n", major, minor); michael@0: michael@0: const GLint pixelAttrs[] = { michael@0: AGL_RGBA, michael@0: AGL_STENCIL_SIZE, 8, michael@0: #if USE_MSAA michael@0: AGL_SAMPLE_BUFFERS_ARB, 1, michael@0: AGL_MULTISAMPLE, michael@0: AGL_SAMPLES_ARB, 8, michael@0: #endif michael@0: AGL_ACCELERATED, michael@0: AGL_DOUBLEBUFFER, michael@0: AGL_NONE michael@0: }; michael@0: AGLPixelFormat format = aglChoosePixelFormat(NULL, 0, pixelAttrs); michael@0: //AGLPixelFormat format = aglCreatePixelFormat(pixelAttrs); michael@0: SkDebugf("----- agl format %p\n", format); michael@0: ctx = aglCreateContext(format, NULL); michael@0: SkDebugf("----- agl context %p\n", ctx); michael@0: aglDestroyPixelFormat(format); michael@0: michael@0: static const GLint interval = 1; michael@0: aglSetInteger(ctx, AGL_SWAP_INTERVAL, &interval); michael@0: aglSetCurrentContext(ctx); michael@0: return ctx; michael@0: } michael@0: michael@0: bool SkOSWindow::attach(SkBackEndTypes /* attachType */) michael@0: { michael@0: if (NULL == fAGLCtx) { michael@0: fAGLCtx = create_gl((WindowRef)fHWND); michael@0: if (NULL == fAGLCtx) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: GLboolean success = true; michael@0: michael@0: int width, height; michael@0: michael@0: success = aglSetWindowRef((AGLContext)fAGLCtx, (WindowRef)fHWND); michael@0: width = this->width(); michael@0: height = this->height(); michael@0: michael@0: GLenum err = aglGetError(); michael@0: if (err) { michael@0: SkDebugf("---- aglSetWindowRef %d %d %s [%d %d]\n", success, err, michael@0: aglErrorString(err), width, height); michael@0: } michael@0: michael@0: if (success) { michael@0: glViewport(0, 0, width, height); michael@0: glClearColor(0, 0, 0, 0); michael@0: glClearStencil(0); michael@0: glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); michael@0: } michael@0: return success; michael@0: } michael@0: michael@0: void SkOSWindow::detach() { michael@0: aglSetWindowRef((AGLContext)fAGLCtx, NULL); michael@0: } michael@0: michael@0: void SkOSWindow::present() { michael@0: aglSwapBuffers((AGLContext)fAGLCtx); michael@0: } michael@0: michael@0: #endif