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