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