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