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)->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 42 void post_skwinevent() 43 { 44 PostMessage(gEventTarget, WM_EVENT_CALLBACK, 0, 0); 45 } 46 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 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 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 106 static unsigned getModifiers(UINT message) { 107 return 0; // TODO 108 } 109 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 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 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 239 void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu) 240 { 241 } 242 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 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 301 void SkEvent::SignalNonEmptyQueue() 302 { 303 post_skwinevent(); 304 //SkDebugf("signal nonempty\n"); 305 } 306 307 static UINT_PTR gTimer; 308 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 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 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 373 void SkOSWindow::detachGL() { 374 wglMakeCurrent(GetDC((HWND)fHWND), 0); 375 wglDeleteContext((HGLRC)fHGLRC); 376 fHGLRC = NULL; 377 } 378 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 bool create_ANGLE(EGLNativeWindowType hWnd, 388 int msaaSampleCount, 389 EGLDisplay* eglDisplay, 390 EGLContext* eglContext, 391 EGLSurface* eglSurface, 392 EGLConfig* eglConfig) { 393 static const EGLint contextAttribs[] = { 394 EGL_CONTEXT_CLIENT_VERSION, 2, 395 EGL_NONE, EGL_NONE 396 }; 397 static const EGLint configAttribList[] = { 398 EGL_RED_SIZE, 8, 399 EGL_GREEN_SIZE, 8, 400 EGL_BLUE_SIZE, 8, 401 EGL_ALPHA_SIZE, 8, 402 EGL_DEPTH_SIZE, 8, 403 EGL_STENCIL_SIZE, 8, 404 EGL_NONE 405 }; 406 static const EGLint surfaceAttribList[] = { 407 EGL_NONE, EGL_NONE 408 }; 409 410 EGLDisplay display = eglGetDisplay(GetDC(hWnd)); 411 if (display == EGL_NO_DISPLAY ) { 412 return false; 413 } 414 415 // Initialize EGL 416 EGLint majorVersion, minorVersion; 417 if (!eglInitialize(display, &majorVersion, &minorVersion)) { 418 return false; 419 } 420 421 EGLint numConfigs; 422 if (!eglGetConfigs(display, NULL, 0, &numConfigs)) { 423 return false; 424 } 425 426 // Choose config 427 bool foundConfig = false; 428 if (msaaSampleCount) { 429 static const int kConfigAttribListCnt = 430 SK_ARRAY_COUNT(configAttribList); 431 EGLint msaaConfigAttribList[kConfigAttribListCnt + 4]; 432 memcpy(msaaConfigAttribList, 433 configAttribList, 434 sizeof(configAttribList)); 435 SkASSERT(EGL_NONE == msaaConfigAttribList[kConfigAttribListCnt - 1]); 436 msaaConfigAttribList[kConfigAttribListCnt - 1] = EGL_SAMPLE_BUFFERS; 437 msaaConfigAttribList[kConfigAttribListCnt + 0] = 1; 438 msaaConfigAttribList[kConfigAttribListCnt + 1] = EGL_SAMPLES; 439 msaaConfigAttribList[kConfigAttribListCnt + 2] = msaaSampleCount; 440 msaaConfigAttribList[kConfigAttribListCnt + 3] = EGL_NONE; 441 if (eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) { 442 SkASSERT(numConfigs > 0); 443 foundConfig = true; 444 } 445 } 446 if (!foundConfig) { 447 if (!eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) { 448 return false; 449 } 450 } 451 452 // Create a surface 453 EGLSurface surface = eglCreateWindowSurface(display, *eglConfig, 454 (EGLNativeWindowType)hWnd, 455 surfaceAttribList); 456 if (surface == EGL_NO_SURFACE) { 457 return false; 458 } 459 460 // Create a GL context 461 EGLContext context = eglCreateContext(display, *eglConfig, 462 EGL_NO_CONTEXT, 463 contextAttribs ); 464 if (context == EGL_NO_CONTEXT ) { 465 return false; 466 } 467 468 // Make the context current 469 if (!eglMakeCurrent(display, surface, surface, context)) { 470 return false; 471 } 472 473 *eglDisplay = display; 474 *eglContext = context; 475 *eglSurface = surface; 476 return true; 477 } 478 479 bool SkOSWindow::attachANGLE(int msaaSampleCount, AttachmentInfo* info) { 480 if (EGL_NO_DISPLAY == fDisplay) { 481 bool bResult = create_ANGLE((HWND)fHWND, 482 msaaSampleCount, 483 &fDisplay, 484 &fContext, 485 &fSurface, 486 &fConfig); 487 if (false == bResult) { 488 return false; 489 } 490 SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface()); 491 492 if (intf) { 493 ANGLE_GL_CALL(intf, ClearStencil(0)); 494 ANGLE_GL_CALL(intf, ClearColor(0, 0, 0, 0)); 495 ANGLE_GL_CALL(intf, StencilMask(0xffffffff)); 496 ANGLE_GL_CALL(intf, Clear(GL_STENCIL_BUFFER_BIT |GL_COLOR_BUFFER_BIT)); 497 } 498 } 499 if (eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { 500 eglGetConfigAttrib(fDisplay, fConfig, EGL_STENCIL_SIZE, &info->fStencilBits); 501 eglGetConfigAttrib(fDisplay, fConfig, EGL_SAMPLES, &info->fSampleCount); 502 503 SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface()); 504 505 if (intf ) { 506 ANGLE_GL_CALL(intf, Viewport(0, 0, 507 SkScalarRoundToInt(this->width()), 508 SkScalarRoundToInt(this->height()))); 509 } 510 return true; 511 } 512 return false; 513 } 514 515 void SkOSWindow::detachANGLE() { 516 eglMakeCurrent(fDisplay, EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT); 517 518 eglDestroyContext(fDisplay, fContext); 519 fContext = EGL_NO_CONTEXT; 520 521 eglDestroySurface(fDisplay, fSurface); 522 fSurface = EGL_NO_SURFACE; 523 524 eglTerminate(fDisplay); 525 fDisplay = EGL_NO_DISPLAY; 526 } 527 528 void SkOSWindow::presentANGLE() { 529 SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface()); 530 531 if (intf) { 532 ANGLE_GL_CALL(intf, Flush()); 533 } 534 535 eglSwapBuffers(fDisplay, fSurface); 536 } 537 #endif // SK_ANGLE 538 #endif // SK_SUPPORT_GPU 539 540 // return true on success 541 bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo* info) { 542 543 // attach doubles as "windowResize" so we need to allo 544 // already bound states to pass through again 545 // TODO: split out the resize functionality 546 // SkASSERT(kNone_BackEndType == fAttached); 547 bool result = true; 548 549 switch (attachType) { 550 case kNone_BackEndType: 551 // nothing to do 552 break; 553 #if SK_SUPPORT_GPU 554 case kNativeGL_BackEndType: 555 result = attachGL(msaaSampleCount, info); 556 break; 557 #if SK_ANGLE 558 case kANGLE_BackEndType: 559 result = attachANGLE(msaaSampleCount, info); 560 break; 561 #endif // SK_ANGLE 562 #endif // SK_SUPPORT_GPU 563 default: 564 SkASSERT(false); 565 result = false; 566 break; 567 } 568 569 if (result) { 570 fAttached = attachType; 571 } 572 573 return result; 574 } 575 576 void SkOSWindow::detach() { 577 switch (fAttached) { 578 case kNone_BackEndType: 579 // nothing to do 580 break; 581 #if SK_SUPPORT_GPU 582 case kNativeGL_BackEndType: 583 detachGL(); 584 break; 585 #if SK_ANGLE 586 case kANGLE_BackEndType: 587 detachANGLE(); 588 break; 589 #endif // SK_ANGLE 590 #endif // SK_SUPPORT_GPU 591 default: 592 SkASSERT(false); 593 break; 594 } 595 fAttached = kNone_BackEndType; 596 } 597 598 void SkOSWindow::present() { 599 switch (fAttached) { 600 case kNone_BackEndType: 601 // nothing to do 602 return; 603 #if SK_SUPPORT_GPU 604 case kNativeGL_BackEndType: 605 presentGL(); 606 break; 607 #if SK_ANGLE 608 case kANGLE_BackEndType: 609 presentANGLE(); 610 break; 611 #endif // SK_ANGLE 612 #endif // SK_SUPPORT_GPU 613 default: 614 SkASSERT(false); 615 break; 616 } 617 } 618 619 #endif 620