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