xref: /aosp_15_r20/external/skia/tools/sk_app/win/Window_win.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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