1//
2// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7#include "win32/Win32Window.h"
8
9Key VirtualKeyCodeToKey(WPARAM key, LPARAM flags)
10{
11    switch (key)
12    {
13        // Check the scancode to distinguish between left and right shift
14      case VK_SHIFT:
15        {
16            static unsigned int lShift = MapVirtualKey(VK_LSHIFT, MAPVK_VK_TO_VSC);
17            unsigned int scancode = static_cast<unsigned int>((flags & (0xFF << 16)) >> 16);
18            return scancode == lShift ? KEY_LSHIFT : KEY_RSHIFT;
19        }
20
21        // Check the "extended" flag to distinguish between left and right alt
22      case VK_MENU:       return (HIWORD(flags) & KF_EXTENDED) ? KEY_RALT : KEY_LALT;
23
24        // Check the "extended" flag to distinguish between left and right control
25      case VK_CONTROL:    return (HIWORD(flags) & KF_EXTENDED) ? KEY_RCONTROL : KEY_LCONTROL;
26
27        // Other keys are reported properly
28      case VK_LWIN:       return KEY_LSYSTEM;
29      case VK_RWIN:       return KEY_RSYSTEM;
30      case VK_APPS:       return KEY_MENU;
31      case VK_OEM_1:      return KEY_SEMICOLON;
32      case VK_OEM_2:      return KEY_SLASH;
33      case VK_OEM_PLUS:   return KEY_EQUAL;
34      case VK_OEM_MINUS:  return KEY_DASH;
35      case VK_OEM_4:      return KEY_LBRACKET;
36      case VK_OEM_6:      return KEY_RBRACKET;
37      case VK_OEM_COMMA:  return KEY_COMMA;
38      case VK_OEM_PERIOD: return KEY_PERIOD;
39      case VK_OEM_7:      return KEY_QUOTE;
40      case VK_OEM_5:      return KEY_BACKSLASH;
41      case VK_OEM_3:      return KEY_TILDE;
42      case VK_ESCAPE:     return KEY_ESCAPE;
43      case VK_SPACE:      return KEY_SPACE;
44      case VK_RETURN:     return KEY_RETURN;
45      case VK_BACK:       return KEY_BACK;
46      case VK_TAB:        return KEY_TAB;
47      case VK_PRIOR:      return KEY_PAGEUP;
48      case VK_NEXT:       return KEY_PAGEDOWN;
49      case VK_END:        return KEY_END;
50      case VK_HOME:       return KEY_HOME;
51      case VK_INSERT:     return KEY_INSERT;
52      case VK_DELETE:     return KEY_DELETE;
53      case VK_ADD:        return KEY_ADD;
54      case VK_SUBTRACT:   return KEY_SUBTRACT;
55      case VK_MULTIPLY:   return KEY_MULTIPLY;
56      case VK_DIVIDE:     return KEY_DIVIDE;
57      case VK_PAUSE:      return KEY_PAUSE;
58      case VK_F1:         return KEY_F1;
59      case VK_F2:         return KEY_F2;
60      case VK_F3:         return KEY_F3;
61      case VK_F4:         return KEY_F4;
62      case VK_F5:         return KEY_F5;
63      case VK_F6:         return KEY_F6;
64      case VK_F7:         return KEY_F7;
65      case VK_F8:         return KEY_F8;
66      case VK_F9:         return KEY_F9;
67      case VK_F10:        return KEY_F10;
68      case VK_F11:        return KEY_F11;
69      case VK_F12:        return KEY_F12;
70      case VK_F13:        return KEY_F13;
71      case VK_F14:        return KEY_F14;
72      case VK_F15:        return KEY_F15;
73      case VK_LEFT:       return KEY_LEFT;
74      case VK_RIGHT:      return KEY_RIGHT;
75      case VK_UP:         return KEY_UP;
76      case VK_DOWN:       return KEY_DOWN;
77      case VK_NUMPAD0:    return KEY_NUMPAD0;
78      case VK_NUMPAD1:    return KEY_NUMPAD1;
79      case VK_NUMPAD2:    return KEY_NUMPAD2;
80      case VK_NUMPAD3:    return KEY_NUMPAD3;
81      case VK_NUMPAD4:    return KEY_NUMPAD4;
82      case VK_NUMPAD5:    return KEY_NUMPAD5;
83      case VK_NUMPAD6:    return KEY_NUMPAD6;
84      case VK_NUMPAD7:    return KEY_NUMPAD7;
85      case VK_NUMPAD8:    return KEY_NUMPAD8;
86      case VK_NUMPAD9:    return KEY_NUMPAD9;
87      case 'A':           return KEY_A;
88      case 'Z':           return KEY_Z;
89      case 'E':           return KEY_E;
90      case 'R':           return KEY_R;
91      case 'T':           return KEY_T;
92      case 'Y':           return KEY_Y;
93      case 'U':           return KEY_U;
94      case 'I':           return KEY_I;
95      case 'O':           return KEY_O;
96      case 'P':           return KEY_P;
97      case 'Q':           return KEY_Q;
98      case 'S':           return KEY_S;
99      case 'D':           return KEY_D;
100      case 'F':           return KEY_F;
101      case 'G':           return KEY_G;
102      case 'H':           return KEY_H;
103      case 'J':           return KEY_J;
104      case 'K':           return KEY_K;
105      case 'L':           return KEY_L;
106      case 'M':           return KEY_M;
107      case 'W':           return KEY_W;
108      case 'X':           return KEY_X;
109      case 'C':           return KEY_C;
110      case 'V':           return KEY_V;
111      case 'B':           return KEY_B;
112      case 'N':           return KEY_N;
113      case '0':           return KEY_NUM0;
114      case '1':           return KEY_NUM1;
115      case '2':           return KEY_NUM2;
116      case '3':           return KEY_NUM3;
117      case '4':           return KEY_NUM4;
118      case '5':           return KEY_NUM5;
119      case '6':           return KEY_NUM6;
120      case '7':           return KEY_NUM7;
121      case '8':           return KEY_NUM8;
122      case '9':           return KEY_NUM9;
123    }
124
125    return Key(0);
126}
127
128LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
129{
130    switch(message)
131    {
132      case WM_NCCREATE:
133        {
134            LPCREATESTRUCT pCreateStruct = (LPCREATESTRUCT)lParam;
135            SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pCreateStruct->lpCreateParams);
136            return DefWindowProcA(hWnd, message, wParam, lParam);
137        }
138    }
139
140    OSWindow *window = (OSWindow*)(LONG_PTR)GetWindowLongPtr(hWnd, GWLP_USERDATA);
141    if (window)
142    {
143        switch (message)
144        {
145          case WM_DESTROY:
146          case WM_CLOSE:
147            {
148                Event event;
149                event.Type = Event::EVENT_CLOSED;
150                window->pushEvent(event);
151                break;
152            }
153
154          case WM_MOVE:
155            {
156                RECT winRect;
157                GetClientRect(hWnd, &winRect);
158
159                POINT topLeft;
160                topLeft.x = winRect.left;
161                topLeft.y = winRect.top;
162                ClientToScreen(hWnd, &topLeft);
163
164                Event event;
165                event.Type        = Event::EVENT_MOVED;
166                event.Move.X      = topLeft.x;
167                event.Move.Y      = topLeft.y;
168                window->pushEvent(event);
169
170                break;
171            }
172
173          case WM_SIZE:
174            {
175                RECT winRect;
176                GetClientRect(hWnd, &winRect);
177
178                POINT topLeft;
179                topLeft.x = winRect.left;
180                topLeft.y = winRect.top;
181                ClientToScreen(hWnd, &topLeft);
182
183                POINT botRight;
184                botRight.x = winRect.right;
185                botRight.y = winRect.bottom;
186                ClientToScreen(hWnd, &botRight);
187
188                Event event;
189                event.Type        = Event::EVENT_RESIZED;
190                event.Size.Width  = botRight.x - topLeft.x;
191                event.Size.Height = botRight.y - topLeft.y;
192                window->pushEvent(event);
193
194                break;
195            }
196
197          case WM_SETFOCUS:
198            {
199                Event event;
200                event.Type = Event::EVENT_GAINED_FOCUS;
201                window->pushEvent(event);
202                break;
203            }
204
205          case WM_KILLFOCUS:
206            {
207                Event event;
208                event.Type = Event::EVENT_LOST_FOCUS;
209                window->pushEvent(event);
210                break;
211            }
212
213          case WM_KEYDOWN:
214          case WM_SYSKEYDOWN:
215          case WM_KEYUP:
216          case WM_SYSKEYUP:
217            {
218                bool down = (message == WM_KEYDOWN || message == WM_SYSKEYDOWN);
219
220                Event event;
221                event.Type        = down ? Event::EVENT_KEY_PRESSED : Event::EVENT_KEY_RELEASED;
222                event.Key.Alt     = HIWORD(GetAsyncKeyState(VK_MENU))    != 0;
223                event.Key.Control = HIWORD(GetAsyncKeyState(VK_CONTROL)) != 0;
224                event.Key.Shift   = HIWORD(GetAsyncKeyState(VK_SHIFT))   != 0;
225                event.Key.System  = HIWORD(GetAsyncKeyState(VK_LWIN)) || HIWORD(GetAsyncKeyState(VK_RWIN));
226                event.Key.Code    = VirtualKeyCodeToKey(wParam, lParam);
227                window->pushEvent(event);
228
229                break;
230            }
231
232          case WM_MOUSEWHEEL:
233            {
234                Event event;
235                event.Type = Event::EVENT_MOUSE_WHEEL_MOVED;
236                event.MouseWheel.Delta = static_cast<short>(HIWORD(wParam)) / 120;
237                window->pushEvent(event);
238                break;
239            }
240
241          case WM_LBUTTONDOWN:
242          case WM_LBUTTONDBLCLK:
243            {
244                Event event;
245                event.Type               = Event::EVENT_MOUSE_BUTTON_PRESSED;
246                event.MouseButton.Button = MOUSEBUTTON_LEFT;
247                event.MouseButton.X      = static_cast<short>(LOWORD(lParam));
248                event.MouseButton.Y      = static_cast<short>(HIWORD(lParam));
249                window->pushEvent(event);
250                break;
251            }
252
253          case WM_LBUTTONUP:
254            {
255                Event event;
256                event.Type               = Event::EVENT_MOUSE_BUTTON_RELEASED;
257                event.MouseButton.Button = MOUSEBUTTON_LEFT;
258                event.MouseButton.X      = static_cast<short>(LOWORD(lParam));
259                event.MouseButton.Y      = static_cast<short>(HIWORD(lParam));
260                window->pushEvent(event);
261                break;
262            }
263
264          case WM_RBUTTONDOWN:
265          case WM_RBUTTONDBLCLK:
266            {
267                Event event;
268                event.Type               = Event::EVENT_MOUSE_BUTTON_PRESSED;
269                event.MouseButton.Button = MOUSEBUTTON_RIGHT;
270                event.MouseButton.X      = static_cast<short>(LOWORD(lParam));
271                event.MouseButton.Y      = static_cast<short>(HIWORD(lParam));
272                window->pushEvent(event);
273                break;
274            }
275
276            // Mouse right button up event
277          case WM_RBUTTONUP:
278            {
279                Event event;
280                event.Type               = Event::EVENT_MOUSE_BUTTON_RELEASED;
281                event.MouseButton.Button = MOUSEBUTTON_RIGHT;
282                event.MouseButton.X      = static_cast<short>(LOWORD(lParam));
283                event.MouseButton.Y      = static_cast<short>(HIWORD(lParam));
284                window->pushEvent(event);
285                break;
286            }
287
288            // Mouse wheel button down event
289          case WM_MBUTTONDOWN:
290          case WM_MBUTTONDBLCLK:
291            {
292                Event event;
293                event.Type               = Event::EVENT_MOUSE_BUTTON_PRESSED;
294                event.MouseButton.Button = MOUSEBUTTON_MIDDLE;
295                event.MouseButton.X      = static_cast<short>(LOWORD(lParam));
296                event.MouseButton.Y      = static_cast<short>(HIWORD(lParam));
297                window->pushEvent(event);
298                break;
299            }
300
301            // Mouse wheel button up event
302          case WM_MBUTTONUP:
303            {
304                Event event;
305                event.Type               = Event::EVENT_MOUSE_BUTTON_RELEASED;
306                event.MouseButton.Button = MOUSEBUTTON_MIDDLE;
307                event.MouseButton.X      = static_cast<short>(LOWORD(lParam));
308                event.MouseButton.Y      = static_cast<short>(HIWORD(lParam));
309                window->pushEvent(event);
310                break;
311            }
312
313            // Mouse X button down event
314          case WM_XBUTTONDOWN:
315          case WM_XBUTTONDBLCLK:
316            {
317                Event event;
318                event.Type               = Event::EVENT_MOUSE_BUTTON_PRESSED;
319                event.MouseButton.Button = (HIWORD(wParam) == XBUTTON1) ? MOUSEBUTTON_BUTTON4 : MOUSEBUTTON_BUTTON5;
320                event.MouseButton.X      = static_cast<short>(LOWORD(lParam));
321                event.MouseButton.Y      = static_cast<short>(HIWORD(lParam));
322                window->pushEvent(event);
323                break;
324            }
325
326            // Mouse X button up event
327          case WM_XBUTTONUP:
328            {
329                Event event;
330                event.Type               = Event::EVENT_MOUSE_BUTTON_RELEASED;
331                event.MouseButton.Button = (HIWORD(wParam) == XBUTTON1) ? MOUSEBUTTON_BUTTON4 : MOUSEBUTTON_BUTTON5;
332                event.MouseButton.X      = static_cast<short>(LOWORD(lParam));
333                event.MouseButton.Y      = static_cast<short>(HIWORD(lParam));
334                window->pushEvent(event);
335                break;
336            }
337
338          case WM_MOUSEMOVE:
339            {
340                int mouseX = static_cast<short>(LOWORD(lParam));
341                int mouseY = static_cast<short>(HIWORD(lParam));
342
343                Event event;
344                event.Type        = Event::EVENT_MOUSE_MOVED;
345                event.MouseMove.X = mouseX;
346                event.MouseMove.Y = mouseY;
347                window->pushEvent(event);
348                break;
349            }
350
351          case WM_MOUSELEAVE:
352            {
353                Event event;
354                event.Type = Event::EVENT_MOUSE_LEFT;
355                window->pushEvent(event);
356                break;
357            }
358        }
359
360    }
361    return DefWindowProcA(hWnd, message, wParam, lParam);
362}
363
364Win32Window::Win32Window()
365    : mNativeWindow(0),
366      mParentWindow(0),
367      mNativeDisplay(0)
368{
369}
370
371Win32Window::~Win32Window()
372{
373    destroy();
374}
375
376bool Win32Window::initialize(const std::string &name, size_t width, size_t height)
377{
378    destroy();
379
380    mParentClassName = name;
381    mChildClassName = name + "Child";
382
383    WNDCLASSEXA parentWindowClass = { 0 };
384    parentWindowClass.cbSize = sizeof(WNDCLASSEXA);
385    parentWindowClass.style = 0;
386    parentWindowClass.lpfnWndProc = WndProc;
387    parentWindowClass.cbClsExtra = 0;
388    parentWindowClass.cbWndExtra = 0;
389    parentWindowClass.hInstance = GetModuleHandle(NULL);
390    parentWindowClass.hIcon = NULL;
391    parentWindowClass.hCursor = LoadCursorA(NULL, IDC_ARROW);
392    parentWindowClass.hbrBackground = 0;
393    parentWindowClass.lpszMenuName = NULL;
394    parentWindowClass.lpszClassName = mParentClassName.c_str();
395    if (!RegisterClassExA(&parentWindowClass))
396    {
397        return false;
398    }
399
400    WNDCLASSEXA childWindowClass = { 0 };
401    childWindowClass.cbSize = sizeof(WNDCLASSEXA);
402    childWindowClass.style = CS_OWNDC;
403    childWindowClass.lpfnWndProc = WndProc;
404    childWindowClass.cbClsExtra = 0;
405    childWindowClass.cbWndExtra = 0;
406    childWindowClass.hInstance = GetModuleHandle(NULL);
407    childWindowClass.hIcon = NULL;
408    childWindowClass.hCursor = LoadCursorA(NULL, IDC_ARROW);
409    childWindowClass.hbrBackground = 0;
410    childWindowClass.lpszMenuName = NULL;
411    childWindowClass.lpszClassName = mChildClassName.c_str();
412    if (!RegisterClassExA(&childWindowClass))
413    {
414        return false;
415    }
416
417    DWORD parentStyle = WS_VISIBLE | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU;
418    DWORD parentExtendedStyle = WS_EX_APPWINDOW;
419
420    RECT sizeRect = { 0, 0, width, height };
421    AdjustWindowRectEx(&sizeRect, parentStyle, FALSE, parentExtendedStyle);
422
423    mParentWindow = CreateWindowExA(parentExtendedStyle, mParentClassName.c_str(), name.c_str(), parentStyle, CW_USEDEFAULT, CW_USEDEFAULT,
424                                    sizeRect.right - sizeRect.left, sizeRect.bottom - sizeRect.top, NULL, NULL,
425                                    GetModuleHandle(NULL), this);
426
427    mNativeWindow = CreateWindowExA(0, mChildClassName.c_str(), name.c_str(), WS_VISIBLE | WS_CHILD, 0, 0, width, height,
428                                    mParentWindow, NULL, GetModuleHandle(NULL), this);
429
430    mNativeDisplay = GetDC(mNativeWindow);
431    if (!mNativeDisplay)
432    {
433        destroy();
434        return false;
435    }
436
437    return true;
438}
439
440void Win32Window::destroy()
441{
442    if (mNativeDisplay)
443    {
444        ReleaseDC(mNativeWindow, mNativeDisplay);
445        mNativeDisplay = 0;
446    }
447
448    if (mNativeWindow)
449    {
450        DestroyWindow(mNativeWindow);
451        mNativeWindow = 0;
452    }
453
454    if (mParentWindow)
455    {
456        DestroyWindow(mParentWindow);
457        mParentWindow = 0;
458    }
459
460    UnregisterClassA(mParentClassName.c_str(), NULL);
461    UnregisterClassA(mChildClassName.c_str(), NULL);
462}
463
464EGLNativeWindowType Win32Window::getNativeWindow() const
465{
466    return mNativeWindow;
467}
468
469EGLNativeDisplayType Win32Window::getNativeDisplay() const
470{
471    return mNativeDisplay;
472}
473
474void Win32Window::messageLoop()
475{
476    MSG msg;
477    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
478    {
479        TranslateMessage(&msg);
480        DispatchMessage(&msg);
481    }
482}
483
484void Win32Window::setMousePosition(int x, int y)
485{
486    RECT winRect;
487    GetClientRect(mNativeWindow, &winRect);
488
489    POINT topLeft;
490    topLeft.x = winRect.left;
491    topLeft.y = winRect.top;
492    ClientToScreen(mNativeWindow, &topLeft);
493
494    SetCursorPos(topLeft.x + x, topLeft.y + y);
495}
496
497OSWindow *CreateOSWindow()
498{
499    return new Win32Window();
500}
501
502bool Win32Window::resize(int width, int height)
503{
504    if (width == mWidth && height == mHeight)
505    {
506        return true;
507    }
508
509    RECT windowRect;
510    if (!GetWindowRect(mParentWindow, &windowRect))
511    {
512        return false;
513    }
514
515    RECT clientRect;
516    if (!GetClientRect(mParentWindow, &clientRect))
517    {
518        return false;
519    }
520
521    LONG diffX = (windowRect.right - windowRect.left) - clientRect.right;
522    LONG diffY = (windowRect.bottom - windowRect.top) - clientRect.bottom;
523    if (!MoveWindow(mParentWindow, windowRect.left, windowRect.top, width + diffX, height + diffY, FALSE))
524    {
525        return false;
526    }
527
528    if (!MoveWindow(mNativeWindow, 0, 0, width, height, FALSE))
529    {
530        return false;
531    }
532
533    return true;
534}
535
536bool Win32Window::setVisible(bool isVisible)
537{
538    int flag = (isVisible ? SW_SHOW : SW_HIDE);
539
540    return (ShowWindow(mNativeWindow, flag) == TRUE) &&
541           (ShowWindow(mParentWindow, flag) == TRUE);
542}
543
544void Win32Window::pushEvent(Event event)
545{
546    OSWindow::pushEvent(event);
547
548    switch (event.Type)
549    {
550      case Event::EVENT_RESIZED:
551        MoveWindow(mNativeWindow, 0, 0, mWidth, mHeight, FALSE);
552        break;
553    }
554}
555