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