win32_app
This commit is contained in:
@@ -255,6 +255,7 @@ type_t type__app_event_kind_t = { type_kind_enum, s8_const_lit("app_event_kind_t
|
||||
};
|
||||
type_t type__app_event_t = { type_kind_struct, s8_const_lit("app_event_t"), sizeof(app_event_t),
|
||||
.members = (type_member_t[]){
|
||||
{.name = s8_const_lit("next"), .type = &(type_t){type_kind_pointer, s8_const_lit("app_event_t*"), sizeof(void *), .base = &type__app_event_t}, .offset = offsetof(app_event_t, next)},
|
||||
{.name = s8_const_lit("kind"), .type = &type__app_event_kind_t, .offset = offsetof(app_event_t, kind)},
|
||||
{.name = s8_const_lit("mouse_button"), .type = &type__app_mouse_button_t, .offset = offsetof(app_event_t, mouse_button)},
|
||||
{.name = s8_const_lit("key"), .type = &type__app_key_t, .offset = offsetof(app_event_t, key)},
|
||||
@@ -268,5 +269,13 @@ type_t type__app_event_t = { type_kind_struct, s8_const_lit("app_event_t"), size
|
||||
{.name = s8_const_lit("window_size"), .type = &type__v2f64_t, .offset = offsetof(app_event_t, window_size)},
|
||||
{.name = s8_const_lit("mouse_pos"), .type = &type__v2f64_t, .offset = offsetof(app_event_t, mouse_pos)},
|
||||
},
|
||||
.count = 12,
|
||||
.count = 13,
|
||||
};
|
||||
type_t type__app_event_list_t = { type_kind_struct, s8_const_lit("app_event_list_t"), sizeof(app_event_list_t),
|
||||
.members = (type_member_t[]){
|
||||
{.name = s8_const_lit("first"), .type = &(type_t){type_kind_pointer, s8_const_lit("app_event_t*"), sizeof(void *), .base = &type__app_event_t}, .offset = offsetof(app_event_list_t, first)},
|
||||
{.name = s8_const_lit("last"), .type = &(type_t){type_kind_pointer, s8_const_lit("app_event_t*"), sizeof(void *), .base = &type__app_event_t}, .offset = offsetof(app_event_list_t, last)},
|
||||
{.name = s8_const_lit("len"), .type = &type__i32, .offset = offsetof(app_event_list_t, len)},
|
||||
},
|
||||
.count = 3,
|
||||
};
|
||||
@@ -93,6 +93,7 @@ typedef enum {
|
||||
|
||||
typedef struct app_event_t app_event_t;
|
||||
struct app_event_t {
|
||||
app_event_t* next;
|
||||
app_event_kind_t kind;
|
||||
app_mouse_button_t mouse_button;
|
||||
app_key_t key;
|
||||
@@ -105,4 +106,11 @@ struct app_event_t {
|
||||
f64 dpr;
|
||||
v2f64_t window_size;
|
||||
v2f64_t mouse_pos;
|
||||
};
|
||||
|
||||
typedef struct app_event_list_t app_event_list_t;
|
||||
struct app_event_list_t {
|
||||
app_event_t* first;
|
||||
app_event_t* last;
|
||||
i32 len;
|
||||
};
|
||||
@@ -159,12 +159,15 @@ void meta_app(ma_arena_t *arena) {
|
||||
} app_event_kind_t;
|
||||
|
||||
struct app_event_t {
|
||||
app_event_t *next;
|
||||
app_event_kind_t kind;
|
||||
|
||||
// data present only during events
|
||||
app_mouse_button_t mouse_button; // @mouse_down @mouse_up
|
||||
app_key_t key; // @key_down @key_up
|
||||
s8_t text; // @text
|
||||
|
||||
// @todo: test why xyz in js????????????
|
||||
v3f64_t mouse_wheel_delta; // @mouse_wheel
|
||||
|
||||
// always present data
|
||||
@@ -177,6 +180,12 @@ void meta_app(ma_arena_t *arena) {
|
||||
v2f64_t window_size;
|
||||
v2f64_t mouse_pos;
|
||||
};
|
||||
|
||||
struct app_event_list_t {
|
||||
app_event_t *first;
|
||||
app_event_t *last;
|
||||
i32 len;
|
||||
};
|
||||
));
|
||||
|
||||
sb8_serial_ast_to_code(h, decls);
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
/*
|
||||
|
||||
.doesn't miss events (always processes all key strokes and buttons etc.)
|
||||
.replayable / deterministic (that is you can serialize all the events, replay them back and get the same results)
|
||||
..rendering and update decoupled
|
||||
.sleeps properly when nothing is happening
|
||||
.animations probably then should be in the rendering part
|
||||
.replayable (that is you can serialize all the events, replay them back and get the same results)
|
||||
..rendering and update decoupled
|
||||
..animations probably then should be in the rendering part
|
||||
|
||||
void app_update(app_event_t *events, i32 event_count) {
|
||||
loop (events) update(it);
|
||||
update_result_t *state = NULL; // contains event
|
||||
loop (events) {
|
||||
state = update(it);
|
||||
}
|
||||
|
||||
{
|
||||
app_event_t *ev = events + event_count - 1;
|
||||
app_event_t *ev = state->ev;
|
||||
|
||||
|
||||
f64 delta_time = app_anim_get_delta_time();
|
||||
f64 time = app_anim_get_time();
|
||||
animate(ev, delta_time, time);
|
||||
render(ev);
|
||||
animate(state, delta_time, time);
|
||||
render(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,10 +42,10 @@ glb_wasm_export i32 wasm_temp_buff1_len = 127;
|
||||
glb_wasm_export char wasm_temp_buff2[128] = {[127] = 0x13};
|
||||
glb_wasm_export i32 wasm_temp_buff2_len = 127;
|
||||
|
||||
global f64 wasm_dpr;
|
||||
global ma_arena_t *wasm_input_text_arena;
|
||||
global STACK(app_event_t, 64) wasm_events;
|
||||
global b32 wasm_event_failed_to_queue;
|
||||
// @todo: event list
|
||||
global f64 wasm_dpr;
|
||||
global STACK(app_event_t, 256) wasm_events;
|
||||
global b32 wasm_event_failed_to_queue;
|
||||
|
||||
global f64 wasm_delta_time;
|
||||
global f64 wasm_time;
|
||||
@@ -53,7 +58,7 @@ struct wasm_cached_t {
|
||||
b8 ctrl, alt, meta, shift;
|
||||
} wasm_cached;
|
||||
|
||||
fn void app_update(app_event_t *events, i32 event_count);
|
||||
fn void app_update(void);
|
||||
fn void app_init(void);
|
||||
|
||||
fn void wasm_add_event(app_event_t event) {
|
||||
@@ -147,7 +152,7 @@ fn_wasm_export void wasm_key_down(char *key, b32 ctrl, b32 shift, b32 alt, b32 m
|
||||
return;
|
||||
}
|
||||
|
||||
s8_t text = s8_copy(wasm_input_text_arena, key8);
|
||||
s8_t text = s8_copy(tcx.temp, key8);
|
||||
wasm_add_event((app_event_t){
|
||||
.kind = app_event_kind_text,
|
||||
.mouse_pos = wasm_cached.mouse_pos,
|
||||
@@ -180,7 +185,7 @@ fn_wasm_export void wasm_key_up(char *key, b32 ctrl, b32 shift, b32 alt, b32 met
|
||||
}
|
||||
|
||||
fn_wasm_export void wasm_update(f64 width, f64 height, f64 dpr) {
|
||||
wasm_time = os_get_milliseconds();
|
||||
wasm_time = os_milliseconds_now();
|
||||
wasm_delta_time = wasm_time - wasm_last_time_milliseconds;
|
||||
|
||||
v2f64_t window_size = (v2f64_t){width / dpr, height / dpr};
|
||||
@@ -202,18 +207,32 @@ fn_wasm_export void wasm_update(f64 width, f64 height, f64 dpr) {
|
||||
wasm_events.data[i].window_size = window_size;
|
||||
}
|
||||
|
||||
app_update(wasm_events.data, wasm_events.len);
|
||||
app_update();
|
||||
|
||||
wasm_events.len = 0;
|
||||
wasm_last_time_milliseconds = wasm_time;
|
||||
ma_set0(wasm_input_text_arena);
|
||||
}
|
||||
|
||||
fn_wasm_export void wasm_init(void) {
|
||||
core_init();
|
||||
wasm_input_text_arena = ma_push_arena(&tcx.perm, kib(1));
|
||||
app_init();
|
||||
wasm_app_init_time_milliseconds = os_get_milliseconds();
|
||||
wasm_app_init_time_milliseconds = os_milliseconds_now();
|
||||
}
|
||||
|
||||
fn app_event_t *app_iterate_events(void) {
|
||||
return wasm_events.data;
|
||||
}
|
||||
|
||||
fn b32 app_is_event_valid(app_event_t *event) {
|
||||
return event < (wasm_events.data + wasm_events.len);
|
||||
}
|
||||
|
||||
fn void app_next_event(app_event_t **event) {
|
||||
event[0] += 1;
|
||||
}
|
||||
|
||||
fn app_event_t *app_get_last_event(void) {
|
||||
return wasm_events.data + wasm_events.len - 1;
|
||||
}
|
||||
|
||||
fn f64 app_get_anim_time(void) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
270
src/app/app_win32_opengl.c
Normal file
270
src/app/app_win32_opengl.c
Normal file
@@ -0,0 +1,270 @@
|
||||
// first load the opengl loading functions
|
||||
fn b32 w32_load_wgl_fns(void);
|
||||
|
||||
// then create opengl context for window
|
||||
fn b32 w32_create_opengl_context(HDC window_handle_dc, i32 opengl_major, i32 opengl_minor);
|
||||
|
||||
// then you can use this to load opengl functions -
|
||||
// either pass this to glad or load them manually
|
||||
fn void *w32_load_opengl_fn(const char *proc);
|
||||
|
||||
// compile time options
|
||||
#define W32_OPENGL_DEBUG 1
|
||||
#define W32_ENABLE_MULTISAMPLING 1
|
||||
|
||||
// Symbols taken from GLFW
|
||||
//
|
||||
// Executables (but not DLLs) exporting this symbol with this value will be
|
||||
// automatically directed to the high-performance GPU on Nvidia Optimus systems
|
||||
// with up-to-date drivers
|
||||
//
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 1;
|
||||
|
||||
// Executables (but not DLLs) exporting this symbol with this value will be
|
||||
// automatically directed to the high-performance GPU on AMD PowerXpress systems
|
||||
// with up-to-date drivers
|
||||
//
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
|
||||
typedef HGLRC MU_wglCreateContext(HDC unnamedParam1);
|
||||
typedef BOOL MU_wglMakeCurrent(HDC unnamedParam1, HGLRC unnamedParam2);
|
||||
typedef BOOL MU_wglDeleteContext(HGLRC unnamedParam1);
|
||||
typedef void *MU_glGetProcAddress(const char *);
|
||||
typedef const char *MU_wglGetExtensionsStringARB(HDC hdc);
|
||||
typedef BOOL MU_wglChoosePixelFormatARB(HDC hdc, const int *piAttribIList, const float *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
|
||||
typedef HGLRC MU_wglCreateContextAttribsARB(HDC hDC, HGLRC hshareContext, const int *attribList);
|
||||
typedef BOOL MU_wglSwapIntervalEXT(int interval);
|
||||
|
||||
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
|
||||
#define WGL_SUPPORT_OPENGL_ARB 0x2010
|
||||
#define WGL_DOUBLE_BUFFER_ARB 0x2011
|
||||
#define WGL_PIXEL_TYPE_ARB 0x2013
|
||||
#define WGL_TYPE_RGBA_ARB 0x202B
|
||||
#define WGL_COLOR_BITS_ARB 0x2014
|
||||
#define WGL_DEPTH_BITS_ARB 0x2022
|
||||
#define WGL_STENCIL_BITS_ARB 0x2023
|
||||
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
||||
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
|
||||
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
|
||||
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
|
||||
#define WGL_CONTEXT_FLAGS_ARB 0x2094
|
||||
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001
|
||||
#define WGL_SAMPLE_BUFFERS_ARB 0x2041
|
||||
#define WGL_SAMPLES_ARB 0x2042
|
||||
|
||||
global MU_wglChoosePixelFormatARB *wglChoosePixelFormatARB;
|
||||
global MU_wglCreateContextAttribsARB *wglCreateContextAttribsARB;
|
||||
global MU_wglSwapIntervalEXT *wglSwapIntervalEXT;
|
||||
|
||||
global MU_glGetProcAddress *wgl_get_proc_address;
|
||||
global void *(*gl_get_proc_address)(const char *str);
|
||||
global HMODULE opengl_hmodule;
|
||||
|
||||
global HGLRC(*mu_wglCreateContext)(HDC unnamedParam1);
|
||||
global BOOL(*mu_wglMakeCurrent)(HDC unnamedParam1, HGLRC unnamedParam2);
|
||||
global BOOL(*mu_wglDeleteContext)(HGLRC unnamedParam1);
|
||||
|
||||
// compares src string with dstlen characters from dst, returns 1 if they are equal, 0 if not
|
||||
fn int w32_are_strings_equal(const char *src, const char *dst, size_t dstlen) {
|
||||
while (*src && dstlen-- && *dst) {
|
||||
if (*src++ != *dst++) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return (dstlen && *src == *dst) || (!dstlen && *src == 0);
|
||||
}
|
||||
|
||||
fn void *w32_load_opengl_fn(const char *proc) {
|
||||
void *func = wgl_get_proc_address(proc);
|
||||
if (!func) {
|
||||
func = GetProcAddress(opengl_hmodule, proc);
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
fn b32 w32_load_wgl_fns(void) {
|
||||
HMODULE opengl32 = LoadLibraryA("opengl32");
|
||||
assert(opengl32);
|
||||
if (opengl32) {
|
||||
opengl_hmodule = opengl32;
|
||||
wgl_get_proc_address = (MU_glGetProcAddress *)GetProcAddress(opengl32, "wglGetProcAddress");
|
||||
gl_get_proc_address = w32_load_opengl_fn;
|
||||
mu_wglCreateContext = (MU_wglCreateContext *)GetProcAddress(opengl32, "wglCreateContext");
|
||||
mu_wglMakeCurrent = (MU_wglMakeCurrent *)GetProcAddress(opengl32, "wglMakeCurrent");
|
||||
mu_wglDeleteContext = (MU_wglDeleteContext *)GetProcAddress(opengl32, "wglDeleteContext");
|
||||
}
|
||||
if (opengl32 == NULL || mu_wglCreateContext == NULL || gl_get_proc_address == NULL || mu_wglMakeCurrent == NULL || mu_wglDeleteContext == NULL) {
|
||||
assert(!"Failed to load Opengl wgl functions from opengl32.lib");
|
||||
}
|
||||
|
||||
// to get WGL functions we need valid GL context, so create dummy window for dummy GL contetx
|
||||
HWND dummy = CreateWindowExW(
|
||||
0, L"STATIC", L"DummyWindow", WS_OVERLAPPED,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
NULL, NULL, NULL, NULL);
|
||||
assert(dummy && "Failed to create dummy window");
|
||||
|
||||
HDC dc = GetDC(dummy);
|
||||
assert(dc && "Failed to get device context for dummy window");
|
||||
|
||||
PIXELFORMATDESCRIPTOR desc = {0};
|
||||
{
|
||||
desc.nSize = sizeof(desc);
|
||||
desc.nVersion = 1;
|
||||
desc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
||||
desc.iPixelType = PFD_TYPE_RGBA;
|
||||
desc.cColorBits = 24;
|
||||
};
|
||||
|
||||
int format = ChoosePixelFormat(dc, &desc);
|
||||
if (!format) {
|
||||
assert(!"Cannot choose OpenGL pixel format for dummy window!");
|
||||
}
|
||||
|
||||
int ok = DescribePixelFormat(dc, format, sizeof(desc), &desc);
|
||||
assert(ok && "Failed to describe OpenGL pixel format");
|
||||
|
||||
// reason to create dummy window is that SetPixelFormat can be called only once for the window
|
||||
if (!SetPixelFormat(dc, format, &desc)) {
|
||||
assert(!"Cannot set OpenGL pixel format for dummy window!");
|
||||
}
|
||||
|
||||
HGLRC rc = mu_wglCreateContext(dc);
|
||||
assert(rc && "Failed to create OpenGL context for dummy window");
|
||||
|
||||
ok = mu_wglMakeCurrent(dc, rc);
|
||||
assert(ok && "Failed to make current OpenGL context for dummy window");
|
||||
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_extensions_string.txt
|
||||
MU_wglGetExtensionsStringARB *wglGetExtensionsStringARB = (MU_wglGetExtensionsStringARB *)gl_get_proc_address("wglGetExtensionsStringARB");
|
||||
if (!wglGetExtensionsStringARB) {
|
||||
assert(!"OpenGL does not support WGL_ARB_extensions_string extension!");
|
||||
}
|
||||
|
||||
const char *ext = wglGetExtensionsStringARB(dc);
|
||||
assert(ext && "Failed to get OpenGL WGL extension string");
|
||||
|
||||
const char *start = ext;
|
||||
for (;;) {
|
||||
while (*ext != 0 && *ext != ' ') {
|
||||
ext++;
|
||||
}
|
||||
size_t length = ext - start;
|
||||
if (w32_are_strings_equal("WGL_ARB_pixel_format", start, length)) {
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_pixel_format.txt
|
||||
wglChoosePixelFormatARB = (MU_wglChoosePixelFormatARB *)gl_get_proc_address("wglChoosePixelFormatARB");
|
||||
}
|
||||
else if (w32_are_strings_equal("WGL_ARB_create_context", start, length)) {
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_create_context.txt
|
||||
wglCreateContextAttribsARB = (MU_wglCreateContextAttribsARB *)gl_get_proc_address("wglCreateContextAttribsARB");
|
||||
}
|
||||
else if (w32_are_strings_equal("WGL_EXT_swap_control", start, length)) {
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/EXT/WGL_EXT_swap_control.txt
|
||||
wglSwapIntervalEXT = (MU_wglSwapIntervalEXT *)gl_get_proc_address("wglSwapIntervalEXT");
|
||||
}
|
||||
|
||||
if (*ext == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
ext++;
|
||||
start = ext;
|
||||
}
|
||||
|
||||
if (!wglChoosePixelFormatARB || !wglCreateContextAttribsARB || !wglSwapIntervalEXT) {
|
||||
assert(!"OpenGL does not support required WGL extensions for modern context!");
|
||||
}
|
||||
|
||||
BOOL ok_b = mu_wglMakeCurrent(NULL, NULL);
|
||||
assert(ok_b);
|
||||
|
||||
ok_b = mu_wglDeleteContext(rc);
|
||||
assert(ok_b);
|
||||
|
||||
ok = ReleaseDC(dummy, dc);
|
||||
assert(ok);
|
||||
|
||||
ok_b = DestroyWindow(dummy);
|
||||
assert(ok_b);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn b32 w32_create_opengl_context(HDC window_handle_dc, i32 opengl_major, i32 opengl_minor) {
|
||||
// set pixel format for OpenGL context
|
||||
int attrib[] =
|
||||
{
|
||||
WGL_DRAW_TO_WINDOW_ARB,
|
||||
true,
|
||||
WGL_SUPPORT_OPENGL_ARB,
|
||||
true,
|
||||
WGL_DOUBLE_BUFFER_ARB,
|
||||
true,
|
||||
WGL_PIXEL_TYPE_ARB,
|
||||
WGL_TYPE_RGBA_ARB,
|
||||
WGL_COLOR_BITS_ARB,
|
||||
32,
|
||||
WGL_DEPTH_BITS_ARB,
|
||||
24,
|
||||
WGL_STENCIL_BITS_ARB,
|
||||
8,
|
||||
|
||||
// uncomment for sRGB framebuffer, from WGL_ARB_framebuffer_sRGB extension
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_framebuffer_sRGB.txt
|
||||
// WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, GL_TRUE,
|
||||
|
||||
// uncomment for multisampeld framebuffer, from WGL_ARB_multisample extension
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_multisample.txt
|
||||
#if W32_ENABLE_MULTISAMPLING
|
||||
WGL_SAMPLE_BUFFERS_ARB,
|
||||
1,
|
||||
WGL_SAMPLES_ARB,
|
||||
4, // 4x MSAA
|
||||
#endif
|
||||
|
||||
0,
|
||||
};
|
||||
|
||||
int format;
|
||||
UINT formats;
|
||||
if (!wglChoosePixelFormatARB(window_handle_dc, attrib, 0, 1, &format, &formats) || formats == 0) {
|
||||
assert(!"OpenGL does not support required pixel format!");
|
||||
}
|
||||
|
||||
PIXELFORMATDESCRIPTOR desc = {0};
|
||||
desc.nSize = sizeof(desc);
|
||||
int ok = DescribePixelFormat(window_handle_dc, format, sizeof(desc), &desc);
|
||||
assert(ok && "Failed to describe OpenGL pixel format");
|
||||
|
||||
if (!SetPixelFormat(window_handle_dc, format, &desc)) {
|
||||
assert(!"Cannot set OpenGL selected pixel format!");
|
||||
}
|
||||
|
||||
// create modern OpenGL context
|
||||
{
|
||||
int attrib[] =
|
||||
{
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB,
|
||||
opengl_major,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB,
|
||||
opengl_minor,
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB,
|
||||
WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
|
||||
#if W32_OPENGL_DEBUG
|
||||
WGL_CONTEXT_FLAGS_ARB,
|
||||
WGL_CONTEXT_DEBUG_BIT_ARB,
|
||||
#endif
|
||||
|
||||
0,
|
||||
};
|
||||
|
||||
HGLRC rc = wglCreateContextAttribsARB(window_handle_dc, 0, attrib);
|
||||
assert(rc && "Cannot create modern OpenGL context! OpenGL version not supported?");
|
||||
|
||||
BOOL ok = mu_wglMakeCurrent(window_handle_dc, rc);
|
||||
assert(ok && "Failed to make current OpenGL context");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user