aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/sk_app/win
diff options
context:
space:
mode:
authorGravatar Brian Osman <brianosman@google.com>2017-11-21 13:18:02 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-11-21 18:37:19 +0000
commiteff04b5ec287e0fee0d44207c10d2d11f7eade8a (patch)
treeea2cf00ea329c81611536aaa9a9f1eca47c67e9a /tools/sk_app/win
parent2aa09dbe8aced37aa6bb285e62df45deb0e81650 (diff)
Remove SampleApp and convert HelloWorld to sk_app
There is still a large amount of views code that could be trimmed down, but which is used to implement samples (in viewer). Seemed simpler to remove some of this code in pieces. Bug: skia: Change-Id: Ia3415060d03c8de604a154e3dc38379b754daab6 Reviewed-on: https://skia-review.googlesource.com/72801 Commit-Queue: Brian Osman <brianosman@google.com> Reviewed-by: Jim Van Verth <jvanverth@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com>
Diffstat (limited to 'tools/sk_app/win')
-rw-r--r--tools/sk_app/win/ANGLEWindowContext_win.cpp177
-rw-r--r--tools/sk_app/win/GLWindowContext_win.cpp141
-rw-r--r--tools/sk_app/win/RasterWindowContext_win.cpp100
-rw-r--r--tools/sk_app/win/VulkanWindowContext_win.cpp79
-rw-r--r--tools/sk_app/win/WindowContextFactory_win.h33
-rw-r--r--tools/sk_app/win/Window_win.cpp393
-rw-r--r--tools/sk_app/win/Window_win.h44
-rw-r--r--tools/sk_app/win/main_win.cpp80
8 files changed, 1047 insertions, 0 deletions
diff --git a/tools/sk_app/win/ANGLEWindowContext_win.cpp b/tools/sk_app/win/ANGLEWindowContext_win.cpp
new file mode 100644
index 0000000000..bfdff5c6f4
--- /dev/null
+++ b/tools/sk_app/win/ANGLEWindowContext_win.cpp
@@ -0,0 +1,177 @@
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include "../GLWindowContext.h"
+#include "WindowContextFactory_win.h"
+#include "gl/GrGLAssembleInterface.h"
+#include "gl/GrGLDefines.h"
+
+using sk_app::GLWindowContext;
+using sk_app::DisplayParams;
+
+namespace {
+
+EGLDisplay get_angle_egl_display(HDC hdc) {
+ PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
+ eglGetPlatformDisplayEXT =
+ (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
+
+ // We expect ANGLE to support this extension
+ if (!eglGetPlatformDisplayEXT) {
+ return EGL_NO_DISPLAY;
+ }
+
+ // We currently only support D3D11 ANGLE.
+ static constexpr EGLint kType = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
+ static constexpr EGLint attribs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, kType, EGL_NONE};
+ return eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, hdc, attribs);
+}
+
+class ANGLEGLWindowContext_win : public GLWindowContext {
+public:
+ ANGLEGLWindowContext_win(HWND, const DisplayParams&);
+ ~ANGLEGLWindowContext_win() override;
+
+protected:
+ void onSwapBuffers() override;
+
+ sk_sp<const GrGLInterface> onInitializeContext() override;
+ void onDestroyContext() override;
+
+private:
+ HWND fHWND;
+ EGLDisplay fDisplay = EGL_NO_DISPLAY;
+ EGLContext fContext = EGL_NO_CONTEXT;
+ EGLSurface fSurface = EGL_NO_SURFACE;
+
+ typedef GLWindowContext INHERITED;
+};
+
+ANGLEGLWindowContext_win::ANGLEGLWindowContext_win(HWND wnd, const DisplayParams& params)
+ : INHERITED(params), fHWND(wnd) {
+ this->initializeContext();
+}
+
+ANGLEGLWindowContext_win::~ANGLEGLWindowContext_win() { this->destroyContext(); }
+
+sk_sp<const GrGLInterface> ANGLEGLWindowContext_win::onInitializeContext() {
+ HDC dc = GetDC(fHWND);
+ fDisplay = get_angle_egl_display(dc);
+ if (EGL_NO_DISPLAY == fDisplay) {
+ return nullptr;
+ }
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ if (!eglInitialize(fDisplay, &majorVersion, &minorVersion)) {
+ SkDebugf("Could not initialize display!\n");
+ return nullptr;
+ }
+ EGLint numConfigs;
+ fSampleCount = this->getDisplayParams().fMSAASampleCount;
+ const int sampleBuffers = fSampleCount > 0 ? 1 : 0;
+ const EGLint configAttribs[] = {EGL_RENDERABLE_TYPE,
+ // We currently only support ES3.
+ EGL_OPENGL_ES3_BIT,
+ EGL_RED_SIZE,
+ 8,
+ EGL_GREEN_SIZE,
+ 8,
+ EGL_BLUE_SIZE,
+ 8,
+ EGL_ALPHA_SIZE,
+ 8,
+ EGL_SAMPLE_BUFFERS,
+ sampleBuffers,
+ EGL_SAMPLES,
+ fSampleCount,
+ EGL_NONE};
+
+ EGLConfig surfaceConfig;
+ if (!eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs)) {
+ SkDebugf("Could not create choose config!\n");
+ return nullptr;
+ }
+ // We currently only support ES3.
+ const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
+ fContext = eglCreateContext(fDisplay, surfaceConfig, nullptr, contextAttribs);
+ if (EGL_NO_CONTEXT == fContext) {
+ SkDebugf("Could not create context!\n");
+ return nullptr;
+ }
+ fSurface = eglCreateWindowSurface(fDisplay, surfaceConfig, fHWND, nullptr);
+ if (EGL_NO_SURFACE == fSurface) {
+ SkDebugf("Could not create surface!\n");
+ return nullptr;
+ }
+ if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
+ SkDebugf("Could not make contxt current!\n");
+ return nullptr;
+ }
+
+ sk_sp<const GrGLInterface> interface(GrGLAssembleInterface(
+ nullptr,
+ [](void* ctx, const char name[]) -> GrGLFuncPtr { return eglGetProcAddress(name); }));
+ if (interface) {
+ interface->fFunctions.fClearStencil(0);
+ interface->fFunctions.fClearColor(0, 0, 0, 0);
+ interface->fFunctions.fStencilMask(0xffffffff);
+ interface->fFunctions.fClear(GR_GL_STENCIL_BUFFER_BIT | GR_GL_COLOR_BUFFER_BIT);
+
+ // use DescribePixelFormat to get the stencil depth.
+ int pixelFormat = GetPixelFormat(dc);
+ PIXELFORMATDESCRIPTOR pfd;
+ DescribePixelFormat(dc, pixelFormat, sizeof(pfd), &pfd);
+ fStencilBits = pfd.cStencilBits;
+
+ RECT rect;
+ GetClientRect(fHWND, &rect);
+ fWidth = rect.right - rect.left;
+ fHeight = rect.bottom - rect.top;
+ interface->fFunctions.fViewport(0, 0, fWidth, fHeight);
+ }
+ return interface;
+}
+
+void ANGLEGLWindowContext_win::onDestroyContext() {
+ eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ if (EGL_NO_CONTEXT != fContext) {
+ eglDestroyContext(fDisplay, fContext);
+ }
+ if (EGL_NO_SURFACE != fSurface) {
+ eglDestroySurface(fDisplay, fSurface);
+ }
+ if (EGL_NO_DISPLAY != fDisplay) {
+ eglTerminate(fDisplay);
+ }
+}
+
+void ANGLEGLWindowContext_win::onSwapBuffers() {
+ if (!eglSwapBuffers(fDisplay, fSurface)) {
+ SkDebugf("Could not complete eglSwapBuffers.\n");
+ }
+}
+
+} // anonymous namespace
+
+namespace sk_app {
+namespace window_context_factory {
+
+WindowContext* NewANGLEForWin(HWND wnd, const DisplayParams& params) {
+ ANGLEGLWindowContext_win* ctx = new ANGLEGLWindowContext_win(wnd, params);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+}
+
+} // namespace window_context_factory
+} // namespace sk_app
diff --git a/tools/sk_app/win/GLWindowContext_win.cpp b/tools/sk_app/win/GLWindowContext_win.cpp
new file mode 100644
index 0000000000..17a6b32962
--- /dev/null
+++ b/tools/sk_app/win/GLWindowContext_win.cpp
@@ -0,0 +1,141 @@
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Windows.h>
+#include <GL/gl.h>
+#include "../GLWindowContext.h"
+#include "GrGLInterface.h"
+#include "WindowContextFactory_win.h"
+#include "win/SkWGL.h"
+
+using sk_app::GLWindowContext;
+using sk_app::DisplayParams;
+
+namespace {
+
+class GLWindowContext_win : public GLWindowContext {
+public:
+ GLWindowContext_win(HWND, const DisplayParams&);
+ ~GLWindowContext_win() override;
+
+protected:
+ void onSwapBuffers() override;
+
+ sk_sp<const GrGLInterface> onInitializeContext() override;
+ void onDestroyContext() override;
+
+private:
+ HWND fHWND;
+ HGLRC fHGLRC;
+
+ typedef GLWindowContext INHERITED;
+};
+
+GLWindowContext_win::GLWindowContext_win(HWND wnd, const DisplayParams& params)
+ : INHERITED(params)
+ , fHWND(wnd)
+ , fHGLRC(NULL) {
+
+ // any config code here (particularly for msaa)?
+
+ this->initializeContext();
+}
+
+GLWindowContext_win::~GLWindowContext_win() {
+ this->destroyContext();
+}
+
+sk_sp<const GrGLInterface> GLWindowContext_win::onInitializeContext() {
+ HDC dc = GetDC(fHWND);
+
+ fHGLRC = SkCreateWGLContext(dc, fDisplayParams.fMSAASampleCount, false /* deepColor */,
+ kGLPreferCompatibilityProfile_SkWGLContextRequest);
+ if (NULL == fHGLRC) {
+ return nullptr;
+ }
+
+ // Look to see if RenderDoc is attached. If so, re-create the context with a core profile
+ if (wglMakeCurrent(dc, fHGLRC)) {
+ const GrGLInterface* glInterface = GrGLCreateNativeInterface();
+ bool renderDocAttached = glInterface->hasExtension("GL_EXT_debug_tool");
+ SkSafeUnref(glInterface);
+ if (renderDocAttached) {
+ wglDeleteContext(fHGLRC);
+ fHGLRC = SkCreateWGLContext(dc, fDisplayParams.fMSAASampleCount, false /* deepColor */,
+ kGLPreferCoreProfile_SkWGLContextRequest);
+ if (NULL == fHGLRC) {
+ return nullptr;
+ }
+ }
+ }
+
+ if (wglMakeCurrent(dc, fHGLRC)) {
+ glClearStencil(0);
+ glClearColor(0, 0, 0, 0);
+ glStencilMask(0xffffffff);
+ glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+ // use DescribePixelFormat to get the stencil and color bit depth.
+ int pixelFormat = GetPixelFormat(dc);
+ PIXELFORMATDESCRIPTOR pfd;
+ DescribePixelFormat(dc, pixelFormat, sizeof(pfd), &pfd);
+ fStencilBits = pfd.cStencilBits;
+
+ // Get sample count if the MSAA WGL extension is present
+ SkWGLExtensions extensions;
+ if (extensions.hasExtension(dc, "WGL_ARB_multisample")) {
+ static const int kSampleCountAttr = SK_WGL_SAMPLES;
+ extensions.getPixelFormatAttribiv(dc,
+ pixelFormat,
+ 0,
+ 1,
+ &kSampleCountAttr,
+ &fSampleCount);
+ } else {
+ fSampleCount = 0;
+ }
+
+ RECT rect;
+ GetClientRect(fHWND, &rect);
+ fWidth = rect.right - rect.left;
+ fHeight = rect.bottom - rect.top;
+ glViewport(0, 0, fWidth, fHeight);
+ }
+ return sk_sp<const GrGLInterface>(GrGLCreateNativeInterface());
+}
+
+
+void GLWindowContext_win::onDestroyContext() {
+ wglDeleteContext(fHGLRC);
+ fHGLRC = NULL;
+}
+
+
+void GLWindowContext_win::onSwapBuffers() {
+ HDC dc = GetDC((HWND)fHWND);
+ SwapBuffers(dc);
+ ReleaseDC((HWND)fHWND, dc);
+}
+
+
+} // anonymous namespace
+
+namespace sk_app {
+namespace window_context_factory {
+
+WindowContext* NewGLForWin(HWND wnd, const DisplayParams& params) {
+ GLWindowContext_win* ctx = new GLWindowContext_win(wnd, params);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+}
+
+} // namespace window_context_factory
+} // namespace sk_app
diff --git a/tools/sk_app/win/RasterWindowContext_win.cpp b/tools/sk_app/win/RasterWindowContext_win.cpp
new file mode 100644
index 0000000000..85bb65e674
--- /dev/null
+++ b/tools/sk_app/win/RasterWindowContext_win.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "../RasterWindowContext.h"
+#include "SkAutoMalloc.h"
+#include "SkSurface.h"
+#include "WindowContextFactory_win.h"
+
+#include <Windows.h>
+
+using sk_app::RasterWindowContext;
+using sk_app::DisplayParams;
+
+namespace {
+
+class RasterWindowContext_win : public RasterWindowContext {
+public:
+ RasterWindowContext_win(HWND, const DisplayParams&);
+
+ sk_sp<SkSurface> getBackbufferSurface() override;
+ void swapBuffers() override;
+ bool isValid() override { return SkToBool(fWnd); }
+ void resize(int w, int h) override;
+ void setDisplayParams(const DisplayParams& params) override;
+
+protected:
+ SkAutoMalloc fSurfaceMemory;
+ sk_sp<SkSurface> fBackbufferSurface;
+ HWND fWnd;
+
+private:
+ typedef RasterWindowContext INHERITED;
+};
+
+RasterWindowContext_win::RasterWindowContext_win(HWND wnd, const DisplayParams& params)
+ : INHERITED(params)
+ , fWnd(wnd) {
+ RECT rect;
+ GetWindowRect(wnd, &rect);
+ this->resize(rect.right - rect.left, rect.bottom - rect.top);
+}
+
+void RasterWindowContext_win::setDisplayParams(const DisplayParams& params) {
+ fDisplayParams = params;
+ RECT rect;
+ GetWindowRect(fWnd, &rect);
+ this->resize(rect.right - rect.left, rect.bottom - rect.top);
+}
+
+void RasterWindowContext_win::resize(int w, int h) {
+ fWidth = w;
+ fHeight = h;
+ fBackbufferSurface.reset();
+ const size_t bmpSize = sizeof(BITMAPINFOHEADER) + w * h * sizeof(uint32_t);
+ fSurfaceMemory.reset(bmpSize);
+ BITMAPINFO* bmpInfo = reinterpret_cast<BITMAPINFO*>(fSurfaceMemory.get());
+ ZeroMemory(bmpInfo, sizeof(BITMAPINFO));
+ bmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmpInfo->bmiHeader.biWidth = w;
+ bmpInfo->bmiHeader.biHeight = -h; // negative means top-down bitmap. Skia draws top-down.
+ bmpInfo->bmiHeader.biPlanes = 1;
+ bmpInfo->bmiHeader.biBitCount = 32;
+ bmpInfo->bmiHeader.biCompression = BI_RGB;
+ void* pixels = bmpInfo->bmiColors;
+
+ SkImageInfo info = SkImageInfo::Make(w, h, fDisplayParams.fColorType, kPremul_SkAlphaType,
+ fDisplayParams.fColorSpace);
+ fBackbufferSurface = SkSurface::MakeRasterDirect(info, pixels, sizeof(uint32_t) * w);
+}
+
+sk_sp<SkSurface> RasterWindowContext_win::getBackbufferSurface() { return fBackbufferSurface; }
+
+void RasterWindowContext_win::swapBuffers() {
+ BITMAPINFO* bmpInfo = reinterpret_cast<BITMAPINFO*>(fSurfaceMemory.get());
+ HDC dc = GetDC(fWnd);
+ StretchDIBits(dc, 0, 0, fWidth, fHeight, 0, 0, fWidth, fHeight, bmpInfo->bmiColors, bmpInfo,
+ DIB_RGB_COLORS, SRCCOPY);
+ ReleaseDC(fWnd, dc);
+}
+
+} // anonymous namespace
+
+namespace sk_app {
+namespace window_context_factory {
+
+WindowContext* NewRasterForWin(HWND wnd, const DisplayParams& params) {
+ WindowContext* ctx = new RasterWindowContext_win(wnd, params);
+ if (!ctx->isValid()) {
+ delete ctx;
+ ctx = nullptr;
+ }
+ return ctx;
+}
+
+} // namespace window_context_factory
+} // namespace sk_app
diff --git a/tools/sk_app/win/VulkanWindowContext_win.cpp b/tools/sk_app/win/VulkanWindowContext_win.cpp
new file mode 100644
index 0000000000..16c527cba0
--- /dev/null
+++ b/tools/sk_app/win/VulkanWindowContext_win.cpp
@@ -0,0 +1,79 @@
+
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Windows.h>
+#include "WindowContextFactory_win.h"
+
+#include "../VulkanWindowContext.h"
+#include "Window_win.h"
+
+#include "vk/GrVkInterface.h"
+#include "vk/GrVkUtil.h"
+
+#include "vk/VkTestUtils.h"
+
+namespace sk_app {
+namespace window_context_factory {
+
+WindowContext* NewVulkanForWin(HWND hwnd, const DisplayParams& params) {
+ PFN_vkGetInstanceProcAddr instProc;
+ PFN_vkGetDeviceProcAddr devProc;
+ if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc, &devProc)) {
+ return nullptr;
+ }
+
+ auto createVkSurface = [hwnd, instProc] (VkInstance instance) -> VkSurfaceKHR {
+ static PFN_vkCreateWin32SurfaceKHR createWin32SurfaceKHR = nullptr;
+ if (!createWin32SurfaceKHR) {
+ createWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)
+ instProc(instance, "vkCreateWin32SurfaceKHR");
+ }
+ HINSTANCE hinstance = GetModuleHandle(0);
+ VkSurfaceKHR surface;
+
+ VkWin32SurfaceCreateInfoKHR surfaceCreateInfo;
+ memset(&surfaceCreateInfo, 0, sizeof(VkWin32SurfaceCreateInfoKHR));
+ surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
+ surfaceCreateInfo.pNext = nullptr;
+ surfaceCreateInfo.flags = 0;
+ surfaceCreateInfo.hinstance = hinstance;
+ surfaceCreateInfo.hwnd = hwnd;
+
+ VkResult res = createWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
+ if (VK_SUCCESS != res) {
+ return VK_NULL_HANDLE;
+ }
+
+ return surface;
+ };
+
+ auto canPresent = [instProc] (VkInstance instance, VkPhysicalDevice physDev,
+ uint32_t queueFamilyIndex) {
+ static PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR
+ getPhysicalDeviceWin32PresentationSupportKHR = nullptr;
+ if (!getPhysicalDeviceWin32PresentationSupportKHR) {
+ getPhysicalDeviceWin32PresentationSupportKHR =
+ (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)
+ instProc(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR");
+ }
+
+ VkBool32 check = getPhysicalDeviceWin32PresentationSupportKHR(physDev, queueFamilyIndex);
+ return (VK_FALSE != check);
+ };
+
+ WindowContext* ctx = new VulkanWindowContext(params, createVkSurface, canPresent,
+ instProc, devProc);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+}
+
+} // namespace window_context_factory
+} // namespace sk_app
diff --git a/tools/sk_app/win/WindowContextFactory_win.h b/tools/sk_app/win/WindowContextFactory_win.h
new file mode 100644
index 0000000000..959b529f49
--- /dev/null
+++ b/tools/sk_app/win/WindowContextFactory_win.h
@@ -0,0 +1,33 @@
+
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef WindowContextFactory_win_DEFINED
+#define WindowContextFactory_win_DEFINED
+
+#include <Windows.h>
+
+namespace sk_app {
+
+class WindowContext;
+struct DisplayParams;
+
+namespace window_context_factory {
+
+WindowContext* NewVulkanForWin(HWND, const DisplayParams&);
+
+WindowContext* NewGLForWin(HWND, const DisplayParams&);
+
+WindowContext* NewANGLEForWin(HWND, const DisplayParams&);
+
+WindowContext* NewRasterForWin(HWND, const DisplayParams&);
+
+} // namespace window_context_factory
+
+} // namespace sk_app
+
+#endif
diff --git a/tools/sk_app/win/Window_win.cpp b/tools/sk_app/win/Window_win.cpp
new file mode 100644
index 0000000000..10db0ec675
--- /dev/null
+++ b/tools/sk_app/win/Window_win.cpp
@@ -0,0 +1,393 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#include "Window_win.h"
+
+#include <tchar.h>
+#include <windows.h>
+#include <windowsx.h>
+
+#include "SkUtils.h"
+#include "../WindowContext.h"
+#include "WindowContextFactory_win.h"
+#ifdef SK_VULKAN
+#include "../VulkanWindowContext.h"
+#endif
+
+namespace sk_app {
+
+static int gWindowX = CW_USEDEFAULT;
+static int gWindowY = 0;
+static int gWindowWidth = CW_USEDEFAULT;
+static int gWindowHeight = 0;
+
+Window* Window::CreateNativeWindow(void* platformData) {
+ HINSTANCE hInstance = (HINSTANCE)platformData;
+
+ Window_win* window = new Window_win();
+ if (!window->init(hInstance)) {
+ delete window;
+ return nullptr;
+ }
+
+ return window;
+}
+
+void Window_win::closeWindow() {
+ RECT r;
+ if (GetWindowRect(fHWnd, &r)) {
+ gWindowX = r.left;
+ gWindowY = r.top;
+ gWindowWidth = r.right - r.left;
+ gWindowHeight = r.bottom - r.top;
+ }
+ DestroyWindow(fHWnd);
+}
+
+Window_win::~Window_win() {
+ this->closeWindow();
+}
+
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+
+bool Window_win::init(HINSTANCE hInstance) {
+ fHInstance = hInstance ? hInstance : GetModuleHandle(nullptr);
+
+ // The main window class name
+ static const TCHAR gSZWindowClass[] = _T("SkiaApp");
+
+ static WNDCLASSEX wcex;
+ static bool wcexInit = false;
+ if (!wcexInit) {
+ wcex.cbSize = sizeof(WNDCLASSEX);
+
+ wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+ wcex.lpfnWndProc = WndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = fHInstance;
+ wcex.hIcon = LoadIcon(fHInstance, (LPCTSTR)IDI_WINLOGO);
+ wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);;
+ wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ wcex.lpszMenuName = nullptr;
+ wcex.lpszClassName = gSZWindowClass;
+ wcex.hIconSm = LoadIcon(fHInstance, (LPCTSTR)IDI_WINLOGO);;
+
+ if (!RegisterClassEx(&wcex)) {
+ return false;
+ }
+ wcexInit = true;
+ }
+
+ /*
+ if (fullscreen)
+ {
+ DEVMODE dmScreenSettings;
+ // If full screen set the screen to maximum size of the users desktop and 32bit.
+ memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
+ dmScreenSettings.dmSize = sizeof(dmScreenSettings);
+ dmScreenSettings.dmPelsWidth = (unsigned long)width;
+ dmScreenSettings.dmPelsHeight = (unsigned long)height;
+ dmScreenSettings.dmBitsPerPel = 32;
+ dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
+
+ // Change the display settings to full screen.
+ ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
+
+ // Set the position of the window to the top left corner.
+ posX = posY = 0;
+ }
+ */
+ // gIsFullscreen = fullscreen;
+
+ fHWnd = CreateWindow(gSZWindowClass, nullptr, WS_OVERLAPPEDWINDOW,
+ gWindowX, gWindowY, gWindowWidth, gWindowHeight,
+ nullptr, nullptr, fHInstance, nullptr);
+ if (!fHWnd)
+ {
+ return false;
+ }
+
+ SetWindowLongPtr(fHWnd, GWLP_USERDATA, (LONG_PTR)this);
+ RegisterTouchWindow(fHWnd, 0);
+
+ return true;
+}
+
+static Window::Key get_key(WPARAM vk) {
+ static const struct {
+ WPARAM fVK;
+ Window::Key fKey;
+ } gPair[] = {
+ { VK_BACK, Window::Key::kBack },
+ { VK_CLEAR, Window::Key::kBack },
+ { VK_RETURN, Window::Key::kOK },
+ { VK_UP, Window::Key::kUp },
+ { VK_DOWN, Window::Key::kDown },
+ { VK_LEFT, Window::Key::kLeft },
+ { VK_RIGHT, Window::Key::kRight },
+ { VK_TAB, Window::Key::kTab },
+ { VK_PRIOR, Window::Key::kPageUp },
+ { VK_NEXT, Window::Key::kPageDown },
+ { VK_HOME, Window::Key::kHome },
+ { VK_END, Window::Key::kEnd },
+ { VK_DELETE, Window::Key::kDelete },
+ { VK_ESCAPE, Window::Key::kEscape },
+ { VK_SHIFT, Window::Key::kShift },
+ { VK_CONTROL, Window::Key::kCtrl },
+ { VK_MENU, Window::Key::kOption },
+ { 'A', Window::Key::kA },
+ { 'C', Window::Key::kC },
+ { 'V', Window::Key::kV },
+ { 'X', Window::Key::kX },
+ { 'Y', Window::Key::kY },
+ { 'Z', Window::Key::kZ },
+ };
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
+ if (gPair[i].fVK == vk) {
+ return gPair[i].fKey;
+ }
+ }
+ return Window::Key::kNONE;
+}
+
+static uint32_t get_modifiers(UINT message, WPARAM wParam, LPARAM lParam) {
+ uint32_t modifiers = 0;
+
+ switch (message) {
+ case WM_UNICHAR:
+ case WM_CHAR:
+ if (0 == (lParam & (1 << 30))) {
+ modifiers |= Window::kFirstPress_ModifierKey;
+ }
+ if (lParam & (1 << 29)) {
+ modifiers |= Window::kOption_ModifierKey;
+ }
+ break;
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ if (0 == (lParam & (1 << 30))) {
+ modifiers |= Window::kFirstPress_ModifierKey;
+ }
+ if (lParam & (1 << 29)) {
+ modifiers |= Window::kOption_ModifierKey;
+ }
+ break;
+
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ if (lParam & (1 << 29)) {
+ modifiers |= Window::kOption_ModifierKey;
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MOUSEMOVE:
+ case WM_MOUSEWHEEL:
+ if (wParam & MK_CONTROL) {
+ modifiers |= Window::kControl_ModifierKey;
+ }
+ if (wParam & MK_SHIFT) {
+ modifiers |= Window::kShift_ModifierKey;
+ }
+ break;
+ }
+
+ return modifiers;
+}
+
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ Window_win* window = (Window_win*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
+
+ bool eventHandled = false;
+
+ switch (message) {
+ case WM_PAINT:
+ hdc = BeginPaint(hWnd, &ps);
+ window->onPaint();
+ EndPaint(hWnd, &ps);
+ eventHandled = true;
+ break;
+
+ case WM_CLOSE:
+ PostQuitMessage(0);
+ eventHandled = true;
+ break;
+
+ case WM_ACTIVATE:
+ // disable/enable rendering here, depending on wParam != WA_INACTIVE
+ break;
+
+ case WM_SIZE:
+ window->onResize(LOWORD(lParam), HIWORD(lParam));
+ eventHandled = true;
+ break;
+
+ case WM_UNICHAR:
+ eventHandled = window->onChar((SkUnichar)wParam,
+ get_modifiers(message, wParam, lParam));
+ break;
+
+ case WM_CHAR: {
+ const uint16_t* c = reinterpret_cast<uint16_t*>(&wParam);
+ eventHandled = window->onChar(SkUTF16_NextUnichar(&c),
+ get_modifiers(message, wParam, lParam));
+ } break;
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ eventHandled = window->onKey(get_key(wParam), Window::kDown_InputState,
+ get_modifiers(message, wParam, lParam));
+ break;
+
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ eventHandled = window->onKey(get_key(wParam), Window::kUp_InputState,
+ get_modifiers(message, wParam, lParam));
+ break;
+
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP: {
+ int xPos = GET_X_LPARAM(lParam);
+ int yPos = GET_Y_LPARAM(lParam);
+
+ //if (!gIsFullscreen)
+ //{
+ // RECT rc = { 0, 0, 640, 480 };
+ // AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
+ // xPos -= rc.left;
+ // yPos -= rc.top;
+ //}
+
+ Window::InputState istate = ((wParam & MK_LBUTTON) != 0) ? Window::kDown_InputState
+ : Window::kUp_InputState;
+
+ eventHandled = window->onMouse(xPos, yPos, istate,
+ get_modifiers(message, wParam, lParam));
+ } break;
+
+ case WM_MOUSEMOVE: {
+ int xPos = GET_X_LPARAM(lParam);
+ int yPos = GET_Y_LPARAM(lParam);
+
+ //if (!gIsFullscreen)
+ //{
+ // RECT rc = { 0, 0, 640, 480 };
+ // AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
+ // xPos -= rc.left;
+ // yPos -= rc.top;
+ //}
+
+ eventHandled = window->onMouse(xPos, yPos, Window::kMove_InputState,
+ get_modifiers(message, wParam, lParam));
+ } break;
+
+ case WM_MOUSEWHEEL:
+ eventHandled = window->onMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f,
+ get_modifiers(message, wParam, lParam));
+ break;
+
+ case WM_TOUCH: {
+ uint16_t numInputs = LOWORD(wParam);
+ std::unique_ptr<TOUCHINPUT[]> inputs(new TOUCHINPUT[numInputs]);
+ if (GetTouchInputInfo((HTOUCHINPUT)lParam, numInputs, inputs.get(),
+ sizeof(TOUCHINPUT))) {
+ RECT rect;
+ GetClientRect(hWnd, &rect);
+ for (uint16_t i = 0; i < numInputs; ++i) {
+ TOUCHINPUT ti = inputs[i];
+ Window::InputState state;
+ if (ti.dwFlags & TOUCHEVENTF_DOWN) {
+ state = Window::kDown_InputState;
+ } else if (ti.dwFlags & TOUCHEVENTF_MOVE) {
+ state = Window::kMove_InputState;
+ } else if (ti.dwFlags & TOUCHEVENTF_UP) {
+ state = Window::kUp_InputState;
+ } else {
+ continue;
+ }
+ // TOUCHINPUT coordinates are in 100ths of pixels
+ // Adjust for that, and make them window relative
+ LONG tx = (ti.x / 100) - rect.left;
+ LONG ty = (ti.y / 100) - rect.top;
+ eventHandled = window->onTouch(ti.dwID, state, tx, ty) || eventHandled;
+ }
+ }
+ } break;
+
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+
+ return eventHandled ? 0 : 1;
+}
+
+void Window_win::setTitle(const char* title) {
+ SetWindowTextA(fHWnd, title);
+}
+
+void Window_win::show() {
+ ShowWindow(fHWnd, SW_SHOW);
+}
+
+
+bool Window_win::attach(BackendType attachType) {
+ fBackend = attachType;
+
+ switch (attachType) {
+ case kNativeGL_BackendType:
+ fWindowContext = window_context_factory::NewGLForWin(fHWnd, fRequestedDisplayParams);
+ break;
+#if SK_ANGLE
+ case kANGLE_BackendType:
+ fWindowContext = window_context_factory::NewANGLEForWin(fHWnd, fRequestedDisplayParams);
+ break;
+#endif
+ case kRaster_BackendType:
+ fWindowContext = window_context_factory::NewRasterForWin(fHWnd,
+ fRequestedDisplayParams);
+ break;
+#ifdef SK_VULKAN
+ case kVulkan_BackendType:
+ fWindowContext = window_context_factory::NewVulkanForWin(fHWnd,
+ fRequestedDisplayParams);
+ break;
+#endif
+ }
+ this->onBackendCreated();
+
+ return (SkToBool(fWindowContext));
+}
+
+void Window_win::onInval() {
+ InvalidateRect(fHWnd, nullptr, false);
+}
+
+void Window_win::setRequestedDisplayParams(const DisplayParams& params, bool allowReattach) {
+ // GL on Windows doesn't let us change MSAA after the window is created
+ if (params.fMSAASampleCount != this->getRequestedDisplayParams().fMSAASampleCount
+ && allowReattach) {
+ // Need to change these early, so attach() creates the window context correctly
+ fRequestedDisplayParams = params;
+
+ delete fWindowContext;
+ this->closeWindow();
+ this->init(fHInstance);
+ this->attach(fBackend);
+ }
+
+ INHERITED::setRequestedDisplayParams(params, allowReattach);
+}
+
+} // namespace sk_app
diff --git a/tools/sk_app/win/Window_win.h b/tools/sk_app/win/Window_win.h
new file mode 100644
index 0000000000..139ab874c6
--- /dev/null
+++ b/tools/sk_app/win/Window_win.h
@@ -0,0 +1,44 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#ifndef Window_win_DEFINED
+#define Window_win_DEFINED
+
+#include <windows.h>
+#include "../Window.h"
+
+namespace sk_app {
+
+class Window_win : public Window {
+public:
+ Window_win() : Window() {}
+ ~Window_win() override;
+
+ bool init(HINSTANCE instance);
+
+ void setTitle(const char*) override;
+ void show() override;
+
+ bool attach(BackendType) override;
+
+ void onInval() override;
+
+ void setRequestedDisplayParams(const DisplayParams&, bool allowReattach) override;
+
+private:
+ void closeWindow();
+
+ HINSTANCE fHInstance;
+ HWND fHWnd;
+ BackendType fBackend;
+
+ typedef Window INHERITED;
+};
+
+} // namespace sk_app
+
+#endif
diff --git a/tools/sk_app/win/main_win.cpp b/tools/sk_app/win/main_win.cpp
new file mode 100644
index 0000000000..4800258973
--- /dev/null
+++ b/tools/sk_app/win/main_win.cpp
@@ -0,0 +1,80 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#include <windows.h>
+#include <tchar.h>
+
+#include "SkTypes.h"
+#include "Timer.h"
+#include "Window_win.h"
+#include "../Application.h"
+
+using sk_app::Application;
+
+static char* tchar_to_utf8(const TCHAR* str) {
+#ifdef _UNICODE
+ int size = WideCharToMultiByte(CP_UTF8, 0, str, wcslen(str), NULL, 0, NULL, NULL);
+ char* str8 = (char*)sk_malloc_throw(size + 1);
+ WideCharToMultiByte(CP_UTF8, 0, str, wcslen(str), str8, size, NULL, NULL);
+ str8[size] = '\0';
+ return str8;
+#else
+ return _strdup(str);
+#endif
+}
+
+// This file can work with GUI or CONSOLE subsystem types since we define _tWinMain and main().
+
+static int main_common(HINSTANCE hInstance, int show, int argc, char**argv);
+
+int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine,
+ int nCmdShow) {
+
+ // convert from lpCmdLine to argc, argv.
+ char* argv[4096];
+ int argc = 0;
+ TCHAR exename[1024], *next;
+ int exenameLen = GetModuleFileName(NULL, exename, SK_ARRAY_COUNT(exename));
+ // we're ignoring the possibility that the exe name exceeds the exename buffer
+ (void)exenameLen;
+ argv[argc++] = tchar_to_utf8(exename);
+ TCHAR* arg = _tcstok_s(lpCmdLine, _T(" "), &next);
+ while (arg != NULL) {
+ argv[argc++] = tchar_to_utf8(arg);
+ arg = _tcstok_s(NULL, _T(" "), &next);
+ }
+ int result = main_common(hInstance, nCmdShow, argc, argv);
+ for (int i = 0; i < argc; ++i) {
+ sk_free(argv[i]);
+ }
+ return result;
+}
+
+int main(int argc, char**argv) {
+ return main_common(GetModuleHandle(NULL), SW_SHOW, argc, argv);
+}
+
+static int main_common(HINSTANCE hInstance, int show, int argc, char**argv) {
+
+ Application* app = Application::Create(argc, argv, (void*)hInstance);
+
+ MSG msg = { 0 };
+
+ // Main message loop
+ while (WM_QUIT != msg.message) {
+ if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ } else {
+ app->onIdle();
+ }
+ }
+
+ delete app;
+
+ return (int)msg.wParam;
+}