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((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, false); 335 if (NULL == fHGLRC) { 336 return false; 337 } 338 glClearStencil(0); 339 glClearColor(0, 0, 0, 0); 340 glStencilMask(0xffffffff); 341 glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); 342 } 343 if (wglMakeCurrent(dc, (HGLRC)fHGLRC)) { 344 // use DescribePixelFormat to get the stencil bit depth. 345 int pixelFormat = GetPixelFormat(dc); 346 PIXELFORMATDESCRIPTOR pfd; 347 DescribePixelFormat(dc, pixelFormat, sizeof(pfd), &pfd); 348 info->fStencilBits = pfd.cStencilBits; 349 350 // Get sample count if the MSAA WGL extension is present 351 SkWGLExtensions extensions; 352 if (extensions.hasExtension(dc, "WGL_ARB_multisample")) { 353 static const int kSampleCountAttr = SK_WGL_SAMPLES; 354 extensions.getPixelFormatAttribiv(dc, 355 pixelFormat, 356 0, 357 1, 358 &kSampleCountAttr, 359 &info->fSampleCount); 360 } else { 361 info->fSampleCount = 0; 362 } 363 364 glViewport(0, 0, SkScalarRound(this->width()), SkScalarRound(this->height())); 365 return true; 366 } 367 return false; 368} 369 370void SkOSWindow::detachGL() { 371 wglMakeCurrent(GetDC((HWND)fHWND), 0); 372 wglDeleteContext((HGLRC)fHGLRC); 373 fHGLRC = NULL; 374} 375 376void SkOSWindow::presentGL() { 377 glFlush(); 378 HDC dc = GetDC((HWND)fHWND); 379 SwapBuffers(dc); 380 ReleaseDC((HWND)fHWND, dc); 381} 382 383#if SK_ANGLE 384bool create_ANGLE(EGLNativeWindowType hWnd, 385 int msaaSampleCount, 386 EGLDisplay* eglDisplay, 387 EGLContext* eglContext, 388 EGLSurface* eglSurface, 389 EGLConfig* eglConfig) { 390 static const EGLint contextAttribs[] = { 391 EGL_CONTEXT_CLIENT_VERSION, 2, 392 EGL_NONE, EGL_NONE 393 }; 394 static const EGLint configAttribList[] = { 395 EGL_RED_SIZE, 8, 396 EGL_GREEN_SIZE, 8, 397 EGL_BLUE_SIZE, 8, 398 EGL_ALPHA_SIZE, 8, 399 EGL_DEPTH_SIZE, 8, 400 EGL_STENCIL_SIZE, 8, 401 EGL_NONE 402 }; 403 static const EGLint surfaceAttribList[] = { 404 EGL_NONE, EGL_NONE 405 }; 406 407 EGLDisplay display = eglGetDisplay(GetDC(hWnd)); 408 if (display == EGL_NO_DISPLAY ) { 409 return false; 410 } 411 412 // Initialize EGL 413 EGLint majorVersion, minorVersion; 414 if (!eglInitialize(display, &majorVersion, &minorVersion)) { 415 return false; 416 } 417 418 EGLint numConfigs; 419 if (!eglGetConfigs(display, NULL, 0, &numConfigs)) { 420 return false; 421 } 422 423 // Choose config 424 bool foundConfig = false; 425 if (msaaSampleCount) { 426 static const int kConfigAttribListCnt = 427 SK_ARRAY_COUNT(configAttribList); 428 EGLint msaaConfigAttribList[kConfigAttribListCnt + 4]; 429 memcpy(msaaConfigAttribList, 430 configAttribList, 431 sizeof(configAttribList)); 432 SkASSERT(EGL_NONE == msaaConfigAttribList[kConfigAttribListCnt - 1]); 433 msaaConfigAttribList[kConfigAttribListCnt - 1] = EGL_SAMPLE_BUFFERS; 434 msaaConfigAttribList[kConfigAttribListCnt + 0] = 1; 435 msaaConfigAttribList[kConfigAttribListCnt + 1] = EGL_SAMPLES; 436 msaaConfigAttribList[kConfigAttribListCnt + 2] = msaaSampleCount; 437 msaaConfigAttribList[kConfigAttribListCnt + 3] = EGL_NONE; 438 if (eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) { 439 SkASSERT(numConfigs > 0); 440 foundConfig = true; 441 } 442 } 443 if (!foundConfig) { 444 if (!eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) { 445 return false; 446 } 447 } 448 449 // Create a surface 450 EGLSurface surface = eglCreateWindowSurface(display, *eglConfig, 451 (EGLNativeWindowType)hWnd, 452 surfaceAttribList); 453 if (surface == EGL_NO_SURFACE) { 454 return false; 455 } 456 457 // Create a GL context 458 EGLContext context = eglCreateContext(display, *eglConfig, 459 EGL_NO_CONTEXT, 460 contextAttribs ); 461 if (context == EGL_NO_CONTEXT ) { 462 return false; 463 } 464 465 // Make the context current 466 if (!eglMakeCurrent(display, surface, surface, context)) { 467 return false; 468 } 469 470 *eglDisplay = display; 471 *eglContext = context; 472 *eglSurface = surface; 473 return true; 474} 475 476bool SkOSWindow::attachANGLE(int msaaSampleCount, AttachmentInfo* info) { 477 if (EGL_NO_DISPLAY == fDisplay) { 478 bool bResult = create_ANGLE((HWND)fHWND, 479 msaaSampleCount, 480 &fDisplay, 481 &fContext, 482 &fSurface, 483 &fConfig); 484 if (false == bResult) { 485 return false; 486 } 487 SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface()); 488 489 if (intf) { 490 ANGLE_GL_CALL(intf, ClearStencil(0)); 491 ANGLE_GL_CALL(intf, ClearColor(0, 0, 0, 0)); 492 ANGLE_GL_CALL(intf, StencilMask(0xffffffff)); 493 ANGLE_GL_CALL(intf, Clear(GL_STENCIL_BUFFER_BIT |GL_COLOR_BUFFER_BIT)); 494 } 495 } 496 if (eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { 497 eglGetConfigAttrib(fDisplay, fConfig, EGL_STENCIL_SIZE, &info->fStencilBits); 498 eglGetConfigAttrib(fDisplay, fConfig, EGL_SAMPLES, &info->fSampleCount); 499 500 SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface()); 501 502 if (intf ) { 503 ANGLE_GL_CALL(intf, Viewport(0, 0, SkScalarRound(this->width()), 504 SkScalarRound(this->height()))); 505 } 506 return true; 507 } 508 return false; 509} 510 511void SkOSWindow::detachANGLE() { 512 eglMakeCurrent(fDisplay, EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT); 513 514 eglDestroyContext(fDisplay, fContext); 515 fContext = EGL_NO_CONTEXT; 516 517 eglDestroySurface(fDisplay, fSurface); 518 fSurface = EGL_NO_SURFACE; 519 520 eglTerminate(fDisplay); 521 fDisplay = EGL_NO_DISPLAY; 522} 523 524void SkOSWindow::presentANGLE() { 525 SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface()); 526 527 if (intf) { 528 ANGLE_GL_CALL(intf, Flush()); 529 } 530 531 eglSwapBuffers(fDisplay, fSurface); 532} 533#endif // SK_ANGLE 534#endif // SK_SUPPORT_GPU 535 536// return true on success 537bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo* info) { 538 539 // attach doubles as "windowResize" so we need to allo 540 // already bound states to pass through again 541 // TODO: split out the resize functionality 542// SkASSERT(kNone_BackEndType == fAttached); 543 bool result = true; 544 545 switch (attachType) { 546 case kNone_BackEndType: 547 // nothing to do 548 break; 549#if SK_SUPPORT_GPU 550 case kNativeGL_BackEndType: 551 result = attachGL(msaaSampleCount, info); 552 break; 553#if SK_ANGLE 554 case kANGLE_BackEndType: 555 result = attachANGLE(msaaSampleCount, info); 556 break; 557#endif // SK_ANGLE 558#endif // SK_SUPPORT_GPU 559 default: 560 SkASSERT(false); 561 result = false; 562 break; 563 } 564 565 if (result) { 566 fAttached = attachType; 567 } 568 569 return result; 570} 571 572void SkOSWindow::detach() { 573 switch (fAttached) { 574 case kNone_BackEndType: 575 // nothing to do 576 break; 577#if SK_SUPPORT_GPU 578 case kNativeGL_BackEndType: 579 detachGL(); 580 break; 581#if SK_ANGLE 582 case kANGLE_BackEndType: 583 detachANGLE(); 584 break; 585#endif // SK_ANGLE 586#endif // SK_SUPPORT_GPU 587 default: 588 SkASSERT(false); 589 break; 590 } 591 fAttached = kNone_BackEndType; 592} 593 594void SkOSWindow::present() { 595 switch (fAttached) { 596 case kNone_BackEndType: 597 // nothing to do 598 return; 599#if SK_SUPPORT_GPU 600 case kNativeGL_BackEndType: 601 presentGL(); 602 break; 603#if SK_ANGLE 604 case kANGLE_BackEndType: 605 presentANGLE(); 606 break; 607#endif // SK_ANGLE 608#endif // SK_SUPPORT_GPU 609 default: 610 SkASSERT(false); 611 break; 612 } 613} 614 615#endif 616