1/* 2* Copyright 2016 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 8#include "include/gpu/vk/GrVkVulkan.h" 9 10#include "tools/sk_app/win/Window_win.h" 11 12#include <tchar.h> 13#include <windows.h> 14#include <windowsx.h> 15 16#include "src/utils/SkUTF.h" 17#include "tools/sk_app/WindowContext.h" 18#include "tools/sk_app/win/WindowContextFactory_win.h" 19#include "tools/skui/ModifierKey.h" 20 21#ifdef SK_VULKAN 22#include "tools/sk_app/VulkanWindowContext.h" 23#endif 24 25namespace sk_app { 26 27static int gWindowX = CW_USEDEFAULT; 28static int gWindowY = 0; 29static int gWindowWidth = CW_USEDEFAULT; 30static int gWindowHeight = 0; 31 32Window* Window::CreateNativeWindow(void* platformData) { 33 HINSTANCE hInstance = (HINSTANCE)platformData; 34 35 Window_win* window = new Window_win(); 36 if (!window->init(hInstance)) { 37 delete window; 38 return nullptr; 39 } 40 41 return window; 42} 43 44void Window_win::closeWindow() { 45 RECT r; 46 if (GetWindowRect(fHWnd, &r)) { 47 gWindowX = r.left; 48 gWindowY = r.top; 49 gWindowWidth = r.right - r.left; 50 gWindowHeight = r.bottom - r.top; 51 } 52 DestroyWindow(fHWnd); 53} 54 55Window_win::~Window_win() { 56 this->closeWindow(); 57} 58 59LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); 60 61 62bool Window_win::init(HINSTANCE hInstance) { 63 fHInstance = hInstance ? hInstance : GetModuleHandle(nullptr); 64 65 // The main window class name 66 static const TCHAR gSZWindowClass[] = _T("SkiaApp"); 67 68 static WNDCLASSEX wcex; 69 static bool wcexInit = false; 70 if (!wcexInit) { 71 wcex.cbSize = sizeof(WNDCLASSEX); 72 73 wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 74 wcex.lpfnWndProc = WndProc; 75 wcex.cbClsExtra = 0; 76 wcex.cbWndExtra = 0; 77 wcex.hInstance = fHInstance; 78 wcex.hIcon = LoadIcon(fHInstance, (LPCTSTR)IDI_WINLOGO); 79 wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); 80 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 81 wcex.lpszMenuName = nullptr; 82 wcex.lpszClassName = gSZWindowClass; 83 wcex.hIconSm = LoadIcon(fHInstance, (LPCTSTR)IDI_WINLOGO); 84 85 if (!RegisterClassEx(&wcex)) { 86 return false; 87 } 88 wcexInit = true; 89 } 90 91 /* 92 if (fullscreen) 93 { 94 DEVMODE dmScreenSettings; 95 // If full screen set the screen to maximum size of the users desktop and 32bit. 96 memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); 97 dmScreenSettings.dmSize = sizeof(dmScreenSettings); 98 dmScreenSettings.dmPelsWidth = (unsigned long)width; 99 dmScreenSettings.dmPelsHeight = (unsigned long)height; 100 dmScreenSettings.dmBitsPerPel = 32; 101 dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; 102 103 // Change the display settings to full screen. 104 ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN); 105 106 // Set the position of the window to the top left corner. 107 posX = posY = 0; 108 } 109 */ 110 // gIsFullscreen = fullscreen; 111 112 fHWnd = CreateWindow(gSZWindowClass, nullptr, WS_OVERLAPPEDWINDOW, 113 gWindowX, gWindowY, gWindowWidth, gWindowHeight, 114 nullptr, nullptr, fHInstance, nullptr); 115 if (!fHWnd) 116 { 117 return false; 118 } 119 120 SetWindowLongPtr(fHWnd, GWLP_USERDATA, (LONG_PTR)this); 121 RegisterTouchWindow(fHWnd, 0); 122 123 return true; 124} 125 126static skui::Key get_key(WPARAM vk) { 127 static const struct { 128 WPARAM fVK; 129 skui::Key fKey; 130 } gPair[] = { 131 { VK_BACK, skui::Key::kBack }, 132 { VK_CLEAR, skui::Key::kBack }, 133 { VK_RETURN, skui::Key::kOK }, 134 { VK_UP, skui::Key::kUp }, 135 { VK_DOWN, skui::Key::kDown }, 136 { VK_LEFT, skui::Key::kLeft }, 137 { VK_RIGHT, skui::Key::kRight }, 138 { VK_TAB, skui::Key::kTab }, 139 { VK_PRIOR, skui::Key::kPageUp }, 140 { VK_NEXT, skui::Key::kPageDown }, 141 { VK_HOME, skui::Key::kHome }, 142 { VK_END, skui::Key::kEnd }, 143 { VK_DELETE, skui::Key::kDelete }, 144 { VK_ESCAPE, skui::Key::kEscape }, 145 { VK_SHIFT, skui::Key::kShift }, 146 { VK_CONTROL, skui::Key::kCtrl }, 147 { VK_MENU, skui::Key::kOption }, 148 { 'A', skui::Key::kA }, 149 { 'C', skui::Key::kC }, 150 { 'V', skui::Key::kV }, 151 { 'X', skui::Key::kX }, 152 { 'Y', skui::Key::kY }, 153 { 'Z', skui::Key::kZ }, 154 }; 155 for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { 156 if (gPair[i].fVK == vk) { 157 return gPair[i].fKey; 158 } 159 } 160 return skui::Key::kNONE; 161} 162 163static skui::ModifierKey get_modifiers(UINT message, WPARAM wParam, LPARAM lParam) { 164 skui::ModifierKey modifiers = skui::ModifierKey::kNone; 165 166 switch (message) { 167 case WM_UNICHAR: 168 case WM_CHAR: 169 if (0 == (lParam & (1 << 30))) { 170 modifiers |= skui::ModifierKey::kFirstPress; 171 } 172 if (lParam & (1 << 29)) { 173 modifiers |= skui::ModifierKey::kOption; 174 } 175 break; 176 177 case WM_KEYDOWN: 178 case WM_SYSKEYDOWN: 179 if (0 == (lParam & (1 << 30))) { 180 modifiers |= skui::ModifierKey::kFirstPress; 181 } 182 if (lParam & (1 << 29)) { 183 modifiers |= skui::ModifierKey::kOption; 184 } 185 break; 186 187 case WM_KEYUP: 188 case WM_SYSKEYUP: 189 if (lParam & (1 << 29)) { 190 modifiers |= skui::ModifierKey::kOption; 191 } 192 break; 193 194 case WM_LBUTTONDOWN: 195 case WM_LBUTTONUP: 196 case WM_MOUSEMOVE: 197 case WM_MOUSEWHEEL: 198 if (wParam & MK_CONTROL) { 199 modifiers |= skui::ModifierKey::kControl; 200 } 201 if (wParam & MK_SHIFT) { 202 modifiers |= skui::ModifierKey::kShift; 203 } 204 break; 205 } 206 207 return modifiers; 208} 209 210LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 211{ 212 PAINTSTRUCT ps; 213 214 Window_win* window = (Window_win*) GetWindowLongPtr(hWnd, GWLP_USERDATA); 215 216 bool eventHandled = false; 217 218 switch (message) { 219 case WM_PAINT: 220 BeginPaint(hWnd, &ps); 221 window->onPaint(); 222 EndPaint(hWnd, &ps); 223 eventHandled = true; 224 break; 225 226 case WM_CLOSE: 227 PostQuitMessage(0); 228 eventHandled = true; 229 break; 230 231 case WM_ACTIVATE: 232 // disable/enable rendering here, depending on wParam != WA_INACTIVE 233 break; 234 235 case WM_SIZE: 236 window->onResize(LOWORD(lParam), HIWORD(lParam)); 237 eventHandled = true; 238 break; 239 240 case WM_UNICHAR: 241 eventHandled = window->onChar((SkUnichar)wParam, 242 get_modifiers(message, wParam, lParam)); 243 break; 244 245 case WM_CHAR: { 246 const uint16_t* cPtr = reinterpret_cast<uint16_t*>(&wParam); 247 SkUnichar c = SkUTF::NextUTF16(&cPtr, cPtr + 2); 248 eventHandled = window->onChar(c, get_modifiers(message, wParam, lParam)); 249 } break; 250 251 case WM_KEYDOWN: 252 case WM_SYSKEYDOWN: 253 eventHandled = window->onKey(get_key(wParam), skui::InputState::kDown, 254 get_modifiers(message, wParam, lParam)); 255 break; 256 257 case WM_KEYUP: 258 case WM_SYSKEYUP: 259 eventHandled = window->onKey(get_key(wParam), skui::InputState::kUp, 260 get_modifiers(message, wParam, lParam)); 261 break; 262 263 case WM_LBUTTONDOWN: 264 case WM_LBUTTONUP: { 265 int xPos = GET_X_LPARAM(lParam); 266 int yPos = GET_Y_LPARAM(lParam); 267 268 //if (!gIsFullscreen) 269 //{ 270 // RECT rc = { 0, 0, 640, 480 }; 271 // AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE); 272 // xPos -= rc.left; 273 // yPos -= rc.top; 274 //} 275 276 skui::InputState istate = ((wParam & MK_LBUTTON) != 0) ? skui::InputState::kDown 277 : skui::InputState::kUp; 278 279 eventHandled = window->onMouse(xPos, yPos, istate, 280 get_modifiers(message, wParam, lParam)); 281 } break; 282 283 case WM_MOUSEMOVE: { 284 int xPos = GET_X_LPARAM(lParam); 285 int yPos = GET_Y_LPARAM(lParam); 286 287 //if (!gIsFullscreen) 288 //{ 289 // RECT rc = { 0, 0, 640, 480 }; 290 // AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE); 291 // xPos -= rc.left; 292 // yPos -= rc.top; 293 //} 294 295 eventHandled = window->onMouse(xPos, yPos, skui::InputState::kMove, 296 get_modifiers(message, wParam, lParam)); 297 } break; 298 299 case WM_MOUSEWHEEL: 300 eventHandled = window->onMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f, 301 get_modifiers(message, wParam, lParam)); 302 break; 303 304 case WM_TOUCH: { 305 uint16_t numInputs = LOWORD(wParam); 306 std::unique_ptr<TOUCHINPUT[]> inputs(new TOUCHINPUT[numInputs]); 307 if (GetTouchInputInfo((HTOUCHINPUT)lParam, numInputs, inputs.get(), 308 sizeof(TOUCHINPUT))) { 309 POINT topLeft = {0, 0}; 310 ClientToScreen(hWnd, &topLeft); 311 for (uint16_t i = 0; i < numInputs; ++i) { 312 TOUCHINPUT ti = inputs[i]; 313 skui::InputState state; 314 if (ti.dwFlags & TOUCHEVENTF_DOWN) { 315 state = skui::InputState::kDown; 316 } else if (ti.dwFlags & TOUCHEVENTF_MOVE) { 317 state = skui::InputState::kMove; 318 } else if (ti.dwFlags & TOUCHEVENTF_UP) { 319 state = skui::InputState::kUp; 320 } else { 321 continue; 322 } 323 // TOUCHINPUT coordinates are in 100ths of pixels 324 // Adjust for that, and make them window relative 325 LONG tx = (ti.x / 100) - topLeft.x; 326 LONG ty = (ti.y / 100) - topLeft.y; 327 eventHandled = window->onTouch(ti.dwID, state, tx, ty) || eventHandled; 328 } 329 } 330 } break; 331 332 default: 333 return DefWindowProc(hWnd, message, wParam, lParam); 334 } 335 336 return eventHandled ? 0 : 1; 337} 338 339void Window_win::setTitle(const char* title) { 340 SetWindowTextA(fHWnd, title); 341} 342 343void Window_win::show() { 344 ShowWindow(fHWnd, SW_SHOW); 345} 346 347 348bool Window_win::attach(BackendType attachType) { 349 fBackend = attachType; 350 fInitializedBackend = true; 351 352 switch (attachType) { 353#ifdef SK_GL 354 case kNativeGL_BackendType: 355 fWindowContext = window_context_factory::MakeGLForWin(fHWnd, fRequestedDisplayParams); 356 break; 357#endif 358#if SK_ANGLE 359 case kANGLE_BackendType: 360 fWindowContext = 361 window_context_factory::MakeANGLEForWin(fHWnd, fRequestedDisplayParams); 362 break; 363#endif 364#ifdef SK_DAWN 365 case kDawn_BackendType: 366 fWindowContext = 367 window_context_factory::MakeDawnD3D12ForWin(fHWnd, fRequestedDisplayParams); 368 break; 369#endif 370 case kRaster_BackendType: 371 fWindowContext = 372 window_context_factory::MakeRasterForWin(fHWnd, fRequestedDisplayParams); 373 break; 374#ifdef SK_VULKAN 375 case kVulkan_BackendType: 376 fWindowContext = 377 window_context_factory::MakeVulkanForWin(fHWnd, fRequestedDisplayParams); 378 break; 379#endif 380#ifdef SK_DIRECT3D 381 case kDirect3D_BackendType: 382 fWindowContext = 383 window_context_factory::MakeD3D12ForWin(fHWnd, fRequestedDisplayParams); 384 break; 385#endif 386 } 387 this->onBackendCreated(); 388 389 return (SkToBool(fWindowContext)); 390} 391 392void Window_win::onInval() { 393 InvalidateRect(fHWnd, nullptr, false); 394} 395 396void Window_win::setRequestedDisplayParams(const DisplayParams& params, bool allowReattach) { 397 // GL on Windows doesn't let us change MSAA after the window is created 398 if (params.fMSAASampleCount != this->getRequestedDisplayParams().fMSAASampleCount 399 && allowReattach) { 400 // Need to change these early, so attach() creates the window context correctly 401 fRequestedDisplayParams = params; 402 403 fWindowContext = nullptr; 404 this->closeWindow(); 405 this->init(fHInstance); 406 if (fInitializedBackend) { 407 this->attach(fBackend); 408 } 409 } 410 411 INHERITED::setRequestedDisplayParams(params, allowReattach); 412} 413 414} // namespace sk_app 415