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
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 (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
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(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
176void 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
219void 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
227void 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
236void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu)
237{
238}
239
240void SkOSWindow::onSetTitle(const char title[]){
241    SetWindowTextA((HWND)fHWND, title);
242}
243
244enum {
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
265static 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
298void SkEvent::SignalNonEmptyQueue()
299{
300    post_skwinevent();
301    //SkDebugf("signal nonempty\n");
302}
303
304static UINT_PTR gTimer;
305
306VOID 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
312void 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
328bool 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
367void SkOSWindow::detachGL() {
368    wglMakeCurrent(GetDC((HWND)fHWND), 0);
369    wglDeleteContext((HGLRC)fHGLRC);
370    fHGLRC = NULL;
371}
372
373void SkOSWindow::presentGL() {
374    glFlush();
375    HDC dc = GetDC((HWND)fHWND);
376    SwapBuffers(dc);
377    ReleaseDC((HWND)fHWND, dc);
378}
379
380#if SK_ANGLE
381bool 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
473bool 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
508void 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
521void 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
534bool 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
569void 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
591void 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