348 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			348 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
| #include "..\ebase.h"
 | ||
| #include "..\emanagers.h"
 | ||
| #include <imm.h>
 | ||
| #pragma comment (lib ,"imm32.lib")
 | ||
| 
 | ||
| // 窗口句柄
 | ||
| static HWND s_HWnd = nullptr;
 | ||
| // 是否打开控制台
 | ||
| static bool s_bShowConsole = false;
 | ||
| 
 | ||
| 
 | ||
| bool e2d::Window::__init(const String & sTitle, UINT32 nWidth, UINT32 nHeight, LPCTSTR pIconID /*= nullptr*/)
 | ||
| {
 | ||
| 	if (!Window::__initMutex(sTitle))
 | ||
| 	{
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	// 注册窗口类
 | ||
| 	WNDCLASSEX wcex = { 0 };
 | ||
| 	wcex.cbSize = sizeof(WNDCLASSEX);
 | ||
| 	wcex.style = CS_HREDRAW | CS_VREDRAW;
 | ||
| 	wcex.lpfnWndProc = Window::WndProc;
 | ||
| 	wcex.cbClsExtra = 0;
 | ||
| 	wcex.cbWndExtra = sizeof(LONG_PTR);
 | ||
| 	wcex.hInstance = HINST_THISCOMPONENT;
 | ||
| 	wcex.hbrBackground = NULL;
 | ||
| 	wcex.lpszMenuName = NULL;
 | ||
| 	wcex.hCursor = LoadCursor(NULL, IDI_APPLICATION);
 | ||
| 	wcex.lpszClassName = L"Easy2DApp";
 | ||
| 	// 设置程序图标
 | ||
| 	if (pIconID)
 | ||
| 	{
 | ||
| 		wcex.hIcon = (HICON)::LoadImage(
 | ||
| 			GetModuleHandle(NULL),
 | ||
| 			pIconID,
 | ||
| 			IMAGE_ICON,
 | ||
| 			0,
 | ||
| 			0,
 | ||
| 			LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
 | ||
| 	}
 | ||
| 
 | ||
| 	RegisterClassEx(&wcex);
 | ||
| 
 | ||
| 	// 因为 CreateWindow 函数使用的是像素大小,获取系统的 DPI 以使它
 | ||
| 	// 适应窗口缩放
 | ||
| 	float dpiX, dpiY;
 | ||
| 
 | ||
| 	// 工厂将返回当前的系统 DPI,这个值也将用来创建窗口
 | ||
| 	Renderer::getID2D1Factory()->GetDesktopDpi(&dpiX, &dpiY);
 | ||
| 
 | ||
| 	nWidth = static_cast<UINT>(ceil(nWidth * dpiX / 96.f));
 | ||
| 	nHeight = static_cast<UINT>(ceil(nHeight * dpiY / 96.f));
 | ||
| 
 | ||
| 	// 获取屏幕分辨率
 | ||
| 	UINT screenWidth = static_cast<UINT>(::GetSystemMetrics(SM_CXSCREEN));
 | ||
| 	UINT screenHeight = static_cast<UINT>(::GetSystemMetrics(SM_CYSCREEN));
 | ||
| 	// 当输入的窗口大小比分辨率大时,给出警告
 | ||
| 	WARN_IF(screenWidth < nWidth || screenHeight < nHeight, "The window is larger than screen!");
 | ||
| 	// 取最小值
 | ||
| 	nWidth = min(nWidth, screenWidth);
 | ||
| 	nHeight = min(nHeight, screenHeight);
 | ||
| 
 | ||
| 	// 创建窗口
 | ||
| 	s_HWnd = CreateWindow(
 | ||
| 		L"Easy2DApp",
 | ||
| 		sTitle,
 | ||
| 		WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX,
 | ||
| 		0, 0, nWidth, nHeight,
 | ||
| 		NULL,
 | ||
| 		NULL,
 | ||
| 		HINST_THISCOMPONENT,
 | ||
| 		NULL
 | ||
| 	);
 | ||
| 
 | ||
| 	HRESULT hr = s_HWnd ? S_OK : E_FAIL;
 | ||
| 
 | ||
| 	if (SUCCEEDED(hr))
 | ||
| 	{
 | ||
| 		// 禁用输入法
 | ||
| 		Window::setTypewritingEnable(false);
 | ||
| 		// 查找是否存在控制台
 | ||
| 		HWND hwnd = ::GetConsoleWindow();
 | ||
| 		if (hwnd)
 | ||
| 		{
 | ||
| 			// 禁用控制台关闭按钮
 | ||
| 			HMENU hmenu = ::GetSystemMenu(hwnd, FALSE);
 | ||
| 			::RemoveMenu(hmenu, SC_CLOSE, MF_BYCOMMAND);
 | ||
| 			// 默认隐藏控制台
 | ||
| 			if (!s_bShowConsole)
 | ||
| 			{
 | ||
| 				::ShowWindow(hwnd, SW_HIDE);
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 	else
 | ||
| 	{
 | ||
| 		UnregisterClass(L"Easy2DApp", HINST_THISCOMPONENT);
 | ||
| 	}
 | ||
| 
 | ||
| 	if (FAILED(hr))
 | ||
| 	{
 | ||
| 		MessageBox(nullptr, L"Create Window Failed!", L"Error", MB_OK);
 | ||
| 	}
 | ||
| 
 | ||
| 	return SUCCEEDED(hr);
 | ||
| }
 | ||
| 
 | ||
| bool e2d::Window::__initMutex(const String & sTitle)
 | ||
| {
 | ||
| 	// 创建进程互斥体
 | ||
| 	HANDLE m_hMutex = ::CreateMutex(NULL, TRUE, L"Easy2DApp-" + sTitle);
 | ||
| 
 | ||
| 	if (m_hMutex == nullptr)
 | ||
| 	{
 | ||
| 		WARN_IF(true, "CreateMutex Failed!");
 | ||
| 		return true;
 | ||
| 	}
 | ||
| 
 | ||
| 	// 如果程序已经存在并且正在运行
 | ||
| 	if (::GetLastError() == ERROR_ALREADY_EXISTS)
 | ||
| 	{
 | ||
| 		// 获取窗口句柄
 | ||
| 		HWND hProgramWnd = ::FindWindow(L"Easy2DApp", sTitle);
 | ||
| 		if (hProgramWnd)
 | ||
| 		{
 | ||
| 			// 获取窗口显示状态
 | ||
| 			WINDOWPLACEMENT wpm;
 | ||
| 			::GetWindowPlacement(hProgramWnd, &wpm);
 | ||
| 			// 将运行的程序窗口还原成正常状态
 | ||
| 			wpm.showCmd = SW_SHOW;
 | ||
| 			::SetWindowPlacement(hProgramWnd, &wpm);
 | ||
| 			::SetWindowPos(hProgramWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
 | ||
| 		}
 | ||
| 
 | ||
| 		// 关闭进程互斥体
 | ||
| 		CloseHandle(m_hMutex);
 | ||
| 		m_hMutex = nullptr;
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	return true;
 | ||
| }
 | ||
| 
 | ||
| void e2d::Window::__uninit()
 | ||
| {
 | ||
| 	// 关闭控制台
 | ||
| 	if (::GetConsoleWindow())
 | ||
| 	{
 | ||
| 		::FreeConsole();
 | ||
| 	}
 | ||
| 	// 关闭窗口
 | ||
| 	DestroyWindow(s_HWnd);
 | ||
| 	s_HWnd = nullptr;
 | ||
| }
 | ||
| 
 | ||
| void e2d::Window::__poll()
 | ||
| {
 | ||
| 	static MSG msg;
 | ||
| 
 | ||
| 	while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
 | ||
| 	{
 | ||
| 		::TranslateMessage(&msg);
 | ||
| 		::DispatchMessage(&msg);
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| double e2d::Window::getWidth()
 | ||
| {
 | ||
| 	return Renderer::getRenderTarget()->GetSize().width;
 | ||
| }
 | ||
| 
 | ||
| double e2d::Window::getHeight()
 | ||
| {
 | ||
| 	return Renderer::getRenderTarget()->GetSize().height;
 | ||
| }
 | ||
| 
 | ||
| e2d::Size e2d::Window::getSize()
 | ||
| {
 | ||
| 	D2D1_SIZE_F size = Renderer::getRenderTarget()->GetSize();
 | ||
| 	return Size(size.width, size.height);
 | ||
| }
 | ||
| 
 | ||
| HWND e2d::Window::getHWnd()
 | ||
| {
 | ||
| 	return s_HWnd;
 | ||
| }
 | ||
| 
 | ||
| void e2d::Window::setSize(UINT32 width, UINT32 height)
 | ||
| {
 | ||
| 	// 获取屏幕分辨率
 | ||
| 	int screenWidth = ::GetSystemMetrics(SM_CXSCREEN);
 | ||
| 	int screenHeight = ::GetSystemMetrics(SM_CYSCREEN);
 | ||
| 	// 获取窗口大小(包含菜单栏)
 | ||
| 	tagRECT rcWindow;
 | ||
| 	::GetWindowRect(s_HWnd, &rcWindow);
 | ||
| 	// 获取客户区大小
 | ||
| 	tagRECT rcClient;
 | ||
| 	::GetClientRect(s_HWnd, &rcClient);
 | ||
| 	// 计算边框大小
 | ||
| 	width += (rcWindow.right - rcWindow.left) - (rcClient.right - rcClient.left);
 | ||
| 	height += (rcWindow.bottom - rcWindow.top) - (rcClient.bottom - rcClient.top);
 | ||
| 	// 修改窗口大小,并设置窗口在屏幕居中
 | ||
| 	::MoveWindow(s_HWnd, (screenWidth - width) / 2, (screenHeight - height) / 2, width, height, TRUE);
 | ||
| }
 | ||
| 
 | ||
| void e2d::Window::setTitle(const String &title)
 | ||
| {
 | ||
| 	// 设置窗口标题
 | ||
| 	::SetWindowText(s_HWnd, title);
 | ||
| }
 | ||
| 
 | ||
| e2d::String e2d::Window::getTitle()
 | ||
| {
 | ||
| 	wchar_t wszTitle[MAX_PATH] = { 0 };
 | ||
| 	::GetWindowText(s_HWnd, wszTitle, MAX_PATH);
 | ||
| 	return wszTitle;
 | ||
| }
 | ||
| 
 | ||
| void e2d::Window::showConsole(bool show /* = true */)
 | ||
| {
 | ||
| 	s_bShowConsole = show;
 | ||
| 	// 查找已存在的控制台句柄
 | ||
| 	HWND hwnd = ::GetConsoleWindow();
 | ||
| 	// 关闭控制台
 | ||
| 	if (show)
 | ||
| 	{
 | ||
| 		if (hwnd)
 | ||
| 		{
 | ||
| 			::ShowWindow(hwnd, SW_SHOWNORMAL);
 | ||
| 		}
 | ||
| 		else
 | ||
| 		{
 | ||
| 			// 显示一个新控制台
 | ||
| 			if (::AllocConsole())
 | ||
| 			{
 | ||
| 				hwnd = ::GetConsoleWindow();
 | ||
| 				// 重定向输入输出
 | ||
| 				FILE * stdoutStream, * stdinStream, * stderrStream;
 | ||
| 				freopen_s(&stdoutStream, "conout$", "w+t", stdout);
 | ||
| 				freopen_s(&stdinStream, "conin$", "r+t", stdin);
 | ||
| 				freopen_s(&stderrStream, "conout$", "w+t", stderr);
 | ||
| 				// 禁用控制台关闭按钮
 | ||
| 				HMENU hmenu = ::GetSystemMenu(hwnd, FALSE);
 | ||
| 				::RemoveMenu(hmenu, SC_CLOSE, MF_BYCOMMAND);
 | ||
| 			}
 | ||
| 			else
 | ||
| 			{
 | ||
| 				MessageBox(nullptr, L"Alloc Console Failed!", L"Error", MB_OK);
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 	else 
 | ||
| 	{
 | ||
| 		if (hwnd)
 | ||
| 		{
 | ||
| 			::ShowWindow(hwnd, SW_HIDE);
 | ||
| 		}
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| void e2d::Window::setTypewritingEnable(bool bEnable)
 | ||
| {
 | ||
| 	static HIMC hImc = nullptr;
 | ||
| 
 | ||
| 	if (bEnable)
 | ||
| 	{
 | ||
| 		if (hImc != nullptr)
 | ||
| 		{
 | ||
| 			::ImmAssociateContext(Window::getHWnd(), hImc);
 | ||
| 			hImc = nullptr;
 | ||
| 		}
 | ||
| 	}
 | ||
| 	else
 | ||
| 	{
 | ||
| 		if (hImc == nullptr)
 | ||
| 		{
 | ||
| 			hImc = ::ImmAssociateContext(Window::getHWnd(), nullptr);
 | ||
| 		}
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| LRESULT e2d::Window::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 | ||
| {
 | ||
| 	LRESULT result = 0;
 | ||
| 
 | ||
| 	switch (message)
 | ||
| 	{
 | ||
| 	// 处理窗口大小变化消息
 | ||
| 	case WM_SIZE:
 | ||
| 	{
 | ||
| 		UINT width = LOWORD(lParam);
 | ||
| 		UINT height = HIWORD(lParam);
 | ||
| 		// 如果程序接收到一个 WM_SIZE 消息,这个方法将调整渲染
 | ||
| 		// 目标适当。它可能会调用失败,但是这里可以忽略有可能的
 | ||
| 		// 错误,因为这个错误将在下一次调用 EndDraw 时产生
 | ||
| 		Renderer::getRenderTarget()->Resize(D2D1::SizeU(width, height));
 | ||
| 	}
 | ||
| 	break;
 | ||
| 
 | ||
| 	// 处理分辨率变化消息
 | ||
| 	case WM_DISPLAYCHANGE:
 | ||
| 	{
 | ||
| 		// 重绘客户区
 | ||
| 		InvalidateRect(hWnd, NULL, FALSE);
 | ||
| 	}
 | ||
| 	result = 0;
 | ||
| 	break;
 | ||
| 
 | ||
| 	// 重绘窗口
 | ||
| 	case WM_PAINT:
 | ||
| 	{
 | ||
| 		e2d::Renderer::__render();
 | ||
| 		ValidateRect(hWnd, NULL);
 | ||
| 	}
 | ||
| 	result = 0;
 | ||
| 	break;
 | ||
| 
 | ||
| 	// 窗口关闭消息
 | ||
| 	case WM_CLOSE:
 | ||
| 	{
 | ||
| 		e2d::Scene * pCurrentScene = e2d::SceneManager::getCurrentScene();
 | ||
| 		if (!pCurrentScene || pCurrentScene->onCloseWindow())
 | ||
| 		{
 | ||
| 			e2d::Game::quit();
 | ||
| 			DestroyWindow(hWnd);
 | ||
| 		}
 | ||
| 	}
 | ||
| 	result = 0;
 | ||
| 	break;
 | ||
| 
 | ||
| 	case WM_DESTROY:
 | ||
| 	{
 | ||
| 		PostQuitMessage(0);
 | ||
| 	}
 | ||
| 	result = 1;
 | ||
| 	break;
 | ||
| 
 | ||
| 	default:
 | ||
| 	{
 | ||
| 		result = DefWindowProc(hWnd, message, wParam, lParam);
 | ||
| 	}
 | ||
| 
 | ||
| 	}
 | ||
| 
 | ||
| 	return result;
 | ||
| } |