/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkTypes.h" #if defined(SK_BUILD_FOR_WIN) #include #include #include "SkWGL.h" #include "SkWindow.h" #include "SkCanvas.h" #include "SkOSMenu.h" #include "SkTime.h" #include "SkUtils.h" #include "SkGraphics.h" #if SK_ANGLE #include "gl/GrGLInterface.h" #include "GLES2/gl2.h" #define ANGLE_GL_CALL(IFACE, X) \ do { \ (IFACE)->fFunctions.f##X; \ } while (false) #endif #define INVALIDATE_DELAY_MS 200 static SkOSWindow* gCurrOSWin; static HWND gEventTarget; #define WM_EVENT_CALLBACK (WM_USER+0) void post_skwinevent() { PostMessage(gEventTarget, WM_EVENT_CALLBACK, 0, 0); } SkOSWindow::SkOSWindow(void* hWnd) { fHWND = hWnd; #if SK_SUPPORT_GPU #if SK_ANGLE fDisplay = EGL_NO_DISPLAY; fContext = EGL_NO_CONTEXT; fSurface = EGL_NO_SURFACE; #endif fHGLRC = NULL; #endif fAttached = kNone_BackEndType; gEventTarget = (HWND)hWnd; } SkOSWindow::~SkOSWindow() { #if SK_SUPPORT_GPU if (NULL != fHGLRC) { wglDeleteContext((HGLRC)fHGLRC); } #if SK_ANGLE if (EGL_NO_CONTEXT != fContext) { eglDestroyContext(fDisplay, fContext); fContext = EGL_NO_CONTEXT; } if (EGL_NO_SURFACE != fSurface) { eglDestroySurface(fDisplay, fSurface); fSurface = EGL_NO_SURFACE; } if (EGL_NO_DISPLAY != fDisplay) { eglTerminate(fDisplay); fDisplay = EGL_NO_DISPLAY; } #endif // SK_ANGLE #endif // SK_SUPPORT_GPU } static SkKey winToskKey(WPARAM vk) { static const struct { WPARAM fVK; SkKey fKey; } gPair[] = { { VK_BACK, kBack_SkKey }, { VK_CLEAR, kBack_SkKey }, { VK_RETURN, kOK_SkKey }, { VK_UP, kUp_SkKey }, { VK_DOWN, kDown_SkKey }, { VK_LEFT, kLeft_SkKey }, { VK_RIGHT, kRight_SkKey } }; for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { if (gPair[i].fVK == vk) { return gPair[i].fKey; } } return kNONE_SkKey; } static unsigned getModifiers(UINT message) { return 0; // TODO } bool SkOSWindow::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_KEYDOWN: { SkKey key = winToskKey(wParam); if (kNONE_SkKey != key) { this->handleKey(key); return true; } } break; case WM_KEYUP: { SkKey key = winToskKey(wParam); if (kNONE_SkKey != key) { this->handleKeyUp(key); return true; } } break; case WM_UNICHAR: this->handleChar((SkUnichar) wParam); return true; case WM_CHAR: { this->handleChar(SkUTF8_ToUnichar((char*)&wParam)); return true; } break; case WM_SIZE: { INT width = LOWORD(lParam); INT height = HIWORD(lParam); this->resize(width, height); break; } case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); this->doPaint(hdc); EndPaint(hWnd, &ps); return true; } break; case WM_TIMER: { RECT* rect = (RECT*)wParam; InvalidateRect(hWnd, rect, FALSE); KillTimer(hWnd, (UINT_PTR)rect); delete rect; return true; } break; case WM_LBUTTONDOWN: this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kDown_State, NULL, getModifiers(message)); return true; case WM_MOUSEMOVE: this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kMoved_State, NULL, getModifiers(message)); return true; case WM_LBUTTONUP: this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kUp_State, NULL, getModifiers(message)); return true; case WM_EVENT_CALLBACK: if (SkEvent::ProcessEvent()) { post_skwinevent(); } return true; } return false; } void SkOSWindow::doPaint(void* ctx) { this->update(NULL); if (kNone_BackEndType == fAttached) { HDC hdc = (HDC)ctx; const SkBitmap& bitmap = this->getBitmap(); BITMAPINFO bmi; memset(&bmi, 0, sizeof(bmi)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = bitmap.width(); bmi.bmiHeader.biHeight = -bitmap.height(); // top-down image bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biSizeImage = 0; // // Do the SetDIBitsToDevice. // // TODO(wjmaclean): // Fix this call to handle SkBitmaps that have rowBytes != width, // i.e. may have padding at the end of lines. The SkASSERT below // may be ignored by builds, and the only obviously safe option // seems to be to copy the bitmap to a temporary (contiguous) // buffer before passing to SetDIBitsToDevice(). SkASSERT(bitmap.width() * bitmap.bytesPerPixel() == bitmap.rowBytes()); bitmap.lockPixels(); int ret = SetDIBitsToDevice(hdc, 0, 0, bitmap.width(), bitmap.height(), 0, 0, 0, bitmap.height(), bitmap.getPixels(), &bmi, DIB_RGB_COLORS); (void)ret; // we're ignoring potential failures for now. bitmap.unlockPixels(); } } #if 0 void SkOSWindow::updateSize() { RECT r; GetWindowRect((HWND)this->getHWND(), &r); this->resize(r.right - r.left, r.bottom - r.top); } #endif void SkOSWindow::onHandleInval(const SkIRect& r) { RECT* rect = new RECT; rect->left = r.fLeft; rect->top = r.fTop; rect->right = r.fRight; rect->bottom = r.fBottom; SetTimer((HWND)fHWND, (UINT_PTR)rect, INVALIDATE_DELAY_MS, NULL); } void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu) { } void SkOSWindow::onSetTitle(const char title[]){ SetWindowTextA((HWND)fHWND, title); } 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_t raw) { static const struct { uint32_t 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; } /////////////////////////////////////////////////////////////////////////////////////// void SkEvent::SignalNonEmptyQueue() { post_skwinevent(); //SkDebugf("signal nonempty\n"); } static UINT_PTR gTimer; VOID CALLBACK sk_timer_proc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { SkEvent::ServiceQueueTimer(); //SkDebugf("timer task fired\n"); } void SkEvent::SignalQueueTimer(SkMSec delay) { if (gTimer) { KillTimer(NULL, gTimer); gTimer = NULL; } if (delay) { gTimer = SetTimer(NULL, 0, delay, sk_timer_proc); //SkDebugf("SetTimer of %d returned %d\n", delay, gTimer); } } #if SK_SUPPORT_GPU bool SkOSWindow::attachGL(int msaaSampleCount, AttachmentInfo* info) { HDC dc = GetDC((HWND)fHWND); if (NULL == fHGLRC) { fHGLRC = SkCreateWGLContext(dc, msaaSampleCount, false); if (NULL == fHGLRC) { return false; } glClearStencil(0); glClearColor(0, 0, 0, 0); glStencilMask(0xffffffff); glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); } if (wglMakeCurrent(dc, (HGLRC)fHGLRC)) { // use DescribePixelFormat to get the stencil bit depth. int pixelFormat = GetPixelFormat(dc); PIXELFORMATDESCRIPTOR pfd; DescribePixelFormat(dc, pixelFormat, sizeof(pfd), &pfd); info->fStencilBits = pfd.cStencilBits; // Get sample count if the MSAA WGL extension is present SkWGLExtensions extensions; if (extensions.hasExtension(dc, "WGL_ARB_multisample")) { static const int kSampleCountAttr = SK_WGL_SAMPLES; extensions.getPixelFormatAttribiv(dc, pixelFormat, 0, 1, &kSampleCountAttr, &info->fSampleCount); } else { info->fSampleCount = 0; } glViewport(0, 0, SkScalarRoundToInt(this->width()), SkScalarRoundToInt(this->height())); return true; } return false; } void SkOSWindow::detachGL() { wglMakeCurrent(GetDC((HWND)fHWND), 0); wglDeleteContext((HGLRC)fHGLRC); fHGLRC = NULL; } void SkOSWindow::presentGL() { glFlush(); HDC dc = GetDC((HWND)fHWND); SwapBuffers(dc); ReleaseDC((HWND)fHWND, dc); } #if SK_ANGLE bool create_ANGLE(EGLNativeWindowType hWnd, int msaaSampleCount, EGLDisplay* eglDisplay, EGLContext* eglContext, EGLSurface* eglSurface, EGLConfig* eglConfig) { static const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE, EGL_NONE }; static const EGLint configAttribList[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 8, EGL_STENCIL_SIZE, 8, EGL_NONE }; static const EGLint surfaceAttribList[] = { EGL_NONE, EGL_NONE }; EGLDisplay display = eglGetDisplay(GetDC(hWnd)); if (display == EGL_NO_DISPLAY ) { return false; } // Initialize EGL EGLint majorVersion, minorVersion; if (!eglInitialize(display, &majorVersion, &minorVersion)) { return false; } EGLint numConfigs; if (!eglGetConfigs(display, NULL, 0, &numConfigs)) { return false; } // Choose config bool foundConfig = false; if (msaaSampleCount) { static const int kConfigAttribListCnt = SK_ARRAY_COUNT(configAttribList); EGLint msaaConfigAttribList[kConfigAttribListCnt + 4]; memcpy(msaaConfigAttribList, configAttribList, sizeof(configAttribList)); SkASSERT(EGL_NONE == msaaConfigAttribList[kConfigAttribListCnt - 1]); msaaConfigAttribList[kConfigAttribListCnt - 1] = EGL_SAMPLE_BUFFERS; msaaConfigAttribList[kConfigAttribListCnt + 0] = 1; msaaConfigAttribList[kConfigAttribListCnt + 1] = EGL_SAMPLES; msaaConfigAttribList[kConfigAttribListCnt + 2] = msaaSampleCount; msaaConfigAttribList[kConfigAttribListCnt + 3] = EGL_NONE; if (eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) { SkASSERT(numConfigs > 0); foundConfig = true; } } if (!foundConfig) { if (!eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) { return false; } } // Create a surface EGLSurface surface = eglCreateWindowSurface(display, *eglConfig, (EGLNativeWindowType)hWnd, surfaceAttribList); if (surface == EGL_NO_SURFACE) { return false; } // Create a GL context EGLContext context = eglCreateContext(display, *eglConfig, EGL_NO_CONTEXT, contextAttribs ); if (context == EGL_NO_CONTEXT ) { return false; } // Make the context current if (!eglMakeCurrent(display, surface, surface, context)) { return false; } *eglDisplay = display; *eglContext = context; *eglSurface = surface; return true; } bool SkOSWindow::attachANGLE(int msaaSampleCount, AttachmentInfo* info) { if (EGL_NO_DISPLAY == fDisplay) { bool bResult = create_ANGLE((HWND)fHWND, msaaSampleCount, &fDisplay, &fContext, &fSurface, &fConfig); if (false == bResult) { return false; } SkAutoTUnref intf(GrGLCreateANGLEInterface()); if (intf) { ANGLE_GL_CALL(intf, ClearStencil(0)); ANGLE_GL_CALL(intf, ClearColor(0, 0, 0, 0)); ANGLE_GL_CALL(intf, StencilMask(0xffffffff)); ANGLE_GL_CALL(intf, Clear(GL_STENCIL_BUFFER_BIT |GL_COLOR_BUFFER_BIT)); } } if (eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { eglGetConfigAttrib(fDisplay, fConfig, EGL_STENCIL_SIZE, &info->fStencilBits); eglGetConfigAttrib(fDisplay, fConfig, EGL_SAMPLES, &info->fSampleCount); SkAutoTUnref intf(GrGLCreateANGLEInterface()); if (intf ) { ANGLE_GL_CALL(intf, Viewport(0, 0, SkScalarRoundToInt(this->width()), SkScalarRoundToInt(this->height()))); } return true; } return false; } void SkOSWindow::detachANGLE() { eglMakeCurrent(fDisplay, EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT); eglDestroyContext(fDisplay, fContext); fContext = EGL_NO_CONTEXT; eglDestroySurface(fDisplay, fSurface); fSurface = EGL_NO_SURFACE; eglTerminate(fDisplay); fDisplay = EGL_NO_DISPLAY; } void SkOSWindow::presentANGLE() { SkAutoTUnref intf(GrGLCreateANGLEInterface()); if (intf) { ANGLE_GL_CALL(intf, Flush()); } eglSwapBuffers(fDisplay, fSurface); } #endif // SK_ANGLE #endif // SK_SUPPORT_GPU // return true on success bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo* info) { // attach doubles as "windowResize" so we need to allo // already bound states to pass through again // TODO: split out the resize functionality // SkASSERT(kNone_BackEndType == fAttached); bool result = true; switch (attachType) { case kNone_BackEndType: // nothing to do break; #if SK_SUPPORT_GPU case kNativeGL_BackEndType: result = attachGL(msaaSampleCount, info); break; #if SK_ANGLE case kANGLE_BackEndType: result = attachANGLE(msaaSampleCount, info); break; #endif // SK_ANGLE #endif // SK_SUPPORT_GPU default: SkASSERT(false); result = false; break; } if (result) { fAttached = attachType; } return result; } void SkOSWindow::detach() { switch (fAttached) { case kNone_BackEndType: // nothing to do break; #if SK_SUPPORT_GPU case kNativeGL_BackEndType: detachGL(); break; #if SK_ANGLE case kANGLE_BackEndType: detachANGLE(); break; #endif // SK_ANGLE #endif // SK_SUPPORT_GPU default: SkASSERT(false); break; } fAttached = kNone_BackEndType; } void SkOSWindow::present() { switch (fAttached) { case kNone_BackEndType: // nothing to do return; #if SK_SUPPORT_GPU case kNativeGL_BackEndType: presentGL(); break; #if SK_ANGLE case kANGLE_BackEndType: presentANGLE(); break; #endif // SK_ANGLE #endif // SK_SUPPORT_GPU default: SkASSERT(false); break; } } #endif