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 32 const int kDefaultWindowWidth = 500; 33 const 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 43 void post_skwinevent(HWND hwnd) 44 { 45 PostMessage(hwnd, WM_EVENT_CALLBACK, 0, 0); 46 } 47 48 SkTHashMap<void*, SkOSWindow*> SkOSWindow::gHwndToOSWindowMap; 49 50 SkOSWindow::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 70 SkOSWindow::~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 95 static 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 116 static unsigned getModifiers(UINT message) { 117 return 0; // TODO 118 } 119 120 bool 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 182 void 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 bitmap.lockPixels(); 211 int ret = SetDIBitsToDevice(hdc, 212 0, 0, 213 bitmap.width(), bitmap.height(), 214 0, 0, 215 0, bitmap.height(), 216 bitmap.getPixels(), 217 &bmi, 218 DIB_RGB_COLORS); 219 (void)ret; // we're ignoring potential failures for now. 220 bitmap.unlockPixels(); 221 } 222 } 223 224 void SkOSWindow::updateSize() 225 { 226 RECT r; 227 GetWindowRect((HWND)fHWND, &r); 228 this->resize(r.right - r.left, r.bottom - r.top); 229 } 230 231 void SkOSWindow::onHandleInval(const SkIRect& r) { 232 RECT rect; 233 rect.left = r.fLeft; 234 rect.top = r.fTop; 235 rect.right = r.fRight; 236 rect.bottom = r.fBottom; 237 InvalidateRect((HWND)fHWND, &rect, FALSE); 238 } 239 240 void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu) 241 { 242 } 243 244 void SkOSWindow::onSetTitle(const char title[]){ 245 SetWindowTextA((HWND)fHWND, title); 246 } 247 248 enum { 249 SK_MacReturnKey = 36, 250 SK_MacDeleteKey = 51, 251 SK_MacEndKey = 119, 252 SK_MacLeftKey = 123, 253 SK_MacRightKey = 124, 254 SK_MacDownKey = 125, 255 SK_MacUpKey = 126, 256 257 SK_Mac0Key = 0x52, 258 SK_Mac1Key = 0x53, 259 SK_Mac2Key = 0x54, 260 SK_Mac3Key = 0x55, 261 SK_Mac4Key = 0x56, 262 SK_Mac5Key = 0x57, 263 SK_Mac6Key = 0x58, 264 SK_Mac7Key = 0x59, 265 SK_Mac8Key = 0x5b, 266 SK_Mac9Key = 0x5c 267 }; 268 269 static SkKey raw2key(uint32_t raw) 270 { 271 static const struct { 272 uint32_t fRaw; 273 SkKey fKey; 274 } gKeys[] = { 275 { SK_MacUpKey, kUp_SkKey }, 276 { SK_MacDownKey, kDown_SkKey }, 277 { SK_MacLeftKey, kLeft_SkKey }, 278 { SK_MacRightKey, kRight_SkKey }, 279 { SK_MacReturnKey, kOK_SkKey }, 280 { SK_MacDeleteKey, kBack_SkKey }, 281 { SK_MacEndKey, kEnd_SkKey }, 282 { SK_Mac0Key, k0_SkKey }, 283 { SK_Mac1Key, k1_SkKey }, 284 { SK_Mac2Key, k2_SkKey }, 285 { SK_Mac3Key, k3_SkKey }, 286 { SK_Mac4Key, k4_SkKey }, 287 { SK_Mac5Key, k5_SkKey }, 288 { SK_Mac6Key, k6_SkKey }, 289 { SK_Mac7Key, k7_SkKey }, 290 { SK_Mac8Key, k8_SkKey }, 291 { SK_Mac9Key, k9_SkKey } 292 }; 293 294 for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++) 295 if (gKeys[i].fRaw == raw) 296 return gKeys[i].fKey; 297 return kNONE_SkKey; 298 } 299 300 /////////////////////////////////////////////////////////////////////////////////////// 301 302 void SkEvent::SignalNonEmptyQueue() 303 { 304 SkOSWindow::ForAllWindows([](void* hWND, SkOSWindow**) { 305 post_skwinevent((HWND)hWND); 306 }); 307 } 308 309 static UINT_PTR gTimer; 310 311 VOID CALLBACK sk_timer_proc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) 312 { 313 SkEvent::ServiceQueueTimer(); 314 //SkDebugf("timer task fired\n"); 315 } 316 317 void SkEvent::SignalQueueTimer(SkMSec delay) 318 { 319 if (gTimer) 320 { 321 KillTimer(NULL, gTimer); 322 gTimer = NULL; 323 } 324 if (delay) 325 { 326 gTimer = SetTimer(NULL, 0, delay, sk_timer_proc); 327 //SkDebugf("SetTimer of %d returned %d\n", delay, gTimer); 328 } 329 } 330 331 #if SK_SUPPORT_GPU 332 333 bool SkOSWindow::attachGL(int msaaSampleCount, bool deepColor, AttachmentInfo* info) { 334 HDC dc = GetDC((HWND)fHWND); 335 if (NULL == fHGLRC) { 336 fHGLRC = SkCreateWGLContext(dc, msaaSampleCount, deepColor, 337 kGLPreferCompatibilityProfile_SkWGLContextRequest); 338 if (NULL == fHGLRC) { 339 return false; 340 } 341 glClearStencil(0); 342 glClearColor(0, 0, 0, 0); 343 glStencilMask(0xffffffff); 344 glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); 345 } 346 if (wglMakeCurrent(dc, (HGLRC)fHGLRC)) { 347 // use DescribePixelFormat to get the stencil and color bit depth. 348 int pixelFormat = GetPixelFormat(dc); 349 PIXELFORMATDESCRIPTOR pfd; 350 DescribePixelFormat(dc, pixelFormat, sizeof(pfd), &pfd); 351 info->fStencilBits = pfd.cStencilBits; 352 // pfd.cColorBits includes alpha, so it will be 32 in 8/8/8/8 and 10/10/10/2 353 info->fColorBits = pfd.cRedBits + pfd.cGreenBits + pfd.cBlueBits; 354 355 // Get sample count if the MSAA WGL extension is present 356 SkWGLExtensions extensions; 357 if (extensions.hasExtension(dc, "WGL_ARB_multisample")) { 358 static const int kSampleCountAttr = SK_WGL_SAMPLES; 359 extensions.getPixelFormatAttribiv(dc, 360 pixelFormat, 361 0, 362 1, 363 &kSampleCountAttr, 364 &info->fSampleCount); 365 } else { 366 info->fSampleCount = 0; 367 } 368 369 glViewport(0, 0, 370 SkScalarRoundToInt(this->width()), 371 SkScalarRoundToInt(this->height())); 372 return true; 373 } 374 return false; 375 } 376 377 void SkOSWindow::detachGL() { 378 wglMakeCurrent(GetDC((HWND)fHWND), 0); 379 wglDeleteContext((HGLRC)fHGLRC); 380 fHGLRC = NULL; 381 } 382 383 void SkOSWindow::presentGL() { 384 HDC dc = GetDC((HWND)fHWND); 385 SwapBuffers(dc); 386 ReleaseDC((HWND)fHWND, dc); 387 } 388 389 #if SK_ANGLE 390 391 static void* get_angle_egl_display(void* nativeDisplay) { 392 PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT; 393 eglGetPlatformDisplayEXT = 394 (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); 395 396 // We expect ANGLE to support this extension 397 if (!eglGetPlatformDisplayEXT) { 398 return EGL_NO_DISPLAY; 399 } 400 401 EGLDisplay display = EGL_NO_DISPLAY; 402 // Try for an ANGLE D3D11 context, fall back to D3D9, and finally GL. 403 EGLint attribs[3][3] = { 404 { 405 EGL_PLATFORM_ANGLE_TYPE_ANGLE, 406 EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, 407 EGL_NONE 408 }, 409 { 410 EGL_PLATFORM_ANGLE_TYPE_ANGLE, 411 EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE, 412 EGL_NONE 413 }, 414 }; 415 for (int i = 0; i < 3 && display == EGL_NO_DISPLAY; ++i) { 416 display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,nativeDisplay, attribs[i]); 417 } 418 return display; 419 } 420 421 struct ANGLEAssembleContext { 422 ANGLEAssembleContext() { 423 fEGL = GetModuleHandle("libEGL.dll"); 424 fGL = GetModuleHandle("libGLESv2.dll"); 425 } 426 427 bool isValid() const { return SkToBool(fEGL) && SkToBool(fGL); } 428 429 HMODULE fEGL; 430 HMODULE fGL; 431 }; 432 433 static GrGLFuncPtr angle_get_gl_proc(void* ctx, const char name[]) { 434 const ANGLEAssembleContext& context = *reinterpret_cast<const ANGLEAssembleContext*>(ctx); 435 GrGLFuncPtr proc = (GrGLFuncPtr) GetProcAddress(context.fGL, name); 436 if (proc) { 437 return proc; 438 } 439 proc = (GrGLFuncPtr) GetProcAddress(context.fEGL, name); 440 if (proc) { 441 return proc; 442 } 443 return eglGetProcAddress(name); 444 } 445 446 static const GrGLInterface* get_angle_gl_interface() { 447 ANGLEAssembleContext context; 448 if (!context.isValid()) { 449 return nullptr; 450 } 451 return GrGLAssembleGLESInterface(&context, angle_get_gl_proc); 452 } 453 454 bool create_ANGLE(EGLNativeWindowType hWnd, 455 int msaaSampleCount, 456 EGLDisplay* eglDisplay, 457 EGLContext* eglContext, 458 EGLSurface* eglSurface, 459 EGLConfig* eglConfig) { 460 static const EGLint contextAttribs[] = { 461 EGL_CONTEXT_CLIENT_VERSION, 2, 462 EGL_NONE, EGL_NONE 463 }; 464 static const EGLint configAttribList[] = { 465 EGL_RED_SIZE, 8, 466 EGL_GREEN_SIZE, 8, 467 EGL_BLUE_SIZE, 8, 468 EGL_ALPHA_SIZE, 8, 469 EGL_DEPTH_SIZE, 8, 470 EGL_STENCIL_SIZE, 8, 471 EGL_NONE 472 }; 473 static const EGLint surfaceAttribList[] = { 474 EGL_NONE, EGL_NONE 475 }; 476 477 EGLDisplay display = get_angle_egl_display(GetDC(hWnd)); 478 479 if (EGL_NO_DISPLAY == display) { 480 SkDebugf("Could not create ANGLE egl display!\n"); 481 return false; 482 } 483 484 // Initialize EGL 485 EGLint majorVersion, minorVersion; 486 if (!eglInitialize(display, &majorVersion, &minorVersion)) { 487 return false; 488 } 489 490 EGLint numConfigs; 491 if (!eglGetConfigs(display, NULL, 0, &numConfigs)) { 492 return false; 493 } 494 495 // Choose config 496 bool foundConfig = false; 497 if (msaaSampleCount) { 498 static const int kConfigAttribListCnt = 499 SK_ARRAY_COUNT(configAttribList); 500 EGLint msaaConfigAttribList[kConfigAttribListCnt + 4]; 501 memcpy(msaaConfigAttribList, 502 configAttribList, 503 sizeof(configAttribList)); 504 SkASSERT(EGL_NONE == msaaConfigAttribList[kConfigAttribListCnt - 1]); 505 msaaConfigAttribList[kConfigAttribListCnt - 1] = EGL_SAMPLE_BUFFERS; 506 msaaConfigAttribList[kConfigAttribListCnt + 0] = 1; 507 msaaConfigAttribList[kConfigAttribListCnt + 1] = EGL_SAMPLES; 508 msaaConfigAttribList[kConfigAttribListCnt + 2] = msaaSampleCount; 509 msaaConfigAttribList[kConfigAttribListCnt + 3] = EGL_NONE; 510 if (eglChooseConfig(display, msaaConfigAttribList, eglConfig, 1, &numConfigs)) { 511 SkASSERT(numConfigs > 0); 512 foundConfig = true; 513 } 514 } 515 if (!foundConfig) { 516 if (!eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) { 517 return false; 518 } 519 } 520 521 // Create a surface 522 EGLSurface surface = eglCreateWindowSurface(display, *eglConfig, 523 (EGLNativeWindowType)hWnd, 524 surfaceAttribList); 525 if (surface == EGL_NO_SURFACE) { 526 return false; 527 } 528 529 // Create a GL context 530 EGLContext context = eglCreateContext(display, *eglConfig, 531 EGL_NO_CONTEXT, 532 contextAttribs ); 533 if (context == EGL_NO_CONTEXT ) { 534 return false; 535 } 536 537 // Make the context current 538 if (!eglMakeCurrent(display, surface, surface, context)) { 539 return false; 540 } 541 542 *eglDisplay = display; 543 *eglContext = context; 544 *eglSurface = surface; 545 return true; 546 } 547 548 bool SkOSWindow::attachANGLE(int msaaSampleCount, AttachmentInfo* info) { 549 if (EGL_NO_DISPLAY == fDisplay) { 550 bool bResult = create_ANGLE((HWND)fHWND, 551 msaaSampleCount, 552 &fDisplay, 553 &fContext, 554 &fSurface, 555 &fConfig); 556 if (false == bResult) { 557 return false; 558 } 559 fANGLEInterface.reset(get_angle_gl_interface()); 560 if (!fANGLEInterface) { 561 this->detachANGLE(); 562 return false; 563 } 564 GL_CALL(fANGLEInterface, ClearStencil(0)); 565 GL_CALL(fANGLEInterface, ClearColor(0, 0, 0, 0)); 566 GL_CALL(fANGLEInterface, StencilMask(0xffffffff)); 567 GL_CALL(fANGLEInterface, Clear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT)); 568 } 569 if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { 570 this->detachANGLE(); 571 return false; 572 } 573 eglGetConfigAttrib(fDisplay, fConfig, EGL_STENCIL_SIZE, &info->fStencilBits); 574 eglGetConfigAttrib(fDisplay, fConfig, EGL_SAMPLES, &info->fSampleCount); 575 576 GL_CALL(fANGLEInterface, Viewport(0, 0, SkScalarRoundToInt(this->width()), 577 SkScalarRoundToInt(this->height()))); 578 return true; 579 } 580 581 void SkOSWindow::detachANGLE() { 582 fANGLEInterface.reset(nullptr); 583 eglMakeCurrent(fDisplay, EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT); 584 585 eglDestroyContext(fDisplay, fContext); 586 fContext = EGL_NO_CONTEXT; 587 588 eglDestroySurface(fDisplay, fSurface); 589 fSurface = EGL_NO_SURFACE; 590 591 eglTerminate(fDisplay); 592 fDisplay = EGL_NO_DISPLAY; 593 } 594 595 void SkOSWindow::presentANGLE() { 596 GL_CALL(fANGLEInterface, Flush()); 597 598 eglSwapBuffers(fDisplay, fSurface); 599 } 600 #endif // SK_ANGLE 601 602 #endif // SK_SUPPORT_GPU 603 604 // return true on success 605 bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, bool deepColor, 606 AttachmentInfo* info) { 607 608 // attach doubles as "windowResize" so we need to allo 609 // already bound states to pass through again 610 // TODO: split out the resize functionality 611 // SkASSERT(kNone_BackEndType == fAttached); 612 bool result = true; 613 614 switch (attachType) { 615 case kNone_BackEndType: 616 // nothing to do 617 break; 618 #if SK_SUPPORT_GPU 619 case kNativeGL_BackEndType: 620 result = attachGL(msaaSampleCount, deepColor, info); 621 break; 622 #if SK_ANGLE 623 case kANGLE_BackEndType: 624 result = attachANGLE(msaaSampleCount, info); 625 break; 626 #endif // SK_ANGLE 627 #endif // SK_SUPPORT_GPU 628 default: 629 SkASSERT(false); 630 result = false; 631 break; 632 } 633 634 if (result) { 635 fAttached = attachType; 636 } 637 638 return result; 639 } 640 641 void SkOSWindow::release() { 642 switch (fAttached) { 643 case kNone_BackEndType: 644 // nothing to do 645 break; 646 #if SK_SUPPORT_GPU 647 case kNativeGL_BackEndType: 648 detachGL(); 649 break; 650 #if SK_ANGLE 651 case kANGLE_BackEndType: 652 detachANGLE(); 653 break; 654 #endif // SK_ANGLE 655 #endif // SK_SUPPORT_GPU 656 default: 657 SkASSERT(false); 658 break; 659 } 660 fAttached = kNone_BackEndType; 661 } 662 663 void SkOSWindow::present() { 664 switch (fAttached) { 665 case kNone_BackEndType: 666 // nothing to do 667 return; 668 #if SK_SUPPORT_GPU 669 case kNativeGL_BackEndType: 670 presentGL(); 671 break; 672 #if SK_ANGLE 673 case kANGLE_BackEndType: 674 presentANGLE(); 675 break; 676 #endif // SK_ANGLE 677 #endif // SK_SUPPORT_GPU 678 default: 679 SkASSERT(false); 680 break; 681 } 682 } 683 684 bool SkOSWindow::makeFullscreen() { 685 if (fFullscreen) { 686 return true; 687 } 688 #if SK_SUPPORT_GPU 689 if (fHGLRC) { 690 this->detachGL(); 691 } 692 #endif // SK_SUPPORT_GPU 693 // This is hacked together from various sources on the web. It can certainly be improved and be 694 // made more robust. 695 696 // Save current window/resolution information. We do this in case we ever implement switching 697 // back to windowed mode. 698 fSavedWindowState.fZoomed = SkToBool(IsZoomed((HWND)fHWND)); 699 if (fSavedWindowState.fZoomed) { 700 SendMessage((HWND)fHWND, WM_SYSCOMMAND, SC_RESTORE, 0); 701 } 702 fSavedWindowState.fStyle = GetWindowLong((HWND)fHWND, GWL_STYLE); 703 fSavedWindowState.fExStyle = GetWindowLong((HWND)fHWND, GWL_EXSTYLE); 704 GetWindowRect((HWND)fHWND, &fSavedWindowState.fRect); 705 DEVMODE currScreenSettings; 706 memset(&currScreenSettings,0,sizeof(currScreenSettings)); 707 currScreenSettings.dmSize = sizeof(currScreenSettings); 708 EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &currScreenSettings); 709 fSavedWindowState.fScreenWidth = currScreenSettings.dmPelsWidth; 710 fSavedWindowState.fScreenHeight = currScreenSettings.dmPelsHeight; 711 fSavedWindowState.fScreenBits = currScreenSettings.dmBitsPerPel; 712 fSavedWindowState.fHWND = fHWND; 713 714 // Try different sizes to find an allowed setting? Use ChangeDisplaySettingsEx? 715 static const int kWidth = 1280; 716 static const int kHeight = 1024; 717 DEVMODE newScreenSettings; 718 memset(&newScreenSettings, 0, sizeof(newScreenSettings)); 719 newScreenSettings.dmSize = sizeof(newScreenSettings); 720 newScreenSettings.dmPelsWidth = kWidth; 721 newScreenSettings.dmPelsHeight = kHeight; 722 newScreenSettings.dmBitsPerPel = 32; 723 newScreenSettings.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; 724 if (ChangeDisplaySettings(&newScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { 725 return false; 726 } 727 RECT WindowRect; 728 WindowRect.left = 0; 729 WindowRect.right = kWidth; 730 WindowRect.top = 0; 731 WindowRect.bottom = kHeight; 732 ShowCursor(FALSE); 733 AdjustWindowRectEx(&WindowRect, WS_POPUP, FALSE, WS_EX_APPWINDOW); 734 HWND fsHWND = CreateWindowEx( 735 WS_EX_APPWINDOW, 736 fWinInit.fClass, 737 NULL, 738 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP, 739 0, 0, WindowRect.right-WindowRect.left, WindowRect.bottom-WindowRect.top, 740 NULL, 741 NULL, 742 fWinInit.fInstance, 743 NULL 744 ); 745 if (!fsHWND) { 746 return false; 747 } 748 // Hide the old window and set the entry in the global mapping for this SkOSWindow to the 749 // new HWND. 750 ShowWindow((HWND)fHWND, SW_HIDE); 751 gHwndToOSWindowMap.remove(fHWND); 752 fHWND = fsHWND; 753 gHwndToOSWindowMap.set(fHWND, this); 754 this->updateSize(); 755 756 fFullscreen = true; 757 return true; 758 } 759 760 void SkOSWindow::setVsync(bool enable) { 761 SkWGLExtensions wgl; 762 wgl.swapInterval(enable ? 1 : 0); 763 } 764 765 void SkOSWindow::closeWindow() { 766 DestroyWindow((HWND)fHWND); 767 if (fFullscreen) { 768 DestroyWindow((HWND)fSavedWindowState.fHWND); 769 } 770 gHwndToOSWindowMap.remove(fHWND); 771 } 772 #endif 773