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)->fFunctions.f##X;                               \
31    } while (false)
32
33#endif
34
35#define INVALIDATE_DELAY_MS 200
36
37static SkOSWindow* gCurrOSWin;
38static HWND gEventTarget;
39
40#define WM_EVENT_CALLBACK (WM_USER+0)
41
42void post_skwinevent()
43{
44    PostMessage(gEventTarget, WM_EVENT_CALLBACK, 0, 0);
45}
46
47SkOSWindow::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
61SkOSWindow::~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
85static 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
106static unsigned getModifiers(UINT message) {
107    return 0;   // TODO
108}
109
110bool 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
179void 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
222void 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
230void 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
239void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu)
240{
241}
242
243void SkOSWindow::onSetTitle(const char title[]){
244    SetWindowTextA((HWND)fHWND, title);
245}
246
247enum {
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
268static 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
301void SkEvent::SignalNonEmptyQueue()
302{
303    post_skwinevent();
304    //SkDebugf("signal nonempty\n");
305}
306
307static UINT_PTR gTimer;
308
309VOID 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
315void 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
331bool 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
373void SkOSWindow::detachGL() {
374    wglMakeCurrent(GetDC((HWND)fHWND), 0);
375    wglDeleteContext((HGLRC)fHGLRC);
376    fHGLRC = NULL;
377}
378
379void SkOSWindow::presentGL() {
380    glFlush();
381    HDC dc = GetDC((HWND)fHWND);
382    SwapBuffers(dc);
383    ReleaseDC((HWND)fHWND, dc);
384}
385
386#if SK_ANGLE
387bool create_ANGLE(EGLNativeWindowType hWnd,
388                  int msaaSampleCount,
389                  EGLDisplay* eglDisplay,
390                  EGLContext* eglContext,
391                  EGLSurface* eglSurface,
392                  EGLConfig* eglConfig) {
393    static const EGLint contextAttribs[] = {
394        EGL_CONTEXT_CLIENT_VERSION, 2,
395        EGL_NONE, EGL_NONE
396    };
397    static const EGLint configAttribList[] = {
398        EGL_RED_SIZE,       8,
399        EGL_GREEN_SIZE,     8,
400        EGL_BLUE_SIZE,      8,
401        EGL_ALPHA_SIZE,     8,
402        EGL_DEPTH_SIZE,     8,
403        EGL_STENCIL_SIZE,   8,
404        EGL_NONE
405    };
406    static const EGLint surfaceAttribList[] = {
407        EGL_NONE, EGL_NONE
408    };
409
410    EGLDisplay display = eglGetDisplay(GetDC(hWnd));
411    if (display == EGL_NO_DISPLAY ) {
412       return false;
413    }
414
415    // Initialize EGL
416    EGLint majorVersion, minorVersion;
417    if (!eglInitialize(display, &majorVersion, &minorVersion)) {
418       return false;
419    }
420
421    EGLint numConfigs;
422    if (!eglGetConfigs(display, NULL, 0, &numConfigs)) {
423       return false;
424    }
425
426    // Choose config
427    bool foundConfig = false;
428    if (msaaSampleCount) {
429        static const int kConfigAttribListCnt =
430                                SK_ARRAY_COUNT(configAttribList);
431        EGLint msaaConfigAttribList[kConfigAttribListCnt + 4];
432        memcpy(msaaConfigAttribList,
433               configAttribList,
434               sizeof(configAttribList));
435        SkASSERT(EGL_NONE == msaaConfigAttribList[kConfigAttribListCnt - 1]);
436        msaaConfigAttribList[kConfigAttribListCnt - 1] = EGL_SAMPLE_BUFFERS;
437        msaaConfigAttribList[kConfigAttribListCnt + 0] = 1;
438        msaaConfigAttribList[kConfigAttribListCnt + 1] = EGL_SAMPLES;
439        msaaConfigAttribList[kConfigAttribListCnt + 2] = msaaSampleCount;
440        msaaConfigAttribList[kConfigAttribListCnt + 3] = EGL_NONE;
441        if (eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) {
442            SkASSERT(numConfigs > 0);
443            foundConfig = true;
444        }
445    }
446    if (!foundConfig) {
447        if (!eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) {
448           return false;
449        }
450    }
451
452    // Create a surface
453    EGLSurface surface = eglCreateWindowSurface(display, *eglConfig,
454                                                (EGLNativeWindowType)hWnd,
455                                                surfaceAttribList);
456    if (surface == EGL_NO_SURFACE) {
457       return false;
458    }
459
460    // Create a GL context
461    EGLContext context = eglCreateContext(display, *eglConfig,
462                                          EGL_NO_CONTEXT,
463                                          contextAttribs );
464    if (context == EGL_NO_CONTEXT ) {
465       return false;
466    }
467
468    // Make the context current
469    if (!eglMakeCurrent(display, surface, surface, context)) {
470       return false;
471    }
472
473    *eglDisplay = display;
474    *eglContext = context;
475    *eglSurface = surface;
476    return true;
477}
478
479bool SkOSWindow::attachANGLE(int msaaSampleCount, AttachmentInfo* info) {
480    if (EGL_NO_DISPLAY == fDisplay) {
481        bool bResult = create_ANGLE((HWND)fHWND,
482                                    msaaSampleCount,
483                                    &fDisplay,
484                                    &fContext,
485                                    &fSurface,
486                                    &fConfig);
487        if (false == bResult) {
488            return false;
489        }
490        SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
491
492        if (intf) {
493            ANGLE_GL_CALL(intf, ClearStencil(0));
494            ANGLE_GL_CALL(intf, ClearColor(0, 0, 0, 0));
495            ANGLE_GL_CALL(intf, StencilMask(0xffffffff));
496            ANGLE_GL_CALL(intf, Clear(GL_STENCIL_BUFFER_BIT |GL_COLOR_BUFFER_BIT));
497        }
498    }
499    if (eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
500        eglGetConfigAttrib(fDisplay, fConfig, EGL_STENCIL_SIZE, &info->fStencilBits);
501        eglGetConfigAttrib(fDisplay, fConfig, EGL_SAMPLES, &info->fSampleCount);
502
503        SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
504
505        if (intf ) {
506            ANGLE_GL_CALL(intf, Viewport(0, 0,
507                                         SkScalarRoundToInt(this->width()),
508                                         SkScalarRoundToInt(this->height())));
509        }
510        return true;
511    }
512    return false;
513}
514
515void SkOSWindow::detachANGLE() {
516    eglMakeCurrent(fDisplay, EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT);
517
518    eglDestroyContext(fDisplay, fContext);
519    fContext = EGL_NO_CONTEXT;
520
521    eglDestroySurface(fDisplay, fSurface);
522    fSurface = EGL_NO_SURFACE;
523
524    eglTerminate(fDisplay);
525    fDisplay = EGL_NO_DISPLAY;
526}
527
528void SkOSWindow::presentANGLE() {
529    SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
530
531    if (intf) {
532        ANGLE_GL_CALL(intf, Flush());
533    }
534
535    eglSwapBuffers(fDisplay, fSurface);
536}
537#endif // SK_ANGLE
538#endif // SK_SUPPORT_GPU
539
540// return true on success
541bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo* info) {
542
543    // attach doubles as "windowResize" so we need to allo
544    // already bound states to pass through again
545    // TODO: split out the resize functionality
546//    SkASSERT(kNone_BackEndType == fAttached);
547    bool result = true;
548
549    switch (attachType) {
550    case kNone_BackEndType:
551        // nothing to do
552        break;
553#if SK_SUPPORT_GPU
554    case kNativeGL_BackEndType:
555        result = attachGL(msaaSampleCount, info);
556        break;
557#if SK_ANGLE
558    case kANGLE_BackEndType:
559        result = attachANGLE(msaaSampleCount, info);
560        break;
561#endif // SK_ANGLE
562#endif // SK_SUPPORT_GPU
563    default:
564        SkASSERT(false);
565        result = false;
566        break;
567    }
568
569    if (result) {
570        fAttached = attachType;
571    }
572
573    return result;
574}
575
576void SkOSWindow::detach() {
577    switch (fAttached) {
578    case kNone_BackEndType:
579        // nothing to do
580        break;
581#if SK_SUPPORT_GPU
582    case kNativeGL_BackEndType:
583        detachGL();
584        break;
585#if SK_ANGLE
586    case kANGLE_BackEndType:
587        detachANGLE();
588        break;
589#endif // SK_ANGLE
590#endif // SK_SUPPORT_GPU
591    default:
592        SkASSERT(false);
593        break;
594    }
595    fAttached = kNone_BackEndType;
596}
597
598void SkOSWindow::present() {
599    switch (fAttached) {
600    case kNone_BackEndType:
601        // nothing to do
602        return;
603#if SK_SUPPORT_GPU
604    case kNativeGL_BackEndType:
605        presentGL();
606        break;
607#if SK_ANGLE
608    case kANGLE_BackEndType:
609        presentANGLE();
610        break;
611#endif // SK_ANGLE
612#endif // SK_SUPPORT_GPU
613    default:
614        SkASSERT(false);
615        break;
616    }
617}
618
619#endif
620