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
25 namespace sk_app {
26
27 static int gWindowX = CW_USEDEFAULT;
28 static int gWindowY = 0;
29 static int gWindowWidth = CW_USEDEFAULT;
30 static int gWindowHeight = 0;
31
CreateNativeWindow(void * platformData)32 Window* 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
closeWindow()44 void 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
~Window_win()55 Window_win::~Window_win() {
56 this->closeWindow();
57 }
58
59 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
60
61
init(HINSTANCE hInstance)62 bool 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
get_key(WPARAM vk)126 static 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
get_modifiers(UINT message,WPARAM wParam,LPARAM lParam)163 static 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
WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)210 LRESULT 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
setTitle(const char * title)339 void Window_win::setTitle(const char* title) {
340 SetWindowTextA(fHWnd, title);
341 }
342
show()343 void Window_win::show() {
344 ShowWindow(fHWnd, SW_SHOW);
345 }
346
347
attach(BackendType attachType)348 bool 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
onInval()392 void Window_win::onInval() {
393 InvalidateRect(fHWnd, nullptr, false);
394 }
395
setRequestedDisplayParams(const DisplayParams & params,bool allowReattach)396 void 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