598 lines
20 KiB
C++
598 lines
20 KiB
C++
#include "DX12LibPCH.h"
|
|
#include "Application.h"
|
|
//#include "..\resource.h"
|
|
|
|
#include "Game.h"
|
|
#include "CommandQueue.h"
|
|
#include "Window.h"
|
|
|
|
constexpr wchar_t WINDOW_CLASS_NAME[] = L"DX12RenderWindowClass";
|
|
|
|
using WindowPtr = std::shared_ptr<Window>;
|
|
using WindowMap = std::map< HWND, WindowPtr >;
|
|
using WindowNameMap = std::map< std::wstring, WindowPtr >;
|
|
|
|
static Application* gs_pSingelton = nullptr;
|
|
static WindowMap gs_Windows;
|
|
static WindowNameMap gs_WindowByName;
|
|
|
|
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
|
|
|
|
// A wrapper struct to allow shared pointers for the window class.
|
|
struct MakeWindow : public Window
|
|
{
|
|
MakeWindow(HWND hWnd, const std::wstring& windowName, int clientWidth, int clientHeight, bool vSync)
|
|
: Window(hWnd, windowName, clientWidth, clientHeight, vSync)
|
|
{}
|
|
};
|
|
|
|
Application::Application(HINSTANCE hInst)
|
|
: m_hInstance(hInst)
|
|
, m_TearingSupported(false)
|
|
{
|
|
// Windows 10 Creators update adds Per Monitor V2 DPI awareness context.
|
|
// Using this awareness context allows the client area of the window
|
|
// to achieve 100% scaling while still allowing non-client window content to
|
|
// be rendered in a DPI sensitive fashion.
|
|
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
|
|
|
#if defined(_DEBUG)
|
|
// Always enable the debug layer before doing anything DX12 related
|
|
// so all possible errors generated while creating DX12 objects
|
|
// are caught by the debug layer.
|
|
ComPtr<ID3D12Debug> debugInterface;
|
|
ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&debugInterface)));
|
|
debugInterface->EnableDebugLayer();
|
|
#endif
|
|
|
|
WNDCLASSEXW wndClass = { 0 };
|
|
|
|
wndClass.cbSize = sizeof(WNDCLASSEX);
|
|
wndClass.style = CS_HREDRAW | CS_VREDRAW;
|
|
wndClass.lpfnWndProc = &WndProc;
|
|
wndClass.hInstance = m_hInstance;
|
|
wndClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
|
wndClass.hIcon = nullptr;//LoadIcon(m_hInstance, MAKEINTRESOURCE(APP_ICON));
|
|
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
wndClass.lpszMenuName = nullptr;
|
|
wndClass.lpszClassName = WINDOW_CLASS_NAME;
|
|
wndClass.hIconSm = nullptr;//LoadIcon(m_hInstance, MAKEINTRESOURCE(APP_ICON));
|
|
|
|
if (!RegisterClassExW(&wndClass))
|
|
{
|
|
MessageBoxA(NULL, "Unable to register the window class.", "Error", MB_OK | MB_ICONERROR);
|
|
}
|
|
|
|
m_dxgiAdapter = GetAdapter(false);
|
|
if (m_dxgiAdapter)
|
|
{
|
|
m_d3d12Device = CreateDevice(m_dxgiAdapter);
|
|
}
|
|
if (m_d3d12Device)
|
|
{
|
|
m_DirectCommandQueue = std::make_shared<CommandQueue>(m_d3d12Device, D3D12_COMMAND_LIST_TYPE_DIRECT);
|
|
m_ComputeCommandQueue = std::make_shared<CommandQueue>(m_d3d12Device, D3D12_COMMAND_LIST_TYPE_COMPUTE);
|
|
m_CopyCommandQueue = std::make_shared<CommandQueue>(m_d3d12Device, D3D12_COMMAND_LIST_TYPE_COPY);
|
|
|
|
m_TearingSupported = CheckTearingSupport();
|
|
}
|
|
}
|
|
|
|
void Application::Create(HINSTANCE hInst)
|
|
{
|
|
if (!gs_pSingelton)
|
|
{
|
|
gs_pSingelton = new Application(hInst);
|
|
}
|
|
}
|
|
|
|
Application& Application::Get()
|
|
{
|
|
assert(gs_pSingelton);
|
|
return *gs_pSingelton;
|
|
}
|
|
|
|
void Application::Destroy()
|
|
{
|
|
if (gs_pSingelton)
|
|
{
|
|
assert(gs_Windows.empty() && gs_WindowByName.empty() &&
|
|
"All windows should be destroyed before destroying the application instance.");
|
|
|
|
delete gs_pSingelton;
|
|
gs_pSingelton = nullptr;
|
|
}
|
|
}
|
|
|
|
Application::~Application()
|
|
{
|
|
Flush();
|
|
}
|
|
|
|
Microsoft::WRL::ComPtr<IDXGIAdapter4> Application::GetAdapter(bool bUseWarp)
|
|
{
|
|
ComPtr<IDXGIFactory4> dxgiFactory;
|
|
UINT createFactoryFlags = 0;
|
|
#if defined(_DEBUG)
|
|
createFactoryFlags = DXGI_CREATE_FACTORY_DEBUG;
|
|
#endif
|
|
|
|
ThrowIfFailed(CreateDXGIFactory2(createFactoryFlags, IID_PPV_ARGS(&dxgiFactory)));
|
|
|
|
ComPtr<IDXGIAdapter1> dxgiAdapter1;
|
|
ComPtr<IDXGIAdapter4> dxgiAdapter4;
|
|
|
|
if (bUseWarp)
|
|
{
|
|
ThrowIfFailed(dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&dxgiAdapter1)));
|
|
ThrowIfFailed(dxgiAdapter1.As(&dxgiAdapter4));
|
|
}
|
|
else
|
|
{
|
|
SIZE_T maxDedicatedVideoMemory = 0;
|
|
for (UINT i = 0; dxgiFactory->EnumAdapters1(i, &dxgiAdapter1) != DXGI_ERROR_NOT_FOUND; ++i)
|
|
{
|
|
DXGI_ADAPTER_DESC1 dxgiAdapterDesc1;
|
|
dxgiAdapter1->GetDesc1(&dxgiAdapterDesc1);
|
|
|
|
// Check to see if the adapter can create a D3D12 device without actually
|
|
// creating it. The adapter with the largest dedicated video memory
|
|
// is favored.
|
|
if ((dxgiAdapterDesc1.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) == 0 &&
|
|
SUCCEEDED(D3D12CreateDevice(dxgiAdapter1.Get(),
|
|
D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), nullptr)) &&
|
|
dxgiAdapterDesc1.DedicatedVideoMemory > maxDedicatedVideoMemory)
|
|
{
|
|
maxDedicatedVideoMemory = dxgiAdapterDesc1.DedicatedVideoMemory;
|
|
ThrowIfFailed(dxgiAdapter1.As(&dxgiAdapter4));
|
|
}
|
|
}
|
|
}
|
|
|
|
return dxgiAdapter4;
|
|
}
|
|
Microsoft::WRL::ComPtr<ID3D12Device2> Application::CreateDevice(Microsoft::WRL::ComPtr<IDXGIAdapter4> adapter)
|
|
{
|
|
ComPtr<ID3D12Device2> d3d12Device2;
|
|
ThrowIfFailed(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&d3d12Device2)));
|
|
// NAME_D3D12_OBJECT(d3d12Device2);
|
|
|
|
// Enable debug messages in debug mode.
|
|
#if defined(_DEBUG)
|
|
ComPtr<ID3D12InfoQueue> pInfoQueue;
|
|
if (SUCCEEDED(d3d12Device2.As(&pInfoQueue)))
|
|
{
|
|
pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE);
|
|
pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE);
|
|
pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE);
|
|
|
|
// Suppress whole categories of messages
|
|
//D3D12_MESSAGE_CATEGORY Categories[] = {};
|
|
|
|
// Suppress messages based on their severity level
|
|
D3D12_MESSAGE_SEVERITY Severities[] =
|
|
{
|
|
D3D12_MESSAGE_SEVERITY_INFO
|
|
};
|
|
|
|
// Suppress individual messages by their ID
|
|
D3D12_MESSAGE_ID DenyIds[] = {
|
|
D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, // I'm really not sure how to avoid this message.
|
|
D3D12_MESSAGE_ID_MAP_INVALID_NULLRANGE, // This warning occurs when using capture frame while graphics debugging.
|
|
D3D12_MESSAGE_ID_UNMAP_INVALID_NULLRANGE, // This warning occurs when using capture frame while graphics debugging.
|
|
};
|
|
|
|
D3D12_INFO_QUEUE_FILTER NewFilter = {};
|
|
//NewFilter.DenyList.NumCategories = _countof(Categories);
|
|
//NewFilter.DenyList.pCategoryList = Categories;
|
|
NewFilter.DenyList.NumSeverities = _countof(Severities);
|
|
NewFilter.DenyList.pSeverityList = Severities;
|
|
NewFilter.DenyList.NumIDs = _countof(DenyIds);
|
|
NewFilter.DenyList.pIDList = DenyIds;
|
|
|
|
ThrowIfFailed(pInfoQueue->PushStorageFilter(&NewFilter));
|
|
}
|
|
#endif
|
|
|
|
return d3d12Device2;
|
|
}
|
|
|
|
bool Application::CheckTearingSupport()
|
|
{
|
|
BOOL allowTearing = FALSE;
|
|
|
|
// Rather than create the DXGI 1.5 factory interface directly, we create the
|
|
// DXGI 1.4 interface and query for the 1.5 interface. This is to enable the
|
|
// graphics debugging tools which will not support the 1.5 factory interface
|
|
// until a future update.
|
|
ComPtr<IDXGIFactory4> factory4;
|
|
if (SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&factory4))))
|
|
{
|
|
ComPtr<IDXGIFactory5> factory5;
|
|
if (SUCCEEDED(factory4.As(&factory5)))
|
|
{
|
|
factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING,
|
|
&allowTearing, sizeof(allowTearing));
|
|
}
|
|
}
|
|
|
|
return allowTearing == TRUE;
|
|
}
|
|
|
|
bool Application::IsTearingSupported() const
|
|
{
|
|
return m_TearingSupported;
|
|
}
|
|
|
|
std::shared_ptr<Window> Application::CreateRenderWindow(const std::wstring& windowName, int clientWidth, int clientHeight, bool vSync)
|
|
{
|
|
// First check if a window with the given name already exists.
|
|
WindowNameMap::iterator windowIter = gs_WindowByName.find(windowName);
|
|
if (windowIter != gs_WindowByName.end())
|
|
{
|
|
return windowIter->second;
|
|
}
|
|
|
|
RECT windowRect = { 0, 0, clientWidth, clientHeight };
|
|
AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);
|
|
|
|
HWND hWnd = CreateWindowW(WINDOW_CLASS_NAME, windowName.c_str(),
|
|
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
windowRect.right - windowRect.left,
|
|
windowRect.bottom - windowRect.top,
|
|
nullptr, nullptr, m_hInstance, nullptr);
|
|
|
|
if (!hWnd)
|
|
{
|
|
MessageBoxA(NULL, "Could not create the render window.", "Error", MB_OK | MB_ICONERROR);
|
|
return nullptr;
|
|
}
|
|
|
|
WindowPtr pWindow = std::make_shared<MakeWindow>(hWnd, windowName, clientWidth, clientHeight, vSync);
|
|
|
|
gs_Windows.insert(WindowMap::value_type(hWnd, pWindow));
|
|
gs_WindowByName.insert(WindowNameMap::value_type(windowName, pWindow));
|
|
|
|
return pWindow;
|
|
}
|
|
|
|
void Application::DestroyWindow(std::shared_ptr<Window> window)
|
|
{
|
|
if (window) window->Destroy();
|
|
}
|
|
|
|
void Application::DestroyWindow(const std::wstring& windowName)
|
|
{
|
|
WindowPtr pWindow = GetWindowByName(windowName);
|
|
if (pWindow)
|
|
{
|
|
DestroyWindow(pWindow);
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Window> Application::GetWindowByName(const std::wstring& windowName)
|
|
{
|
|
std::shared_ptr<Window> window;
|
|
WindowNameMap::iterator iter = gs_WindowByName.find(windowName);
|
|
if (iter != gs_WindowByName.end())
|
|
{
|
|
window = iter->second;
|
|
}
|
|
|
|
return window;
|
|
}
|
|
|
|
|
|
int Application::Run(std::shared_ptr<Game> pGame)
|
|
{
|
|
if (!pGame->Initialize()) return 1;
|
|
if (!pGame->LoadContent()) return 2;
|
|
|
|
MSG msg = { 0 };
|
|
while (msg.message != WM_QUIT)
|
|
{
|
|
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
// Flush any commands in the commands queues before quiting.
|
|
Flush();
|
|
|
|
pGame->UnloadContent();
|
|
pGame->Destroy();
|
|
|
|
return static_cast<int>(msg.wParam);
|
|
}
|
|
|
|
void Application::Quit(int exitCode)
|
|
{
|
|
PostQuitMessage(exitCode);
|
|
}
|
|
|
|
Microsoft::WRL::ComPtr<ID3D12Device2> Application::GetDevice() const
|
|
{
|
|
return m_d3d12Device;
|
|
}
|
|
|
|
std::shared_ptr<CommandQueue> Application::GetCommandQueue(D3D12_COMMAND_LIST_TYPE type) const
|
|
{
|
|
std::shared_ptr<CommandQueue> commandQueue;
|
|
switch (type)
|
|
{
|
|
case D3D12_COMMAND_LIST_TYPE_DIRECT:
|
|
commandQueue = m_DirectCommandQueue;
|
|
break;
|
|
case D3D12_COMMAND_LIST_TYPE_COMPUTE:
|
|
commandQueue = m_ComputeCommandQueue;
|
|
break;
|
|
case D3D12_COMMAND_LIST_TYPE_COPY:
|
|
commandQueue = m_CopyCommandQueue;
|
|
break;
|
|
default:
|
|
assert(false && "Invalid command queue type.");
|
|
}
|
|
|
|
return commandQueue;
|
|
}
|
|
|
|
void Application::Flush()
|
|
{
|
|
m_DirectCommandQueue->Flush();
|
|
m_ComputeCommandQueue->Flush();
|
|
m_CopyCommandQueue->Flush();
|
|
}
|
|
|
|
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> Application::CreateDescriptorHeap(UINT numDescriptors, D3D12_DESCRIPTOR_HEAP_TYPE type)
|
|
{
|
|
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
|
|
desc.Type = type;
|
|
desc.NumDescriptors = numDescriptors;
|
|
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
|
|
desc.NodeMask = 0;
|
|
|
|
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> descriptorHeap;
|
|
ThrowIfFailed(m_d3d12Device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&descriptorHeap)));
|
|
|
|
return descriptorHeap;
|
|
}
|
|
|
|
UINT Application::GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE type) const
|
|
{
|
|
return m_d3d12Device->GetDescriptorHandleIncrementSize(type);
|
|
}
|
|
|
|
|
|
// Remove a window from our window lists.
|
|
static void RemoveWindow(HWND hWnd)
|
|
{
|
|
WindowMap::iterator windowIter = gs_Windows.find(hWnd);
|
|
if (windowIter != gs_Windows.end())
|
|
{
|
|
WindowPtr pWindow = windowIter->second;
|
|
gs_WindowByName.erase(pWindow->GetWindowName());
|
|
gs_Windows.erase(windowIter);
|
|
}
|
|
}
|
|
|
|
// Convert the message ID into a MouseButton ID
|
|
MouseButtonEventArgs::MouseButton DecodeMouseButton(UINT messageID)
|
|
{
|
|
MouseButtonEventArgs::MouseButton mouseButton = MouseButtonEventArgs::None;
|
|
switch (messageID)
|
|
{
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
case WM_LBUTTONDBLCLK:
|
|
{
|
|
mouseButton = MouseButtonEventArgs::Left;
|
|
}
|
|
break;
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONUP:
|
|
case WM_RBUTTONDBLCLK:
|
|
{
|
|
mouseButton = MouseButtonEventArgs::Right;
|
|
}
|
|
break;
|
|
case WM_MBUTTONDOWN:
|
|
case WM_MBUTTONUP:
|
|
case WM_MBUTTONDBLCLK:
|
|
{
|
|
mouseButton = MouseButtonEventArgs::Middel;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return mouseButton;
|
|
}
|
|
|
|
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
WindowPtr pWindow;
|
|
{
|
|
WindowMap::iterator iter = gs_Windows.find(hwnd);
|
|
if (iter != gs_Windows.end())
|
|
{
|
|
pWindow = iter->second;
|
|
}
|
|
}
|
|
|
|
if (pWindow)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_PAINT:
|
|
{
|
|
// Delta time will be filled in by the Window.
|
|
UpdateEventArgs updateEventArgs(0.0f, 0.0f);
|
|
pWindow->OnUpdate(updateEventArgs);
|
|
RenderEventArgs renderEventArgs(0.0f, 0.0f);
|
|
// Delta time will be filled in by the Window.
|
|
pWindow->OnRender(renderEventArgs);
|
|
}
|
|
break;
|
|
case WM_SYSKEYDOWN:
|
|
case WM_KEYDOWN:
|
|
{
|
|
MSG charMsg;
|
|
// Get the Unicode character (UTF-16)
|
|
unsigned int c = 0;
|
|
// For printable characters, the next message will be WM_CHAR.
|
|
// This message contains the character code we need to send the KeyPressed event.
|
|
// Inspired by the SDL 1.2 implementation.
|
|
if (PeekMessage(&charMsg, hwnd, 0, 0, PM_NOREMOVE) && charMsg.message == WM_CHAR)
|
|
{
|
|
GetMessage(&charMsg, hwnd, 0, 0);
|
|
c = static_cast<unsigned int>(charMsg.wParam);
|
|
}
|
|
bool shift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
|
|
bool control = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
|
|
bool alt = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
|
|
KeyCode::Key key = (KeyCode::Key)wParam;
|
|
unsigned int scanCode = (lParam & 0x00FF0000) >> 16;
|
|
KeyEventArgs keyEventArgs(key, c, KeyEventArgs::Pressed, shift, control, alt);
|
|
pWindow->OnKeyPressed(keyEventArgs);
|
|
}
|
|
break;
|
|
case WM_SYSKEYUP:
|
|
case WM_KEYUP:
|
|
{
|
|
bool shift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
|
|
bool control = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
|
|
bool alt = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
|
|
KeyCode::Key key = (KeyCode::Key)wParam;
|
|
unsigned int c = 0;
|
|
unsigned int scanCode = (lParam & 0x00FF0000) >> 16;
|
|
|
|
// Determine which key was released by converting the key code and the scan code
|
|
// to a printable character (if possible).
|
|
// Inspired by the SDL 1.2 implementation.
|
|
unsigned char keyboardState[256];
|
|
GetKeyboardState(keyboardState);
|
|
wchar_t translatedCharacters[4];
|
|
if (int result = ToUnicodeEx(static_cast<UINT>(wParam), scanCode, keyboardState, translatedCharacters, 4, 0, NULL) > 0)
|
|
{
|
|
c = translatedCharacters[0];
|
|
}
|
|
|
|
KeyEventArgs keyEventArgs(key, c, KeyEventArgs::Released, shift, control, alt);
|
|
pWindow->OnKeyReleased(keyEventArgs);
|
|
}
|
|
break;
|
|
// The default window procedure will play a system notification sound
|
|
// when pressing the Alt+Enter keyboard combination if this message is
|
|
// not handled.
|
|
case WM_SYSCHAR:
|
|
break;
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
bool lButton = (wParam & MK_LBUTTON) != 0;
|
|
bool rButton = (wParam & MK_RBUTTON) != 0;
|
|
bool mButton = (wParam & MK_MBUTTON) != 0;
|
|
bool shift = (wParam & MK_SHIFT) != 0;
|
|
bool control = (wParam & MK_CONTROL) != 0;
|
|
|
|
int x = ((int)(short)LOWORD(lParam));
|
|
int y = ((int)(short)HIWORD(lParam));
|
|
|
|
MouseMotionEventArgs mouseMotionEventArgs(lButton, mButton, rButton, control, shift, x, y);
|
|
pWindow->OnMouseMoved(mouseMotionEventArgs);
|
|
}
|
|
break;
|
|
case WM_LBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_MBUTTONDOWN:
|
|
{
|
|
bool lButton = (wParam & MK_LBUTTON) != 0;
|
|
bool rButton = (wParam & MK_RBUTTON) != 0;
|
|
bool mButton = (wParam & MK_MBUTTON) != 0;
|
|
bool shift = (wParam & MK_SHIFT) != 0;
|
|
bool control = (wParam & MK_CONTROL) != 0;
|
|
|
|
int x = ((int)(short)LOWORD(lParam));
|
|
int y = ((int)(short)HIWORD(lParam));
|
|
|
|
MouseButtonEventArgs mouseButtonEventArgs(DecodeMouseButton(message), MouseButtonEventArgs::Pressed, lButton, mButton, rButton, control, shift, x, y);
|
|
pWindow->OnMouseButtonPressed(mouseButtonEventArgs);
|
|
}
|
|
break;
|
|
case WM_LBUTTONUP:
|
|
case WM_RBUTTONUP:
|
|
case WM_MBUTTONUP:
|
|
{
|
|
bool lButton = (wParam & MK_LBUTTON) != 0;
|
|
bool rButton = (wParam & MK_RBUTTON) != 0;
|
|
bool mButton = (wParam & MK_MBUTTON) != 0;
|
|
bool shift = (wParam & MK_SHIFT) != 0;
|
|
bool control = (wParam & MK_CONTROL) != 0;
|
|
|
|
int x = ((int)(short)LOWORD(lParam));
|
|
int y = ((int)(short)HIWORD(lParam));
|
|
|
|
MouseButtonEventArgs mouseButtonEventArgs(DecodeMouseButton(message), MouseButtonEventArgs::Released, lButton, mButton, rButton, control, shift, x, y);
|
|
pWindow->OnMouseButtonReleased(mouseButtonEventArgs);
|
|
}
|
|
break;
|
|
case WM_MOUSEWHEEL:
|
|
{
|
|
// The distance the mouse wheel is rotated.
|
|
// A positive value indicates the wheel was rotated to the right.
|
|
// A negative value indicates the wheel was rotated to the left.
|
|
float zDelta = ((int)(short)HIWORD(wParam)) / (float)WHEEL_DELTA;
|
|
short keyStates = (short)LOWORD(wParam);
|
|
|
|
bool lButton = (keyStates & MK_LBUTTON) != 0;
|
|
bool rButton = (keyStates & MK_RBUTTON) != 0;
|
|
bool mButton = (keyStates & MK_MBUTTON) != 0;
|
|
bool shift = (keyStates & MK_SHIFT) != 0;
|
|
bool control = (keyStates & MK_CONTROL) != 0;
|
|
|
|
int x = ((int)(short)LOWORD(lParam));
|
|
int y = ((int)(short)HIWORD(lParam));
|
|
|
|
// Convert the screen coordinates to client coordinates.
|
|
POINT clientToScreenPoint;
|
|
clientToScreenPoint.x = x;
|
|
clientToScreenPoint.y = y;
|
|
ScreenToClient(hwnd, &clientToScreenPoint);
|
|
|
|
MouseWheelEventArgs mouseWheelEventArgs(zDelta, lButton, mButton, rButton, control, shift, (int)clientToScreenPoint.x, (int)clientToScreenPoint.y);
|
|
pWindow->OnMouseWheel(mouseWheelEventArgs);
|
|
}
|
|
break;
|
|
case WM_SIZE:
|
|
{
|
|
int width = ((int)(short)LOWORD(lParam));
|
|
int height = ((int)(short)HIWORD(lParam));
|
|
|
|
ResizeEventArgs resizeEventArgs(width, height);
|
|
pWindow->OnResize(resizeEventArgs);
|
|
}
|
|
break;
|
|
case WM_DESTROY:
|
|
{
|
|
// If a window is being destroyed, remove it from the
|
|
// window maps.
|
|
RemoveWindow(hwnd);
|
|
|
|
if (gs_Windows.empty())
|
|
{
|
|
// If there are no more windows, quit the application.
|
|
PostQuitMessage(0);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return DefWindowProcW(hwnd, message, wParam, lParam);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return DefWindowProcW(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
} |