Project Alice
Loading...
Searching...
No Matches
window_win.cpp
Go to the documentation of this file.
1#include "window.hpp"
2#include "map.hpp"
3#include "opengl_wrapper.hpp"
4#include "resource.h"
5#include "system_state.hpp"
6
7#ifndef UNICODE
8#define UNICODE
9#endif
10#define NOMINMAX
11#define WIN32_LEAN_AND_MEAN
12
13#include "Windows.h"
14#include "Windowsx.h"
15#include "wglew.h"
16#include "sound.hpp"
17
18#define WM_GRAPHNOTIFY (WM_APP + 1)
19
20namespace window {
21
22bool is_key_depressed(sys::state const& game_state, sys::virtual_key key) {
23 return GetKeyState(int32_t(key)) & 0x8000;
24}
25
26void get_window_size(sys::state const& game_state, int& width, int& height) {
27 RECT getRect{};
28 GetWindowRect(game_state.win_ptr->hwnd, &getRect);
29 width = (getRect.right - getRect.left);
30 height = (getRect.bottom - getRect.top);
31}
32
33bool is_in_fullscreen(sys::state const& game_state) {
34 return (game_state.win_ptr) && game_state.win_ptr->in_fullscreen;
35}
36
37void set_borderless_full_screen(sys::state& game_state, bool fullscreen) {
38 if(game_state.win_ptr && game_state.win_ptr->hwnd && game_state.win_ptr->in_fullscreen != fullscreen) {
39 if(!fullscreen) {
40
41 auto monitor_handle = MonitorFromWindow(game_state.win_ptr->hwnd, MONITOR_DEFAULTTOPRIMARY);
42 MONITORINFO mi;
43 mi.cbSize = sizeof(mi);
44 GetMonitorInfoW(monitor_handle, &mi);
45
46 int left = (mi.rcWork.right - mi.rcWork.left) / 2 - game_state.win_ptr->creation_x_size / 2;
47 int top = (mi.rcWork.bottom - mi.rcWork.top) / 2 - game_state.win_ptr->creation_y_size / 2;
48
49 DWORD win32Style = WS_VISIBLE | WS_CAPTION | WS_MINIMIZEBOX | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_SYSMENU |
50 WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
51
52 RECT rectangle = {left, top, left + game_state.win_ptr->creation_x_size, top + game_state.win_ptr->creation_y_size};
53 AdjustWindowRectExForDpi(&rectangle, win32Style, false, 0, GetDpiForWindow(game_state.win_ptr->hwnd));
54 int32_t final_width = rectangle.right - rectangle.left;
55 int32_t final_height = rectangle.bottom - rectangle.top;
56
57 SetWindowLongW(game_state.win_ptr->hwnd, GWL_STYLE, win32Style);
58 SetWindowPos(game_state.win_ptr->hwnd, HWND_NOTOPMOST, rectangle.left, rectangle.top, final_width, final_height,
59 SWP_NOREDRAW);
60 SetWindowRgn(game_state.win_ptr->hwnd, NULL, TRUE);
61 ShowWindow(game_state.win_ptr->hwnd, SW_MAXIMIZE);
62
63 game_state.win_ptr->in_fullscreen = false;
64 } else {
65 // ShowWindow(game_state.win_ptr->hwnd, SW_SHOWNORMAL);
66
67 auto monitor_handle = MonitorFromWindow(game_state.win_ptr->hwnd, MONITOR_DEFAULTTOPRIMARY);
68 MONITORINFO mi;
69 mi.cbSize = sizeof(mi);
70 GetMonitorInfoW(monitor_handle, &mi);
71
72 DWORD win32Style = WS_VISIBLE | WS_BORDER | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
73
74 RECT rectangle = mi.rcMonitor;
75 AdjustWindowRectExForDpi(&rectangle, win32Style, false, WS_EX_TOPMOST, GetDpiForWindow(game_state.win_ptr->hwnd));
76 int32_t win_width = (rectangle.right - rectangle.left);
77 int32_t win_height = (rectangle.bottom - rectangle.top);
78
79 SetWindowLongW(game_state.win_ptr->hwnd, GWL_STYLE, win32Style);
80 SetWindowPos(game_state.win_ptr->hwnd, HWND_TOPMOST, rectangle.left, rectangle.top, win_width, win_height, SWP_NOREDRAW);
81
82 game_state.win_ptr->in_fullscreen = true;
83 }
84 }
85}
86
87void close_window(sys::state& game_state) {
88 if(game_state.win_ptr && game_state.win_ptr->hwnd)
89 PostMessageW(game_state.win_ptr->hwnd, WM_CLOSE, 0, 0);
90}
91
93 uint32_t val =
97 return sys::key_modifiers(val);
98}
99
100LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
101
102 static int drag_x_start = 0;
103 static int drag_y_start = 0;
104
105 if(message == WM_CREATE) {
106 CREATESTRUCTW* cptr = (CREATESTRUCTW*)lParam;
107 sys::state* create_state = (sys::state*)(cptr->lpCreateParams);
108
109 create_state->win_ptr->hwnd = hwnd;
110 create_state->win_ptr->opengl_window_dc = GetDC(hwnd);
111
112 // setup opengl here
113 ogl::initialize_opengl(*create_state);
114
115 RECT crect{};
116 GetClientRect(hwnd, &crect);
117 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)create_state);
118
119 return 0;
120 }
121
122 sys::state* state = (sys::state*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
123 if(!state || !(state->win_ptr))
124 return DefWindowProcW(hwnd, message, wParam, lParam);
125
126 switch(message) {
127 case WM_CLOSE:
128 DestroyWindow(hwnd);
129 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) nullptr);
130 return 0;
131 case WM_DESTROY:
132 ogl::shutdown_opengl(*state);
133 ReleaseDC(hwnd, state->win_ptr->opengl_window_dc);
134 PostQuitMessage(0);
135 return 0;
136 case WM_SETFOCUS:
137 if(state->win_ptr->in_fullscreen)
138 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOREDRAW | SWP_NOSIZE | SWP_NOMOVE);
139 if(state->user_settings.mute_on_focus_lost) {
140 sound::resume_all(*state);
141 }
142 return 0;
143 case WM_KILLFOCUS:
144 if(state->win_ptr->in_fullscreen)
145 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOREDRAW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
146 if(state->user_settings.mute_on_focus_lost) {
147 sound::pause_all(*state);
148 }
149 return 0;
150 case WM_LBUTTONDOWN: {
151 SetCapture(hwnd);
152 auto x = GET_X_LPARAM(lParam);
153 auto y = GET_Y_LPARAM(lParam);
154 state->on_lbutton_down(x, y, get_current_modifiers());
155 state->mouse_x_position = x;
156 state->mouse_y_position = y;
157 state->win_ptr->left_mouse_down = true;
158 return 0;
159 }
160 case WM_LBUTTONUP: {
161 ReleaseCapture();
162 auto x = GET_X_LPARAM(lParam);
163 auto y = GET_Y_LPARAM(lParam);
164 state->on_lbutton_up(x, y, get_current_modifiers());
165 state->mouse_x_position = x;
166 state->mouse_y_position = y;
167 state->win_ptr->left_mouse_down = false;
168 return 0;
169 }
170 case WM_MOUSEMOVE: {
171 auto x = GET_X_LPARAM(lParam);
172 auto y = GET_Y_LPARAM(lParam);
173 state->on_mouse_move(x, y, get_current_modifiers());
174
175 if(wParam & MK_LBUTTON)
176 state->on_mouse_drag(x, y, get_current_modifiers());
177
178 state->mouse_x_position = x;
179 state->mouse_y_position = y;
180
181 return 0;
182 }
183 case WM_RBUTTONDOWN: {
184 auto x = GET_X_LPARAM(lParam);
185 auto y = GET_Y_LPARAM(lParam);
186 state->on_rbutton_down(x, y, get_current_modifiers());
187 state->mouse_x_position = x;
188 state->mouse_y_position = y;
189 return 0;
190 }
191 case WM_RBUTTONUP: {
192 auto x = GET_X_LPARAM(lParam);
193 auto y = GET_Y_LPARAM(lParam);
194 state->on_rbutton_up(x, y, get_current_modifiers());
195 state->mouse_x_position = x;
196 state->mouse_y_position = y;
197 return 0;
198 }
199 case WM_MBUTTONDOWN: {
200 auto x = GET_X_LPARAM(lParam);
201 auto y = GET_Y_LPARAM(lParam);
202 state->on_mbutton_down(x, y, get_current_modifiers());
203 state->mouse_x_position = x;
204 state->mouse_y_position = y;
205 return 0;
206 }
207 case WM_MBUTTONUP: {
208 auto x = GET_X_LPARAM(lParam);
209 auto y = GET_Y_LPARAM(lParam);
210 state->on_mbutton_up(x, y, get_current_modifiers());
211 state->mouse_x_position = x;
212 state->mouse_y_position = y;
213 return 0;
214 }
215 case WM_SIZE: {
217
218 if(wParam == SIZE_MAXIMIZED) {
220 } else if(wParam == SIZE_MINIMIZED) {
222 } else if(wParam == SIZE_RESTORED) {
224 } else {
225 // other
226 break;
227 }
228
229 state->on_resize(LOWORD(lParam), HIWORD(lParam), t);
230 state->x_size = LOWORD(lParam);
231 state->y_size = HIWORD(lParam);
232
233 // TODO MAP CAMERA HERE CODE HERE
234 // state->map_camera = map::flat_camera(glm::vec2{ state->x_size, state->y_size }, glm::vec2{
235 // state->map_provinces_texture.size_x, state->map_provinces_texture.size_y });
236
237 return 0;
238 }
239 case WM_MOUSEWHEEL: {
240 state->on_mouse_wheel(state->mouse_x_position, state->mouse_y_position, get_current_modifiers(), (float)(GET_WHEEL_DELTA_WPARAM(wParam)) / 120.0f);
241 return 0;
242 }
243 case WM_KEYDOWN: // fallthrough
244 case WM_SYSKEYDOWN:
245 {
247 switch(key) {
248 case sys::virtual_key::RETURN: [[fallthrough]];
249 case sys::virtual_key::BACK: [[fallthrough]];
250 case sys::virtual_key::DELETE_KEY: [[fallthrough]];
251 case sys::virtual_key::LEFT: [[fallthrough]];
252 case sys::virtual_key::RIGHT: [[fallthrough]];
253 case sys::virtual_key::UP: [[fallthrough]];
255 break;
256 default:
257 if((HIWORD(lParam) & KF_REPEAT) != 0)
258 return 0;
259 }
260 state->on_key_down(sys::virtual_key(wParam), get_current_modifiers());
261 return 0;
262 }
263 case WM_SYSKEYUP:
264 case WM_KEYUP:
265 state->on_key_up(sys::virtual_key(wParam), get_current_modifiers());
266 return 0;
267 case WM_CHAR: {
268 if(state->ui_state.edit_target) {
269 state->on_text(char32_t(wParam));
270 }
271 return 0;
272 }
273
274 case WM_PAINT:
275 case WM_DISPLAYCHANGE: {
276 PAINTSTRUCT ps;
277 BeginPaint(hwnd, &ps);
278 EndPaint(hwnd, &ps);
279 return 0;
280 }
281
282 case WM_DPICHANGED:
283 return 0;
284
285 case WM_GRAPHNOTIFY:
286 // this is the message that tells us there is a DirectShow event
288 break;
289 case WM_GETMINMAXINFO:
290 LPMINMAXINFO info = (LPMINMAXINFO)lParam;
291 info->ptMinTrackSize.x = 640;
292 info->ptMinTrackSize.y = 400;
293 }
294 return DefWindowProcW(hwnd, message, wParam, lParam);
295}
296
297void create_window(sys::state& game_state, creation_parameters const& params) {
298 game_state.win_ptr = std::make_unique<window_data_impl>();
299 game_state.win_ptr->creation_x_size = params.size_x;
300 game_state.win_ptr->creation_y_size = params.size_y;
301 game_state.win_ptr->in_fullscreen = params.borderless_fullscreen;
302
303 // create window
304 WNDCLASSEXW wcex = {};
305
306 wcex.cbSize = sizeof(WNDCLASSEXW);
307 wcex.style = CS_OWNDC;
308 wcex.lpfnWndProc = WndProc;
309 wcex.cbClsExtra = 0;
310 wcex.cbWndExtra = sizeof(LONG_PTR);
311 wcex.hInstance = GetModuleHandleW(nullptr);
312 wcex.hbrBackground = NULL;
313 wcex.lpszMenuName = NULL;
314 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
315 wcex.hIcon = (HICON)LoadImage(GetModuleHandleW(nullptr), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON,
316 GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 0);
317 wcex.lpszClassName = L"project_alice_class";
318 if(RegisterClassExW(&wcex) == 0) {
319 window::emit_error_message("Unable to register window class", true);
320 }
321
322 DWORD win32Style = !params.borderless_fullscreen ? (WS_VISIBLE | WS_CAPTION | WS_MINIMIZEBOX | WS_THICKFRAME | WS_MAXIMIZEBOX |
323 WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS)
324 : WS_VISIBLE | WS_BORDER | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
325
326 game_state.win_ptr->hwnd = CreateWindowExW(0, L"project_alice_class", L"Project Alice", win32Style, CW_USEDEFAULT,
327 CW_USEDEFAULT, 0, 0, NULL, NULL, GetModuleHandleW(nullptr), &game_state);
328
329 if(!game_state.win_ptr->hwnd)
330 return;
331
332 SetCursor(LoadCursor(NULL, IDC_ARROW));
333
334 if(!params.borderless_fullscreen) {
335
336 auto monitor_handle = MonitorFromWindow(game_state.win_ptr->hwnd, MONITOR_DEFAULTTOPRIMARY);
337 MONITORINFO mi;
338 mi.cbSize = sizeof(mi);
339 GetMonitorInfoW(monitor_handle, &mi);
340
341 int left = (mi.rcWork.right - mi.rcWork.left) / 2 - game_state.win_ptr->creation_x_size / 2;
342 int top = (mi.rcWork.bottom - mi.rcWork.top) / 2 - game_state.win_ptr->creation_y_size / 2;
343
344 RECT rectangle = {left, top, left + game_state.win_ptr->creation_x_size, top + game_state.win_ptr->creation_y_size};
345 AdjustWindowRectExForDpi(&rectangle, win32Style, false, 0, GetDpiForWindow(game_state.win_ptr->hwnd));
346 int32_t final_width = rectangle.right - rectangle.left;
347 int32_t final_height = rectangle.bottom - rectangle.top;
348
349 SetWindowLongW(game_state.win_ptr->hwnd, GWL_STYLE, win32Style);
350 SetWindowPos(game_state.win_ptr->hwnd, HWND_NOTOPMOST, rectangle.left, rectangle.top, final_width, final_height,
351 SWP_FRAMECHANGED);
352 SetWindowRgn(game_state.win_ptr->hwnd, NULL, TRUE);
353
354 if(params.initial_state == window_state::maximized)
355 ShowWindow(game_state.win_ptr->hwnd, SW_MAXIMIZE);
356 else if(params.initial_state == window_state::minimized)
357 ShowWindow(game_state.win_ptr->hwnd, SW_MINIMIZE);
358 else
359 ShowWindow(game_state.win_ptr->hwnd, SW_SHOWNORMAL);
360 } else {
361
362 auto monitor_handle = MonitorFromWindow(game_state.win_ptr->hwnd, MONITOR_DEFAULTTOPRIMARY);
363 MONITORINFO mi;
364 mi.cbSize = sizeof(mi);
365 GetMonitorInfoW(monitor_handle, &mi);
366
367 RECT rectangle = mi.rcMonitor;
368 AdjustWindowRectExForDpi(&rectangle, win32Style, false, WS_EX_TOPMOST, GetDpiForWindow(game_state.win_ptr->hwnd));
369 int32_t win_width = (rectangle.right - rectangle.left);
370 int32_t win_height = (rectangle.bottom - rectangle.top);
371
372 SetWindowLongW(game_state.win_ptr->hwnd, GWL_STYLE, win32Style);
373 SetWindowPos(game_state.win_ptr->hwnd, HWND_TOPMOST, rectangle.left, rectangle.top, win_width, win_height, SWP_FRAMECHANGED);
374 ShowWindow(game_state.win_ptr->hwnd, SW_SHOWNORMAL);
375 }
376
377 UpdateWindow(game_state.win_ptr->hwnd);
378
380 sound::start_music(game_state, game_state.user_settings.master_volume * game_state.user_settings.music_volume);
381
382 change_cursor(game_state, cursor_type::busy);
383 game_state.on_create();
385
386 MSG msg;
387 // pump message loop
388 while(true) {
389 if(PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) {
390 if(msg.message == WM_QUIT) {
391 break;
392 }
393 if(game_state.ui_state.edit_target)
394 TranslateMessage(&msg);
395 DispatchMessageW(&msg);
396 } else {
397 // Run game code
398
399 game_state.render();
400 SwapBuffers(game_state.win_ptr->opengl_window_dc);
401 }
402 }
403}
404
405void change_cursor(sys::state& state, cursor_type type) {
406 auto root = simple_fs::get_root(state.common_fs);
407 auto gfx_dir = simple_fs::open_directory(root, NATIVE("gfx"));
408 auto cursors_dir = simple_fs::open_directory(gfx_dir, NATIVE("cursors"));
409
410 if(state.win_ptr->cursors[uint8_t(type)] == HCURSOR(NULL)) {
411 native_string_view fname = NATIVE("normal.cur");
412 switch(type) {
414 fname = NATIVE("normal.cur");
415 break;
417 fname = NATIVE("busy.ani");
418 break;
420 fname = NATIVE("dragselect.ani");
421 break;
423 fname = NATIVE("attack_move.ani");
424 break;
426 fname = NATIVE("friendly_move.ani");
427 break;
429 fname = NATIVE("no_move.ani");
430 break;
431 default:
432 fname = NATIVE("normal.cur");
433 break;
434 }
435
436 // adapted from https://stackoverflow.com/questions/34065/how-to-read-a-value-from-the-windows-registry
437
438 HKEY hKey;
439 auto response = RegOpenKeyExW(HKEY_CURRENT_USER, NATIVE("Software\\Microsoft\\Accessibility"), 0, KEY_READ, &hKey);
440 bool exists = (response == ERROR_SUCCESS);
441 auto cursor_size_key = NATIVE("CursorSize");
442
443 uint32_t cursor_size = 1;
444
445 if(exists) {
446 DWORD dwBufferSize(sizeof(DWORD));
447 DWORD nResult(0);
448 LONG get_cursor_size_error = RegQueryValueExW(hKey,
449 cursor_size_key,
450 0,
451 NULL,
452 reinterpret_cast<LPBYTE>(&nResult),
453 &dwBufferSize);
454 if(get_cursor_size_error == ERROR_SUCCESS) {
455 cursor_size = nResult;
456 }
457 }
458
459 RegCloseKey(hKey);
460
461 if(auto f = simple_fs::peek_file(cursors_dir, fname); f) {
462 auto path = simple_fs::get_full_name(*f);
463 state.win_ptr->cursors[uint8_t(type)] = (HCURSOR)LoadImageW(nullptr, path.c_str(), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE); //.cur or .ani
464
465 if(state.win_ptr->cursors[uint8_t(type)] == HCURSOR(NULL)) {
466 state.win_ptr->cursors[uint8_t(type)] = LoadCursor(nullptr, IDC_ARROW); //default system cursor
467 state.ui_state.cursor_size = GetSystemMetrics(SM_CXCURSOR) * cursor_size / 2;
468 } else {
469 state.ui_state.cursor_size = GetSystemMetrics(SM_CXCURSOR) * cursor_size / 2;
470 }
471 } else {
472 state.win_ptr->cursors[uint8_t(type)] = LoadCursor(nullptr, IDC_ARROW); //default system cursor
473 state.ui_state.cursor_size = GetSystemMetrics(SM_CXCURSOR) * cursor_size / 2;
474 }
475 }
476 SetCursor(state.win_ptr->cursors[uint8_t(type)]);
477 SetClassLongPtr(state.win_ptr->hwnd, GCLP_HCURSOR, reinterpret_cast<LONG_PTR>(state.win_ptr->cursors[uint8_t(type)]));
478}
479
480void emit_error_message(std::string const& content, bool fatal) {
481 static const char* msg1 = "Project Alice has encountered a fatal error";
482 static const char* msg2 = "Project Alice has encountered the following problems";
483 MessageBoxA(nullptr, content.c_str(), fatal ? msg1 : msg2, MB_OK | (fatal ? MB_ICONERROR : MB_ICONWARNING));
484 if(fatal) {
485 std::exit(EXIT_FAILURE);
486 }
487}
488} // namespace window
void initialize_opengl(sys::state &state)
void shutdown_opengl(sys::state &state)
directory open_directory(directory const &dir, native_string_view directory_name)
directory get_root(file_system const &fs)
native_string get_full_name(directory const &f)
std::optional< unopened_file > peek_file(directory const &dir, native_string_view file_name)
void pause_all(sys::state &state)
Definition: sound_nix.cpp:226
void initialize_sound_system(sys::state &state)
Definition: sound_nix.cpp:84
void resume_all(sys::state &state)
Definition: sound_nix.cpp:240
void update_music_track(sys::state &state)
Definition: sound_nix.cpp:255
void start_music(sys::state &state, float v)
Definition: sound_nix.cpp:214
virtual_key
Definition: constants.hpp:5
key_modifiers
Definition: constants.hpp:156
window_state
Definition: window.hpp:46
void set_borderless_full_screen(sys::state &game_state, bool fullscreen)
Definition: window_nix.cpp:76
void get_window_size(sys::state const &game_state, int &width, int &height)
Definition: window_nix.cpp:68
bool is_key_depressed(sys::state const &game_state, sys::virtual_key key)
Definition: window_nix.cpp:60
bool is_in_fullscreen(sys::state const &game_state)
Definition: window_nix.cpp:72
sys::key_modifiers get_current_modifiers()
Definition: window_win.cpp:92
void emit_error_message(std::string const &content, bool fatal)
Definition: window_nix.cpp:355
void close_window(sys::state &game_state)
Definition: window_nix.cpp:101
void change_cursor(sys::state &state, cursor_type type)
Definition: window_nix.cpp:351
cursor_type
Definition: window.hpp:61
void create_window(sys::state &game_state, creation_parameters const &params)
Definition: window_nix.cpp:278
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
Definition: window_win.cpp:100
#define NATIVE(X)
std::string_view native_string_view
uint uint32_t
uchar uint8_t
#define IDI_ICON1
Definition: resource.h:5
#define WM_GRAPHNOTIFY
Definition: sound_win.cpp:5
struct HWND__ * HWND
Definition: sound_win.hpp:8
user_settings_s user_settings
void on_create()
ui::state ui_state
std::unique_ptr< window::window_data_impl > win_ptr
element_base * edit_target