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