This repository has been archived by the owner on Jan 13, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Window.cpp
302 lines (268 loc) · 7.7 KB
/
Window.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#include "Window.h"
#include <sstream>
#include "resource.h"
#include "Exception.h"
// 定义static
Window::WindowClass Window::WindowClass::wndClass;
Window::WindowClass::WindowClass() noexcept
: hInst(GetModuleHandle(nullptr))
{
// register window class
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_OWNDC; // 每个窗口独立渲染
wc.lpfnWndProc = HandleMsgSetup; // 用来处理消息的函数
wc.cbClsExtra = 0; // 额外数据
wc.cbWndExtra = 0;
wc.hInstance = GetInstance();
wc.hIcon = static_cast<HICON>(LoadImage(
GetInstance(), MAKEINTRESOURCE(IDI_ICON1),
IMAGE_ICON, 32, 32, 0
));
wc.hCursor = nullptr;
wc.hbrBackground = nullptr;
wc.lpszMenuName = nullptr; // 不需要menu
wc.lpszClassName = GetName(); // 窗口实例从哪个类创建
wc.hIconSm = static_cast<HICON>(LoadImage(
GetInstance(), MAKEINTRESOURCE(IDI_ICON1),
IMAGE_ICON, 16, 16, 0
));
RegisterClassEx(&wc);
}
Window::WindowClass::~WindowClass()
{
UnregisterClass(GetName(), GetInstance());
}
const char* Window::WindowClass::GetName() noexcept
{
// 获取窗口类名
return wndClassName;
}
HINSTANCE Window::WindowClass::GetInstance() noexcept
{
// 获取窗口实例
return wndClass.hInst;
}
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;
// 一般设置大小是整个窗口大小,比如包括边框总共是640*480
// 用这个adjust就让client区域真正变成640*480,并在这基础上自动计算整个窗口大小
// 所以下面create时传的大小参数要继续用wr
if (AdjustWindowRect(&wr, WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, FALSE) == 0) {
THROW_ERROR("AdjustWindowRect failed");
}
// create window & get hWnd
hWnd = CreateWindow(
WindowClass::GetName(), // 窗口类名
name, // 标题
WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, // style
CW_USEDEFAULT,
CW_USEDEFAULT,
wr.right - wr.left, // width
wr.bottom - wr.top, // height
nullptr, // 父亲handle
nullptr, // menu handle
WindowClass::GetInstance(),
this // 自定义参数 handle
);
if (hWnd == nullptr) {
THROW_ERROR("CreateWindow failed");
}
// show
ShowWindow(hWnd, SW_SHOW); //窗口句柄 和 显示方式
// 创建图形
pGfx = std::make_unique<Graphics>(hWnd, width, height);
}
Window::~Window()
{
DestroyWindow(hWnd);
}
void Window::SetTitle(const std::string& title)
{
if (SetWindowText(hWnd, title.c_str()) == 0)
{
THROW_ERROR("SetWindowText failed");
}
}
std::optional<int> Window::ProcessMessages() noexcept
{
MSG msg;
// while queue has messages, remove and dispatch them (but do not block on empty queue)
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
// check for quit because peekmessage does not signal this via return val
if (msg.message == WM_QUIT)
{
// return optional wrapping int (arg to PostQuitMessage is in wparam) signals quit
return msg.wParam;
}
// TranslateMessage will post auxilliary WM_CHAR messages from key msgs
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// return empty optional when not quitting app
return {};
}
int Window::GetWidth(void) const noexcept
{
return width;
}
int Window::GetHeight(void) const noexcept
{
return height;
}
Graphics& Window::Gfx()
{
if (!pGfx)
{
THROW_ERROR("no graphics");
}
return *pGfx;
}
LRESULT WINAPI 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
// 第一次创建窗口时
// WM_NCCREATE 会先于 WM_CREATE
// 做初始化
if (msg == WM_NCCREATE)
{
// extract ptr to window class from creation data
const CREATESTRUCTW* const pCreate = reinterpret_cast<CREATESTRUCTW*>(lParam);
// 这里就拿到了之前create时传的自定义参数
Window* const pWnd = static_cast<Window*>(pCreate->lpCreateParams);
// set WinAPI-managed user data to store ptr to window instance
// 把窗口类和窗口本身联系起来
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pWnd));
// set message proc to normal (non-setup) handler now that setup is finished
//自此消息处理将交给HandleMsgThunk
SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&Window::HandleMsgThunk));
// forward message to window instance 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 WINAPI Window::HandleMsgThunk(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) noexcept
{
// 拿到窗口指针并交给真正的消息处理程序处理
// retrieve ptr to window instance
Window* const pWnd = reinterpret_cast<Window*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
// forward message to window instance handler
return pWnd->HandleMsg(hWnd, msg, wParam, lParam);
}
LRESULT Window::HandleMsg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) noexcept
{
//// 输出收到的消息
//static WindowsMessageMap mm;
//OutputDebugString(mm(msg, lParam, wParam).c_str());
switch (msg) {
case WM_CLOSE: // 改变“关闭”的默认行为
PostQuitMessage(0); // 把退出消息发给消息队列,参数是退出时的返回值,需要winmain中的return配合
return 0; // 这里直接返回,不走下面的def了,避免类被析构2次。现在这里只发quit消息,不实际销毁窗口,由析构函数处理
// clear keystate when window loses focus to prevent input getting "stuck"
// 焦点不在窗口上时(比如有弹窗),就清空键盘状态,以免最后一个按键一直触发
case WM_KILLFOCUS:
kbd.ClearState();
break;
/*********** KEYBOARD MESSAGES ***********/
case WM_KEYDOWN:
// syskey commands need to be handled to track ALT key (VK_MENU) and F10
case WM_SYSKEYDOWN:
// filter autorepeat
if (!(lParam & 0x40000000) || kbd.AutorepeatIsEnabled()) {
kbd.OnKeyPressed(static_cast<unsigned char>(wParam));
}
break;
case WM_KEYUP:
case WM_SYSKEYUP:
kbd.OnKeyReleased(static_cast<unsigned char>(wParam));
break;
case WM_CHAR:
kbd.OnChar(static_cast<unsigned char>(wParam));
break;
/*********** END KEYBOARD MESSAGES ***********/
/************* MOUSE MESSAGES ****************/
case WM_MOUSEMOVE:
{
const POINTS pt = MAKEPOINTS(lParam);
// 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 && pt.y < height)
{
mouse.OnMouseMove(pt.x, pt.y);
if (!mouse.IsInWindow())
{
SetCapture(hWnd);
mouse.OnMouseEnter();
}
}
// not in client -> log move / maintain capture if button down
else
{
if (wParam & (MK_LBUTTON | MK_RBUTTON))
{
mouse.OnMouseMove(pt.x, pt.y);
}
// button up -> release capture / log event for leaving
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);
// release mouse if outside of window
if (pt.x < 0 || pt.x >= width || pt.y < 0 || pt.y >= height)
{
ReleaseCapture();
mouse.OnMouseLeave();
}
break;
}
case WM_RBUTTONUP:
{
const POINTS pt = MAKEPOINTS(lParam);
mouse.OnRightReleased(pt.x, pt.y);
// release mouse if outside of window
if (pt.x < 0 || pt.x >= width || pt.y < 0 || pt.y >= height)
{
ReleaseCapture();
mouse.OnMouseLeave();
}
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;
}
/************** END MOUSE MESSAGES **************/
}
return DefWindowProc(hWnd, msg, wParam, lParam); // 默认消息处理
}