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