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 // SK_ANGLE
34
35#if SK_COMMAND_BUFFER
36#include "gl/command_buffer/SkCommandBufferGLContext.h"
37
38#define COMMAND_BUFFER_GL_CALL(IFACE, X)                                 \
39    do {                                                        \
40        (IFACE)->fFunctions.f##X;                               \
41    } while (false)
42
43#endif // SK_COMMAND_BUFFER
44
45#define WM_EVENT_CALLBACK (WM_USER+0)
46
47void post_skwinevent(HWND hwnd)
48{
49    PostMessage(hwnd, WM_EVENT_CALLBACK, 0, 0);
50}
51
52SkTHashMap<void*, SkOSWindow*> SkOSWindow::gHwndToOSWindowMap;
53
54SkOSWindow::SkOSWindow(const void* winInit) {
55    fWinInit = *(const WindowInit*)winInit;
56
57    fHWND = CreateWindow(fWinInit.fClass, NULL, WS_OVERLAPPEDWINDOW,
58                         CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, fWinInit.fInstance, NULL);
59    gHwndToOSWindowMap.set(fHWND, this);
60#if SK_SUPPORT_GPU
61#if SK_ANGLE
62    fDisplay = EGL_NO_DISPLAY;
63    fContext = EGL_NO_CONTEXT;
64    fSurface = EGL_NO_SURFACE;
65#endif
66#if SK_COMMAND_BUFFER
67    fCommandBuffer = nullptr;
68#endif // SK_COMMAND_BUFFER
69
70    fHGLRC = NULL;
71#endif
72    fAttached = kNone_BackEndType;
73    fFullscreen = false;
74}
75
76SkOSWindow::~SkOSWindow() {
77#if SK_SUPPORT_GPU
78    if (fHGLRC) {
79        wglDeleteContext((HGLRC)fHGLRC);
80    }
81#if SK_ANGLE
82    if (EGL_NO_CONTEXT != fContext) {
83        eglDestroyContext(fDisplay, fContext);
84        fContext = EGL_NO_CONTEXT;
85    }
86
87    if (EGL_NO_SURFACE != fSurface) {
88        eglDestroySurface(fDisplay, fSurface);
89        fSurface = EGL_NO_SURFACE;
90    }
91
92    if (EGL_NO_DISPLAY != fDisplay) {
93        eglTerminate(fDisplay);
94        fDisplay = EGL_NO_DISPLAY;
95    }
96#endif // SK_ANGLE
97#if SK_COMMAND_BUFFER
98    delete fCommandBuffer;
99#endif // SK_COMMAND_BUFFER
100
101#endif // SK_SUPPORT_GPU
102    this->closeWindow();
103}
104
105static SkKey winToskKey(WPARAM vk) {
106    static const struct {
107        WPARAM    fVK;
108        SkKey    fKey;
109    } gPair[] = {
110        { VK_BACK,    kBack_SkKey },
111        { VK_CLEAR,    kBack_SkKey },
112        { VK_RETURN, kOK_SkKey },
113        { VK_UP,     kUp_SkKey },
114        { VK_DOWN,     kDown_SkKey },
115        { VK_LEFT,     kLeft_SkKey },
116        { VK_RIGHT,     kRight_SkKey }
117    };
118    for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
119        if (gPair[i].fVK == vk) {
120            return gPair[i].fKey;
121        }
122    }
123    return kNONE_SkKey;
124}
125
126static unsigned getModifiers(UINT message) {
127    return 0;   // TODO
128}
129
130bool SkOSWindow::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
131    switch (message) {
132        case WM_KEYDOWN: {
133            SkKey key = winToskKey(wParam);
134            if (kNONE_SkKey != key) {
135                this->handleKey(key);
136                return true;
137            }
138        } break;
139        case WM_KEYUP: {
140            SkKey key = winToskKey(wParam);
141            if (kNONE_SkKey != key) {
142                this->handleKeyUp(key);
143                return true;
144            }
145        } break;
146        case WM_UNICHAR:
147            this->handleChar((SkUnichar) wParam);
148            return true;
149        case WM_CHAR: {
150            const uint16_t* c = reinterpret_cast<uint16_t*>(&wParam);
151            this->handleChar(SkUTF16_NextUnichar(&c));
152            return true;
153        } break;
154        case WM_SIZE: {
155            INT width = LOWORD(lParam);
156            INT height = HIWORD(lParam);
157            this->resize(width, height);
158            break;
159        }
160        case WM_PAINT: {
161            PAINTSTRUCT ps;
162            HDC hdc = BeginPaint(hWnd, &ps);
163            this->doPaint(hdc);
164            EndPaint(hWnd, &ps);
165            return true;
166            } break;
167
168        case WM_LBUTTONDOWN:
169            this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
170                              Click::kDown_State, NULL, getModifiers(message));
171            return true;
172
173        case WM_MOUSEMOVE:
174            this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
175                              Click::kMoved_State, NULL, getModifiers(message));
176            return true;
177
178        case WM_LBUTTONUP:
179            this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
180                              Click::kUp_State, NULL, getModifiers(message));
181            return true;
182
183        case WM_EVENT_CALLBACK:
184            if (SkEvent::ProcessEvent()) {
185                post_skwinevent(hWnd);
186            }
187            return true;
188    }
189    return false;
190}
191
192void SkOSWindow::doPaint(void* ctx) {
193    this->update(NULL);
194
195    if (kNone_BackEndType == fAttached)
196    {
197        HDC hdc = (HDC)ctx;
198        const SkBitmap& bitmap = this->getBitmap();
199
200        BITMAPINFO bmi;
201        memset(&bmi, 0, sizeof(bmi));
202        bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
203        bmi.bmiHeader.biWidth       = bitmap.width();
204        bmi.bmiHeader.biHeight      = -bitmap.height(); // top-down image
205        bmi.bmiHeader.biPlanes      = 1;
206        bmi.bmiHeader.biBitCount    = 32;
207        bmi.bmiHeader.biCompression = BI_RGB;
208        bmi.bmiHeader.biSizeImage   = 0;
209
210        //
211        // Do the SetDIBitsToDevice.
212        //
213        // TODO(wjmaclean):
214        //       Fix this call to handle SkBitmaps that have rowBytes != width,
215        //       i.e. may have padding at the end of lines. The SkASSERT below
216        //       may be ignored by builds, and the only obviously safe option
217        //       seems to be to copy the bitmap to a temporary (contiguous)
218        //       buffer before passing to SetDIBitsToDevice().
219        SkASSERT(bitmap.width() * bitmap.bytesPerPixel() == bitmap.rowBytes());
220        bitmap.lockPixels();
221        int ret = SetDIBitsToDevice(hdc,
222            0, 0,
223            bitmap.width(), bitmap.height(),
224            0, 0,
225            0, bitmap.height(),
226            bitmap.getPixels(),
227            &bmi,
228            DIB_RGB_COLORS);
229        (void)ret; // we're ignoring potential failures for now.
230        bitmap.unlockPixels();
231    }
232}
233
234void SkOSWindow::updateSize()
235{
236    RECT    r;
237    GetWindowRect((HWND)fHWND, &r);
238    this->resize(r.right - r.left, r.bottom - r.top);
239}
240
241void SkOSWindow::onHandleInval(const SkIRect& r) {
242    RECT rect;
243    rect.left    = r.fLeft;
244    rect.top     = r.fTop;
245    rect.right   = r.fRight;
246    rect.bottom  = r.fBottom;
247    InvalidateRect((HWND)fHWND, &rect, FALSE);
248}
249
250void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu)
251{
252}
253
254void SkOSWindow::onSetTitle(const char title[]){
255    SetWindowTextA((HWND)fHWND, title);
256}
257
258enum {
259    SK_MacReturnKey     = 36,
260    SK_MacDeleteKey     = 51,
261    SK_MacEndKey        = 119,
262    SK_MacLeftKey       = 123,
263    SK_MacRightKey      = 124,
264    SK_MacDownKey       = 125,
265    SK_MacUpKey         = 126,
266
267    SK_Mac0Key          = 0x52,
268    SK_Mac1Key          = 0x53,
269    SK_Mac2Key          = 0x54,
270    SK_Mac3Key          = 0x55,
271    SK_Mac4Key          = 0x56,
272    SK_Mac5Key          = 0x57,
273    SK_Mac6Key          = 0x58,
274    SK_Mac7Key          = 0x59,
275    SK_Mac8Key          = 0x5b,
276    SK_Mac9Key          = 0x5c
277};
278
279static SkKey raw2key(uint32_t raw)
280{
281    static const struct {
282        uint32_t  fRaw;
283        SkKey   fKey;
284    } gKeys[] = {
285        { SK_MacUpKey,      kUp_SkKey       },
286        { SK_MacDownKey,    kDown_SkKey     },
287        { SK_MacLeftKey,    kLeft_SkKey     },
288        { SK_MacRightKey,   kRight_SkKey    },
289        { SK_MacReturnKey,  kOK_SkKey       },
290        { SK_MacDeleteKey,  kBack_SkKey     },
291        { SK_MacEndKey,     kEnd_SkKey      },
292        { SK_Mac0Key,       k0_SkKey        },
293        { SK_Mac1Key,       k1_SkKey        },
294        { SK_Mac2Key,       k2_SkKey        },
295        { SK_Mac3Key,       k3_SkKey        },
296        { SK_Mac4Key,       k4_SkKey        },
297        { SK_Mac5Key,       k5_SkKey        },
298        { SK_Mac6Key,       k6_SkKey        },
299        { SK_Mac7Key,       k7_SkKey        },
300        { SK_Mac8Key,       k8_SkKey        },
301        { SK_Mac9Key,       k9_SkKey        }
302    };
303
304    for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++)
305        if (gKeys[i].fRaw == raw)
306            return gKeys[i].fKey;
307    return kNONE_SkKey;
308}
309
310///////////////////////////////////////////////////////////////////////////////////////
311
312void SkEvent::SignalNonEmptyQueue()
313{
314    SkOSWindow::ForAllWindows([](void* hWND, SkOSWindow**) {
315        post_skwinevent((HWND)hWND);
316    });
317}
318
319static UINT_PTR gTimer;
320
321VOID CALLBACK sk_timer_proc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
322{
323    SkEvent::ServiceQueueTimer();
324    //SkDebugf("timer task fired\n");
325}
326
327void SkEvent::SignalQueueTimer(SkMSec delay)
328{
329    if (gTimer)
330    {
331        KillTimer(NULL, gTimer);
332        gTimer = NULL;
333    }
334    if (delay)
335    {
336        gTimer = SetTimer(NULL, 0, delay, sk_timer_proc);
337        //SkDebugf("SetTimer of %d returned %d\n", delay, gTimer);
338    }
339}
340
341#if SK_SUPPORT_GPU
342
343bool SkOSWindow::attachGL(int msaaSampleCount, AttachmentInfo* info) {
344    HDC dc = GetDC((HWND)fHWND);
345    if (NULL == fHGLRC) {
346        fHGLRC = SkCreateWGLContext(dc, msaaSampleCount,
347                kGLPreferCompatibilityProfile_SkWGLContextRequest);
348        if (NULL == fHGLRC) {
349            return false;
350        }
351        glClearStencil(0);
352        glClearColor(0, 0, 0, 0);
353        glStencilMask(0xffffffff);
354        glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
355    }
356    if (wglMakeCurrent(dc, (HGLRC)fHGLRC)) {
357        // use DescribePixelFormat to get the stencil bit depth.
358        int pixelFormat = GetPixelFormat(dc);
359        PIXELFORMATDESCRIPTOR pfd;
360        DescribePixelFormat(dc, pixelFormat, sizeof(pfd), &pfd);
361        info->fStencilBits = pfd.cStencilBits;
362
363        // Get sample count if the MSAA WGL extension is present
364        SkWGLExtensions extensions;
365        if (extensions.hasExtension(dc, "WGL_ARB_multisample")) {
366            static const int kSampleCountAttr = SK_WGL_SAMPLES;
367            extensions.getPixelFormatAttribiv(dc,
368                                              pixelFormat,
369                                              0,
370                                              1,
371                                              &kSampleCountAttr,
372                                              &info->fSampleCount);
373        } else {
374            info->fSampleCount = 0;
375        }
376
377        glViewport(0, 0,
378                   SkScalarRoundToInt(this->width()),
379                   SkScalarRoundToInt(this->height()));
380        return true;
381    }
382    return false;
383}
384
385void SkOSWindow::detachGL() {
386    wglMakeCurrent(GetDC((HWND)fHWND), 0);
387    wglDeleteContext((HGLRC)fHGLRC);
388    fHGLRC = NULL;
389}
390
391void SkOSWindow::presentGL() {
392    HDC dc = GetDC((HWND)fHWND);
393    SwapBuffers(dc);
394    ReleaseDC((HWND)fHWND, dc);
395}
396
397#if SK_ANGLE
398
399bool create_ANGLE(EGLNativeWindowType hWnd,
400                  int msaaSampleCount,
401                  EGLDisplay* eglDisplay,
402                  EGLContext* eglContext,
403                  EGLSurface* eglSurface,
404                  EGLConfig* eglConfig) {
405    static const EGLint contextAttribs[] = {
406        EGL_CONTEXT_CLIENT_VERSION, 2,
407        EGL_NONE, EGL_NONE
408    };
409    static const EGLint configAttribList[] = {
410        EGL_RED_SIZE,       8,
411        EGL_GREEN_SIZE,     8,
412        EGL_BLUE_SIZE,      8,
413        EGL_ALPHA_SIZE,     8,
414        EGL_DEPTH_SIZE,     8,
415        EGL_STENCIL_SIZE,   8,
416        EGL_NONE
417    };
418    static const EGLint surfaceAttribList[] = {
419        EGL_NONE, EGL_NONE
420    };
421
422    EGLDisplay display = SkANGLEGLContext::GetD3DEGLDisplay(GetDC(hWnd), false);
423
424    if (EGL_NO_DISPLAY == display) {
425        SkDebugf("Could not create ANGLE egl display!\n");
426        return false;
427    }
428
429    // Initialize EGL
430    EGLint majorVersion, minorVersion;
431    if (!eglInitialize(display, &majorVersion, &minorVersion)) {
432       return false;
433    }
434
435    EGLint numConfigs;
436    if (!eglGetConfigs(display, NULL, 0, &numConfigs)) {
437       return false;
438    }
439
440    // Choose config
441    bool foundConfig = false;
442    if (msaaSampleCount) {
443        static const int kConfigAttribListCnt =
444                                SK_ARRAY_COUNT(configAttribList);
445        EGLint msaaConfigAttribList[kConfigAttribListCnt + 4];
446        memcpy(msaaConfigAttribList,
447               configAttribList,
448               sizeof(configAttribList));
449        SkASSERT(EGL_NONE == msaaConfigAttribList[kConfigAttribListCnt - 1]);
450        msaaConfigAttribList[kConfigAttribListCnt - 1] = EGL_SAMPLE_BUFFERS;
451        msaaConfigAttribList[kConfigAttribListCnt + 0] = 1;
452        msaaConfigAttribList[kConfigAttribListCnt + 1] = EGL_SAMPLES;
453        msaaConfigAttribList[kConfigAttribListCnt + 2] = msaaSampleCount;
454        msaaConfigAttribList[kConfigAttribListCnt + 3] = EGL_NONE;
455        if (eglChooseConfig(display, msaaConfigAttribList, eglConfig, 1, &numConfigs)) {
456            SkASSERT(numConfigs > 0);
457            foundConfig = true;
458        }
459    }
460    if (!foundConfig) {
461        if (!eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) {
462           return false;
463        }
464    }
465
466    // Create a surface
467    EGLSurface surface = eglCreateWindowSurface(display, *eglConfig,
468                                                (EGLNativeWindowType)hWnd,
469                                                surfaceAttribList);
470    if (surface == EGL_NO_SURFACE) {
471       return false;
472    }
473
474    // Create a GL context
475    EGLContext context = eglCreateContext(display, *eglConfig,
476                                          EGL_NO_CONTEXT,
477                                          contextAttribs );
478    if (context == EGL_NO_CONTEXT ) {
479       return false;
480    }
481
482    // Make the context current
483    if (!eglMakeCurrent(display, surface, surface, context)) {
484       return false;
485    }
486
487    *eglDisplay = display;
488    *eglContext = context;
489    *eglSurface = surface;
490    return true;
491}
492
493bool SkOSWindow::attachANGLE(int msaaSampleCount, AttachmentInfo* info) {
494    if (EGL_NO_DISPLAY == fDisplay) {
495        bool bResult = create_ANGLE((HWND)fHWND,
496                                    msaaSampleCount,
497                                    &fDisplay,
498                                    &fContext,
499                                    &fSurface,
500                                    &fConfig);
501        if (false == bResult) {
502            return false;
503        }
504        SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
505
506        if (intf) {
507            ANGLE_GL_CALL(intf, ClearStencil(0));
508            ANGLE_GL_CALL(intf, ClearColor(0, 0, 0, 0));
509            ANGLE_GL_CALL(intf, StencilMask(0xffffffff));
510            ANGLE_GL_CALL(intf, Clear(GL_STENCIL_BUFFER_BIT |GL_COLOR_BUFFER_BIT));
511        }
512    }
513    if (eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
514        eglGetConfigAttrib(fDisplay, fConfig, EGL_STENCIL_SIZE, &info->fStencilBits);
515        eglGetConfigAttrib(fDisplay, fConfig, EGL_SAMPLES, &info->fSampleCount);
516
517        SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
518
519        if (intf ) {
520            ANGLE_GL_CALL(intf, Viewport(0, 0,
521                                         SkScalarRoundToInt(this->width()),
522                                         SkScalarRoundToInt(this->height())));
523        }
524        return true;
525    }
526    return false;
527}
528
529void SkOSWindow::detachANGLE() {
530    eglMakeCurrent(fDisplay, EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT);
531
532    eglDestroyContext(fDisplay, fContext);
533    fContext = EGL_NO_CONTEXT;
534
535    eglDestroySurface(fDisplay, fSurface);
536    fSurface = EGL_NO_SURFACE;
537
538    eglTerminate(fDisplay);
539    fDisplay = EGL_NO_DISPLAY;
540}
541
542void SkOSWindow::presentANGLE() {
543    SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
544
545    if (intf) {
546        ANGLE_GL_CALL(intf, Flush());
547    }
548
549    eglSwapBuffers(fDisplay, fSurface);
550}
551#endif // SK_ANGLE
552
553#if SK_COMMAND_BUFFER
554
555bool SkOSWindow::attachCommandBuffer(int msaaSampleCount, AttachmentInfo* info) {
556    if (!fCommandBuffer) {
557        fCommandBuffer = SkCommandBufferGLContext::Create((HWND)fHWND, msaaSampleCount);
558        if (!fCommandBuffer)
559            return false;
560
561        SkAutoTUnref<const GrGLInterface> intf(GrGLCreateCommandBufferInterface());
562        if (intf) {
563            COMMAND_BUFFER_GL_CALL(intf, ClearStencil(0));
564            COMMAND_BUFFER_GL_CALL(intf, ClearColor(0, 0, 0, 0));
565            COMMAND_BUFFER_GL_CALL(intf, StencilMask(0xffffffff));
566            COMMAND_BUFFER_GL_CALL(intf, Clear(GL_STENCIL_BUFFER_BIT |GL_COLOR_BUFFER_BIT));
567        }
568    }
569
570    if (fCommandBuffer->makeCurrent()) {
571        info->fStencilBits = fCommandBuffer->getStencilBits();
572        info->fSampleCount = fCommandBuffer->getSampleCount();
573
574        SkAutoTUnref<const GrGLInterface> intf(GrGLCreateCommandBufferInterface());
575
576        if (intf ) {
577            COMMAND_BUFFER_GL_CALL(intf, Viewport(0, 0,
578                                         SkScalarRoundToInt(this->width()),
579                                         SkScalarRoundToInt(this->height())));
580        }
581        return true;
582    }
583    return false;
584}
585
586void SkOSWindow::detachCommandBuffer() {
587    delete fCommandBuffer;
588    fCommandBuffer = nullptr;
589}
590
591void SkOSWindow::presentCommandBuffer() {
592    fCommandBuffer->presentCommandBuffer();
593}
594#endif // SK_COMMAND_BUFFER
595
596#endif // SK_SUPPORT_GPU
597
598// return true on success
599bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo* info) {
600
601    // attach doubles as "windowResize" so we need to allo
602    // already bound states to pass through again
603    // TODO: split out the resize functionality
604//    SkASSERT(kNone_BackEndType == fAttached);
605    bool result = true;
606
607    switch (attachType) {
608    case kNone_BackEndType:
609        // nothing to do
610        break;
611#if SK_SUPPORT_GPU
612    case kNativeGL_BackEndType:
613        result = attachGL(msaaSampleCount, info);
614        break;
615#if SK_ANGLE
616    case kANGLE_BackEndType:
617        result = attachANGLE(msaaSampleCount, info);
618        break;
619#endif // SK_ANGLE
620#if SK_COMMAND_BUFFER
621    case kCommandBufferES2_BackEndType:
622        result = attachCommandBuffer(msaaSampleCount, info);
623        break;
624#endif // SK_COMMAND_BUFFER
625#endif // SK_SUPPORT_GPU
626    default:
627        SkASSERT(false);
628        result = false;
629        break;
630    }
631
632    if (result) {
633        fAttached = attachType;
634    }
635
636    return result;
637}
638
639void SkOSWindow::detach() {
640    switch (fAttached) {
641    case kNone_BackEndType:
642        // nothing to do
643        break;
644#if SK_SUPPORT_GPU
645    case kNativeGL_BackEndType:
646        detachGL();
647        break;
648#if SK_ANGLE
649    case kANGLE_BackEndType:
650        detachANGLE();
651        break;
652#endif // SK_ANGLE
653#if SK_COMMAND_BUFFER
654    case kCommandBufferES2_BackEndType:
655        detachCommandBuffer();
656        break;
657#endif // SK_COMMAND_BUFFER
658#endif // SK_SUPPORT_GPU
659    default:
660        SkASSERT(false);
661        break;
662    }
663    fAttached = kNone_BackEndType;
664}
665
666void SkOSWindow::present() {
667    switch (fAttached) {
668    case kNone_BackEndType:
669        // nothing to do
670        return;
671#if SK_SUPPORT_GPU
672    case kNativeGL_BackEndType:
673        presentGL();
674        break;
675#if SK_ANGLE
676    case kANGLE_BackEndType:
677        presentANGLE();
678        break;
679#endif // SK_ANGLE
680#if SK_COMMAND_BUFFER
681    case kCommandBufferES2_BackEndType:
682        presentCommandBuffer();
683        break;
684#endif // SK_COMMAND_BUFFER
685#endif // SK_SUPPORT_GPU
686    default:
687        SkASSERT(false);
688        break;
689    }
690}
691
692bool SkOSWindow::makeFullscreen() {
693    if (fFullscreen) {
694        return true;
695    }
696#if SK_SUPPORT_GPU
697    if (fHGLRC) {
698        this->detachGL();
699    }
700#endif // SK_SUPPORT_GPU
701    // This is hacked together from various sources on the web. It can certainly be improved and be
702    // made more robust.
703
704    // Save current window/resolution information. We do this in case we ever implement switching
705    // back to windowed mode.
706    fSavedWindowState.fZoomed = SkToBool(IsZoomed((HWND)fHWND));
707    if (fSavedWindowState.fZoomed) {
708        SendMessage((HWND)fHWND, WM_SYSCOMMAND, SC_RESTORE, 0);
709    }
710    fSavedWindowState.fStyle = GetWindowLong((HWND)fHWND, GWL_STYLE);
711    fSavedWindowState.fExStyle = GetWindowLong((HWND)fHWND, GWL_EXSTYLE);
712    GetWindowRect((HWND)fHWND, &fSavedWindowState.fRect);
713    DEVMODE currScreenSettings;
714    memset(&currScreenSettings,0,sizeof(currScreenSettings));
715    currScreenSettings.dmSize = sizeof(currScreenSettings);
716    EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &currScreenSettings);
717    fSavedWindowState.fScreenWidth = currScreenSettings.dmPelsWidth;
718    fSavedWindowState.fScreenHeight = currScreenSettings.dmPelsHeight;
719    fSavedWindowState.fScreenBits = currScreenSettings.dmBitsPerPel;
720    fSavedWindowState.fHWND = fHWND;
721
722    // Try different sizes to find an allowed setting? Use ChangeDisplaySettingsEx?
723    static const int kWidth = 1280;
724    static const int kHeight = 1024;
725    DEVMODE newScreenSettings;
726    memset(&newScreenSettings, 0, sizeof(newScreenSettings));
727    newScreenSettings.dmSize = sizeof(newScreenSettings);
728    newScreenSettings.dmPelsWidth    = kWidth;
729    newScreenSettings.dmPelsHeight   = kHeight;
730    newScreenSettings.dmBitsPerPel   = 32;
731    newScreenSettings.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
732    if (ChangeDisplaySettings(&newScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
733        return false;
734    }
735    RECT WindowRect;
736    WindowRect.left = 0;
737    WindowRect.right = kWidth;
738    WindowRect.top = 0;
739    WindowRect.bottom = kHeight;
740    ShowCursor(FALSE);
741    AdjustWindowRectEx(&WindowRect, WS_POPUP, FALSE, WS_EX_APPWINDOW);
742    HWND fsHWND = CreateWindowEx(
743        WS_EX_APPWINDOW,
744        fWinInit.fClass,
745        NULL,
746        WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP,
747        0, 0, WindowRect.right-WindowRect.left, WindowRect.bottom-WindowRect.top,
748        NULL,
749        NULL,
750        fWinInit.fInstance,
751        NULL
752    );
753    if (!fsHWND) {
754        return false;
755    }
756    // Hide the old window and set the entry in the global mapping for this SkOSWindow to the
757    // new HWND.
758    ShowWindow((HWND)fHWND, SW_HIDE);
759    gHwndToOSWindowMap.remove(fHWND);
760    fHWND = fsHWND;
761    gHwndToOSWindowMap.set(fHWND, this);
762    this->updateSize();
763
764    fFullscreen = true;
765    return true;
766}
767
768void SkOSWindow::setVsync(bool enable) {
769    SkWGLExtensions wgl;
770    wgl.swapInterval(enable ? 1 : 0);
771}
772
773void SkOSWindow::closeWindow() {
774    DestroyWindow((HWND)fHWND);
775    if (fFullscreen) {
776        DestroyWindow((HWND)fSavedWindowState.fHWND);
777    }
778    gHwndToOSWindowMap.remove(fHWND);
779}
780#endif
781