Home | History | Annotate | Download | only in win
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 #include "SkTypes.h"
      9 
     10 #if defined(SK_BUILD_FOR_WIN)
     11 
     12 #include <GL/gl.h>
     13 #include <WindowsX.h>
     14 #include "SkWGL.h"
     15 #include "SkWindow.h"
     16 #include "SkCanvas.h"
     17 #include "SkOSMenu.h"
     18 #include "SkTime.h"
     19 #include "SkUtils.h"
     20 
     21 #include "SkGraphics.h"
     22 
     23 #if SK_ANGLE
     24 #include "gl/GrGLInterface.h"
     25 
     26 #include "GLES2/gl2.h"
     27 
     28 #define ANGLE_GL_CALL(IFACE, X)                                 \
     29     do {                                                        \
     30         (IFACE)->f##X;                                          \
     31     } while (false)
     32 
     33 #endif
     34 
     35 #define INVALIDATE_DELAY_MS 200
     36 
     37 static SkOSWindow* gCurrOSWin;
     38 static HWND gEventTarget;
     39 
     40 #define WM_EVENT_CALLBACK (WM_USER+0)
     41 
     42 void post_skwinevent()
     43 {
     44     PostMessage(gEventTarget, WM_EVENT_CALLBACK, 0, 0);
     45 }
     46 
     47 SkOSWindow::SkOSWindow(void* hWnd) {
     48     fHWND = hWnd;
     49 #if SK_SUPPORT_GPU
     50 #if SK_ANGLE
     51     fDisplay = EGL_NO_DISPLAY;
     52     fContext = EGL_NO_CONTEXT;
     53     fSurface = EGL_NO_SURFACE;
     54 #endif
     55     fHGLRC = NULL;
     56 #endif
     57     fAttached = kNone_BackEndType;
     58     gEventTarget = (HWND)hWnd;
     59 }
     60 
     61 SkOSWindow::~SkOSWindow() {
     62 #if SK_SUPPORT_GPU
     63     if (NULL != fHGLRC) {
     64         wglDeleteContext((HGLRC)fHGLRC);
     65     }
     66 #if SK_ANGLE
     67     if (EGL_NO_CONTEXT != fContext) {
     68         eglDestroyContext(fDisplay, fContext);
     69         fContext = EGL_NO_CONTEXT;
     70     }
     71 
     72     if (EGL_NO_SURFACE != fSurface) {
     73         eglDestroySurface(fDisplay, fSurface);
     74         fSurface = EGL_NO_SURFACE;
     75     }
     76 
     77     if (EGL_NO_DISPLAY != fDisplay) {
     78         eglTerminate(fDisplay);
     79         fDisplay = EGL_NO_DISPLAY;
     80     }
     81 #endif // SK_ANGLE
     82 #endif // SK_SUPPORT_GPU
     83 }
     84 
     85 static SkKey winToskKey(WPARAM vk) {
     86     static const struct {
     87         WPARAM    fVK;
     88         SkKey    fKey;
     89     } gPair[] = {
     90         { VK_BACK,    kBack_SkKey },
     91         { VK_CLEAR,    kBack_SkKey },
     92         { VK_RETURN, kOK_SkKey },
     93         { VK_UP,     kUp_SkKey },
     94         { VK_DOWN,     kDown_SkKey },
     95         { VK_LEFT,     kLeft_SkKey },
     96         { VK_RIGHT,     kRight_SkKey }
     97     };
     98     for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
     99         if (gPair[i].fVK == vk) {
    100             return gPair[i].fKey;
    101         }
    102     }
    103     return kNONE_SkKey;
    104 }
    105 
    106 static unsigned getModifiers(UINT message) {
    107     return 0;   // TODO
    108 }
    109 
    110 bool SkOSWindow::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    111     switch (message) {
    112         case WM_KEYDOWN: {
    113             SkKey key = winToskKey(wParam);
    114             if (kNONE_SkKey != key) {
    115                 this->handleKey(key);
    116                 return true;
    117             }
    118         } break;
    119         case WM_KEYUP: {
    120             SkKey key = winToskKey(wParam);
    121             if (kNONE_SkKey != key) {
    122                 this->handleKeyUp(key);
    123                 return true;
    124             }
    125         } break;
    126         case WM_UNICHAR:
    127             this->handleChar(wParam);
    128             return true;
    129         case WM_CHAR: {
    130             this->handleChar(SkUTF8_ToUnichar((char*)&wParam));
    131             return true;
    132         } break;
    133         case WM_SIZE:
    134             this->resize(lParam & 0xFFFF, lParam >> 16);
    135             break;
    136         case WM_PAINT: {
    137             PAINTSTRUCT ps;
    138             HDC hdc = BeginPaint(hWnd, &ps);
    139             this->doPaint(hdc);
    140             EndPaint(hWnd, &ps);
    141             return true;
    142             } break;
    143 
    144         case WM_TIMER: {
    145             RECT* rect = (RECT*)wParam;
    146             InvalidateRect(hWnd, rect, FALSE);
    147             KillTimer(hWnd, (UINT_PTR)rect);
    148             delete rect;
    149             return true;
    150         } break;
    151 
    152         case WM_LBUTTONDOWN:
    153             this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
    154                               Click::kDown_State, NULL, getModifiers(message));
    155             return true;
    156 
    157         case WM_MOUSEMOVE:
    158             this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
    159                               Click::kMoved_State, NULL, getModifiers(message));
    160             return true;
    161 
    162         case WM_LBUTTONUP:
    163             this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
    164                               Click::kUp_State, NULL, getModifiers(message));
    165             return true;
    166 
    167         case WM_EVENT_CALLBACK:
    168             if (SkEvent::ProcessEvent()) {
    169                 post_skwinevent();
    170             }
    171             return true;
    172     }
    173     return false;
    174 }
    175 
    176 void SkOSWindow::doPaint(void* ctx) {
    177     this->update(NULL);
    178 
    179     if (kNone_BackEndType == fAttached)
    180     {
    181         HDC hdc = (HDC)ctx;
    182         const SkBitmap& bitmap = this->getBitmap();
    183 
    184         BITMAPINFO bmi;
    185         memset(&bmi, 0, sizeof(bmi));
    186         bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
    187         bmi.bmiHeader.biWidth       = bitmap.width();
    188         bmi.bmiHeader.biHeight      = -bitmap.height(); // top-down image
    189         bmi.bmiHeader.biPlanes      = 1;
    190         bmi.bmiHeader.biBitCount    = 32;
    191         bmi.bmiHeader.biCompression = BI_RGB;
    192         bmi.bmiHeader.biSizeImage   = 0;
    193 
    194         //
    195         // Do the SetDIBitsToDevice.
    196         //
    197         // TODO(wjmaclean):
    198         //       Fix this call to handle SkBitmaps that have rowBytes != width,
    199         //       i.e. may have padding at the end of lines. The SkASSERT below
    200         //       may be ignored by builds, and the only obviously safe option
    201         //       seems to be to copy the bitmap to a temporary (contiguous)
    202         //       buffer before passing to SetDIBitsToDevice().
    203         SkASSERT(bitmap.width() * bitmap.bytesPerPixel() == bitmap.rowBytes());
    204         bitmap.lockPixels();
    205         int ret = SetDIBitsToDevice(hdc,
    206             0, 0,
    207             bitmap.width(), bitmap.height(),
    208             0, 0,
    209             0, bitmap.height(),
    210             bitmap.getPixels(),
    211             &bmi,
    212             DIB_RGB_COLORS);
    213         (void)ret; // we're ignoring potential failures for now.
    214         bitmap.unlockPixels();
    215     }
    216 }
    217 
    218 #if 0
    219 void SkOSWindow::updateSize()
    220 {
    221     RECT    r;
    222     GetWindowRect((HWND)this->getHWND(), &r);
    223     this->resize(r.right - r.left, r.bottom - r.top);
    224 }
    225 #endif
    226 
    227 void SkOSWindow::onHandleInval(const SkIRect& r) {
    228     RECT* rect = new RECT;
    229     rect->left    = r.fLeft;
    230     rect->top     = r.fTop;
    231     rect->right   = r.fRight;
    232     rect->bottom  = r.fBottom;
    233     SetTimer((HWND)fHWND, (UINT_PTR)rect, INVALIDATE_DELAY_MS, NULL);
    234 }
    235 
    236 void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu)
    237 {
    238 }
    239 
    240 void SkOSWindow::onSetTitle(const char title[]){
    241     SetWindowTextA((HWND)fHWND, title);
    242 }
    243 
    244 enum {
    245     SK_MacReturnKey     = 36,
    246     SK_MacDeleteKey     = 51,
    247     SK_MacEndKey        = 119,
    248     SK_MacLeftKey       = 123,
    249     SK_MacRightKey      = 124,
    250     SK_MacDownKey       = 125,
    251     SK_MacUpKey         = 126,
    252 
    253     SK_Mac0Key          = 0x52,
    254     SK_Mac1Key          = 0x53,
    255     SK_Mac2Key          = 0x54,
    256     SK_Mac3Key          = 0x55,
    257     SK_Mac4Key          = 0x56,
    258     SK_Mac5Key          = 0x57,
    259     SK_Mac6Key          = 0x58,
    260     SK_Mac7Key          = 0x59,
    261     SK_Mac8Key          = 0x5b,
    262     SK_Mac9Key          = 0x5c
    263 };
    264 
    265 static SkKey raw2key(uint32_t raw)
    266 {
    267     static const struct {
    268         uint32_t  fRaw;
    269         SkKey   fKey;
    270     } gKeys[] = {
    271         { SK_MacUpKey,      kUp_SkKey       },
    272         { SK_MacDownKey,    kDown_SkKey     },
    273         { SK_MacLeftKey,    kLeft_SkKey     },
    274         { SK_MacRightKey,   kRight_SkKey    },
    275         { SK_MacReturnKey,  kOK_SkKey       },
    276         { SK_MacDeleteKey,  kBack_SkKey     },
    277         { SK_MacEndKey,     kEnd_SkKey      },
    278         { SK_Mac0Key,       k0_SkKey        },
    279         { SK_Mac1Key,       k1_SkKey        },
    280         { SK_Mac2Key,       k2_SkKey        },
    281         { SK_Mac3Key,       k3_SkKey        },
    282         { SK_Mac4Key,       k4_SkKey        },
    283         { SK_Mac5Key,       k5_SkKey        },
    284         { SK_Mac6Key,       k6_SkKey        },
    285         { SK_Mac7Key,       k7_SkKey        },
    286         { SK_Mac8Key,       k8_SkKey        },
    287         { SK_Mac9Key,       k9_SkKey        }
    288     };
    289 
    290     for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++)
    291         if (gKeys[i].fRaw == raw)
    292             return gKeys[i].fKey;
    293     return kNONE_SkKey;
    294 }
    295 
    296 ///////////////////////////////////////////////////////////////////////////////////////
    297 
    298 void SkEvent::SignalNonEmptyQueue()
    299 {
    300     post_skwinevent();
    301     //SkDebugf("signal nonempty\n");
    302 }
    303 
    304 static UINT_PTR gTimer;
    305 
    306 VOID CALLBACK sk_timer_proc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
    307 {
    308     SkEvent::ServiceQueueTimer();
    309     //SkDebugf("timer task fired\n");
    310 }
    311 
    312 void SkEvent::SignalQueueTimer(SkMSec delay)
    313 {
    314     if (gTimer)
    315     {
    316         KillTimer(NULL, gTimer);
    317         gTimer = NULL;
    318     }
    319     if (delay)
    320     {
    321         gTimer = SetTimer(NULL, 0, delay, sk_timer_proc);
    322         //SkDebugf("SetTimer of %d returned %d\n", delay, gTimer);
    323     }
    324 }
    325 
    326 #if SK_SUPPORT_GPU
    327 
    328 bool SkOSWindow::attachGL(int msaaSampleCount, AttachmentInfo* info) {
    329     HDC dc = GetDC((HWND)fHWND);
    330     if (NULL == fHGLRC) {
    331         fHGLRC = SkCreateWGLContext(dc, msaaSampleCount, false);
    332         if (NULL == fHGLRC) {
    333             return false;
    334         }
    335         glClearStencil(0);
    336         glClearColor(0, 0, 0, 0);
    337         glStencilMask(0xffffffff);
    338         glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    339     }
    340     if (wglMakeCurrent(dc, (HGLRC)fHGLRC)) {
    341         // use DescribePixelFormat to get the stencil bit depth.
    342         int pixelFormat = GetPixelFormat(dc);
    343         PIXELFORMATDESCRIPTOR pfd;
    344         DescribePixelFormat(dc, pixelFormat, sizeof(pfd), &pfd);
    345         info->fStencilBits = pfd.cStencilBits;
    346 
    347         // Get sample count if the MSAA WGL extension is present
    348         SkWGLExtensions extensions;
    349         if (extensions.hasExtension(dc, "WGL_ARB_multisample")) {
    350             static const int kSampleCountAttr = SK_WGL_SAMPLES;
    351             extensions.getPixelFormatAttribiv(dc,
    352                                               pixelFormat,
    353                                               0,
    354                                               1,
    355                                               &kSampleCountAttr,
    356                                               &info->fSampleCount);
    357         } else {
    358             info->fSampleCount = 0;
    359         }
    360 
    361         glViewport(0, 0, SkScalarRound(this->width()), SkScalarRound(this->height()));
    362         return true;
    363     }
    364     return false;
    365 }
    366 
    367 void SkOSWindow::detachGL() {
    368     wglMakeCurrent(GetDC((HWND)fHWND), 0);
    369     wglDeleteContext((HGLRC)fHGLRC);
    370     fHGLRC = NULL;
    371 }
    372 
    373 void SkOSWindow::presentGL() {
    374     glFlush();
    375     HDC dc = GetDC((HWND)fHWND);
    376     SwapBuffers(dc);
    377     ReleaseDC((HWND)fHWND, dc);
    378 }
    379 
    380 #if SK_ANGLE
    381 bool create_ANGLE(EGLNativeWindowType hWnd,
    382                   int msaaSampleCount,
    383                   EGLDisplay* eglDisplay,
    384                   EGLContext* eglContext,
    385                   EGLSurface* eglSurface,
    386                   EGLConfig* eglConfig) {
    387     static const EGLint contextAttribs[] = {
    388         EGL_CONTEXT_CLIENT_VERSION, 2,
    389         EGL_NONE, EGL_NONE
    390     };
    391     static const EGLint configAttribList[] = {
    392         EGL_RED_SIZE,       8,
    393         EGL_GREEN_SIZE,     8,
    394         EGL_BLUE_SIZE,      8,
    395         EGL_ALPHA_SIZE,     8,
    396         EGL_DEPTH_SIZE,     8,
    397         EGL_STENCIL_SIZE,   8,
    398         EGL_NONE
    399     };
    400     static const EGLint surfaceAttribList[] = {
    401         EGL_NONE, EGL_NONE
    402     };
    403 
    404     EGLDisplay display = eglGetDisplay(GetDC(hWnd));
    405     if (display == EGL_NO_DISPLAY ) {
    406        return false;
    407     }
    408 
    409     // Initialize EGL
    410     EGLint majorVersion, minorVersion;
    411     if (!eglInitialize(display, &majorVersion, &minorVersion)) {
    412        return false;
    413     }
    414 
    415     EGLint numConfigs;
    416     if (!eglGetConfigs(display, NULL, 0, &numConfigs)) {
    417        return false;
    418     }
    419 
    420     // Choose config
    421     bool foundConfig = false;
    422     if (msaaSampleCount) {
    423         static const int kConfigAttribListCnt =
    424                                 SK_ARRAY_COUNT(configAttribList);
    425         EGLint msaaConfigAttribList[kConfigAttribListCnt + 4];
    426         memcpy(msaaConfigAttribList,
    427                configAttribList,
    428                sizeof(configAttribList));
    429         SkASSERT(EGL_NONE == msaaConfigAttribList[kConfigAttribListCnt - 1]);
    430         msaaConfigAttribList[kConfigAttribListCnt - 1] = EGL_SAMPLE_BUFFERS;
    431         msaaConfigAttribList[kConfigAttribListCnt + 0] = 1;
    432         msaaConfigAttribList[kConfigAttribListCnt + 1] = EGL_SAMPLES;
    433         msaaConfigAttribList[kConfigAttribListCnt + 2] = msaaSampleCount;
    434         msaaConfigAttribList[kConfigAttribListCnt + 3] = EGL_NONE;
    435         if (eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) {
    436             SkASSERT(numConfigs > 0);
    437             foundConfig = true;
    438         }
    439     }
    440     if (!foundConfig) {
    441         if (!eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) {
    442            return false;
    443         }
    444     }
    445 
    446     // Create a surface
    447     EGLSurface surface = eglCreateWindowSurface(display, *eglConfig,
    448                                                 (EGLNativeWindowType)hWnd,
    449                                                 surfaceAttribList);
    450     if (surface == EGL_NO_SURFACE) {
    451        return false;
    452     }
    453 
    454     // Create a GL context
    455     EGLContext context = eglCreateContext(display, *eglConfig,
    456                                           EGL_NO_CONTEXT,
    457                                           contextAttribs );
    458     if (context == EGL_NO_CONTEXT ) {
    459        return false;
    460     }
    461 
    462     // Make the context current
    463     if (!eglMakeCurrent(display, surface, surface, context)) {
    464        return false;
    465     }
    466 
    467     *eglDisplay = display;
    468     *eglContext = context;
    469     *eglSurface = surface;
    470     return true;
    471 }
    472 
    473 bool SkOSWindow::attachANGLE(int msaaSampleCount, AttachmentInfo* info) {
    474     if (EGL_NO_DISPLAY == fDisplay) {
    475         bool bResult = create_ANGLE((HWND)fHWND,
    476                                     msaaSampleCount,
    477                                     &fDisplay,
    478                                     &fContext,
    479                                     &fSurface,
    480                                     &fConfig);
    481         if (false == bResult) {
    482             return false;
    483         }
    484         SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
    485 
    486         if (intf) {
    487             ANGLE_GL_CALL(intf, ClearStencil(0));
    488             ANGLE_GL_CALL(intf, ClearColor(0, 0, 0, 0));
    489             ANGLE_GL_CALL(intf, StencilMask(0xffffffff));
    490             ANGLE_GL_CALL(intf, Clear(GL_STENCIL_BUFFER_BIT |GL_COLOR_BUFFER_BIT));
    491         }
    492     }
    493     if (eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
    494         eglGetConfigAttrib(fDisplay, fConfig, EGL_STENCIL_SIZE, &info->fStencilBits);
    495         eglGetConfigAttrib(fDisplay, fConfig, EGL_SAMPLES, &info->fSampleCount);
    496 
    497         SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
    498 
    499         if (intf ) {
    500             ANGLE_GL_CALL(intf, Viewport(0, 0, SkScalarRound(this->width()),
    501                                       SkScalarRound(this->height())));
    502         }
    503         return true;
    504     }
    505     return false;
    506 }
    507 
    508 void SkOSWindow::detachANGLE() {
    509     eglMakeCurrent(fDisplay, EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT);
    510 
    511     eglDestroyContext(fDisplay, fContext);
    512     fContext = EGL_NO_CONTEXT;
    513 
    514     eglDestroySurface(fDisplay, fSurface);
    515     fSurface = EGL_NO_SURFACE;
    516 
    517     eglTerminate(fDisplay);
    518     fDisplay = EGL_NO_DISPLAY;
    519 }
    520 
    521 void SkOSWindow::presentANGLE() {
    522     SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
    523 
    524     if (intf) {
    525         ANGLE_GL_CALL(intf, Flush());
    526     }
    527 
    528     eglSwapBuffers(fDisplay, fSurface);
    529 }
    530 #endif // SK_ANGLE
    531 #endif // SK_SUPPORT_GPU
    532 
    533 // return true on success
    534 bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo* info) {
    535 
    536     // attach doubles as "windowResize" so we need to allo
    537     // already bound states to pass through again
    538     // TODO: split out the resize functionality
    539 //    SkASSERT(kNone_BackEndType == fAttached);
    540     bool result = true;
    541 
    542     switch (attachType) {
    543     case kNone_BackEndType:
    544         // nothing to do
    545         break;
    546 #if SK_SUPPORT_GPU
    547     case kNativeGL_BackEndType:
    548         result = attachGL(msaaSampleCount, info);
    549         break;
    550 #if SK_ANGLE
    551     case kANGLE_BackEndType:
    552         result = attachANGLE(msaaSampleCount, info);
    553         break;
    554 #endif // SK_ANGLE
    555 #endif // SK_SUPPORT_GPU
    556     default:
    557         SkASSERT(false);
    558         result = false;
    559         break;
    560     }
    561 
    562     if (result) {
    563         fAttached = attachType;
    564     }
    565 
    566     return result;
    567 }
    568 
    569 void SkOSWindow::detach() {
    570     switch (fAttached) {
    571     case kNone_BackEndType:
    572         // nothing to do
    573         break;
    574 #if SK_SUPPORT_GPU
    575     case kNativeGL_BackEndType:
    576         detachGL();
    577         break;
    578 #if SK_ANGLE
    579     case kANGLE_BackEndType:
    580         detachANGLE();
    581         break;
    582 #endif // SK_ANGLE
    583 #endif // SK_SUPPORT_GPU
    584     default:
    585         SkASSERT(false);
    586         break;
    587     }
    588     fAttached = kNone_BackEndType;
    589 }
    590 
    591 void SkOSWindow::present() {
    592     switch (fAttached) {
    593     case kNone_BackEndType:
    594         // nothing to do
    595         return;
    596 #if SK_SUPPORT_GPU
    597     case kNativeGL_BackEndType:
    598         presentGL();
    599         break;
    600 #if SK_ANGLE
    601     case kANGLE_BackEndType:
    602         presentANGLE();
    603         break;
    604 #endif // SK_ANGLE
    605 #endif // SK_SUPPORT_GPU
    606     default:
    607         SkASSERT(false);
    608         break;
    609     }
    610 }
    611 
    612 #endif
    613