/****************************************************************************************** * Chili Direct3D Engine * * Copyright 2018 PlanetChili * * * * This file is part of Chili Direct3D Engine. * * * * Chili Direct3D Engine is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * The Chili Direct3D Engine is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with The Chili Direct3D Engine. If not, see . * ******************************************************************************************/ #include "Window.h" #include #include "resource.h" // Window Class Stuff Window::WindowClass Window::WindowClass::wndClass; Window::WindowClass::WindowClass() noexcept : hInst( GetModuleHandle( nullptr ) ) { WNDCLASSEX wc = { 0 }; wc.cbSize = sizeof( wc ); wc.style = CS_OWNDC; wc.lpfnWndProc = HandleMsgSetup; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = GetInstance(); // wc.hIcon = nullptr; // 指定图标 wc.hIcon = static_cast( LoadImage( GetInstance(), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 32, 32, 0)); wc.hCursor = nullptr; wc.hbrBackground = nullptr; wc.lpszMenuName = nullptr; wc.lpszClassName = GetName(); // wc.hIconSm = nullptr; // 指定小图标 wc.hIconSm = static_cast( LoadImage( GetInstance(), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 16, 16, 0)); RegisterClassEx( &wc ); } Window::WindowClass::~WindowClass() { UnregisterClass( wndClassName,GetInstance() ); } const char* Window::WindowClass::GetName() noexcept { return wndClassName; } HINSTANCE Window::WindowClass::GetInstance() noexcept { return wndClass.hInst; } // Window Stuff Window::Window( int width,int height,const char* name ) : width(width), height(height) { // calculate window size based on desired client region size RECT wr; wr.left = 100; wr.right = width + wr.left; wr.top = 100; wr.bottom = height + wr.top; if (AdjustWindowRect(&wr, WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, FALSE) == 0) { throw CHWND_LAST_EXCEPT(); } // create window & get hWnd hWnd = CreateWindow( WindowClass::GetName(),name, WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, CW_USEDEFAULT,CW_USEDEFAULT,wr.right - wr.left,wr.bottom - wr.top, nullptr,nullptr,WindowClass::GetInstance(),this // this 用来将该Window的实例和WinAPI连接起来 ); // check for error if (hWnd == nullptr) { throw CHWND_LAST_EXCEPT(); } // show window ShowWindow( hWnd,SW_SHOWDEFAULT ); } Window::~Window() { DestroyWindow( hWnd ); } void Window::SetTitle(const std::string& title) { if (SetWindowText(hWnd, title.c_str()) == 0) { throw CHWND_LAST_EXCEPT(); } } LRESULT CALLBACK Window::HandleMsgSetup( HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam ) noexcept { // use create parameter passed in from CreateWindow() to store window class pointer at WinAPI side if( msg == WM_NCCREATE ) { // extract ptr to window class from creation data // 使用CREATESTRUCT函数的第一个参数lpCreateParams,通过reinterpret_cast,获取到CREATESTRUCT函数的指针 // lpCreateParams的指针和CREATESTRUCT的指针指向同一个物理地址,只是寻址对象不同 const CREATESTRUCTW* const pCreate = reinterpret_cast(lParam); // 取得指向Window的指针 Window* const pWnd = static_cast(pCreate->lpCreateParams); // 将Window类,存储到WinAPI // set WinAPI-managed user data to store ptr to window class SetWindowLongPtr( hWnd,GWLP_USERDATA,reinterpret_cast(pWnd) ); // set message proc to normal (non-setup) handler now that setup is finished SetWindowLongPtr( hWnd,GWLP_WNDPROC,reinterpret_cast(&Window::HandleMsgThunk) ); // forward message to window class handler return pWnd->HandleMsg( hWnd,msg,wParam,lParam ); } // if we get a message before the WM_NCCREATE message, handle with default handler return DefWindowProc( hWnd,msg,wParam,lParam ); } LRESULT CALLBACK Window::HandleMsgThunk( HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam ) noexcept { // retrieve ptr to window class Window* const pWnd = reinterpret_cast(GetWindowLongPtr( hWnd,GWLP_USERDATA )); // forward message to window class handler return pWnd->HandleMsg( hWnd,msg,wParam,lParam ); } LRESULT Window::HandleMsg( HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam ) noexcept { switch( msg ) { // we don't want the DefProc to handle this message because // we want our destructor to destroy the window, so return 0 instead of break case WM_CLOSE: PostQuitMessage( 0 ); return 0; // 如果窗口失去焦点,立刻清空所有队列中的成员 case WM_KILLFOCUS: kbd.ClearState(); break; // 记录Key被按下 case WM_KEYDOWN: // Bipset的第28,29,30位固定是0,30位代表前一次按键状态,1代表按下。 // 因此28-31的16进制值如果是4的话,表示前一次是按下的状态,这次是持续按键 // 如果Key被持续按下,第一个条件不成立,不执行操作 // 在以上的基础上,提供Autorepeat的选项。如果启用Autorepeat,那么即使持续按键,也执行操作 if (!(lParam & 0x40000000) || kbd.AutorepeatIsEnabled()) // filter autorepeat { kbd.OnKeyPressed(static_cast(wParam)); } break; // 记录SysKey被按下 case WM_SYSKEYDOWN: // 同上 if (!(lParam & 0x40000000) || kbd.AutorepeatIsEnabled()) // filter autorepeat { kbd.OnKeyPressed(static_cast(wParam)); } break; // 记录SysKey被释放 case WM_SYSKEYUP: // 记录Key释放 case WM_KEYUP: kbd.OnKeyReleased(static_cast(wParam)); break; // 记录文字输入 case WM_CHAR: kbd.OnChar(static_cast(wParam)); break; // 鼠标移动 case WM_MOUSEMOVE: { const POINTS pt = MAKEPOINTS(lParam); mouse.OnMouseMove(pt.x, pt.y); // in client region -> log move, and log enter + capture mouse (if not previously in window) // 判断是否在窗口内 if (pt.x >= 0 && pt.x <= width && pt.y >= 0 and pt.y <= height) { // 如果鼠标在窗口内,那么一定会记录鼠标的移动 mouse.OnMouseMove(pt.x, pt.y); // 判断之前的鼠标状态。如果之前不在窗口内,那么说明现在是从外面移动进入窗口内 if (!mouse.IsInWindow()) { SetCapture(hWnd); mouse.OnMouseEnter(); } } else { // 当鼠标移动到了窗口外,如果之前左键或者右键是按住的状态,那么被识别为拖拽 if (mouse.LeftIsPressed() || mouse.RightIsPressed()) { // 拖拽过程中,依然会记录鼠标的移动 mouse.OnMouseMove(pt.x, pt.y); } else { // 否则的话,释放对鼠标的捕获 ReleaseCapture(); mouse.OnMouseLeave(); } } break; } // 点击左键 case WM_LBUTTONDOWN: { const POINTS pt = MAKEPOINTS(lParam); mouse.OnLeftPressed(pt.x, pt.y); break; } // 点击右键 case WM_RBUTTONDOWN: { const POINTS pt = MAKEPOINTS(lParam); mouse.OnRightPressed(pt.x, pt.y); break; } // 释放左键 case WM_LBUTTONUP: { const POINTS pt = MAKEPOINTS(lParam); mouse.OnLeftReleased(pt.x, pt.y); break; } // 释放右键 { case WM_RBUTTONUP: const POINTS pt = MAKEPOINTS(lParam); mouse.OnRightReleased(pt.x, pt.y); break; } // 鼠标滚轮 case WM_MOUSEWHEEL: { const POINTS pt = MAKEPOINTS(lParam); const int delta = GET_WHEEL_DELTA_WPARAM(wParam); mouse.OnWheelDelta(pt.x, pt.y, delta); break; } } return DefWindowProc( hWnd,msg,wParam,lParam ); } // Window Exception Stuff Window::Exception::Exception(int line, const char* file, HRESULT hr) noexcept : ChiliException(line, file), hr(hr) {} const char* Window::Exception::what() const noexcept { std::ostringstream oss; oss << GetType() << std::endl << "[Error Code] " << GetErrorCode() << std::endl << "[Description] " << GetErrorString() << std::endl << GetOriginString(); whatBuffer = oss.str(); return whatBuffer.c_str(); } const char* Window::Exception::GetType() const noexcept { return "Chili Window Exception"; } std::string Window::Exception::TranslateErrorCode(HRESULT hr) noexcept { char* pMsgBuf = nullptr; // windows will allocate memory for err string and make our pointer point to it DWORD nMsgLen = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&pMsgBuf), 0, nullptr ); // 0 string length returned indicates a failure if (nMsgLen == 0) { return "Unidentified error code"; } // copy error string from windows-allocated buffer to std::string std::string errorString = pMsgBuf; // free windows buffer LocalFree(pMsgBuf); return errorString; } HRESULT Window::Exception::GetErrorCode() const noexcept { return hr; } std::string Window::Exception::GetErrorString() const noexcept { return TranslateErrorCode(hr); }