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 
post_skwinevent()42 void post_skwinevent()
43 {
44     PostMessage(gEventTarget, WM_EVENT_CALLBACK, 0, 0);
45 }
46 
SkOSWindow(void * hWnd)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 
~SkOSWindow()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 
winToskKey(WPARAM vk)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 
getModifiers(UINT message)106 static unsigned getModifiers(UINT message) {
107     return 0;   // TODO
108 }
109 
wndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)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 
doPaint(void * ctx)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 
onHandleInval(const SkIRect & r)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 
onAddMenu(const SkOSMenu * sk_menu)239 void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu)
240 {
241 }
242 
onSetTitle(const char title[])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 
raw2key(uint32_t raw)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 
SignalNonEmptyQueue()301 void SkEvent::SignalNonEmptyQueue()
302 {
303     post_skwinevent();
304     //SkDebugf("signal nonempty\n");
305 }
306 
307 static UINT_PTR gTimer;
308 
sk_timer_proc(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime)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 
SignalQueueTimer(SkMSec delay)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 
attachGL(int msaaSampleCount,AttachmentInfo * info)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 
detachGL()373 void SkOSWindow::detachGL() {
374     wglMakeCurrent(GetDC((HWND)fHWND), 0);
375     wglDeleteContext((HGLRC)fHGLRC);
376     fHGLRC = NULL;
377 }
378 
presentGL()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 
create_ANGLE(EGLNativeWindowType hWnd,int msaaSampleCount,EGLDisplay * eglDisplay,EGLContext * eglContext,EGLSurface * eglSurface,EGLConfig * eglConfig)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 
attachANGLE(int msaaSampleCount,AttachmentInfo * info)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 
detachANGLE()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 
presentANGLE()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
attach(SkBackEndTypes attachType,int msaaSampleCount,AttachmentInfo * info)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 
detach()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 
present()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