win32_app
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_inc.h"
|
||||
#include "app.gen.h"
|
||||
|
||||
#include "core/core.c"
|
||||
#include "core/core_inc.c"
|
||||
#include "app.gen.c"
|
||||
#include "app_win32_opengl.c"
|
||||
|
||||
#include <windowsx.h>
|
||||
|
||||
#pragma comment(linker, "/subsystem:windows")
|
||||
#pragma comment(lib, "gdi32.lib")
|
||||
@@ -10,120 +13,361 @@
|
||||
#pragma comment(lib, "winmm.lib")
|
||||
|
||||
|
||||
b32 w32_good_scheduling = false;
|
||||
b32 w32_good_scheduling;
|
||||
WNDCLASSW w32_wc;
|
||||
HWND w32_window_handle;
|
||||
HDC w32_dc;
|
||||
b32 w32_quit_app;
|
||||
|
||||
LRESULT CALLBACK w32_window_proc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
app_event_list_t w32_event_list;
|
||||
ma_arena_t *w32_event_arena;
|
||||
|
||||
fn v2f64_t w32_get_window_size(HWND window) {
|
||||
RECT window_rect;
|
||||
GetClientRect(window, &window_rect);
|
||||
f64 x = window_rect.right - window_rect.left;
|
||||
f64 y = window_rect.bottom - window_rect.top;
|
||||
return (v2f64_t){x, y};
|
||||
}
|
||||
|
||||
fn v2f64_t w32_get_mouse_pos(HWND window) {
|
||||
POINT p;
|
||||
GetCursorPos(&p);
|
||||
ScreenToClient(window, &p);
|
||||
return (v2f64_t){p.x, p.y};
|
||||
}
|
||||
|
||||
fn f64 w32_get_dpr(HWND window_handle) {
|
||||
UINT dpi = GetDpiForWindow(window_handle);
|
||||
if (dpi == 0) {
|
||||
return 1.0;
|
||||
}
|
||||
f64 result = (f64)dpi / 96.0;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void w32_push_event(app_event_t event) {
|
||||
app_event_t *ev = ma_push_type(w32_event_arena, app_event_t);
|
||||
*ev = event;
|
||||
|
||||
if (GetKeyState(VK_CONTROL) & 0x8000) ev->ctrl = true;
|
||||
if (GetKeyState(VK_SHIFT) & 0x8000) ev->shift = true;
|
||||
if (GetKeyState(VK_MENU) & 0x8000) ev->alt = true;
|
||||
ev->window_size = w32_get_window_size(w32_window_handle);
|
||||
ev->mouse_pos = w32_get_mouse_pos(w32_window_handle);
|
||||
ev->dpr = w32_get_dpr(w32_window_handle);
|
||||
|
||||
SLLQ_APPEND(w32_event_list.first, w32_event_list.last, ev);
|
||||
w32_event_list.len += 1;
|
||||
}
|
||||
|
||||
fn LRESULT CALLBACK w32_window_proc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
switch (msg) {
|
||||
case WM_CLOSE: PostQuitMessage(0); break;
|
||||
case WM_KEYUP: {
|
||||
app_key_t key = w32_map_wparam_to_app_key(wparam);
|
||||
case WM_CLOSE: {
|
||||
ExitProcess(0);
|
||||
} break;
|
||||
|
||||
case WM_KEYDOWN: {
|
||||
app_key_t key = w32_map_wparam_to_app_key(wparam);
|
||||
w32_push_event((app_event_t){
|
||||
.kind = app_event_kind_key_down,
|
||||
.key = w32_map_wparam_to_app_key(wparam)
|
||||
});
|
||||
} break;
|
||||
case WM_KEYUP: {
|
||||
w32_push_event((app_event_t){
|
||||
.kind = app_event_kind_key_up,
|
||||
.key = w32_map_wparam_to_app_key(wparam)
|
||||
});
|
||||
} break;
|
||||
|
||||
|
||||
case WM_LBUTTONDOWN: {
|
||||
SetCapture(wnd);
|
||||
w32_push_event((app_event_t){
|
||||
.kind = app_event_kind_mouse_down,
|
||||
.mouse_button = app_mouse_button_left,
|
||||
});
|
||||
} break;
|
||||
case WM_LBUTTONUP: {
|
||||
ReleaseCapture();
|
||||
w32_push_event((app_event_t){
|
||||
.kind = app_event_kind_mouse_up,
|
||||
.mouse_button = app_mouse_button_left,
|
||||
});
|
||||
} break;
|
||||
|
||||
|
||||
case WM_RBUTTONDOWN: {
|
||||
w32_push_event((app_event_t){
|
||||
.kind = app_event_kind_mouse_down,
|
||||
.mouse_button = app_mouse_button_right,
|
||||
});
|
||||
} break;
|
||||
case WM_RBUTTONUP: {
|
||||
w32_push_event((app_event_t){
|
||||
.kind = app_event_kind_mouse_up,
|
||||
.mouse_button = app_mouse_button_right,
|
||||
});
|
||||
} break;
|
||||
|
||||
|
||||
case WM_MBUTTONDOWN: {
|
||||
SetCapture(wnd);
|
||||
w32_push_event((app_event_t){
|
||||
.kind = app_event_kind_mouse_down,
|
||||
.mouse_button = app_mouse_button_middle,
|
||||
});
|
||||
} break;
|
||||
case WM_MBUTTONUP: {
|
||||
ReleaseCapture();
|
||||
w32_push_event((app_event_t){
|
||||
.kind = app_event_kind_mouse_up,
|
||||
.mouse_button = app_mouse_button_middle,
|
||||
});
|
||||
} break;
|
||||
|
||||
case WM_MOUSEWHEEL: {
|
||||
int zDelta = GET_WHEEL_DELTA_WPARAM(wparam);
|
||||
w32_push_event((app_event_t){
|
||||
.kind = app_event_kind_mouse_wheel,
|
||||
.mouse_wheel_delta = (v3f64_t){0, zDelta},
|
||||
});
|
||||
} break;
|
||||
|
||||
case WM_CHAR: {
|
||||
//https://handmade.network/forums/t/2011-keyboard_inputs_-_scancodes,_raw_input,_text_input,_key_names
|
||||
// WM_UNICHAR
|
||||
/*
|
||||
I looked closer to how you process WM_CHAR in example code and I believe it is very wrong. wParam doesn't contain two ushorts joined in one 32-bit value. It is always one UTF-16 value. That means 16-bits. If Windows wants to send you unicode codepoint with value >0xFFFF, then it sends two UTF-16 WM_CHAR messages. Each with one part of UTF-16 surrogate pair. So your first WM_CHAR needs to remember it, and second one construct full unicode codepoint. For example, this is mentioned here: http://www.catch22.net/tuts/unicode-text-editing
|
||||
*/
|
||||
} break
|
||||
|
||||
|
||||
default: return DefWindowProcW(wnd, msg, wparam, lparam);
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
|
||||
typedef enum W32_PROCESS_DPI_AWARENESS {
|
||||
W32_PROCESS_DPI_UNAWARE = 0,
|
||||
W32_PROCESS_SYSTEM_DPI_AWARE = 1,
|
||||
W32_PROCESS_PER_MONITOR_DPI_AWARE = 2
|
||||
} W32_PROCESS_DPI_AWARENESS;
|
||||
app_event_list_t w32_get_events(ma_arena_t *arena) {
|
||||
w32_event_arena = arena;
|
||||
memory_zero(&w32_event_list, sizeof(w32_event_list));
|
||||
|
||||
typedef unsigned MU_TimeBeginPeriod(unsigned);
|
||||
typedef HRESULT MU_SetProcessDpiAwareness(W32_PROCESS_DPI_AWARENESS);
|
||||
|
||||
HMODULE shcore = LoadLibraryA("Shcore.dll");
|
||||
if (shcore) {
|
||||
MU_SetProcessDpiAwareness *set_dpi_awr = (MU_SetProcessDpiAwareness *)GetProcAddress(shcore, "SetProcessDpiAwareness");
|
||||
if (set_dpi_awr) {
|
||||
HRESULT hr = set_dpi_awr(W32_PROCESS_PER_MONITOR_DPI_AWARE);
|
||||
assert(SUCCEEDED(hr) && "Failed to set dpi awareness");
|
||||
}
|
||||
MSG msg;
|
||||
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
|
||||
HMODULE winmm = LoadLibraryA("winmm.dll");
|
||||
if (winmm) {
|
||||
MU_TimeBeginPeriod *timeBeginPeriod = (MU_TimeBeginPeriod *)GetProcAddress(winmm, "timeBeginPeriod");
|
||||
if (timeBeginPeriod) {
|
||||
if (timeBeginPeriod(1) == 0) {
|
||||
w32_good_scheduling = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return w32_event_list;
|
||||
}
|
||||
|
||||
WNDCLASSW wc = {0};
|
||||
{
|
||||
wc.lpfnWndProc = w32_window_proc;
|
||||
wc.hInstance = GetModuleHandleW(NULL);
|
||||
wc.lpszClassName = L"HelloClassName";
|
||||
wc.hCursor = LoadCursor(0, IDC_ARROW);
|
||||
wc.hIcon = NULL; // LoadIcon(wc.hInstance, IDI_APPLICATION);
|
||||
wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
|
||||
ATOM result = RegisterClassW(&wc);
|
||||
assert(result != 0);
|
||||
w32_wc = wc;
|
||||
}
|
||||
///////////////////////////////
|
||||
// canvas functions
|
||||
|
||||
RECT window_rect = {0};
|
||||
{
|
||||
window_rect.left = 0;
|
||||
window_rect.right = 1280;
|
||||
window_rect.bottom = 732;
|
||||
window_rect.top = 12;
|
||||
AdjustWindowRectEx(&window_rect, WS_OVERLAPPEDWINDOW, false, 0);
|
||||
}
|
||||
w32_window_handle = CreateWindowW(w32_wc.lpszClassName, L"Zzz... Window, hello!", WS_OVERLAPPEDWINDOW, window_rect.left, window_rect.top, window_rect.right - window_rect.left, window_rect.bottom - window_rect.top, NULL, NULL, hInstance, NULL);
|
||||
assert(w32_window_handle);
|
||||
typedef struct w32_canvas_t w32_canvas_t;
|
||||
struct w32_canvas_t {
|
||||
u32 *memory;
|
||||
HWND window_handle;
|
||||
HBITMAP dib;
|
||||
HDC dib_dc;
|
||||
v2f64_t window_size;
|
||||
};
|
||||
|
||||
w32_dc = GetDC(w32_window_handle);
|
||||
assert(w32_dc);
|
||||
ShowWindow(w32_window_handle, SW_SHOW);
|
||||
|
||||
// @todo: rebuild on resize
|
||||
// Create a writable backbuffer bitmap
|
||||
uint32_t *mem = 0;
|
||||
w32_canvas_t w32_create_canvas(HWND window_handle) {
|
||||
v2f64_t window_size = w32_get_window_size(window_handle);
|
||||
w32_canvas_t result = {.window_handle = window_handle, .window_size = window_size};
|
||||
HDC window_dc = GetDC(window_handle);
|
||||
BITMAPINFO bminfo = {0};
|
||||
{
|
||||
bminfo.bmiHeader.biSize = sizeof(bminfo.bmiHeader);
|
||||
bminfo.bmiHeader.biWidth = (LONG)1280;
|
||||
bminfo.bmiHeader.biHeight = (LONG)720;
|
||||
bminfo.bmiHeader.biWidth = (LONG)window_size.x;
|
||||
bminfo.bmiHeader.biHeight = (LONG)window_size.y;
|
||||
bminfo.bmiHeader.biPlanes = 1;
|
||||
bminfo.bmiHeader.biBitCount = 32;
|
||||
bminfo.bmiHeader.biCompression = BI_RGB; // AA RR GG BB
|
||||
bminfo.bmiHeader.biXPelsPerMeter = 1;
|
||||
bminfo.bmiHeader.biYPelsPerMeter = 1;
|
||||
}
|
||||
HBITMAP dib = CreateDIBSection(w32_dc, &bminfo, DIB_RGB_COLORS, (void **)&mem, 0, 0);
|
||||
HDC dib_dc = CreateCompatibleDC(w32_dc);
|
||||
result.dib = CreateDIBSection(window_dc, &bminfo, DIB_RGB_COLORS, (void **)&result.memory, 0, 0);
|
||||
result.dib_dc = CreateCompatibleDC(window_dc);
|
||||
return result;
|
||||
}
|
||||
|
||||
void w32_destroy_canvas(w32_canvas_t *canvas) {
|
||||
if (canvas->memory) {
|
||||
DeleteDC(canvas->dib_dc);
|
||||
DeleteObject(canvas->dib);
|
||||
memory_zero(canvas, sizeof(*canvas));
|
||||
}
|
||||
}
|
||||
|
||||
void w32_present_canvas(w32_canvas_t *canvas) {
|
||||
if (canvas->memory) {
|
||||
SelectObject(canvas->dib_dc, canvas->dib);
|
||||
HDC window_dc = GetDC(canvas->window_handle);
|
||||
BitBlt(window_dc, 0, 0, (int)canvas->window_size.x, (int)canvas->window_size.y, canvas->dib_dc, 0, 0, SRCCOPY);
|
||||
}
|
||||
}
|
||||
|
||||
void w32_try_resizing_canvas(w32_canvas_t *canvas, HWND window_handle) {
|
||||
v2f64_t window_size = w32_get_window_size(window_handle);
|
||||
if (canvas->window_size.x != window_size.x || canvas->window_size.y != window_size.y) {
|
||||
w32_destroy_canvas(canvas);
|
||||
*canvas = w32_create_canvas(window_handle);
|
||||
}
|
||||
}
|
||||
|
||||
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
|
||||
tcx.temp = ma_create(ma_default_reserve_size);
|
||||
|
||||
// Set DPI aware, @todo: verify / new way to do this | @todo: get dpi ratio
|
||||
{
|
||||
typedef enum W32_PROCESS_DPI_AWARENESS {
|
||||
W32_PROCESS_DPI_UNAWARE = 0,
|
||||
W32_PROCESS_SYSTEM_DPI_AWARE = 1,
|
||||
W32_PROCESS_PER_MONITOR_DPI_AWARE = 2
|
||||
} W32_PROCESS_DPI_AWARENESS;
|
||||
typedef HRESULT MU_SetProcessDpiAwareness(W32_PROCESS_DPI_AWARENESS);
|
||||
|
||||
HMODULE shcore = LoadLibraryA("Shcore.dll");
|
||||
if (shcore) {
|
||||
MU_SetProcessDpiAwareness *set_dpi_awr = (MU_SetProcessDpiAwareness *)GetProcAddress(shcore, "SetProcessDpiAwareness");
|
||||
if (set_dpi_awr) {
|
||||
HRESULT hr = set_dpi_awr(W32_PROCESS_PER_MONITOR_DPI_AWARE);
|
||||
assert(SUCCEEDED(hr) && "Failed to set dpi awareness");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setup better scheduling, @todo: should we do this?
|
||||
{
|
||||
typedef unsigned MU_TimeBeginPeriod(unsigned);
|
||||
|
||||
HMODULE winmm = LoadLibraryA("winmm.dll");
|
||||
if (winmm) {
|
||||
MU_TimeBeginPeriod *timeBeginPeriod = (MU_TimeBeginPeriod *)GetProcAddress(winmm, "timeBeginPeriod");
|
||||
if (timeBeginPeriod) {
|
||||
if (timeBeginPeriod(1) == 0) {
|
||||
w32_good_scheduling = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create window
|
||||
{
|
||||
WNDCLASSW wc = {0};
|
||||
{
|
||||
wc.lpfnWndProc = w32_window_proc;
|
||||
wc.hInstance = GetModuleHandleW(NULL);
|
||||
wc.lpszClassName = L"HelloClassName";
|
||||
wc.hCursor = LoadCursor(0, IDC_ARROW);
|
||||
wc.hIcon = NULL; // LoadIcon(wc.hInstance, IDI_APPLICATION);
|
||||
wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
|
||||
ATOM result = RegisterClassW(&wc);
|
||||
assert(result != 0);
|
||||
w32_wc = wc;
|
||||
}
|
||||
|
||||
f64 primary_monitor_size_x = (f64)GetSystemMetrics(SM_CXSCREEN);
|
||||
f64 primary_monitor_size_y = (f64)GetSystemMetrics(SM_CYSCREEN);
|
||||
RECT window_rect = {0};
|
||||
{
|
||||
window_rect.left = 0 + 0;
|
||||
window_rect.right = 0 + (int)(primary_monitor_size_x * 0.8);
|
||||
window_rect.bottom = 30 + (int)(primary_monitor_size_y * 0.8);
|
||||
window_rect.top = 30 + 0;
|
||||
AdjustWindowRectEx(&window_rect, WS_OVERLAPPEDWINDOW, false, 0);
|
||||
}
|
||||
w32_window_handle = CreateWindowW(w32_wc.lpszClassName, L"Zzz... Window, hello!", WS_OVERLAPPEDWINDOW, window_rect.left, window_rect.top, window_rect.right - window_rect.left, window_rect.bottom - window_rect.top, NULL, NULL, hInstance, NULL);
|
||||
assert(w32_window_handle);
|
||||
|
||||
w32_dc = GetDC(w32_window_handle);
|
||||
assert(w32_dc);
|
||||
|
||||
b32 ok = w32_load_wgl_fns();
|
||||
assert(ok);
|
||||
|
||||
ok = w32_create_opengl_context(w32_dc, 4, 5);
|
||||
assert(ok);
|
||||
|
||||
ShowWindow(w32_window_handle, SW_SHOW);
|
||||
}
|
||||
|
||||
// w32_canvas_t canvas = w32_create_canvas(w32_window_handle);
|
||||
|
||||
f64 time_frame_start = os_seconds_now();
|
||||
f64 time_delta = 0.01666666666666;
|
||||
f64 time_total = 0.0;
|
||||
f64 time_update = 0.0;
|
||||
|
||||
u64 consecutive_missed_frames = 0;
|
||||
u64 missed_frames = 0;
|
||||
u64 frame = 0;
|
||||
for (;;) {
|
||||
MSG msg;
|
||||
if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
if (msg.message == WM_QUIT) {
|
||||
break;
|
||||
}
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
continue;
|
||||
}
|
||||
app_event_list_t event_list = w32_get_events(tcx.temp);
|
||||
|
||||
for (i32 y = 0; y < 720; y++) {
|
||||
for (i32 x = 0; x < 1280; x++) {
|
||||
mem[x + y * 1280] = 0xFFFF0000;
|
||||
for (app_event_t *ev = event_list.first; ev; ev = ev->next) {
|
||||
if (ev->kind == app_event_kind_key_down && ev->key == app_key_escape) {
|
||||
ExitProcess(0);
|
||||
}
|
||||
}
|
||||
|
||||
SelectObject(dib_dc, dib);
|
||||
BitBlt(w32_dc, 0, 0, 1280, 720, dib_dc, 0, 0, SRCCOPY);
|
||||
Sleep(10);
|
||||
// w32_try_resizing_canvas(&canvas, w32_window_handle);
|
||||
// debugf("time_update: %f", time_update);
|
||||
|
||||
#if 0
|
||||
for (i32 y = 0; y < canvas.window_size.y; y++) {
|
||||
for (i32 x = 0; x < canvas.window_size.x; x++) {
|
||||
canvas.memory[x + y * (int)canvas.window_size.x] = 0xFFFF0000;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// w32_present_canvas(&canvas);
|
||||
|
||||
///////////////////////////////
|
||||
// end of frame timings
|
||||
|
||||
ma_set0(tcx.temp);
|
||||
|
||||
f64 time_update_partial = os_seconds_now() - time_frame_start;
|
||||
time_update = time_update_partial;
|
||||
if (time_update < time_delta) {
|
||||
consecutive_missed_frames = 0;
|
||||
|
||||
// @todo: we are currently busy looping when we don't get the good schduling
|
||||
// is that actually a good tactic??
|
||||
if (w32_good_scheduling) {
|
||||
f64 time_to_sleep = time_delta - time_update;
|
||||
f64 time_to_sleep_in_ms = (time_to_sleep * 1000.0) - 1.0;
|
||||
if (time_to_sleep > 0.0) {
|
||||
DWORD ms = (DWORD)time_to_sleep_in_ms;
|
||||
if (ms) {
|
||||
Sleep(ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// busy loop if we dont have good scheduling
|
||||
// or we woke up early
|
||||
time_update = os_seconds_now() - time_frame_start;
|
||||
while (time_update < time_delta) {
|
||||
time_update = os_seconds_now() - time_frame_start;
|
||||
}
|
||||
} else {
|
||||
missed_frames += 1;
|
||||
consecutive_missed_frames += 1;
|
||||
}
|
||||
|
||||
frame += 1;
|
||||
|
||||
// @todo:
|
||||
// should this be time_delta or time_update ????
|
||||
// probably want the locked frame rate and total should reflect that, so choosing
|
||||
// time_delta seems the correct choice but not really sure what is correct.
|
||||
time_total += time_delta;
|
||||
time_frame_start = os_seconds_now();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user