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